Merge "Reformat files using Android-style imports."
diff --git a/Android.mk b/Android.mk
index bb1334a..5da9bdd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -360,6 +360,19 @@
endif # art_test_bother
+# Valgrind.
+.PHONY: valgrind-test-art-target
+valgrind-test-art-target: valgrind-test-art-target-gtest
+ $(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
+.PHONY: valgrind-test-art-target32
+valgrind-test-art-target32: valgrind-test-art-target-gtest32
+ $(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
+.PHONY: valgrind-test-art-target64
+valgrind-test-art-target64: valgrind-test-art-target-gtest64
+ $(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
########################################################################
# oat-target and oat-target-sync rules
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index fc4dd55..123bcaa 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -296,8 +296,8 @@
ifdef ART_IMT_SIZE
art_cflags += -DIMT_SIZE=$(ART_IMT_SIZE)
else
- # Default is 64
- art_cflags += -DIMT_SIZE=64
+ # Default is 43
+ art_cflags += -DIMT_SIZE=43
endif
ifeq ($(ART_HEAP_POISONING),true)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index f73657c..424aa7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -65,15 +65,18 @@
$(call dexpreopt-remove-classes.dex,$@)
# Dex file dependencies for each gtest.
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+
ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main
ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
+ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
-ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -89,14 +92,25 @@
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
-ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
+ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
$(HOST_CORE_IMAGE_default_no-pic_64) \
- $(HOST_CORE_IMAGE_default_no-pic_32) \
- $(HOST_OUT_EXECUTABLES)/patchoatd
-ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
+ $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_default_no-pic_64) \
- $(TARGET_CORE_IMAGE_default_no-pic_32) \
- $(TARGET_OUT_EXECUTABLES)/patchoatd
+ $(TARGET_CORE_IMAGE_default_no-pic_32)
+
+ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+ $(HOST_OUT_EXECUTABLES)/patchoatd
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+ $(TARGET_OUT_EXECUTABLES)/patchoatd
+
+
+ART_GTEST_dex2oat_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_dex2oat_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
@@ -157,6 +171,7 @@
cmdline/cmdline_parser_test.cc \
dexdump/dexdump_test.cc \
dexlist/dexlist_test.cc \
+ dex2oat/dex2oat_test.cc \
imgdiag/imgdiag_test.cc \
oatdump/oatdump_test.cc \
profman/profile_assistant_test.cc \
@@ -277,6 +292,7 @@
compiler/optimizing/suspend_check_test.cc \
compiler/utils/dedupe_set_test.cc \
compiler/utils/intrusive_forward_list_test.cc \
+ compiler/utils/string_reference_test.cc \
compiler/utils/swap_space_test.cc \
compiler/utils/test_dex_file_builder_test.cc \
compiler/utils/transform_array_ref_test.cc \
@@ -365,6 +381,7 @@
COMPILER_GTEST_HOST_SRC_FILES_mips := \
$(COMPILER_GTEST_COMMON_SRC_FILES_mips) \
compiler/utils/mips/assembler_mips_test.cc \
+ compiler/utils/mips/assembler_mips32r6_test.cc \
COMPILER_GTEST_HOST_SRC_FILES_mips64 := \
$(COMPILER_GTEST_COMMON_SRC_FILES_mips64) \
@@ -428,6 +445,9 @@
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST_RULES :=
+ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_VALGRIND_GTEST_RULES :=
ART_TEST_HOST_GTEST_DEPENDENCIES :=
ART_GTEST_TARGET_ANDROID_ROOT := '/system'
@@ -435,6 +455,28 @@
ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT)
endif
+ART_VALGRIND_TARGET_DEPENDENCIES := \
+ $(TARGET_OUT_EXECUTABLES)/valgrind \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_ARCH)-linux \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_ARCH)-linux.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_ARCH)-linux.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp
+
+ifdef TARGET_2ND_ARCH
+ART_VALGRIND_TARGET_DEPENDENCIES += \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so
+endif
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := valgrind-target-suppressions.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := test/valgrind-target-suppressions.txt
+LOCAL_MODULE_PATH := $(ART_TARGET_TEST_OUT)
+include $(BUILD_PREBUILT)
+
# Define a make rule for a target device gtest.
# $(1): gtest name - the name of the test we're building such as leb128_test.
# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
@@ -451,7 +493,8 @@
$$($(2)TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so \
$$($(2)TARGET_OUT_SHARED_LIBRARIES)/libopenjdkd.so \
$$(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar \
- $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar
+ $$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \
+ $$(ART_TARGET_TEST_OUT)/valgrind-target-suppressions.txt
.PHONY: $$(gtest_rule)
$$(gtest_rule): test-art-target-sync
@@ -470,7 +513,27 @@
ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule)
ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule)
+.PHONY: valgrind-$$(gtest_rule)
+valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync
+ $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
+ $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
+ $(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1)
+ $(hide) $$(call ART_TEST_SKIP,$$@) && \
+ (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+ valgrind --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
+ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
+ $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
+ && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \
+ && $$(call ART_TEST_PASSED,$$@)) \
+ || $$(call ART_TEST_FAILED,$$@))
+ $(hide) rm -f /tmp/$$@-$$$$PPID
+
+ ART_TEST_TARGET_VALGRIND_GTEST$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += valgrind-$$(gtest_rule)
+ ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule)
+ ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
+
# Clear locally defined variables.
+ valgrind_gtest_rule :=
gtest_rule :=
endef # define-art-gtest-rule-target
@@ -593,6 +656,7 @@
endif
ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
+ ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
ifdef TARGET_2ND_ARCH
$$(eval $$(call define-art-gtest-rule-target,$$(art_gtest_name),2ND_,$$(2nd_library_path)))
endif
@@ -603,8 +667,13 @@
test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES)
$$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
+.PHONY: valgrind-test-art-target-gtest-$$(art_gtest_name)
+valgrind-test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES)
+ $$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
+
# Clear locally defined variables.
ART_TEST_TARGET_GTEST_$$(art_gtest_name)_RULES :=
+ ART_TEST_TARGET_VALGRIND_GTEST_$$(art_gtest_name)_RULES :=
else # host
LOCAL_CLANG := $$(ART_HOST_CLANG)
LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS)
@@ -687,9 +756,6 @@
rule_name := $(3)test-art-$(1)-gtest$(4)
ifeq ($(3),valgrind-)
- ifneq ($(1),host)
- $$(error valgrind tests only wired up for the host)
- endif
dependencies := $$(ART_TEST_$(2)_VALGRIND_GTEST$(4)_RULES)
else
dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES)
@@ -705,9 +771,12 @@
endef # define-test-art-gtest-combination
$(eval $(call define-test-art-gtest-combination,target,TARGET,,))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,))
$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(ART_PHONY_TEST_TARGET_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(ART_PHONY_TEST_TARGET_SUFFIX)))
ifdef TARGET_2ND_ARCH
$(eval $(call define-test-art-gtest-combination,target,TARGET,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
+$(eval $(call define-test-art-gtest-combination,target,TARGET,valgrind-,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
endif
$(eval $(call define-test-art-gtest-combination,host,HOST,,))
$(eval $(call define-test-art-gtest-combination,host,HOST,valgrind-,))
@@ -739,6 +808,9 @@
ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST_RULES :=
+ART_TEST_TARGET_VALGRIND_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_VALGRIND_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_VALGRIND_GTEST_RULES :=
ART_GTEST_TARGET_ANDROID_ROOT :=
ART_GTEST_class_linker_test_DEX_DEPS :=
ART_GTEST_compiler_driver_test_DEX_DEPS :=
@@ -751,12 +823,17 @@
ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
+ART_GTEST_dex2oat_test_DEX_DEPS :=
+ART_GTEST_dex2oat_test_HOST_DEPS :=
+ART_GTEST_dex2oat_test_TARGET_DEPS :=
ART_GTEST_object_test_DEX_DEPS :=
ART_GTEST_proxy_test_DEX_DEPS :=
ART_GTEST_reflection_test_DEX_DEPS :=
ART_GTEST_stub_test_DEX_DEPS :=
ART_GTEST_transaction_test_DEX_DEPS :=
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
ART_VALGRIND_DEPENDENCIES :=
+ART_VALGRIND_TARGET_DEPENDENCIES :=
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
ART_TEST_HOST_GTEST_MainStripped_DEX :=
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 7c53e01..7ded3bf 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -30,18 +30,15 @@
bool UsuallyEquals(double expected, double actual);
// This has a gtest dependency, which is why it's in the gtest only.
- bool operator==(const TestProfilerOptions& lhs, const TestProfilerOptions& rhs) {
+ bool operator==(const ProfileSaverOptions& lhs, const ProfileSaverOptions& rhs) {
return lhs.enabled_ == rhs.enabled_ &&
- lhs.output_file_name_ == rhs.output_file_name_ &&
- lhs.period_s_ == rhs.period_s_ &&
- lhs.duration_s_ == rhs.duration_s_ &&
- lhs.interval_us_ == rhs.interval_us_ &&
- UsuallyEquals(lhs.backoff_coefficient_, rhs.backoff_coefficient_) &&
- UsuallyEquals(lhs.start_immediately_, rhs.start_immediately_) &&
- UsuallyEquals(lhs.top_k_threshold_, rhs.top_k_threshold_) &&
- UsuallyEquals(lhs.top_k_change_threshold_, rhs.top_k_change_threshold_) &&
- lhs.profile_type_ == rhs.profile_type_ &&
- lhs.max_stack_depth_ == rhs.max_stack_depth_;
+ lhs.min_save_period_ms_ == rhs.min_save_period_ms_ &&
+ lhs.save_resolved_classes_delay_ms_ == rhs.save_resolved_classes_delay_ms_ &&
+ lhs.startup_method_samples_ == rhs.startup_method_samples_ &&
+ lhs.min_methods_to_save_ == rhs.min_methods_to_save_ &&
+ lhs.min_classes_to_save_ == rhs.min_classes_to_save_ &&
+ lhs.min_notification_before_wake_ == rhs.min_notification_before_wake_ &&
+ lhs.max_notification_before_wake_ == rhs.max_notification_before_wake_;
}
bool UsuallyEquals(double expected, double actual) {
@@ -476,68 +473,21 @@
} // TEST_F
/*
-* -X-profile-*
+* -Xps-*
*/
-TEST_F(CmdlineParserTest, TestProfilerOptions) {
- /*
- * Test successes
- */
+TEST_F(CmdlineParserTest, ProfileSaverOptions) {
+ ProfileSaverOptions opt = ProfileSaverOptions(true, 1, 2, 3, 4, 5, 6, 7);
- {
- TestProfilerOptions opt;
- opt.enabled_ = true;
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xenable-profiler",
- M::ProfilerOpts);
- }
-
- {
- TestProfilerOptions opt;
- // also need to test 'enabled'
- opt.output_file_name_ = "hello_world.txt";
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xprofile-filename:hello_world.txt ",
- M::ProfilerOpts);
- }
-
- {
- TestProfilerOptions opt = TestProfilerOptions();
- // also need to test 'enabled'
- opt.output_file_name_ = "output.txt";
- opt.period_s_ = 123u;
- opt.duration_s_ = 456u;
- opt.interval_us_ = 789u;
- opt.backoff_coefficient_ = 2.0;
- opt.start_immediately_ = true;
- opt.top_k_threshold_ = 50.0;
- opt.top_k_change_threshold_ = 60.0;
- opt.profile_type_ = kProfilerMethod;
- opt.max_stack_depth_ = 1337u;
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xprofile-filename:output.txt "
- "-Xprofile-period:123 "
- "-Xprofile-duration:456 "
- "-Xprofile-interval:789 "
- "-Xprofile-backoff:2.0 "
- "-Xprofile-start-immediately "
- "-Xprofile-top-k-threshold:50.0 "
- "-Xprofile-top-k-change-threshold:60.0 "
- "-Xprofile-type:method "
- "-Xprofile-max-stack-depth:1337",
- M::ProfilerOpts);
- }
-
- {
- TestProfilerOptions opt = TestProfilerOptions();
- opt.profile_type_ = kProfilerBoundedStack;
-
- EXPECT_SINGLE_PARSE_VALUE(opt,
- "-Xprofile-type:stack",
- M::ProfilerOpts);
- }
+ EXPECT_SINGLE_PARSE_VALUE(opt,
+ "-Xjitsaveprofilinginfo "
+ "-Xps-min-save-period-ms:1 "
+ "-Xps-save-resolved-classes-delay-ms:2 "
+ "-Xps-startup-method-samples:3 "
+ "-Xps-min-methods-to-save:4 "
+ "-Xps-min-classes-to-save:5 "
+ "-Xps-min-notification-before-wake:6 "
+ "-Xps-max-notification-before-wake:7",
+ M::ProfileSaverOpts);
} // TEST_F
/* -Xexperimental:_ */
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 4797540..9b4042c 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -31,7 +31,7 @@
#include "experimental_flags.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
-#include "profiler_options.h"
+#include "jit/profile_saver_options.h"
namespace art {
@@ -633,84 +633,17 @@
static const char* Name() { return "LogVerbosity"; }
};
-// TODO: Replace with art::ProfilerOptions for the real thing.
-struct TestProfilerOptions {
- // Whether or not the applications should be profiled.
- bool enabled_;
- // Destination file name where the profiling data will be saved into.
- std::string output_file_name_;
- // Generate profile every n seconds.
- uint32_t period_s_;
- // Run profile for n seconds.
- uint32_t duration_s_;
- // Microseconds between samples.
- uint32_t interval_us_;
- // Coefficient to exponential backoff.
- double backoff_coefficient_;
- // Whether the profile should start upon app startup or be delayed by some random offset.
- bool start_immediately_;
- // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
- double top_k_threshold_;
- // How much the top K% samples needs to change in order for the app to be recompiled.
- double top_k_change_threshold_;
- // The type of profile data dumped to the disk.
- ProfileDataType profile_type_;
- // The max depth of the stack collected by the profiler
- uint32_t max_stack_depth_;
-
- TestProfilerOptions() :
- enabled_(false),
- output_file_name_(),
- period_s_(0),
- duration_s_(0),
- interval_us_(0),
- backoff_coefficient_(0),
- start_immediately_(0),
- top_k_threshold_(0),
- top_k_change_threshold_(0),
- profile_type_(ProfileDataType::kProfilerMethod),
- max_stack_depth_(0) {
- }
-
- TestProfilerOptions(const TestProfilerOptions&) = default;
- TestProfilerOptions(TestProfilerOptions&&) = default;
-};
-
-static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
- stream << "TestProfilerOptions {" << std::endl;
-
-#define PRINT_TO_STREAM(field) \
- stream << #field << ": '" << options.field << "'" << std::endl;
-
- PRINT_TO_STREAM(enabled_);
- PRINT_TO_STREAM(output_file_name_);
- PRINT_TO_STREAM(period_s_);
- PRINT_TO_STREAM(duration_s_);
- PRINT_TO_STREAM(interval_us_);
- PRINT_TO_STREAM(backoff_coefficient_);
- PRINT_TO_STREAM(start_immediately_);
- PRINT_TO_STREAM(top_k_threshold_);
- PRINT_TO_STREAM(top_k_change_threshold_);
- PRINT_TO_STREAM(profile_type_);
- PRINT_TO_STREAM(max_stack_depth_);
-
- stream << "}";
-
- return stream;
-#undef PRINT_TO_STREAM
-}
-
template <>
-struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
- using Result = CmdlineParseResult<TestProfilerOptions>;
+struct CmdlineType<ProfileSaverOptions> : CmdlineTypeParser<ProfileSaverOptions> {
+ using Result = CmdlineParseResult<ProfileSaverOptions>;
private:
using StringResult = CmdlineParseResult<std::string>;
using DoubleResult = CmdlineParseResult<double>;
template <typename T>
- static Result ParseInto(TestProfilerOptions& options,
- T TestProfilerOptions::*pField,
+ static Result ParseInto(ProfileSaverOptions& options,
+ T ProfileSaverOptions::*pField,
CmdlineParseResult<T>&& result) {
assert(pField != nullptr);
@@ -722,36 +655,6 @@
return Result::CastError(result);
}
- template <typename T>
- static Result ParseIntoRangeCheck(TestProfilerOptions& options,
- T TestProfilerOptions::*pField,
- CmdlineParseResult<T>&& result,
- T min,
- T max) {
- if (result.IsSuccess()) {
- const T& value = result.GetValue();
-
- if (value < min || value > max) {
- CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
- return Result::CastError(out_of_range);
- }
- }
-
- return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
- }
-
- static StringResult ParseStringAfterChar(const std::string& s, char c) {
- std::string parsed_value;
-
- std::string::size_type colon = s.find(c);
- if (colon == std::string::npos) {
- return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
- }
- // Add one to remove the char we were trimming until.
- parsed_value = s.substr(colon + 1);
- return StringResult::Success(parsed_value);
- }
-
static std::string RemovePrefix(const std::string& source) {
size_t prefix_idx = source.find(":");
@@ -763,87 +666,64 @@
}
public:
- Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
+ Result ParseAndAppend(const std::string& option, ProfileSaverOptions& existing) {
// Special case which doesn't include a wildcard argument definition.
// We pass-it through as-is.
- if (option == "-Xenable-profiler") {
+ if (option == "-Xjitsaveprofilinginfo") {
existing.enabled_ = true;
return Result::SuccessNoValue();
}
- // The rest of these options are always the wildcard from '-Xprofile-*'
+ // The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
- if (StartsWith(option, "filename:")) {
- CmdlineType<std::string> type_parser;
-
- return ParseInto(existing,
- &TestProfilerOptions::output_file_name_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "period:")) {
+ if (StartsWith(option, "min-save-period-ms:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::period_s_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "duration:")) {
+ &ProfileSaverOptions::min_save_period_ms_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "save-resolved-classes-delay-ms:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::duration_s_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "interval:")) {
+ &ProfileSaverOptions::save_resolved_classes_delay_ms_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "startup-method-samples:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::interval_us_,
- type_parser.Parse(suffix));
- } else if (StartsWith(option, "backoff:")) {
- CmdlineType<double> type_parser;
-
- return ParseIntoRangeCheck(existing,
- &TestProfilerOptions::backoff_coefficient_,
- type_parser.Parse(suffix),
- 1.0,
- 10.0);
-
- } else if (option == "start-immediately") {
- existing.start_immediately_ = true;
- return Result::SuccessNoValue();
- } else if (StartsWith(option, "top-k-threshold:")) {
- CmdlineType<double> type_parser;
-
- return ParseIntoRangeCheck(existing,
- &TestProfilerOptions::top_k_threshold_,
- type_parser.Parse(suffix),
- 0.0,
- 100.0);
- } else if (StartsWith(option, "top-k-change-threshold:")) {
- CmdlineType<double> type_parser;
-
- return ParseIntoRangeCheck(existing,
- &TestProfilerOptions::top_k_change_threshold_,
- type_parser.Parse(suffix),
- 0.0,
- 100.0);
- } else if (option == "type:method") {
- existing.profile_type_ = kProfilerMethod;
- return Result::SuccessNoValue();
- } else if (option == "type:stack") {
- existing.profile_type_ = kProfilerBoundedStack;
- return Result::SuccessNoValue();
- } else if (StartsWith(option, "max-stack-depth:")) {
+ &ProfileSaverOptions::startup_method_samples_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "min-methods-to-save:")) {
CmdlineType<unsigned int> type_parser;
-
return ParseInto(existing,
- &TestProfilerOptions::max_stack_depth_,
- type_parser.Parse(suffix));
+ &ProfileSaverOptions::min_methods_to_save_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "min-classes-to-save:")) {
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::min_classes_to_save_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "min-notification-before-wake:")) {
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::min_notification_before_wake_,
+ type_parser.Parse(suffix));
+ }
+ if (StartsWith(option, "max-notification-before-wake:")) {
+ CmdlineType<unsigned int> type_parser;
+ return ParseInto(existing,
+ &ProfileSaverOptions::max_notification_before_wake_,
+ type_parser.Parse(suffix));
} else {
return Result::Failure(std::string("Invalid suboption '") + option + "'");
}
}
- static const char* Name() { return "TestProfilerOptions"; }
+ static const char* Name() { return "ProfileSaverOptions"; }
static constexpr bool kCanParseBlankless = true;
};
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 9479ff3..2a81804 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -174,6 +174,7 @@
kCall,
kCallRelative, // NOTE: Actual patching is instruction_set-dependent.
kType,
+ kTypeRelative, // NOTE: Actual patching is instruction_set-dependent.
kString,
kStringRelative, // NOTE: Actual patching is instruction_set-dependent.
kDexCacheArray, // NOTE: Actual patching is instruction_set-dependent.
@@ -215,6 +216,16 @@
return patch;
}
+ static LinkerPatch RelativeTypePatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t pc_insn_offset,
+ uint32_t target_type_idx) {
+ LinkerPatch patch(literal_offset, Type::kTypeRelative, target_dex_file);
+ patch.type_idx_ = target_type_idx;
+ patch.pc_insn_offset_ = pc_insn_offset;
+ return patch;
+ }
+
static LinkerPatch StringPatch(size_t literal_offset,
const DexFile* target_dex_file,
uint32_t target_string_idx) {
@@ -258,6 +269,7 @@
bool IsPcRelative() const {
switch (GetType()) {
case Type::kCallRelative:
+ case Type::kTypeRelative:
case Type::kStringRelative:
case Type::kDexCacheArray:
return true;
@@ -274,12 +286,12 @@
}
const DexFile* TargetTypeDexFile() const {
- DCHECK(patch_type_ == Type::kType);
+ DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative);
return target_dex_file_;
}
uint32_t TargetTypeIndex() const {
- DCHECK(patch_type_ == Type::kType);
+ DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative);
return type_idx_;
}
@@ -304,7 +316,9 @@
}
uint32_t PcInsnOffset() const {
- DCHECK(patch_type_ == Type::kStringRelative || patch_type_ == Type::kDexCacheArray);
+ DCHECK(patch_type_ == Type::kTypeRelative ||
+ patch_type_ == Type::kStringRelative ||
+ patch_type_ == Type::kDexCacheArray);
return pc_insn_offset_;
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a4b4889..e52dda3 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -47,7 +47,6 @@
#include "driver/compiler_options.h"
#include "jni_internal.h"
#include "object_lock.h"
-#include "profiler.h"
#include "runtime.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 26ab281..7f2e193 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -780,9 +780,9 @@
EF_MIPS_PIC |
EF_MIPS_CPIC |
EF_MIPS_ABI_O32 |
- features->AsMipsInstructionSetFeatures()->IsR6()
- ? EF_MIPS_ARCH_32R6
- : EF_MIPS_ARCH_32R2);
+ (features->AsMipsInstructionSetFeatures()->IsR6()
+ ? EF_MIPS_ARCH_32R6
+ : EF_MIPS_ARCH_32R2));
break;
}
case kMips64: {
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 7277107..fdd14be 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -211,11 +211,13 @@
if ((insn & 0xfffffc00) == 0x91000000) {
// ADD immediate, 64-bit with imm12 == 0 (unset).
if (!kEmitCompilerReadBarrier) {
- DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType();
+ DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
+ patch.GetType() == LinkerPatch::Type::kTypeRelative) << patch.GetType();
} else {
// With the read barrier (non-baker) enabled, it could be kDexCacheArray in the
// HLoadString::LoadKind::kDexCachePcRelative case of VisitLoadString().
DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative ||
+ patch.GetType() == LinkerPatch::Type::kTypeRelative ||
patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType();
}
shift = 0u; // No shift for ADD.
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 6d1f944..672018b 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -444,7 +444,7 @@
EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(132 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+ EXPECT_EQ(133 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
}
TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 4232002..cdc7df1 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1066,6 +1066,14 @@
target_offset);
break;
}
+ case LinkerPatch::Type::kTypeRelative: {
+ uint32_t target_offset = GetTargetObjectOffset(GetTargetType(patch));
+ writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_,
+ patch,
+ offset_ + literal_offset,
+ target_offset);
+ break;
+ }
case LinkerPatch::Type::kCall: {
uint32_t target_offset = GetTargetOffset(patch);
PatchCodeAddress(&patched_code_, literal_offset, target_offset);
@@ -2187,6 +2195,7 @@
oat_dex_file.dex_file_location_checksum_,
/* oat_dex_file */ nullptr,
verify,
+ verify,
&error_msg));
if (dex_files.back() == nullptr) {
LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index e9fcfe2..d786e82 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -847,7 +847,7 @@
}
// Try index range obtained by induction variable analysis.
// Disables dynamic bce if OOB is certain.
- if (InductionRangeFitsIn(&array_range, bounds_check, index, &try_dynamic_bce)) {
+ if (InductionRangeFitsIn(&array_range, bounds_check, &try_dynamic_bce)) {
ReplaceInstruction(bounds_check, index);
return;
}
@@ -1299,33 +1299,30 @@
* parameter try_dynamic_bce is set to false if OOB is certain.
*/
bool InductionRangeFitsIn(ValueRange* array_range,
- HInstruction* context,
- HInstruction* index,
+ HBoundsCheck* context,
bool* try_dynamic_bce) {
InductionVarRange::Value v1;
InductionVarRange::Value v2;
bool needs_finite_test = false;
- if (induction_range_.GetInductionRange(context, index, &v1, &v2, &needs_finite_test)) {
- do {
- if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) &&
- v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) {
- DCHECK(v1.a_constant == 1 || v1.instruction == nullptr);
- DCHECK(v2.a_constant == 1 || v2.instruction == nullptr);
- ValueRange index_range(GetGraph()->GetArena(),
- ValueBound(v1.instruction, v1.b_constant),
- ValueBound(v2.instruction, v2.b_constant));
- // If analysis reveals a certain OOB, disable dynamic BCE.
- if (index_range.GetLower().LessThan(array_range->GetLower()) ||
- index_range.GetUpper().GreaterThan(array_range->GetUpper())) {
- *try_dynamic_bce = false;
- return false;
- }
- // Use analysis for static bce only if loop is finite.
- if (!needs_finite_test && index_range.FitsIn(array_range)) {
- return true;
- }
+ HInstruction* index = context->InputAt(0);
+ HInstruction* hint = ValueBound::HuntForDeclaration(context->InputAt(1));
+ if (induction_range_.GetInductionRange(context, index, hint, &v1, &v2, &needs_finite_test)) {
+ if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) &&
+ v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) {
+ DCHECK(v1.a_constant == 1 || v1.instruction == nullptr);
+ DCHECK(v2.a_constant == 1 || v2.instruction == nullptr);
+ ValueRange index_range(GetGraph()->GetArena(),
+ ValueBound(v1.instruction, v1.b_constant),
+ ValueBound(v2.instruction, v2.b_constant));
+ // If analysis reveals a certain OOB, disable dynamic BCE. Otherwise,
+ // use analysis for static bce only if loop is finite.
+ if (index_range.GetLower().LessThan(array_range->GetLower()) ||
+ index_range.GetUpper().GreaterThan(array_range->GetUpper())) {
+ *try_dynamic_bce = false;
+ } else if (!needs_finite_test && index_range.FitsIn(array_range)) {
+ return true;
}
- } while (induction_range_.RefineOuter(&v1, &v2));
+ }
}
return false;
}
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 6e851bf..12aa152 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -146,6 +146,13 @@
: mirror::Array::LengthOffset().Uint32Value();
}
+uint32_t CodeGenerator::GetArrayDataOffset(HArrayGet* array_get) {
+ DCHECK(array_get->GetType() == Primitive::kPrimChar || !array_get->IsStringCharAt());
+ return array_get->IsStringCharAt()
+ ? mirror::String::ValueOffset().Uint32Value()
+ : mirror::Array::DataOffset(Primitive::ComponentSize(array_get->GetType())).Uint32Value();
+}
+
bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
DCHECK_EQ((*block_order_)[current_block_index_], current);
return GetNextBlockToEmit() == FirstNonEmptyBlock(next);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 82a54d2..9364be3 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -345,6 +345,11 @@
// accessing the String's `count` field in String intrinsics.
static uint32_t GetArrayLengthOffset(HArrayLength* array_length);
+ // Helper that returns the offset of the array's data.
+ // Note: Besides the normal arrays, we also use the HArrayGet for
+ // accessing the String's `value` field in String intrinsics.
+ static uint32_t GetArrayDataOffset(HArrayGet* array_get);
+
void EmitParallelMoves(Location from1,
Location to1,
Primitive::Type type1,
@@ -449,10 +454,15 @@
SlowPathCode* slow_path) = 0;
// Check if the desired_string_load_kind is supported. If it is, return it,
- // otherwise return a fall-back info that should be used instead.
+ // otherwise return a fall-back kind that should be used instead.
virtual HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) = 0;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ virtual HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) = 0;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -496,6 +506,20 @@
LabelType label;
};
+ // Type patch info used for recording locations of required linker patches and
+ // target types. The actual type address can be absolute or PC-relative.
+ // TODO: Consider merging with MethodPatchInfo and StringPatchInfo - all these
+ // classes contain the dex file, some index and the label.
+ template <typename LabelType>
+ struct TypePatchInfo {
+ TypePatchInfo(const DexFile& df, uint32_t index)
+ : dex_file(df), type_index(index), label() { }
+
+ const DexFile& dex_file;
+ uint32_t type_index;
+ LabelType label;
+ };
+
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
size_t number_of_fpu_registers,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 6e74d08..5316d59 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -176,8 +176,11 @@
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- arm_codegen->InvokeRuntime(
- QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this);
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ arm_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -427,7 +430,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -490,8 +495,12 @@
Register reg_out = out_.AsRegister<Register>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -504,7 +513,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute the actual memory offset and store it in `index`.
Register index_reg = index_.AsRegister<Register>();
@@ -552,7 +561,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ AddConstant(index_reg, index_reg, offset_);
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -792,6 +805,9 @@
boot_image_string_patches_(StringReferenceValueComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_type_patches_(TypeReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
boot_image_address_patches_(std::less<uint32_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Always save the LR register to mimic Quick.
@@ -4284,11 +4300,11 @@
Register obj = obj_loc.AsRegister<Register>();
Location index = locations->InAt(1);
Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4302,7 +4318,6 @@
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4316,7 +4331,6 @@
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4330,7 +4344,6 @@
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4344,7 +4357,6 @@
}
case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4361,7 +4373,6 @@
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -4396,7 +4407,6 @@
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
@@ -4409,7 +4419,6 @@
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
SRegister out = out_loc.AsFpuRegister<SRegister>();
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4422,7 +4431,6 @@
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>();
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
@@ -5075,13 +5083,71 @@
__ Pop(static_cast<Register>(reg));
}
+HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadClass::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ break;
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_class_load_kind;
+}
+
void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
- InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- Location::RegisterLocation(R0),
- /* code_generator_supports_read_barrier */ true);
+ if (cls->NeedsAccessCheck()) {
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(R0),
+ /* code_generator_supports_read_barrier */ true);
+ return;
+ }
+
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
+ load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
@@ -5098,37 +5164,97 @@
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
- Register current_method = locations->InAt(0).AsRegister<Register>();
- if (cls->IsReferrersClass()) {
- DCHECK(!cls->CanCallRuntime());
- DCHECK(!cls->MustGenerateClinitCheck());
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- } else {
- // /* GcRoot<mirror::Class>[] */ out =
- // current_method.ptr_sized_fields_->dex_cache_resolved_types_
- __ LoadFromOffset(kLoadWord,
- out,
- current_method,
- ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
- // /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ bool generate_null_check = false;
+ switch (cls->GetLoadKind()) {
+ case HLoadClass::LoadKind::kReferrersClass: {
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+ GenerateGcRootFieldLoad(
+ cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+ cls->GetTypeIndex()));
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ CodeGeneratorARM::PcRelativePatchInfo* labels =
+ codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+ __ BindTrackedLabel(&labels->movw_label);
+ __ movw(out, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->movt_label);
+ __ movt(out, /* placeholder */ 0u);
+ __ BindTrackedLabel(&labels->add_pc_label);
+ __ add(out, out, ShifterOperand(PC));
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address));
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives
+ // a 128B range. To try and reduce the number of literals if we load multiple types,
+ // simply split the dex cache address to a 128B aligned base loaded from a literal
+ // and the remaining offset embedded in the load.
+ static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes.");
+ DCHECK_ALIGNED(cls->GetAddress(), 4u);
+ constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2;
+ uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits);
+ uint32_t offset = address & MaxInt<uint32_t>(offset_bits);
+ __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ // /* GcRoot<mirror::Class> */ out = *(base_address + offset)
+ GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ Register base_reg = locations->InAt(0).AsRegister<Register>();
+ HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase();
+ int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset();
+ // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset)
+ GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheViaMethod: {
+ // /* GcRoot<mirror::Class>[] */ out =
+ // current_method.ptr_sized_fields_->dex_cache_resolved_types_
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+ __ LoadFromOffset(kLoadWord,
+ out,
+ current_method,
+ ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value());
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex());
+ GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+ generate_null_check = !cls->IsInDexCache();
+ }
+ }
- if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
- DCHECK(cls->CanCallRuntime());
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
- codegen_->AddSlowPath(slow_path);
- if (!cls->IsInDexCache()) {
- __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
- }
- if (cls->MustGenerateClinitCheck()) {
- GenerateClassInitializationCheck(slow_path, out);
- } else {
- __ Bind(slow_path->GetExitLabel());
- }
+ if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ DCHECK(cls->CanCallRuntime());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
+ if (generate_null_check) {
+ __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
+ }
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
@@ -5260,6 +5386,7 @@
uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits);
uint32_t offset = address & MaxInt<uint32_t>(offset_bits);
__ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ // /* GcRoot<mirror::String> */ out = *(base_address + offset)
GenerateGcRootFieldLoad(load, out_loc, out, offset);
break;
}
@@ -5267,6 +5394,7 @@
Register base_reg = locations->InAt(0).AsRegister<Register>();
HArmDexCacheArraysBase* base = load->InputAt(0)->AsArmDexCacheArraysBase();
int32_t offset = load->GetDexCacheElementOffset() - base->GetElementOffset();
+ // /* GcRoot<mirror::String> */ out = *(dex_cache_arrays_base + offset)
GenerateGcRootFieldLoad(load, out_loc, base_reg, offset);
break;
}
@@ -6083,8 +6211,9 @@
// /* HeapReference<Object> */ ref = *(obj + offset)
Location no_index = Location::NoLocation();
+ ScaleFactor no_scale_factor = TIMES_1;
GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, offset, no_index, temp, needs_null_check);
+ instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
}
void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -6097,10 +6226,14 @@
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ ScaleFactor scale_factor = TIMES_4;
GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, data_offset, index, temp, needs_null_check);
+ instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
}
void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -6108,6 +6241,7 @@
Register obj,
uint32_t offset,
Location index,
+ ScaleFactor scale_factor,
Location temp,
bool needs_null_check) {
DCHECK(kEmitCompilerReadBarrier);
@@ -6162,17 +6296,22 @@
// The actual reference load.
if (index.IsValid()) {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- // /* HeapReference<Object> */ ref =
- // *(obj + offset + index * sizeof(HeapReference<Object>))
+ // Load types involving an "index": ArrayGet and
+ // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
+ // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
if (index.IsConstant()) {
size_t computed_offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset;
+ (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
__ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ // Handle the special case of the
+ // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use
+ // a register pair as index ("long offset"), of which only the low
+ // part contains data.
+ Register index_reg = index.IsRegisterPair()
+ ? index.AsRegisterPairLow<Register>()
+ : index.AsRegister<Register>();
+ __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor));
__ LoadFromOffset(kLoadWord, ref_reg, IP, offset);
}
} else {
@@ -6450,6 +6589,11 @@
return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
}
+CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch(
+ const DexFile& dex_file, uint32_t type_index) {
+ return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_);
+}
+
CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch(
const DexFile& dex_file, uint32_t element_offset) {
return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_);
@@ -6468,6 +6612,13 @@
[this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
}
+Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+ uint32_t type_index) {
+ return boot_image_type_patches_.GetOrCreate(
+ TypeReference(&dex_file, type_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) {
bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
@@ -6487,6 +6638,8 @@
/* MOVW+MOVT for each base */ 2u * pc_relative_dex_cache_patches_.size() +
boot_image_string_patches_.size() +
/* MOVW+MOVT for each base */ 2u * pc_relative_string_patches_.size() +
+ boot_image_type_patches_.size() +
+ /* MOVW+MOVT for each base */ 2u * pc_relative_type_patches_.size() +
boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
@@ -6562,6 +6715,35 @@
add_pc_offset,
string_index));
}
+ for (const auto& entry : boot_image_type_patches_) {
+ const TypeReference& target_type = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = literal->GetLabel()->Position();
+ linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+ target_type.dex_file,
+ target_type.type_index));
+ }
+ for (const PcRelativePatchInfo& info : pc_relative_type_patches_) {
+ const DexFile& dex_file = info.target_dex_file;
+ uint32_t type_index = info.offset_or_index;
+ DCHECK(info.add_pc_label.IsBound());
+ uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position());
+ // Add MOVW patch.
+ DCHECK(info.movw_label.IsBound());
+ uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position());
+ linker_patches->push_back(LinkerPatch::RelativeTypePatch(movw_offset,
+ &dex_file,
+ add_pc_offset,
+ type_index));
+ // Add MOVT patch.
+ DCHECK(info.movt_label.IsBound());
+ uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position());
+ linker_patches->push_back(LinkerPatch::RelativeTypePatch(movt_offset,
+ &dex_file,
+ add_pc_offset,
+ type_index));
+ }
for (const auto& entry : boot_image_address_patches_) {
DCHECK(GetCompilerOptions().GetIncludePatchInformation());
Literal* literal = entry.second;
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 0020f7b..477c4f1 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -24,6 +24,7 @@
#include "parallel_move_resolver.h"
#include "utils/arm/assembler_thumb2.h"
#include "utils/string_reference.h"
+#include "utils/type_reference.h"
namespace art {
namespace arm {
@@ -407,6 +408,11 @@
HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -419,10 +425,10 @@
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
// The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
- // and boot image strings. The only difference is the interpretation of the offset_or_index.
- // The PC-relative address is loaded with three instructions, MOVW+MOVT
- // to load the offset to base_reg and then ADD base_reg, PC. The offset is
- // calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
+ // and boot image strings/types. The only difference is the interpretation of the
+ // offset_or_index. The PC-relative address is loaded with three instructions,
+ // MOVW+MOVT to load the offset to base_reg and then ADD base_reg, PC. The offset
+ // is calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we
// currently emit these 3 instructions together, instruction scheduling could
// split this sequence apart, so we keep separate labels for each of them.
struct PcRelativePatchInfo {
@@ -431,7 +437,7 @@
PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
const DexFile& target_dex_file;
- // Either the dex cache array element offset or the string index.
+ // Either the dex cache array element offset or the string/type index.
uint32_t offset_or_index;
Label movw_label;
Label movt_label;
@@ -439,9 +445,11 @@
};
PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+ PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index);
PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
uint32_t element_offset);
Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index);
+ Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index);
Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
Literal* DeduplicateDexCacheAddressLiteral(uint32_t address);
@@ -464,6 +472,16 @@
Location index,
Location temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ uint32_t offset,
+ Location index,
+ ScaleFactor scale_factor,
+ Location temp,
+ bool needs_null_check);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -519,16 +537,6 @@
void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- Register obj,
- uint32_t offset,
- Location index,
- Location temp,
- bool needs_null_check);
-
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
@@ -536,6 +544,9 @@
using BootStringToLiteralMap = ArenaSafeMap<StringReference,
Literal*,
StringReferenceValueComparator>;
+ using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
+ Literal*,
+ TypeReferenceValueComparator>;
Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
@@ -568,6 +579,10 @@
BootStringToLiteralMap boot_image_string_patches_;
// PC-relative String patch info.
ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+ BootTypeToLiteralMap boot_image_type_patches_;
+ // PC-relative type patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
// Deduplication map for patchable boot image addresses.
Uint32ToLiteralMap boot_image_address_patches_;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5560ae2..fc2c2c3 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -237,8 +237,11 @@
codegen->EmitParallelMoves(
locations->InAt(0), LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt,
locations->InAt(1), LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt);
- arm64_codegen->InvokeRuntime(
- QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this);
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ arm64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -595,7 +598,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -658,8 +663,12 @@
Primitive::Type type = Primitive::kPrimNot;
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -677,7 +686,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute the actual memory offset and store it in `index`.
Register index_reg = RegisterFrom(index_, Primitive::kPrimInt);
@@ -725,7 +734,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ Add(index_reg, index_reg, Operand(offset_));
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -920,6 +933,9 @@
boot_image_string_patches_(StringReferenceValueComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_type_patches_(TypeReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
boot_image_address_patches_(std::less<uint32_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Save the link register (containing the return address) to mimic Quick.
@@ -2051,8 +2067,8 @@
Register obj = InputRegisterAt(instruction, 0);
LocationSummary* locations = instruction->GetLocations();
Location index = locations->InAt(1);
- uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value();
Location out = locations->Out();
+ uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
@@ -2951,75 +2967,48 @@
/* false_target */ nullptr);
}
-enum SelectVariant {
- kCsel,
- kCselFalseConst,
- kCselTrueConst,
- kFcsel,
-};
-
static inline bool IsConditionOnFloatingPointValues(HInstruction* condition) {
return condition->IsCondition() &&
Primitive::IsFloatingPointType(condition->InputAt(0)->GetType());
}
-static inline bool IsRecognizedCselConstant(HInstruction* constant) {
- if (constant->IsConstant()) {
- int64_t value = Int64FromConstant(constant->AsConstant());
- if ((value == -1) || (value == 0) || (value == 1)) {
- return true;
- }
- }
- return false;
-}
-
-static inline SelectVariant GetSelectVariant(HSelect* select) {
- if (Primitive::IsFloatingPointType(select->GetType())) {
- return kFcsel;
- } else if (IsRecognizedCselConstant(select->GetFalseValue())) {
- return kCselFalseConst;
- } else if (IsRecognizedCselConstant(select->GetTrueValue())) {
- return kCselTrueConst;
- } else {
- return kCsel;
- }
-}
-
-static inline bool HasSwappedInputs(SelectVariant variant) {
- return variant == kCselTrueConst;
-}
-
-static inline Condition GetConditionForSelect(HCondition* condition, SelectVariant variant) {
- IfCondition cond = HasSwappedInputs(variant) ? condition->GetOppositeCondition()
- : condition->GetCondition();
+static inline Condition GetConditionForSelect(HCondition* condition) {
+ IfCondition cond = condition->AsCondition()->GetCondition();
return IsConditionOnFloatingPointValues(condition) ? ARM64FPCondition(cond, condition->IsGtBias())
: ARM64Condition(cond);
}
void LocationsBuilderARM64::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
- switch (GetSelectVariant(select)) {
- case kCsel:
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
- break;
- case kCselFalseConst:
- locations->SetInAt(0, Location::ConstantLocation(select->InputAt(0)->AsConstant()));
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
- break;
- case kCselTrueConst:
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::ConstantLocation(select->InputAt(1)->AsConstant()));
- locations->SetOut(Location::RequiresRegister());
- break;
- case kFcsel:
- locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister());
- break;
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ } else {
+ HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
+ HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
+ bool is_true_value_constant = cst_true_value != nullptr;
+ bool is_false_value_constant = cst_false_value != nullptr;
+ // Ask VIXL whether we should synthesize constants in registers.
+ // We give an arbitrary register to VIXL when dealing with non-constant inputs.
+ Operand true_op = is_true_value_constant ?
+ Operand(Int64FromConstant(cst_true_value)) : Operand(x1);
+ Operand false_op = is_false_value_constant ?
+ Operand(Int64FromConstant(cst_false_value)) : Operand(x2);
+ bool true_value_in_register = false;
+ bool false_value_in_register = false;
+ MacroAssembler::GetCselSynthesisInformation(
+ x0, true_op, false_op, &true_value_in_register, &false_value_in_register);
+ true_value_in_register |= !is_true_value_constant;
+ false_value_in_register |= !is_false_value_constant;
+
+ locations->SetInAt(1, true_value_in_register ? Location::RequiresRegister()
+ : Location::ConstantLocation(cst_true_value));
+ locations->SetInAt(0, false_value_in_register ? Location::RequiresRegister()
+ : Location::ConstantLocation(cst_false_value));
+ locations->SetOut(Location::RequiresRegister());
}
+
if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
locations->SetInAt(2, Location::RequiresRegister());
}
@@ -3027,45 +3016,34 @@
void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
HInstruction* cond = select->GetCondition();
- SelectVariant variant = GetSelectVariant(select);
Condition csel_cond;
if (IsBooleanValueOrMaterializedCondition(cond)) {
if (cond->IsCondition() && cond->GetNext() == select) {
- // Condition codes set from previous instruction.
- csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+ // Use the condition flags set by the previous instruction.
+ csel_cond = GetConditionForSelect(cond->AsCondition());
} else {
__ Cmp(InputRegisterAt(select, 2), 0);
- csel_cond = HasSwappedInputs(variant) ? eq : ne;
+ csel_cond = ne;
}
} else if (IsConditionOnFloatingPointValues(cond)) {
GenerateFcmp(cond);
- csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+ csel_cond = GetConditionForSelect(cond->AsCondition());
} else {
__ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
- csel_cond = GetConditionForSelect(cond->AsCondition(), variant);
+ csel_cond = GetConditionForSelect(cond->AsCondition());
}
- switch (variant) {
- case kCsel:
- case kCselFalseConst:
- __ Csel(OutputRegister(select),
- InputRegisterAt(select, 1),
- InputOperandAt(select, 0),
- csel_cond);
- break;
- case kCselTrueConst:
- __ Csel(OutputRegister(select),
- InputRegisterAt(select, 0),
- InputOperandAt(select, 1),
- csel_cond);
- break;
- case kFcsel:
- __ Fcsel(OutputFPRegister(select),
- InputFPRegisterAt(select, 1),
- InputFPRegisterAt(select, 0),
- csel_cond);
- break;
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ __ Fcsel(OutputFPRegister(select),
+ InputFPRegisterAt(select, 1),
+ InputFPRegisterAt(select, 0),
+ csel_cond);
+ } else {
+ __ Csel(OutputRegister(select),
+ InputOperandAt(select, 1),
+ InputOperandAt(select, 0),
+ csel_cond);
}
}
@@ -3761,6 +3739,12 @@
return NewPcRelativePatch(dex_file, string_index, adrp_label, &pc_relative_string_patches_);
}
+vixl::Label* CodeGeneratorARM64::NewPcRelativeTypePatch(const DexFile& dex_file,
+ uint32_t type_index,
+ vixl::Label* adrp_label) {
+ return NewPcRelativePatch(dex_file, type_index, adrp_label, &pc_relative_type_patches_);
+}
+
vixl::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
uint32_t element_offset,
vixl::Label* adrp_label) {
@@ -3787,6 +3771,13 @@
[this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
}
+vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageTypeLiteral(
+ const DexFile& dex_file, uint32_t type_index) {
+ return boot_image_type_patches_.GetOrCreate(
+ TypeReference(&dex_file, type_index),
+ [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
+}
+
vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageAddressLiteral(uint64_t address) {
bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
@@ -3806,6 +3797,8 @@
pc_relative_dex_cache_patches_.size() +
boot_image_string_patches_.size() +
pc_relative_string_patches_.size() +
+ boot_image_type_patches_.size() +
+ pc_relative_type_patches_.size() +
boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
@@ -3846,6 +3839,19 @@
info.pc_insn_label->location(),
info.offset_or_index));
}
+ for (const auto& entry : boot_image_type_patches_) {
+ const TypeReference& target_type = entry.first;
+ vixl::Literal<uint32_t>* literal = entry.second;
+ linker_patches->push_back(LinkerPatch::TypePatch(literal->offset(),
+ target_type.dex_file,
+ target_type.type_index));
+ }
+ for (const PcRelativePatchInfo& info : pc_relative_type_patches_) {
+ linker_patches->push_back(LinkerPatch::RelativeTypePatch(info.label.location(),
+ &info.target_dex_file,
+ info.pc_insn_label->location(),
+ info.offset_or_index));
+ }
for (const auto& entry : boot_image_address_patches_) {
DCHECK(GetCompilerOptions().GetIncludePatchInformation());
vixl::Literal<uint32_t>* literal = entry.second;
@@ -3911,13 +3917,63 @@
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
+HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadClass::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ break;
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_class_load_kind;
+}
+
void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
- InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassLocationSummary(
- cls,
- LocationFrom(calling_convention.GetRegisterAt(0)),
- LocationFrom(vixl::x0),
- /* code_generator_supports_read_barrier */ true);
+ if (cls->NeedsAccessCheck()) {
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ LocationFrom(calling_convention.GetRegisterAt(0)),
+ LocationFrom(vixl::x0),
+ /* code_generator_supports_read_barrier */ true);
+ return;
+ }
+
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) {
@@ -3933,35 +3989,111 @@
Location out_loc = cls->GetLocations()->Out();
Register out = OutputRegister(cls);
- Register current_method = InputRegisterAt(cls, 0);
- if (cls->IsReferrersClass()) {
- DCHECK(!cls->CanCallRuntime());
- DCHECK(!cls->MustGenerateClinitCheck());
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
- } else {
- MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize);
- // /* GcRoot<mirror::Class>[] */ out =
- // current_method.ptr_sized_fields_->dex_cache_resolved_types_
- __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
- // /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(
- cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
- if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
- DCHECK(cls->CanCallRuntime());
- SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
- codegen_->AddSlowPath(slow_path);
- if (!cls->IsInDexCache()) {
- __ Cbz(out, slow_path->GetEntryLabel());
+ bool generate_null_check = false;
+ switch (cls->GetLoadKind()) {
+ case HLoadClass::LoadKind::kReferrersClass: {
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ Register current_method = InputRegisterAt(cls, 0);
+ GenerateGcRootFieldLoad(
+ cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value());
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+ cls->GetTypeIndex()));
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ // Add ADRP with its PC-relative type patch.
+ const DexFile& dex_file = cls->GetDexFile();
+ uint32_t type_index = cls->GetTypeIndex();
+ vixl::Label* adrp_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(adrp_label);
+ __ adrp(out.X(), /* offset placeholder */ 0);
}
- if (cls->MustGenerateClinitCheck()) {
- GenerateClassInitializationCheck(slow_path, out);
- } else {
- __ Bind(slow_path->GetExitLabel());
+ // Add ADD with its PC-relative type patch.
+ vixl::Label* add_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index, adrp_label);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(add_label);
+ __ add(out.X(), out.X(), Operand(/* offset placeholder */ 0));
}
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress()));
+ __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress()));
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(cls->GetAddress(), 0u);
+ // LDR immediate has a 12-bit offset multiplied by the size and for 32-bit loads
+ // that gives a 16KiB range. To try and reduce the number of literals if we load
+ // multiple types, simply split the dex cache address to a 16KiB aligned base
+ // loaded from a literal and the remaining offset embedded in the load.
+ static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes.");
+ DCHECK_ALIGNED(cls->GetAddress(), 4u);
+ constexpr size_t offset_bits = /* encoded bits */ 12 + /* scale */ 2;
+ uint64_t base_address = cls->GetAddress() & ~MaxInt<uint64_t>(offset_bits);
+ uint32_t offset = cls->GetAddress() & MaxInt<uint64_t>(offset_bits);
+ __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ // /* GcRoot<mirror::Class> */ out = *(base_address + offset)
+ GenerateGcRootFieldLoad(cls, out_loc, out.X(), offset);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ // Add ADRP with its PC-relative DexCache access patch.
+ const DexFile& dex_file = cls->GetDexFile();
+ uint32_t element_offset = cls->GetDexCacheElementOffset();
+ vixl::Label* adrp_label = codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset);
+ {
+ vixl::SingleEmissionCheckScope guard(GetVIXLAssembler());
+ __ Bind(adrp_label);
+ __ adrp(out.X(), /* offset placeholder */ 0);
+ }
+ // Add LDR with its PC-relative DexCache access patch.
+ vixl::Label* ldr_label =
+ codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
+ // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */
+ GenerateGcRootFieldLoad(cls, out_loc, out.X(), /* offset placeholder */ 0, ldr_label);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheViaMethod: {
+ MemberOffset resolved_types_offset =
+ ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize);
+ // /* GcRoot<mirror::Class>[] */ out =
+ // current_method.ptr_sized_fields_->dex_cache_resolved_types_
+ Register current_method = InputRegisterAt(cls, 0);
+ __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value()));
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ GenerateGcRootFieldLoad(
+ cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ }
+
+ if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ DCHECK(cls->CanCallRuntime());
+ SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
+ if (generate_null_check) {
+ __ Cbz(out, slow_path->GetEntryLabel());
+ }
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
@@ -4082,6 +4214,7 @@
uint64_t base_address = load->GetAddress() & ~MaxInt<uint64_t>(offset_bits);
uint32_t offset = load->GetAddress() & MaxInt<uint64_t>(offset_bits);
__ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address));
+ // /* GcRoot<mirror::String> */ out = *(base_address + offset)
GenerateGcRootFieldLoad(load, out_loc, out.X(), offset);
break;
}
@@ -4098,6 +4231,7 @@
// Add LDR with its PC-relative DexCache access patch.
vixl::Label* ldr_label =
codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label);
+ // /* GcRoot<mirror::String> */ out = *(base_address + offset) /* PC-relative */
GenerateGcRootFieldLoad(load, out_loc, out.X(), /* offset placeholder */ 0, ldr_label);
break;
}
@@ -4976,8 +5110,16 @@
// /* HeapReference<Object> */ ref = *(obj + offset)
Location no_index = Location::NoLocation();
- GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, offset, no_index, temp, needs_null_check, use_load_acquire);
+ size_t no_scale_factor = 0U;
+ GenerateReferenceLoadWithBakerReadBarrier(instruction,
+ ref,
+ obj,
+ offset,
+ no_index,
+ no_scale_factor,
+ temp,
+ needs_null_check,
+ use_load_acquire);
}
void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -4994,10 +5136,21 @@
// never use Load-Acquire instructions on ARM64.
const bool use_load_acquire = false;
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
- GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, data_offset, index, temp, needs_null_check, use_load_acquire);
+ size_t scale_factor = Primitive::ComponentSizeShift(Primitive::kPrimNot);
+ GenerateReferenceLoadWithBakerReadBarrier(instruction,
+ ref,
+ obj,
+ data_offset,
+ index,
+ scale_factor,
+ temp,
+ needs_null_check,
+ use_load_acquire);
}
void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -5005,15 +5158,16 @@
vixl::Register obj,
uint32_t offset,
Location index,
+ size_t scale_factor,
Register temp,
bool needs_null_check,
bool use_load_acquire) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
- // If `index` is a valid location, then we are emitting an array
- // load, so we shouldn't be using a Load Acquire instruction.
- // In other words: `index.IsValid()` => `!use_load_acquire`.
- DCHECK(!index.IsValid() || !use_load_acquire);
+ // If we are emitting an array load, we should not be using a
+ // Load Acquire instruction. In other words:
+ // `instruction->IsArrayGet()` => `!use_load_acquire`.
+ DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
@@ -5070,20 +5224,33 @@
// The actual reference load.
if (index.IsValid()) {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- // /* HeapReference<Object> */ ref =
- // *(obj + offset + index * sizeof(HeapReference<Object>))
- const size_t shift_amount = Primitive::ComponentSizeShift(type);
- if (index.IsConstant()) {
- uint32_t computed_offset = offset + (Int64ConstantFrom(index) << shift_amount);
- Load(type, ref_reg, HeapOperand(obj, computed_offset));
+ // Load types involving an "index".
+ if (use_load_acquire) {
+ // UnsafeGetObjectVolatile intrinsic case.
+ // Register `index` is not an index in an object array, but an
+ // offset to an object reference field within object `obj`.
+ DCHECK(instruction->IsInvoke()) << instruction->DebugName();
+ DCHECK(instruction->GetLocations()->Intrinsified());
+ DCHECK(instruction->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)
+ << instruction->AsInvoke()->GetIntrinsic();
+ DCHECK_EQ(offset, 0U);
+ DCHECK_EQ(scale_factor, 0U);
+ DCHECK_EQ(needs_null_check, 0U);
+ // /* HeapReference<Object> */ ref = *(obj + index)
+ MemOperand field = HeapOperand(obj, XRegisterFrom(index));
+ LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false);
} else {
- temp2 = temps.AcquireW();
- __ Add(temp2, obj, offset);
- Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, shift_amount));
- temps.Release(temp2);
+ // ArrayGet and UnsafeGetObject intrinsics cases.
+ // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
+ if (index.IsConstant()) {
+ uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor);
+ Load(type, ref_reg, HeapOperand(obj, computed_offset));
+ } else {
+ temp2 = temps.AcquireW();
+ __ Add(temp2, obj, offset);
+ Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, scale_factor));
+ temps.Release(temp2);
+ }
}
} else {
// /* HeapReference<Object> */ ref = *(obj + offset)
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 422963e..d4bf695 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -26,6 +26,7 @@
#include "parallel_move_resolver.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/string_reference.h"
+#include "utils/type_reference.h"
#include "vixl/a64/disasm-a64.h"
#include "vixl/a64/macro-assembler-a64.h"
@@ -460,6 +461,11 @@
HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -482,6 +488,14 @@
uint32_t string_index,
vixl::Label* adrp_label = nullptr);
+ // Add a new PC-relative type patch for an instruction and return the label
+ // to be bound before the instruction. The instruction will be either the
+ // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing
+ // to the associated ADRP patch label).
+ vixl::Label* NewPcRelativeTypePatch(const DexFile& dex_file,
+ uint32_t type_index,
+ vixl::Label* adrp_label = nullptr);
+
// Add a new PC-relative dex cache array patch for an instruction and return
// the label to be bound before the instruction. The instruction will be
// either the ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label`
@@ -492,6 +506,8 @@
vixl::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
uint32_t string_index);
+ vixl::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+ uint32_t type_index);
vixl::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address);
vixl::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address);
@@ -515,6 +531,17 @@
Location index,
vixl::Register temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ vixl::Register obj,
+ uint32_t offset,
+ Location index,
+ size_t scale_factor,
+ vixl::Register temp,
+ bool needs_null_check,
+ bool use_load_acquire);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -570,17 +597,6 @@
void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl::Register obj,
- uint32_t offset,
- Location index,
- vixl::Register temp,
- bool needs_null_check,
- bool use_load_acquire);
-
using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>;
using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::Literal<uint32_t>*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference,
@@ -589,6 +605,9 @@
using BootStringToLiteralMap = ArenaSafeMap<StringReference,
vixl::Literal<uint32_t>*,
StringReferenceValueComparator>;
+ using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
+ vixl::Literal<uint32_t>*,
+ TypeReferenceValueComparator>;
vixl::Literal<uint32_t>* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
vixl::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value);
@@ -598,13 +617,14 @@
vixl::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method);
// The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
- // and boot image strings. The only difference is the interpretation of the offset_or_index.
+ // and boot image strings/types. The only difference is the interpretation of the
+ // offset_or_index.
struct PcRelativePatchInfo {
PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
: target_dex_file(dex_file), offset_or_index(off_or_idx), label(), pc_insn_label() { }
const DexFile& target_dex_file;
- // Either the dex cache array element offset or the string index.
+ // Either the dex cache array element offset or the string/type index.
uint32_t offset_or_index;
vixl::Label label;
vixl::Label* pc_insn_label;
@@ -646,6 +666,10 @@
BootStringToLiteralMap boot_image_string_patches_;
// PC-relative String patch info.
ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+ BootTypeToLiteralMap boot_image_type_patches_;
+ // PC-relative type patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
// Deduplication map for patchable boot image addresses.
Uint32ToLiteralMap boot_image_address_patches_;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index ed0767e..4d44c18 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -166,11 +166,15 @@
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ mips_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this,
IsDirectEntrypoint(kQuickThrowArrayBounds));
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -1635,11 +1639,11 @@
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsRegister<Register>();
Location index = locations->InAt(1);
- Primitive::Type type = instruction->GetType();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+ Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1653,7 +1657,6 @@
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1667,7 +1670,6 @@
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1682,7 +1684,6 @@
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1699,7 +1700,6 @@
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1714,7 +1714,6 @@
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
Register out = locations->Out().AsRegisterPairLow<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1729,7 +1728,6 @@
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
FRegister out = locations->Out().AsFpuRegister<FRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1744,7 +1742,6 @@
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
FRegister out = locations->Out().AsFpuRegister<FRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -3767,6 +3764,13 @@
return HLoadString::LoadKind::kDexCacheViaMethod;
}
+HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) {
+ DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass);
+ // TODO: Implement other kinds.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
@@ -4724,7 +4728,6 @@
Primitive::Type input_type = conversion->GetInputType();
bool has_sign_extension = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2();
bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
- bool fpu_32bit = codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint();
DCHECK_NE(input_type, result_type);
@@ -4733,7 +4736,9 @@
Register dst_low = locations->Out().AsRegisterPairLow<Register>();
Register src = locations->InAt(0).AsRegister<Register>();
- __ Move(dst_low, src);
+ if (dst_low != src) {
+ __ Move(dst_low, src);
+ }
__ Sra(dst_high, src, 31);
} else if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
Register dst = locations->Out().AsRegister<Register>();
@@ -4762,7 +4767,9 @@
}
break;
case Primitive::kPrimInt:
- __ Move(dst, src);
+ if (dst != src) {
+ __ Move(dst, src);
+ }
break;
default:
@@ -4919,11 +4926,7 @@
uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min());
__ LoadConst32(TMP, High32Bits(min_val));
__ Mtc1(ZERO, FTMP);
- if (fpu_32bit) {
- __ Mtc1(TMP, static_cast<FRegister>(FTMP + 1));
- } else {
- __ Mthc1(TMP, FTMP);
- }
+ __ MoveToFpuHigh(TMP, FTMP);
}
if (isR6) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 8c0bae6..6487f28 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -349,6 +349,11 @@
HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 8c73e35..2e78884 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -127,10 +127,14 @@
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ mips64_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -918,13 +922,13 @@
// TODO: review; anything else?
- // TODO: remove once all the issues with register saving/restoring are sorted out.
- for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) {
- blocked_core_registers_[kCoreCalleeSaves[i]] = true;
- }
-
- for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
- blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
+ if (GetGraph()->IsDebuggable()) {
+ // Stubs do not save callee-save floating point registers. If the graph
+ // is debuggable, we need to deal with these registers differently. For
+ // now, just block them.
+ for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) {
+ blocked_fpu_registers_[kFpuCalleeSaves[i]] = true;
+ }
}
}
@@ -1289,11 +1293,11 @@
LocationSummary* locations = instruction->GetLocations();
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
Location index = locations->InAt(1);
- Primitive::Type type = instruction->GetType();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+ Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1307,7 +1311,6 @@
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1321,7 +1324,6 @@
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1336,7 +1338,6 @@
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1353,7 +1354,6 @@
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
LoadOperandType load_type = (type == Primitive::kPrimNot) ? kLoadUnsignedWord : kLoadWord;
if (index.IsConstant()) {
@@ -1369,7 +1369,6 @@
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1384,7 +1383,6 @@
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1399,7 +1397,6 @@
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -2984,19 +2981,6 @@
}
HandleInvoke(invoke);
-
- // While SetupBlockedRegisters() blocks registers S2-S8 due to their
- // clobbering somewhere else, reduce further register pressure by avoiding
- // allocation of a register for the current method pointer like on x86 baseline.
- // TODO: remove this once all the issues with register saving/restoring are
- // sorted out.
- if (invoke->HasCurrentMethodInput()) {
- LocationSummary* locations = invoke->GetLocations();
- Location location = locations->InAt(invoke->GetSpecialInputIndex());
- if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) {
- locations->SetInAt(invoke->GetSpecialInputIndex(), Location::NoLocation());
- }
- }
}
static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) {
@@ -3014,6 +2998,13 @@
return HLoadString::LoadKind::kDexCacheViaMethod;
}
+HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) {
+ DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass);
+ // TODO: Implement other kinds.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+}
+
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
MethodReference target_method ATTRIBUTE_UNUSED) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 9785a2e..4b462cc 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -340,6 +340,11 @@
HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8c643a0..1261619 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -148,10 +148,14 @@
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ x86_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -444,7 +448,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -507,8 +513,12 @@
Register reg_out = out_.AsRegister<Register>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -521,7 +531,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute the actual memory offset and store it in `index`.
Register index_reg = index_.AsRegister<Register>();
@@ -569,7 +579,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ AddImmediate(index_reg, Immediate(offset_));
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -804,6 +818,7 @@
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
constant_area_start_(-1),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
method_address_offset_(-1) {
@@ -4445,6 +4460,11 @@
__ Bind(&string_patches_.back().label);
}
+void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) {
+ type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex());
+ __ Bind(&type_patches_.back().label);
+}
+
Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
uint32_t element_offset) {
// Add the patch entry and bind its label at the end of the instruction.
@@ -4459,7 +4479,8 @@
relative_call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
simple_patches_.size() +
- string_patches_.size();
+ string_patches_.size() +
+ type_patches_.size();
linker_patches->reserve(size);
// The label points to the end of the "movl" insn but the literal offset for method
// patch needs to point to the embedded constant which occupies the last 4 bytes.
@@ -4495,6 +4516,13 @@
GetMethodAddressOffset(),
info.string_index));
}
+ for (const TypePatchInfo<Label>& info : type_patches_) {
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RelativeTypePatch(literal_offset,
+ &info.dex_file,
+ GetMethodAddressOffset(),
+ info.type_index));
+ }
} else {
for (const StringPatchInfo<Label>& info : string_patches_) {
uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
@@ -4502,6 +4530,12 @@
&info.dex_file,
info.string_index));
}
+ for (const TypePatchInfo<Label>& info : type_patches_) {
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+ &info.dex_file,
+ info.type_index));
+ }
}
}
@@ -5033,11 +5067,11 @@
Register obj = obj_loc.AsRegister<Register>();
Location index = locations->InAt(1);
Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movzxb(out, Address(obj,
@@ -5049,7 +5083,6 @@
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movsxb(out, Address(obj,
@@ -5061,7 +5094,6 @@
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movsxw(out, Address(obj,
@@ -5073,7 +5105,6 @@
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movzxw(out, Address(obj,
@@ -5085,7 +5116,6 @@
}
case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movl(out, Address(obj,
@@ -5100,7 +5130,6 @@
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -5134,7 +5163,6 @@
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
DCHECK_NE(obj, out_loc.AsRegisterPairLow<Register>());
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
@@ -5152,7 +5180,6 @@
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movss(out, Address(obj,
@@ -5164,7 +5191,6 @@
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movsd(out, Address(obj,
@@ -5866,13 +5892,72 @@
__ popl(static_cast<Register>(reg));
}
+HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadClass::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ FALLTHROUGH_INTENDED;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT.
+ // We disable pc-relative load when there is an irreducible loop, as the optimization
+ // is incompatible with it.
+ // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
+ // with irreducible loops.
+ if (GetGraph()->HasIrreducibleLoops()) {
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ }
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ break;
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_class_load_kind;
+}
+
void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
- InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- Location::RegisterLocation(EAX),
- /* code_generator_supports_read_barrier */ true);
+ if (cls->NeedsAccessCheck()) {
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(EAX),
+ /* code_generator_supports_read_barrier */ true);
+ return;
+ }
+
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod ||
+ load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
@@ -5889,39 +5974,86 @@
Location out_loc = locations->Out();
Register out = out_loc.AsRegister<Register>();
- Register current_method = locations->InAt(0).AsRegister<Register>();
- if (cls->IsReferrersClass()) {
- DCHECK(!cls->CanCallRuntime());
- DCHECK(!cls->MustGenerateClinitCheck());
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
- } else {
- // /* GcRoot<mirror::Class>[] */ out =
- // current_method.ptr_sized_fields_->dex_cache_resolved_types_
- __ movl(out, Address(current_method,
- ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
- // /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(
- cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+ bool generate_null_check = false;
+ switch (cls->GetLoadKind()) {
+ case HLoadClass::LoadKind::kReferrersClass: {
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ movl(out, Immediate(/* placeholder */ 0));
+ codegen_->RecordTypePatch(cls);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ Register method_address = locations->InAt(0).AsRegister<Register>();
+ __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset));
+ codegen_->RecordTypePatch(cls);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ __ movl(out, Immediate(address));
+ codegen_->RecordSimplePatch();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ // /* GcRoot<mirror::Class> */ out = *address
+ GenerateGcRootFieldLoad(cls, out_loc, Address::Absolute(address));
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ Register base_reg = locations->InAt(0).AsRegister<Register>();
+ uint32_t offset = cls->GetDexCacheElementOffset();
+ Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset);
+ // /* GcRoot<mirror::Class> */ out = *(base + offset) /* PC-relative */
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheViaMethod: {
+ // /* GcRoot<mirror::Class>[] */ out =
+ // current_method.ptr_sized_fields_->dex_cache_resolved_types_
+ Register current_method = locations->InAt(0).AsRegister<Register>();
+ __ movl(out, Address(current_method,
+ ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value()));
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ }
- if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
- DCHECK(cls->CanCallRuntime());
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
- codegen_->AddSlowPath(slow_path);
+ if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ DCHECK(cls->CanCallRuntime());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
- if (!cls->IsInDexCache()) {
- __ testl(out, out);
- __ j(kEqual, slow_path->GetEntryLabel());
- }
+ if (generate_null_check) {
+ __ testl(out, out);
+ __ j(kEqual, slow_path->GetEntryLabel());
+ }
- if (cls->MustGenerateClinitCheck()) {
- GenerateClassInitializationCheck(slow_path, out);
- } else {
- __ Bind(slow_path->GetExitLabel());
- }
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
@@ -6038,6 +6170,7 @@
case HLoadString::LoadKind::kDexCacheAddress: {
DCHECK_NE(load->GetAddress(), 0u);
uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ // /* GcRoot<mirror::String> */ out = *address
GenerateGcRootFieldLoad(load, out_loc, Address::Absolute(address));
break;
}
@@ -6045,6 +6178,7 @@
Register base_reg = locations->InAt(0).AsRegister<Register>();
uint32_t offset = load->GetDexCacheElementOffset();
Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset);
+ // /* GcRoot<mirror::String> */ out = *(base + offset) /* PC-relative */
GenerateGcRootFieldLoad(
load, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label);
break;
@@ -6846,6 +6980,9 @@
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
Address src = index.IsConstant() ?
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 98dc8ca..2a9fb80 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -391,6 +391,11 @@
HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -405,6 +410,7 @@
void RecordSimplePatch();
void RecordStringPatch(HLoadString* load_string);
+ void RecordTypePatch(HLoadClass* load_class);
Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
@@ -485,6 +491,14 @@
Location index,
Location temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ const Address& src,
+ Location temp,
+ bool needs_null_check);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -555,15 +569,6 @@
static constexpr int32_t kDummy32BitOffset = 256;
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- Register obj,
- const Address& src,
- Location temp,
- bool needs_null_check);
-
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
struct PcRelativeDexCacheAccessInfo {
@@ -594,6 +599,8 @@
ArenaDeque<Label> simple_patches_;
// String patch locations.
ArenaDeque<StringPatchInfo<Label>> string_patches_;
+ // Type patch locations.
+ ArenaDeque<TypePatchInfo<Label>> type_patches_;
// Offset to the start of the constant area in the assembled code.
// Used for fixups to the constant area.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 72de3e6..5e30203 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -204,10 +204,14 @@
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ x86_64_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -465,7 +469,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -528,8 +534,12 @@
CpuRegister reg_out = out_.AsRegister<CpuRegister>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.AsRegister())) << out_;
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -542,7 +552,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute real offset and store it in index_.
Register index_reg = index_.AsRegister<CpuRegister>().AsRegister();
@@ -590,7 +600,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ AddImmediate(CpuRegister(index_reg), Immediate(offset_));
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -894,6 +908,11 @@
__ Bind(&string_patches_.back().label);
}
+void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) {
+ type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex());
+ __ Bind(&type_patches_.back().label);
+}
+
Label* CodeGeneratorX86_64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
uint32_t element_offset) {
// Add a patch entry and return the label.
@@ -908,7 +927,8 @@
relative_call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
simple_patches_.size() +
- string_patches_.size();
+ string_patches_.size() +
+ type_patches_.size();
linker_patches->reserve(size);
// The label points to the end of the "movl" insn but the literal offset for method
// patch needs to point to the embedded constant which occupies the last 4 bytes.
@@ -944,6 +964,14 @@
info.label.Position(),
info.string_index));
}
+ for (const TypePatchInfo<Label>& info : type_patches_) {
+ // These are always PC-relative, see GetSupportedLoadClassKind().
+ uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment;
+ linker_patches->push_back(LinkerPatch::RelativeTypePatch(literal_offset,
+ &info.dex_file,
+ info.label.Position(),
+ info.type_index));
+ }
}
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -1023,6 +1051,7 @@
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister));
}
@@ -4533,11 +4562,11 @@
CpuRegister obj = obj_loc.AsRegister<CpuRegister>();
Location index = locations->InAt(1);
Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movzxb(out, Address(obj,
@@ -4549,7 +4578,6 @@
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movsxb(out, Address(obj,
@@ -4561,7 +4589,6 @@
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movsxw(out, Address(obj,
@@ -4573,7 +4600,6 @@
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movzxw(out, Address(obj,
@@ -4585,7 +4611,6 @@
}
case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movl(out, Address(obj,
@@ -4600,7 +4625,6 @@
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -4634,7 +4658,6 @@
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movq(out, Address(obj,
@@ -4646,7 +4669,6 @@
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movss(out, Address(obj,
@@ -4658,7 +4680,6 @@
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movsd(out, Address(obj,
@@ -5310,13 +5331,64 @@
// No need for memory fence, thanks to the x86-64 memory model.
}
+HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadClass::LoadKind::kBootImageAddress:
+ // TODO: Implement for read barrier.
+ return HLoadClass::LoadKind::kDexCacheViaMethod;
+ default:
+ break;
+ }
+ }
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ // We prefer the always-available RIP-relative address for the x86-64 boot image.
+ return HLoadClass::LoadKind::kBootImageLinkTimePcRelative;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ break;
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ return desired_class_load_kind;
+}
+
void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
- InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- Location::RegisterLocation(RAX),
- /* code_generator_supports_read_barrier */ true);
+ if (cls->NeedsAccessCheck()) {
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(RAX),
+ /* code_generator_supports_read_barrier */ true);
+ return;
+ }
+
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
@@ -5333,37 +5405,86 @@
Location out_loc = locations->Out();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
- CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
- if (cls->IsReferrersClass()) {
- DCHECK(!cls->CanCallRuntime());
- DCHECK(!cls->MustGenerateClinitCheck());
- // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
- GenerateGcRootFieldLoad(
- cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
- } else {
- // /* GcRoot<mirror::Class>[] */ out =
- // current_method.ptr_sized_fields_->dex_cache_resolved_types_
- __ movq(out, Address(current_method,
- ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
- // /* GcRoot<mirror::Class> */ out = out[type_index]
- GenerateGcRootFieldLoad(
- cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
-
- if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
- DCHECK(cls->CanCallRuntime());
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
- codegen_->AddSlowPath(slow_path);
- if (!cls->IsInDexCache()) {
- __ testl(out, out);
- __ j(kEqual, slow_path->GetEntryLabel());
- }
- if (cls->MustGenerateClinitCheck()) {
- GenerateClassInitializationCheck(slow_path, out);
+ bool generate_null_check = false;
+ switch (cls->GetLoadKind()) {
+ case HLoadClass::LoadKind::kReferrersClass: {
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value()));
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false));
+ codegen_->RecordTypePatch(cls);
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ __ movl(out, Immediate(address)); // Zero-extended.
+ codegen_->RecordSimplePatch();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheAddress: {
+ DCHECK_NE(cls->GetAddress(), 0u);
+ // /* GcRoot<mirror::Class> */ out = *address
+ if (IsUint<32>(cls->GetAddress())) {
+ Address address = Address::Absolute(cls->GetAddress(), /* no_rip */ true);
+ GenerateGcRootFieldLoad(cls, out_loc, address);
} else {
- __ Bind(slow_path->GetExitLabel());
+ // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address).
+ __ movq(out, Immediate(cls->GetAddress()));
+ GenerateGcRootFieldLoad(cls, out_loc, Address(out, 0));
}
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ uint32_t offset = cls->GetDexCacheElementOffset();
+ Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset);
+ Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
+ /* no_rip */ false);
+ // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */
+ GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheViaMethod: {
+ // /* GcRoot<mirror::Class>[] */ out =
+ // current_method.ptr_sized_fields_->dex_cache_resolved_types_
+ CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>();
+ __ movq(out,
+ Address(current_method,
+ ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value()));
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ GenerateGcRootFieldLoad(
+ cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << cls->GetLoadKind();
+ UNREACHABLE();
+ }
+
+ if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ DCHECK(cls->CanCallRuntime());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
+ if (generate_null_check) {
+ __ testl(out, out);
+ __ j(kEqual, slow_path->GetEntryLabel());
+ }
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
@@ -5454,6 +5575,7 @@
}
case HLoadString::LoadKind::kDexCacheAddress: {
DCHECK_NE(load->GetAddress(), 0u);
+ // /* GcRoot<mirror::String> */ out = *address
if (IsUint<32>(load->GetAddress())) {
Address address = Address::Absolute(load->GetAddress(), /* no_rip */ true);
GenerateGcRootFieldLoad(load, out_loc, address);
@@ -5469,6 +5591,7 @@
Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset);
Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset,
/* no_rip */ false);
+ // /* GcRoot<mirror::String> */ out = *address /* PC-relative */
GenerateGcRootFieldLoad(load, out_loc, address, fixup_label);
break;
}
@@ -6310,6 +6433,9 @@
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
Address src = index.IsConstant() ?
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 7cf1245..d7cfd37 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -388,6 +388,11 @@
HLoadString::LoadKind GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) OVERRIDE;
+ // Check if the desired_class_load_kind is supported. If it is, return it,
+ // otherwise return a fall-back kind that should be used instead.
+ HLoadClass::LoadKind GetSupportedLoadClassKind(
+ HLoadClass::LoadKind desired_class_load_kind) OVERRIDE;
+
// Check if the desired_dispatch_info is supported. If it is, return it,
// otherwise return a fall-back info that should be used instead.
HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
@@ -400,6 +405,7 @@
void RecordSimplePatch();
void RecordStringPatch(HLoadString* load_string);
+ void RecordTypePatch(HLoadClass* load_class);
Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset);
void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE;
@@ -427,6 +433,14 @@
Location index,
Location temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ const Address& src,
+ Location temp,
+ bool needs_null_check);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -529,15 +543,6 @@
static constexpr int32_t kDummy32BitOffset = 256;
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- CpuRegister obj,
- const Address& src,
- Location temp,
- bool needs_null_check);
-
struct PcRelativeDexCacheAccessInfo {
PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off)
: target_dex_file(dex_file), element_offset(element_off), label() { }
@@ -569,6 +574,8 @@
ArenaDeque<Label> simple_patches_;
// String patch locations.
ArenaDeque<StringPatchInfo<Label>> string_patches_;
+ // Type patch locations.
+ ArenaDeque<TypePatchInfo<Label>> type_patches_;
// Fixups for jump tables need to be handled specially.
ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_;
diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc
index e9072b9..14c318e 100644
--- a/compiler/optimizing/dex_cache_array_fixups_arm.cc
+++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc
@@ -44,8 +44,23 @@
}
private:
+ void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
+ // If this is a load with PC-relative access to the dex cache types array,
+ // we need to add the dex cache arrays base as the special input.
+ if (load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCachePcRelative) {
+ // Initialize base for target dex file if needed.
+ const DexFile& dex_file = load_class->GetDexFile();
+ HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file);
+ // Update the element offset in base.
+ DexCacheArraysLayout layout(kArmPointerSize, &dex_file);
+ base->UpdateElementOffset(layout.TypeOffset(load_class->GetTypeIndex()));
+ // Add the special argument base to the load.
+ load_class->AddSpecialInput(base);
+ }
+ }
+
void VisitLoadString(HLoadString* load_string) OVERRIDE {
- // If this is a load with PC-relative access to the dex cache methods array,
+ // If this is a load with PC-relative access to the dex cache strings array,
// we need to add the dex cache arrays base as the special input.
if (load_string->GetLoadKind() == HLoadString::LoadKind::kDexCachePcRelative) {
// Initialize base for target dex file if needed.
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 3084a4f..4af8d19 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -372,6 +372,10 @@
}
void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
+ StartAttributeStream("load_kind") << load_class->GetLoadKind();
+ const char* descriptor = load_class->GetDexFile().GetTypeDescriptor(
+ load_class->GetDexFile().GetTypeId(load_class->GetTypeIndex()));
+ StartAttributeStream("class_name") << PrettyDescriptor(descriptor);
StartAttributeStream("gen_clinit_check") << std::boolalpha
<< load_class->MustGenerateClinitCheck() << std::noboolalpha;
StartAttributeStream("needs_access_check") << std::boolalpha
@@ -399,6 +403,16 @@
<< array_length->IsStringLength() << std::noboolalpha;
}
+ void VisitBoundsCheck(HBoundsCheck* bounds_check) OVERRIDE {
+ StartAttributeStream("is_string_char_at") << std::boolalpha
+ << bounds_check->IsStringCharAt() << std::noboolalpha;
+ }
+
+ void VisitArrayGet(HArrayGet* array_get) OVERRIDE {
+ StartAttributeStream("is_string_char_at") << std::boolalpha
+ << array_get->IsStringCharAt() << std::noboolalpha;
+ }
+
void VisitArraySet(HArraySet* array_set) OVERRIDE {
StartAttributeStream("value_can_be_null") << std::boolalpha
<< array_set->GetValueCanBeNull() << std::noboolalpha;
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 0a5cf80..52426d7 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -670,7 +670,7 @@
// an unsigned entity, for example, as in the following loop that uses the full range:
// for (int i = INT_MIN; i < INT_MAX; i++) // TC = UINT_MAX
// (2) The TC is only valid if the loop is taken, otherwise TC = 0, as in:
- // for (int i = 12; i < U; i++) // TC = 0 when U < 12
+ // for (int i = 12; i < U; i++) // TC = 0 when U <= 12
// If this cannot be determined at compile-time, the TC is only valid within the
// loop-body proper, not the loop-header unless enforced with an explicit taken-test.
// (3) The TC is only valid if the loop is finite, otherwise TC has no value, as in:
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index f1965f0..7c74816 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -132,7 +132,7 @@
InductionInfo* a,
InductionInfo* b,
Primitive::Type type) {
- DCHECK(a != nullptr);
+ DCHECK(a != nullptr && b != nullptr);
return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, type);
}
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index bc920d9..5e587e0 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -73,9 +73,12 @@
return v;
}
-/**
- * Corrects a value for type to account for arithmetic wrap-around in lower precision.
- */
+/** Helper method to test for a constant value. */
+static bool IsConstantValue(InductionVarRange::Value v) {
+ return v.is_known && v.a_constant == 0;
+}
+
+/** Corrects a value for type to account for arithmetic wrap-around in lower precision. */
static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primitive::Type type) {
switch (type) {
case Primitive::kPrimShort:
@@ -85,26 +88,15 @@
// TODO: maybe some room for improvement, like allowing widening conversions
const int32_t min = Primitive::MinValueOfIntegralType(type);
const int32_t max = Primitive::MaxValueOfIntegralType(type);
- return (v.is_known && v.a_constant == 0 && min <= v.b_constant && v.b_constant <= max)
+ return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
? v
: InductionVarRange::Value();
}
default:
- // At int or higher.
return v;
}
}
-/** Helper method to test for a constant value. */
-static bool IsConstantValue(InductionVarRange::Value v) {
- return v.is_known && v.a_constant == 0;
-}
-
-/** Helper method to test for same constant value. */
-static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) {
- return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant;
-}
-
/** Helper method to insert an instruction. */
static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
DCHECK(block != nullptr);
@@ -119,22 +111,22 @@
//
InductionVarRange::InductionVarRange(HInductionVarAnalysis* induction_analysis)
- : induction_analysis_(induction_analysis) {
+ : induction_analysis_(induction_analysis),
+ chase_hint_(nullptr) {
DCHECK(induction_analysis != nullptr);
}
bool InductionVarRange::GetInductionRange(HInstruction* context,
HInstruction* instruction,
+ HInstruction* chase_hint,
/*out*/Value* min_val,
/*out*/Value* max_val,
/*out*/bool* needs_finite_test) {
- HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop == nullptr) {
- return false; // no loop
- }
- HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
- if (info == nullptr) {
- return false; // no induction information
+ HLoopInformation* loop = nullptr;
+ HInductionVarAnalysis::InductionInfo* info = nullptr;
+ HInductionVarAnalysis::InductionInfo* trip = nullptr;
+ if (!HasInductionInfo(context, instruction, &loop, &info, &trip)) {
+ return false;
}
// Type int or lower (this is not too restrictive since intended clients, like
// bounds check elimination, will have truncated higher precision induction
@@ -148,45 +140,15 @@
default:
return false;
}
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = context->GetBlock() != header;
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
// Find range.
+ chase_hint_ = chase_hint;
+ bool in_body = context->GetBlock() != loop->GetHeader();
*min_val = GetVal(info, trip, in_body, /* is_min */ true);
*max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
*needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
return true;
}
-bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val,
- /*in-out*/ Value* max_val) const {
- if (min_val->instruction != nullptr || max_val->instruction != nullptr) {
- Value v1_min = RefineOuter(*min_val, /* is_min */ true);
- Value v2_max = RefineOuter(*max_val, /* is_min */ false);
- // The refined range is safe if both sides refine the same instruction. Otherwise, since two
- // different ranges are combined, the new refined range is safe to pass back to the client if
- // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
- if (min_val->instruction != max_val->instruction) {
- Value v1_max = RefineOuter(*min_val, /* is_min */ false);
- Value v2_min = RefineOuter(*max_val, /* is_min */ true);
- if (!IsConstantValue(v1_max) ||
- !IsConstantValue(v2_min) ||
- v1_max.b_constant > v2_min.b_constant) {
- return false;
- }
- }
- // Did something change?
- if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
- *min_val = v1_min;
- *max_val = v2_max;
- return true;
- }
- }
- return false;
-}
-
bool InductionVarRange::CanGenerateCode(HInstruction* context,
HInstruction* instruction,
/*out*/bool* needs_finite_test,
@@ -226,7 +188,7 @@
bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
ConstantRequest request,
- /*out*/ int64_t *value) const {
+ /*out*/ int64_t* value) const {
if (info != nullptr) {
// A direct 32-bit or 64-bit constant fetch. This immediately satisfies
// any of the three requests (kExact, kAtMost, and KAtLeast).
@@ -236,27 +198,16 @@
return true;
}
}
- // Try range analysis while traversing outward on loops.
- bool in_body = true; // no known trip count
- Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
- Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
- do {
- // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies.
- if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) {
- if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) {
- *value = v_max.b_constant;
- return true;
- } else if (request == kAtLeast) {
- *value = v_min.b_constant;
- return true;
- }
- }
- } while (RefineOuter(&v_min, &v_max));
- // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies
- // (e.g. array length == maxint and c == 1 would yield minint).
- if (request == kAtLeast) {
- if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) {
- *value = v_min.b_constant;
+ // Try range analysis on the invariant, but only on proper range to avoid wrap-around anomalies.
+ Value min_val = GetVal(info, nullptr, /* in_body */ true, /* is_min */ true);
+ Value max_val = GetVal(info, nullptr, /* in_body */ true, /* is_min */ false);
+ if (IsConstantValue(min_val) &&
+ IsConstantValue(max_val) && min_val.b_constant <= max_val.b_constant) {
+ if ((request == kExact && min_val.b_constant == max_val.b_constant) || request == kAtMost) {
+ *value = max_val.b_constant;
+ return true;
+ } else if (request == kAtLeast) {
+ *value = min_val.b_constant;
return true;
}
}
@@ -264,6 +215,51 @@
return false;
}
+bool InductionVarRange::HasInductionInfo(
+ HInstruction* context,
+ HInstruction* instruction,
+ /*out*/ HLoopInformation** loop,
+ /*out*/ HInductionVarAnalysis::InductionInfo** info,
+ /*out*/ HInductionVarAnalysis::InductionInfo** trip) const {
+ HLoopInformation* l = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
+ if (l != nullptr) {
+ HInductionVarAnalysis::InductionInfo* i = induction_analysis_->LookupInfo(l, instruction);
+ if (i != nullptr) {
+ *loop = l;
+ *info = i;
+ *trip = induction_analysis_->LookupInfo(l, l->GetHeader()->GetLastInstruction());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool InductionVarRange::IsWellBehavedTripCount(HInductionVarAnalysis::InductionInfo* trip) const {
+ if (trip != nullptr) {
+ // Both bounds that define a trip-count are well-behaved if they either are not defined
+ // in any loop, or are contained in a proper interval. This allows finding the min/max
+ // of an expression by chasing outward.
+ InductionVarRange range(induction_analysis_);
+ HInductionVarAnalysis::InductionInfo* lower = trip->op_b->op_a;
+ HInductionVarAnalysis::InductionInfo* upper = trip->op_b->op_b;
+ int64_t not_used = 0;
+ return (!HasFetchInLoop(lower) || range.IsConstant(lower, kAtLeast, ¬_used)) &&
+ (!HasFetchInLoop(upper) || range.IsConstant(upper, kAtLeast, ¬_used));
+ }
+ return true;
+}
+
+bool InductionVarRange::HasFetchInLoop(HInductionVarAnalysis::InductionInfo* info) const {
+ if (info != nullptr) {
+ if (info->induction_class == HInductionVarAnalysis::kInvariant &&
+ info->operation == HInductionVarAnalysis::kFetch) {
+ return info->fetch->GetBlock()->GetLoopInformation() != nullptr;
+ }
+ return HasFetchInLoop(info->op_a) || HasFetchInLoop(info->op_b);
+ }
+ return false;
+}
+
bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const {
if (info != nullptr) {
if (info->induction_class == HInductionVarAnalysis::kLinear) {
@@ -299,13 +295,13 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const {
- // Detect common situation where an offset inside the trip count cancels out during range
+ // Detect common situation where an offset inside the trip-count cancels out during range
// analysis (finding max a * (TC - 1) + OFFSET for a == 1 and TC = UPPER - OFFSET or finding
// min a * (TC - 1) + OFFSET for a == -1 and TC = OFFSET - UPPER) to avoid losing information
// with intermediate results that only incorporate single instructions.
if (trip != nullptr) {
HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
- if (trip_expr->operation == HInductionVarAnalysis::kSub) {
+ if (trip_expr->type == info->type && trip_expr->operation == HInductionVarAnalysis::kSub) {
int64_t stride_value = 0;
if (IsConstant(info->op_a, kExact, &stride_value)) {
if (!is_min && stride_value == 1) {
@@ -349,12 +345,25 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const {
- // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
- // more likely range analysis will compare the same instructions as terminal nodes.
+ // Stop chasing the instruction at constant or hint.
int64_t value;
- if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
+ if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
return Value(static_cast<int32_t>(value));
- } else if (instruction->IsAdd()) {
+ } else if (instruction == chase_hint_) {
+ return Value(instruction, 1, 0);
+ }
+ // Special cases when encountering a single instruction that denotes trip count in the
+ // loop-body: min is 1 and, when chasing constants, max of safe trip-count is max int
+ if (in_body && trip != nullptr && instruction == trip->op_a->fetch) {
+ if (is_min) {
+ return Value(1);
+ } else if (chase_hint_ == nullptr && !IsUnsafeTripCount(trip)) {
+ return Value(std::numeric_limits<int32_t>::max());
+ }
+ }
+ // Chase the instruction a bit deeper into the HIR tree, so that it becomes more likely
+ // range analysis will compare the same instructions as terminal nodes.
+ if (instruction->IsAdd()) {
if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
return AddValue(Value(static_cast<int32_t>(value)),
GetFetch(instruction->InputAt(1), trip, in_body, is_min));
@@ -362,19 +371,35 @@
return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min),
Value(static_cast<int32_t>(value)));
}
- } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
- return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
+ } else if (instruction->IsArrayLength()) {
+ // Return extreme values when chasing constants. Otherwise, chase deeper.
+ if (chase_hint_ == nullptr) {
+ return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max());
+ } else if (instruction->InputAt(0)->IsNewArray()) {
+ return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
+ }
} else if (instruction->IsTypeConversion()) {
// Since analysis is 32-bit (or narrower) we allow a widening along the path.
if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
}
- } else if (is_min) {
- // Special case for finding minimum: minimum of trip-count in loop-body is 1.
- if (trip != nullptr && in_body && instruction == trip->op_a->fetch) {
- return Value(1);
- }
+ }
+ // Chase an invariant fetch that is defined by an outer loop if the trip-count used
+ // so far is well-behaved in both bounds and the next trip-count is safe.
+ // Example:
+ // for (int i = 0; i <= 100; i++) // safe
+ // for (int j = 0; j <= i; j++) // well-behaved
+ // j is in range [0, i ] (if i is chase hint)
+ // or in range [0, 100] (otherwise)
+ HLoopInformation* next_loop = nullptr;
+ HInductionVarAnalysis::InductionInfo* next_info = nullptr;
+ HInductionVarAnalysis::InductionInfo* next_trip = nullptr;
+ bool next_in_body = true; // inner loop is always in body of outer loop
+ if (HasInductionInfo(instruction, instruction, &next_loop, &next_info, &next_trip) &&
+ IsWellBehavedTripCount(trip) &&
+ !IsUnsafeTripCount(next_trip)) {
+ return GetVal(next_info, next_trip, next_in_body, is_min);
}
return Value(instruction, 1, 0);
}
@@ -421,9 +446,8 @@
break;
}
break;
- case HInductionVarAnalysis::kLinear: {
+ case HInductionVarAnalysis::kLinear:
return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type);
- }
case HInductionVarAnalysis::kWrapAround:
case HInductionVarAnalysis::kPeriodic:
return MergeVal(GetVal(info->op_a, trip, in_body, is_min),
@@ -438,20 +462,18 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const {
+ // Constant times range.
+ int64_t value = 0;
+ if (IsConstant(info1, kExact, &value)) {
+ return MulRangeAndConstant(value, info2, trip, in_body, is_min);
+ } else if (IsConstant(info2, kExact, &value)) {
+ return MulRangeAndConstant(value, info1, trip, in_body, is_min);
+ }
+ // Interval ranges.
Value v1_min = GetVal(info1, trip, in_body, /* is_min */ true);
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
- // Try to refine first operand.
- if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) {
- RefineOuter(&v1_min, &v1_max);
- }
- // Constant times range.
- if (IsSameConstantValue(v1_min, v1_max)) {
- return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min);
- } else if (IsSameConstantValue(v2_min, v2_max)) {
- return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min);
- }
// Positive range vs. positive or negative range.
if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
@@ -476,14 +498,16 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const {
+ // Range divided by constant.
+ int64_t value = 0;
+ if (IsConstant(info2, kExact, &value)) {
+ return DivRangeAndConstant(value, info1, trip, in_body, is_min);
+ }
+ // Interval ranges.
Value v1_min = GetVal(info1, trip, in_body, /* is_min */ true);
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
- // Range divided by constant.
- if (IsSameConstantValue(v2_min, v2_max)) {
- return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min);
- }
// Positive range vs. positive or negative range.
if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
@@ -503,18 +527,30 @@
return Value();
}
-InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min,
- Value v_max,
- Value c,
- bool is_min) const {
- return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c);
+InductionVarRange::Value InductionVarRange::MulRangeAndConstant(
+ int64_t value,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const {
+ if (CanLongValueFitIntoInt(value)) {
+ Value c(static_cast<int32_t>(value));
+ return MulValue(GetVal(info, trip, in_body, is_min == value >= 0), c);
+ }
+ return Value();
}
-InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min,
- Value v_max,
- Value c,
- bool is_min) const {
- return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c);
+InductionVarRange::Value InductionVarRange::DivRangeAndConstant(
+ int64_t value,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const {
+ if (CanLongValueFitIntoInt(value)) {
+ Value c(static_cast<int32_t>(value));
+ return DivValue(GetVal(info, trip, in_body, is_min == value >= 0), c);
+ }
+ return Value();
}
InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
@@ -580,28 +616,6 @@
return Value();
}
-InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const {
- if (v.instruction == nullptr) {
- return v; // nothing to refine
- }
- HLoopInformation* loop =
- v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop == nullptr) {
- return v; // no loop
- }
- HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction);
- if (info == nullptr) {
- return v; // no induction information
- }
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = true; // inner always in more outer
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
- // Try to refine "a x instruction + b" with outer loop range information on instruction.
- return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant));
-}
-
bool InductionVarRange::GenerateCode(HInstruction* context,
HInstruction* instruction,
HGraph* graph,
@@ -611,27 +625,18 @@
/*out*/HInstruction** taken_test,
/*out*/bool* needs_finite_test,
/*out*/bool* needs_taken_test) const {
- HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop == nullptr) {
- return false; // no loop
- }
- HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
- if (info == nullptr) {
- return false; // no induction information
- }
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = context->GetBlock() != header;
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
- if (trip == nullptr) {
- return false; // codegen relies on trip count
+ HLoopInformation* loop = nullptr;
+ HInductionVarAnalysis::InductionInfo* info = nullptr;
+ HInductionVarAnalysis::InductionInfo* trip = nullptr;
+ if (!HasInductionInfo(context, instruction, &loop, &info, &trip) || trip == nullptr) {
+ return false; // codegen needs all information, including tripcount
}
// Determine what tests are needed. A finite test is needed if the evaluation code uses the
// trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
// the computed range). A taken test is needed for any unknown trip-count, even if evaluation
// code does not use the trip-count explicitly (since there could be an implicit relation
// between e.g. an invariant subscript and a not-taken condition).
+ bool in_body = context->GetBlock() != loop->GetHeader();
*needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
*needs_taken_test = IsBodyTripCount(trip);
// Code generation for taken test: generate the code when requested or otherwise analyze
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 0af4156..00aaa16 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -57,21 +57,19 @@
explicit InductionVarRange(HInductionVarAnalysis* induction);
/**
- * Given a context denoted by the first instruction, returns a possibly conservative
- * lower and upper bound on the instruction's value in the output parameters min_val
- * and max_val, respectively. The need_finite_test flag denotes if an additional finite-test
- * is needed to protect the range evaluation inside its loop. Returns false on failure.
+ * Given a context denoted by the first instruction, returns a possibly conservative lower
+ * and upper bound on the instruction's value in the output parameters min_val and max_val,
+ * respectively. The need_finite_test flag denotes if an additional finite-test is needed
+ * to protect the range evaluation inside its loop. The parameter chase_hint defines an
+ * instruction at which chasing may stop. Returns false on failure.
*/
bool GetInductionRange(HInstruction* context,
HInstruction* instruction,
+ HInstruction* chase_hint,
/*out*/ Value* min_val,
/*out*/ Value* max_val,
/*out*/ bool* needs_finite_test);
- /** Refines the values with induction of next outer loop. Returns true on change. */
- bool RefineOuter(/*in-out*/ Value* min_val,
- /*in-out*/ Value* max_val) const;
-
/**
* Returns true if range analysis is able to generate code for the lower and upper
* bound expressions on the instruction in the given context. The need_finite_test
@@ -132,11 +130,20 @@
*/
bool IsConstant(HInductionVarAnalysis::InductionInfo* info,
ConstantRequest request,
- /*out*/ int64_t *value) const;
+ /*out*/ int64_t* value) const;
+ /** Returns whether induction information can be obtained. */
+ bool HasInductionInfo(HInstruction* context,
+ HInstruction* instruction,
+ /*out*/ HLoopInformation** loop,
+ /*out*/ HInductionVarAnalysis::InductionInfo** info,
+ /*out*/ HInductionVarAnalysis::InductionInfo** trip) const;
+
+ bool HasFetchInLoop(HInductionVarAnalysis::InductionInfo* info) const;
bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const;
bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
+ bool IsWellBehavedTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
Value GetLinear(HInductionVarAnalysis::InductionInfo* info,
HInductionVarAnalysis::InductionInfo* trip,
@@ -161,8 +168,16 @@
bool in_body,
bool is_min) const;
- Value MulRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const;
- Value DivRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const;
+ Value MulRangeAndConstant(int64_t value,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
+ Value DivRangeAndConstant(int64_t value,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ bool in_body,
+ bool is_min) const;
Value AddValue(Value v1, Value v2) const;
Value SubValue(Value v1, Value v2) const;
@@ -171,12 +186,6 @@
Value MergeVal(Value v1, Value v2, bool is_min) const;
/**
- * Returns refined value using induction of next outer loop or the input value if no
- * further refinement is possible.
- */
- Value RefineOuter(Value val, bool is_min) const;
-
- /**
* Generates code for lower/upper/taken-test in the HIR. Returns true on success.
* With values nullptr, the method can be used to determine if code generation
* would be successful without generating actual code yet.
@@ -200,7 +209,10 @@
bool is_min) const;
/** Results of prior induction variable analysis. */
- HInductionVarAnalysis *induction_analysis_;
+ HInductionVarAnalysis* induction_analysis_;
+
+ /** Instruction at which chasing may stop. */
+ HInstruction* chase_hint_;
friend class HInductionVarAnalysis;
friend class InductionVarRangeTest;
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index dc04dc2..4ea170f 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -66,6 +66,8 @@
entry_block_->AddInstruction(x_);
y_ = new (&allocator_) HParameterValue(graph_->GetDexFile(), 0, 0, Primitive::kPrimInt);
entry_block_->AddInstruction(y_);
+ // Set arbitrary range analysis hint while testing private methods.
+ SetHint(x_);
}
/** Constructs loop with given upper bound. */
@@ -111,6 +113,11 @@
iva_->Run();
}
+ /** Sets hint. */
+ void SetHint(HInstruction* hint) {
+ range_.chase_hint_ = hint;
+ }
+
/** Constructs an invariant. */
HInductionVarAnalysis::InductionInfo* CreateInvariant(char opc,
HInductionVarAnalysis::InductionInfo* a,
@@ -122,6 +129,7 @@
case 'n': op = HInductionVarAnalysis::kNeg; break;
case '*': op = HInductionVarAnalysis::kMul; break;
case '/': op = HInductionVarAnalysis::kDiv; break;
+ case '<': op = HInductionVarAnalysis::kLT; break;
default: op = HInductionVarAnalysis::kNop; break;
}
return iva_->CreateInvariantOp(op, a, b);
@@ -137,22 +145,21 @@
return CreateFetch(graph_->GetIntConstant(c));
}
- /** Constructs a trip-count. */
+ /** Constructs a constant trip-count. */
HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc, bool in_loop, bool safe) {
- Primitive::Type type = Primitive::kPrimInt;
+ HInductionVarAnalysis::InductionOp op = HInductionVarAnalysis::kTripCountInBodyUnsafe;
if (in_loop && safe) {
- return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr, type);
+ op = HInductionVarAnalysis::kTripCountInLoop;
} else if (in_loop) {
- return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr, type);
+ op = HInductionVarAnalysis::kTripCountInLoopUnsafe;
} else if (safe) {
- return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr, type);
- } else {
- return iva_->CreateTripCount(
- HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr, type);
+ op = HInductionVarAnalysis::kTripCountInBody;
}
+ // Return TC with taken-test 0 < TC.
+ return iva_->CreateTripCount(op,
+ CreateConst(tc),
+ CreateInvariant('<', CreateConst(0), CreateConst(tc)),
+ Primitive::kPrimInt);
}
/** Constructs a linear a * i + b induction. */
@@ -197,13 +204,13 @@
}
Value GetMin(HInductionVarAnalysis::InductionInfo* info,
- HInductionVarAnalysis::InductionInfo* induc) {
- return range_.GetVal(info, induc, /* in_body */ true, /* is_min */ true);
+ HInductionVarAnalysis::InductionInfo* trip) {
+ return range_.GetVal(info, trip, /* in_body */ true, /* is_min */ true);
}
Value GetMax(HInductionVarAnalysis::InductionInfo* info,
- HInductionVarAnalysis::InductionInfo* induc) {
- return range_.GetVal(info, induc, /* in_body */ true, /* is_min */ false);
+ HInductionVarAnalysis::InductionInfo* trip) {
+ return range_.GetVal(info, trip, /* in_body */ true, /* is_min */ false);
}
Value GetMul(HInductionVarAnalysis::InductionInfo* info1,
@@ -558,6 +565,31 @@
ExpectEqual(Value(), MaxValue(Value(55), Value(y_, 1, -50)));
}
+TEST_F(InductionVarRangeTest, ArrayLengthAndHints) {
+ HInstruction* new_array = new (&allocator_)
+ HNewArray(x_,
+ graph_->GetCurrentMethod(),
+ 0, Primitive::kPrimInt,
+ graph_->GetDexFile(),
+ kQuickAllocArray);
+ entry_block_->AddInstruction(new_array);
+ HInstruction* array_length = new (&allocator_) HArrayLength(new_array, 0);
+ entry_block_->AddInstruction(array_length);
+ // With null hint: yields extreme constants.
+ const int32_t max_value = std::numeric_limits<int32_t>::max();
+ SetHint(nullptr);
+ ExpectEqual(Value(0), GetMin(CreateFetch(array_length), nullptr));
+ ExpectEqual(Value(max_value), GetMax(CreateFetch(array_length), nullptr));
+ // With explicit hint: yields the length instruction.
+ SetHint(array_length);
+ ExpectEqual(Value(array_length, 1, 0), GetMin(CreateFetch(array_length), nullptr));
+ ExpectEqual(Value(array_length, 1, 0), GetMax(CreateFetch(array_length), nullptr));
+ // With any non-null hint: chases beyond the length instruction.
+ SetHint(x_);
+ ExpectEqual(Value(x_, 1, 0), GetMin(CreateFetch(array_length), nullptr));
+ ExpectEqual(Value(x_, 1, 0), GetMax(CreateFetch(array_length), nullptr));
+}
+
//
// Tests on public methods.
//
@@ -570,23 +602,20 @@
bool needs_finite_test = true;
// In context of header: known.
- range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(999), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
- range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, increment_, x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(1), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
}
TEST_F(InductionVarRangeTest, ConstantTripCountDown) {
@@ -597,23 +626,20 @@
bool needs_finite_test = true;
// In context of header: known.
- range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(1), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
- range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, increment_, x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(999), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
}
TEST_F(InductionVarRangeTest, SymbolicTripCountUp) {
@@ -625,23 +651,20 @@
bool needs_taken_test = true;
// In context of header: upper unknown.
- range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(0), v1);
ExpectEqual(Value(x_, 1, -1), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
- range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, increment_, x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(1), v1);
ExpectEqual(Value(x_, 1, 0), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
HInstruction* lower = nullptr;
HInstruction* upper = nullptr;
@@ -695,23 +718,20 @@
bool needs_taken_test = true;
// In context of header: lower unknown.
- range_.GetInductionRange(condition_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(condition_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
// In context of loop-body: known.
- range_.GetInductionRange(increment_, condition_->InputAt(0), &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, condition_->InputAt(0), x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(x_, 1, 1), v1);
ExpectEqual(Value(1000), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
- range_.GetInductionRange(increment_, increment_, &v1, &v2, &needs_finite_test);
+ range_.GetInductionRange(increment_, increment_, x_, &v1, &v2, &needs_finite_test);
EXPECT_FALSE(needs_finite_test);
ExpectEqual(Value(x_, 1, 0), v1);
ExpectEqual(Value(999), v2);
- EXPECT_FALSE(range_.RefineOuter(&v1, &v2));
HInstruction* lower = nullptr;
HInstruction* upper = nullptr;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 59de895..c67b2d5 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -756,7 +756,15 @@
invoke_instruction->ReplaceWith(return_replacement);
}
invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
- FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp);
+ FixUpReturnReferenceType(method, return_replacement);
+ if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) {
+ // Actual return value has a more specific type than the method's declared
+ // return type. Run RTP again on the outer graph to propagate it.
+ ReferenceTypePropagation(graph_,
+ outer_compilation_unit_.GetDexCache(),
+ handles_,
+ /* is_first_run */ false).Run();
+ }
return true;
}
@@ -1159,6 +1167,15 @@
}
}
+ // We have replaced formal arguments with actual arguments. If actual types
+ // are more specific than the declared ones, run RTP again on the inner graph.
+ if (ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
+ ReferenceTypePropagation(callee_graph,
+ dex_compilation_unit.GetDexCache(),
+ handles_,
+ /* is_first_run */ false).Run();
+ }
+
size_t number_of_instructions_budget = kMaximumNumberOfHInstructions;
size_t number_of_inlined_instructions =
RunOptimizations(callee_graph, code_item, dex_compilation_unit);
@@ -1332,13 +1349,87 @@
return number_of_inlined_instructions;
}
-void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction,
- ArtMethod* resolved_method,
- HInstruction* return_replacement,
- bool do_rtp) {
+static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti,
+ bool declared_can_be_null,
+ HInstruction* actual_obj)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (declared_can_be_null && !actual_obj->CanBeNull()) {
+ return true;
+ }
+
+ ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo();
+ return (actual_rti.IsExact() && !declared_rti.IsExact()) ||
+ declared_rti.IsStrictSupertypeOf(actual_rti);
+}
+
+ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) {
+ return ReferenceTypePropagation::IsAdmissible(klass)
+ ? ReferenceTypeInfo::Create(handles_->NewHandle(klass))
+ : graph_->GetInexactObjectRti();
+}
+
+bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) {
+ // If this is an instance call, test whether the type of the `this` argument
+ // is more specific than the class which declares the method.
+ if (!resolved_method->IsStatic()) {
+ if (IsReferenceTypeRefinement(GetClassRTI(resolved_method->GetDeclaringClass()),
+ /* declared_can_be_null */ false,
+ invoke_instruction->InputAt(0u))) {
+ return true;
+ }
+ }
+
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+
+ // Iterate over the list of parameter types and test whether any of the
+ // actual inputs has a more specific reference type than the type declared in
+ // the signature.
+ const DexFile::TypeList* param_list = resolved_method->GetParameterTypeList();
+ for (size_t param_idx = 0,
+ input_idx = resolved_method->IsStatic() ? 0 : 1,
+ e = (param_list == nullptr ? 0 : param_list->Size());
+ param_idx < e;
+ ++param_idx, ++input_idx) {
+ HInstruction* input = invoke_instruction->InputAt(input_idx);
+ if (input->GetType() == Primitive::kPrimNot) {
+ mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType(
+ param_list->GetTypeItem(param_idx).type_idx_,
+ pointer_size);
+ if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
+ /* declared_can_be_null */ true,
+ input)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction,
+ HInstruction* return_replacement) {
// Check the integrity of reference types and run another type propagation if needed.
if (return_replacement != nullptr) {
if (return_replacement->GetType() == Primitive::kPrimNot) {
+ // Test if the return type is a refinement of the declared return type.
+ if (IsReferenceTypeRefinement(invoke_instruction->GetReferenceTypeInfo(),
+ /* declared_can_be_null */ true,
+ return_replacement)) {
+ return true;
+ }
+ } else if (return_replacement->IsInstanceOf()) {
+ // Inlining InstanceOf into an If may put a tighter bound on reference types.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInstruction* return_replacement) {
+ if (return_replacement != nullptr) {
+ if (return_replacement->GetType() == Primitive::kPrimNot) {
if (!return_replacement->GetReferenceTypeInfo().IsValid()) {
// Make sure that we have a valid type for the return. We may get an invalid one when
// we inline invokes with multiple branches and create a Phi for the result.
@@ -1347,36 +1438,7 @@
DCHECK(return_replacement->IsPhi());
size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
- if (cls != nullptr && !cls->IsErroneous()) {
- ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls);
- return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
- return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
- } else {
- // Return inexact object type on failures.
- return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti());
- }
- }
-
- if (do_rtp) {
- // If the return type is a refinement of the declared type run the type propagation again.
- ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo();
- ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo();
- if (invoke_rti.IsStrictSupertypeOf(return_rti)
- || (return_rti.IsExact() && !invoke_rti.IsExact())
- || !return_replacement->CanBeNull()) {
- ReferenceTypePropagation(graph_,
- outer_compilation_unit_.GetDexCache(),
- handles_,
- /* is_first_run */ false).Run();
- }
- }
- } else if (return_replacement->IsInstanceOf()) {
- if (do_rtp) {
- // Inlining InstanceOf into an If may put a tighter bound on reference types.
- ReferenceTypePropagation(graph_,
- outer_compilation_unit_.GetDexCache(),
- handles_,
- /* is_first_run */ false).Run();
+ return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
}
}
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 7cf1424..02d3a5f 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -124,10 +124,18 @@
uint32_t dex_pc) const
SHARED_REQUIRES(Locks::mutator_lock_);
- void FixUpReturnReferenceType(HInvoke* invoke_instruction,
- ArtMethod* resolved_method,
- HInstruction* return_replacement,
- bool do_rtp)
+ void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Creates an instance of ReferenceTypeInfo from `klass` if `klass` is
+ // admissible (see ReferenceTypePropagation::IsAdmissible for details).
+ // Otherwise returns inexact Object RTI.
+ ReferenceTypeInfo GetClassRTI(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ bool ReturnTypeMoreSpecific(HInvoke* invoke_instruction, HInstruction* return_replacement)
SHARED_REQUIRES(Locks::mutator_lock_);
// Add a type guard on the given `receiver`. This will add to the graph:
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index f2286e4..b412529 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -933,7 +933,7 @@
IsOutermostCompilingClass(type_index),
dex_pc,
needs_access_check,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index));
+ /* is_in_dex_cache */ false);
AppendInstruction(load_class);
HInstruction* cls = load_class;
@@ -1024,7 +1024,7 @@
is_outer_class,
dex_pc,
/*needs_access_check*/ false,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index));
+ /* is_in_dex_cache */ false);
AppendInstruction(load_class);
clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
AppendInstruction(clinit_check);
@@ -1376,15 +1376,13 @@
}
}
- bool is_in_cache =
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index);
HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
storage_index,
outer_dex_file,
is_outer_class,
dex_pc,
/*needs_access_check*/ false,
- is_in_cache);
+ /* is_in_dex_cache */ false);
AppendInstruction(constant);
HInstruction* cls = constant;
@@ -1653,7 +1651,7 @@
IsOutermostCompilingClass(type_index),
dex_pc,
!can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index));
+ /* is_in_dex_cache */ false);
AppendInstruction(cls);
TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -2621,8 +2619,6 @@
Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index);
- bool is_in_dex_cache =
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index);
AppendInstruction(new (arena_) HLoadClass(
graph_->GetCurrentMethod(),
type_index,
@@ -2630,7 +2626,7 @@
IsOutermostCompilingClass(type_index),
dex_pc,
!can_access,
- is_in_dex_cache));
+ /* is_in_dex_cache */ false));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index eb1d156..3041c4d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -101,6 +101,7 @@
void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type);
void SimplifyIsNaN(HInvoke* invoke);
void SimplifyFP2Int(HInvoke* invoke);
+ void SimplifyStringCharAt(HInvoke* invoke);
void SimplifyStringIsEmptyOrLength(HInvoke* invoke);
void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
@@ -235,22 +236,40 @@
void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) {
DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
- HConstant* input_cst = instruction->GetConstantRight();
- HInstruction* input_other = instruction->GetLeastConstantLeft();
+ HInstruction* shift_amount = instruction->GetRight();
+ HInstruction* value = instruction->GetLeft();
- if (input_cst != nullptr) {
- int64_t cst = Int64FromConstant(input_cst);
- int64_t mask = (input_other->GetType() == Primitive::kPrimLong)
- ? kMaxLongShiftDistance
- : kMaxIntShiftDistance;
- if ((cst & mask) == 0) {
+ int64_t implicit_mask = (value->GetType() == Primitive::kPrimLong)
+ ? kMaxLongShiftDistance
+ : kMaxIntShiftDistance;
+
+ if (shift_amount->IsConstant()) {
+ int64_t cst = Int64FromConstant(shift_amount->AsConstant());
+ if ((cst & implicit_mask) == 0) {
// Replace code looking like
- // SHL dst, src, 0
+ // SHL dst, value, 0
// with
- // src
- instruction->ReplaceWith(input_other);
+ // value
+ instruction->ReplaceWith(value);
instruction->GetBlock()->RemoveInstruction(instruction);
RecordSimplification();
+ return;
+ }
+ }
+
+ // Shift operations implicitly mask the shift amount according to the type width. Get rid of
+ // unnecessary explicit masking operations on the shift amount.
+ // Replace code looking like
+ // AND masked_shift, shift, <superset of implicit mask>
+ // SHL dst, value, masked_shift
+ // with
+ // SHL dst, value, shift
+ if (shift_amount->IsAnd()) {
+ HAnd* and_insn = shift_amount->AsAnd();
+ HConstant* mask = and_insn->GetConstantRight();
+ if ((mask != nullptr) && ((Int64FromConstant(mask) & implicit_mask) == implicit_mask)) {
+ instruction->ReplaceInput(and_insn->GetLeastConstantLeft(), 1);
+ RecordSimplification();
}
}
}
@@ -1685,13 +1704,32 @@
invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
}
+void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) {
+ HInstruction* str = invoke->InputAt(0);
+ HInstruction* index = invoke->InputAt(1);
+ uint32_t dex_pc = invoke->GetDexPc();
+ ArenaAllocator* arena = GetGraph()->GetArena();
+ // We treat String as an array to allow DCE and BCE to seamlessly work on strings,
+ // so create the HArrayLength, HBoundsCheck and HArrayGet.
+ HArrayLength* length = new (arena) HArrayLength(str, dex_pc, /* is_string_length */ true);
+ invoke->GetBlock()->InsertInstructionBefore(length, invoke);
+ HBoundsCheck* bounds_check =
+ new (arena) HBoundsCheck(index, length, dex_pc, invoke->GetDexMethodIndex());
+ invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke);
+ HArrayGet* array_get =
+ new (arena) HArrayGet(str, index, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get);
+ bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment());
+ GetGraph()->SetHasBoundsChecks(true);
+}
+
void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke) {
HInstruction* str = invoke->InputAt(0);
uint32_t dex_pc = invoke->GetDexPc();
// We treat String as an array to allow DCE and BCE to seamlessly work on strings,
// so create the HArrayLength.
- HArrayLength* length = new (GetGraph()->GetArena()) HArrayLength(str, dex_pc);
- length->MarkAsStringLength();
+ HArrayLength* length =
+ new (GetGraph()->GetArena()) HArrayLength(str, dex_pc, /* is_string_length */ true);
HInstruction* replacement;
if (invoke->GetIntrinsic() == Intrinsics::kStringIsEmpty) {
// For String.isEmpty(), create the `HEqual` representing the `length == 0`.
@@ -1752,6 +1790,9 @@
case Intrinsics::kDoubleDoubleToLongBits:
SimplifyFP2Int(instruction);
break;
+ case Intrinsics::kStringCharAt:
+ SimplifyStringCharAt(instruction);
+ break;
case Intrinsics::kStringIsEmpty:
case Intrinsics::kStringLength:
SimplifyStringIsEmptyOrLength(instruction);
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index e4a711e..983d31d 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -19,6 +19,7 @@
#include "common_arm64.h"
#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace arm64 {
@@ -30,7 +31,7 @@
void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access,
HInstruction* array,
HInstruction* index,
- int access_size) {
+ size_t data_offset) {
if (kEmitCompilerReadBarrier) {
// The read barrier instrumentation does not support the
// HArm64IntermediateAddress instruction yet.
@@ -55,8 +56,7 @@
// Proceed to extract the base address computation.
ArenaAllocator* arena = GetGraph()->GetArena();
- HIntConstant* offset =
- GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value());
+ HIntConstant* offset = GetGraph()->GetIntConstant(data_offset);
HArm64IntermediateAddress* address =
new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc);
address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
@@ -189,17 +189,20 @@
}
void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
+ size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
TryExtractArrayAccessAddress(instruction,
instruction->GetArray(),
instruction->GetIndex(),
- Primitive::ComponentSize(instruction->GetType()));
+ data_offset);
}
void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
+ size_t access_size = Primitive::ComponentSize(instruction->GetComponentType());
+ size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value();
TryExtractArrayAccessAddress(instruction,
instruction->GetArray(),
instruction->GetIndex(),
- Primitive::ComponentSize(instruction->GetComponentType()));
+ data_offset);
}
void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index da26998..4735f85 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -38,7 +38,7 @@
void TryExtractArrayAccessAddress(HInstruction* access,
HInstruction* array,
HInstruction* index,
- int access_size);
+ size_t data_offset);
bool TryMergeIntoUsersShifterOperand(HInstruction* instruction);
bool TryMergeIntoShifterOperand(HInstruction* use,
HInstruction* bitfield_op,
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 83a5127..3429a8f 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -239,6 +239,7 @@
UNREACHABLE_INTRINSIC(Arch, LongCompare) \
UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
UNREACHABLE_INTRINSIC(Arch, LongSignum) \
+UNREACHABLE_INTRINSIC(Arch, StringCharAt) \
UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \
UNREACHABLE_INTRINSIC(Arch, StringLength) \
UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 29f7672..f43f8ed 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -47,19 +47,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathARM slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathARM for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathARM for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -524,8 +511,8 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
if (is_volatile) {
__ dmb(ISH);
}
@@ -581,10 +568,11 @@
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in InstructionCodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+ // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -919,9 +907,10 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS below).
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
}
@@ -932,58 +921,18 @@
GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
- ArmAssembler* assembler = GetAssembler();
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array
- const MemberOffset value_offset = mirror::String::ValueOffset();
- // Location of count
- const MemberOffset count_offset = mirror::String::CountOffset();
-
- Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer.
- Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character.
- Register out = locations->Out().AsRegister<Register>(); // Result character.
-
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register array_temp = locations->GetTemp(1).AsRegister<Register>();
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
- codegen_->AddSlowPath(slow_path);
-
- __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length.
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ cmp(idx, ShifterOperand(temp));
- __ b(slow_path->GetEntryLabel(), CS);
-
- __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value.
-
- // Load the value.
- __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx].
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
// The inputs plus one temp.
LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -1384,6 +1333,12 @@
}
void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ if (kEmitCompilerReadBarrier) {
+ return;
+ }
+
CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
LocationSummary* locations = invoke->GetLocations();
if (locations == nullptr) {
@@ -1468,11 +1423,11 @@
}
}
-// TODO: Implement read barriers in the SystemArrayCopy intrinsic.
-// Note that this code path is not used (yet) because we do not
-// intrinsify methods that can go into the IntrinsicSlowPathARM
-// slow path.
void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ DCHECK(!kEmitCompilerReadBarrier);
+
ArmAssembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -2031,7 +1986,7 @@
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
+ // Temporary registers to store lengths of strings and for calculations.
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
@@ -2059,28 +2014,55 @@
Register dstObj = locations->InAt(3).AsRegister<Register>();
Register dstBegin = locations->InAt(4).AsRegister<Register>();
- Register src_ptr = locations->GetTemp(0).AsRegister<Register>();
- Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>();
+ Register num_chr = locations->GetTemp(0).AsRegister<Register>();
+ Register src_ptr = locations->GetTemp(1).AsRegister<Register>();
Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
- Register tmp = locations->GetTemp(3).AsRegister<Register>();
// src range to copy.
__ add(src_ptr, srcObj, ShifterOperand(value_offset));
- __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1));
__ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
// dst to be copied.
__ add(dst_ptr, dstObj, ShifterOperand(data_offset));
__ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
+ __ subs(num_chr, srcEnd, ShifterOperand(srcBegin));
+
// Do the copy.
- Label loop, done;
- __ Bind(&loop);
- __ cmp(src_ptr, ShifterOperand(src_ptr_end));
+ Label loop, remainder, done;
+
+ // Early out for valid zero-length retrievals.
__ b(&done, EQ);
- __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex));
- __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex));
- __ b(&loop);
+
+ // Save repairing the value of num_chr on the < 4 character path.
+ __ subs(IP, num_chr, ShifterOperand(4));
+ __ b(&remainder, LT);
+
+ // Keep the result of the earlier subs, we are going to fetch at least 4 characters.
+ __ mov(num_chr, ShifterOperand(IP));
+
+ // Main loop used for longer fetches loads and stores 4x16-bit characters at a time.
+ // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code
+ // to rectify these everywhere this intrinsic applies.)
+ __ Bind(&loop);
+ __ ldr(IP, Address(src_ptr, char_size * 2));
+ __ subs(num_chr, num_chr, ShifterOperand(4));
+ __ str(IP, Address(dst_ptr, char_size * 2));
+ __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex));
+ __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex));
+ __ b(&loop, GE);
+
+ __ adds(num_chr, num_chr, ShifterOperand(4));
+ __ b(&done, EQ);
+
+ // Main loop for < 4 character case and remainder handling. Loads and stores one
+ // 16-bit Java character at a time.
+ __ Bind(&remainder);
+ __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex));
+ __ subs(num_chr, num_chr, ShifterOperand(1));
+ __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex));
+ __ b(&remainder, GT);
+
__ Bind(&done);
}
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index d776fb4..1685cf9 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -149,19 +149,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathARM64 slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathARM64 for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -791,8 +778,15 @@
// UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
UseScratchRegisterScope temps(masm);
Register temp = temps.AcquireW();
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
+ trg_loc,
+ base,
+ /* offset */ 0U,
+ /* index */ offset_loc,
+ /* scale_factor */ 0U,
+ temp,
+ /* needs_null_check */ false,
+ is_volatile);
} else {
// Other cases.
MemOperand mem_op(base.X(), offset);
@@ -821,7 +815,8 @@
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
@@ -1102,9 +1097,10 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS below).
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
}
@@ -1119,59 +1115,18 @@
GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- // In case we need to go in the slow path, we can't have the output be the same
- // as the input: the current liveness analysis considers the input to be live
- // at the point of the call.
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
- vixl::MacroAssembler* masm = GetVIXLAssembler();
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array
- const MemberOffset value_offset = mirror::String::ValueOffset();
- // Location of count
- const MemberOffset count_offset = mirror::String::CountOffset();
-
- Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer.
- Register idx = WRegisterFrom(locations->InAt(1)); // Index of character.
- Register out = WRegisterFrom(locations->Out()); // Result character.
-
- UseScratchRegisterScope temps(masm);
- Register temp = temps.AcquireW();
- Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling.
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
- codegen_->AddSlowPath(slow_path);
-
- __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length.
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ Cmp(idx, temp);
- __ B(hs, slow_path->GetEntryLabel());
-
- __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value.
-
- // Load the value.
- __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx].
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
invoke->InputAt(1)->CanBeNull()
@@ -1745,6 +1700,7 @@
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
}
void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
@@ -1770,29 +1726,57 @@
Register dstBegin = XRegisterFrom(locations->InAt(4));
Register src_ptr = XRegisterFrom(locations->GetTemp(0));
- Register src_ptr_end = XRegisterFrom(locations->GetTemp(1));
+ Register num_chr = XRegisterFrom(locations->GetTemp(1));
+ Register tmp1 = XRegisterFrom(locations->GetTemp(2));
UseScratchRegisterScope temps(masm);
Register dst_ptr = temps.AcquireX();
- Register tmp = temps.AcquireW();
+ Register tmp2 = temps.AcquireX();
- // src range to copy.
+ // src address to copy from.
__ Add(src_ptr, srcObj, Operand(value_offset));
- __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1));
__ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
- // dst to be copied.
+ // dst address start to copy to.
__ Add(dst_ptr, dstObj, Operand(data_offset));
__ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
+ __ Sub(num_chr, srcEnd, srcBegin);
+
// Do the copy.
- vixl::Label loop, done;
+ vixl::Label loop;
+ vixl::Label done;
+ vixl::Label remainder;
+
+ // Early out for valid zero-length retrievals.
+ __ Cbz(num_chr, &done);
+
+ // Save repairing the value of num_chr on the < 8 character path.
+ __ Subs(tmp1, num_chr, 8);
+ __ B(lt, &remainder);
+
+ // Keep the result of the earlier subs, we are going to fetch at least 8 characters.
+ __ Mov(num_chr, tmp1);
+
+ // Main loop used for longer fetches loads and stores 8x16-bit characters at a time.
+ // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.)
__ Bind(&loop);
- __ Cmp(src_ptr, src_ptr_end);
- __ B(&done, eq);
- __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex));
- __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex));
- __ B(&loop);
+ __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, vixl::PostIndex));
+ __ Subs(num_chr, num_chr, 8);
+ __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, vixl::PostIndex));
+ __ B(ge, &loop);
+
+ __ Adds(num_chr, num_chr, 8);
+ __ B(eq, &done);
+
+ // Main loop for < 8 character case and remainder handling. Loads and stores one
+ // 16-bit Java character at a time.
+ __ Bind(&remainder);
+ __ Ldrh(tmp1, MemOperand(src_ptr, char_size, vixl::PostIndex));
+ __ Subs(num_chr, num_chr, 1);
+ __ Strh(tmp1, MemOperand(dst_ptr, char_size, vixl::PostIndex));
+ __ B(gt, &remainder);
+
__ Bind(&done);
}
@@ -2033,6 +2017,12 @@
// We want to use two temporary registers in order to reduce the register pressure in arm64.
// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ if (kEmitCompilerReadBarrier) {
+ return;
+ }
+
// Check to see if we have known failures that will cause us to have to bail out
// to the runtime, and just generate the runtime call directly.
HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
@@ -2085,6 +2075,10 @@
}
void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ DCHECK(!kEmitCompilerReadBarrier);
+
vixl::MacroAssembler* masm = GetVIXLAssembler();
LocationSummary* locations = invoke->GetLocations();
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 140f56a..d4f44d6 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1872,54 +1872,6 @@
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-// char java.lang.String.charAt(int index)
-void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- // The inputs will be considered live at the last instruction and restored. This would overwrite
- // the output with kNoOutputOverlap.
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
- MipsAssembler* assembler = GetAssembler();
-
- // Location of reference to data array
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- Register obj = locations->InAt(0).AsRegister<Register>();
- Register idx = locations->InAt(1).AsRegister<Register>();
- Register out = locations->Out().AsRegister<Register>();
-
- // TODO: Maybe we can support range check elimination. Overall,
- // though, I think it's not worth the cost.
- // TODO: For simplicity, the index parameter is requested in a
- // register, so different from Quick we will not optimize the
- // code for constants (which would save a register).
-
- SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
- codegen_->AddSlowPath(slow_path);
-
- // Load the string size
- __ Lw(TMP, obj, count_offset);
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- // Revert to slow path if idx is too large, or negative
- __ Bgeu(idx, TMP, slow_path->GetEntryLabel());
-
- // out = obj[2*idx].
- __ Sll(TMP, idx, 1); // idx * 2
- __ Addu(TMP, TMP, obj); // Address of char at location idx
- __ Lhu(out, TMP, value_offset); // Load char at location idx
-
- __ Bind(slow_path->GetExitLabel());
-}
-
// int java.lang.String.compareTo(String anotherString)
void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 6c4e64e..9243f4c 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1371,52 +1371,6 @@
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-// char java.lang.String.charAt(int index)
-void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
- Mips64Assembler* assembler = GetAssembler();
-
- // Location of reference to data array
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
- GpuRegister idx = locations->InAt(1).AsRegister<GpuRegister>();
- GpuRegister out = locations->Out().AsRegister<GpuRegister>();
-
- // TODO: Maybe we can support range check elimination. Overall,
- // though, I think it's not worth the cost.
- // TODO: For simplicity, the index parameter is requested in a
- // register, so different from Quick we will not optimize the
- // code for constants (which would save a register).
-
- SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke);
- codegen_->AddSlowPath(slow_path);
-
- // Load the string size
- __ Lw(TMP, obj, count_offset);
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- // Revert to slow path if idx is too large, or negative
- __ Bgeuc(idx, TMP, slow_path->GetEntryLabel());
-
- // out = obj[2*idx].
- __ Sll(TMP, idx, 1); // idx * 2
- __ Daddu(TMP, TMP, obj); // Address of char at location idx
- __ Lhu(out, TMP, value_offset); // Load char at location idx
-
- __ Bind(slow_path->GetExitLabel());
-}
-
// int java.lang.String.compareTo(String anotherString)
void IntrinsicLocationsBuilderMIPS64::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 05377f9..031cd13 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -60,19 +60,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathX86 slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathX86 for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathX86 for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -1030,48 +1017,6 @@
GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
}
-void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) {
- // The inputs plus one temp.
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array.
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count.
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- Register obj = locations->InAt(0).AsRegister<Register>();
- Register idx = locations->InAt(1).AsRegister<Register>();
- Register out = locations->Out().AsRegister<Register>();
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);
- codegen_->AddSlowPath(slow_path);
-
- X86Assembler* assembler = GetAssembler();
-
- __ cmpl(idx, Address(obj, count_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ j(kAboveEqual, slow_path->GetEntryLabel());
-
- // out = out[2*idx].
- __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset));
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderX86::VisitSystemArrayCopyChar(HInvoke* invoke) {
// We need at least two of the positions or length to be an integer constant,
// or else we won't have enough free registers.
@@ -1864,8 +1809,9 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ Address src(base, offset, ScaleFactor::TIMES_1, 0);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke, output_loc, base, src, temp, /* needs_null_check */ false);
} else {
__ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
codegen->GenerateReadBarrierSlow(
@@ -1920,16 +1866,17 @@
if (is_volatile) {
// Need to use XMM to read volatile.
locations->AddTemp(Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
} else {
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
} else {
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in InstructionCodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier.
+ // path in InstructionCodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -2151,9 +2098,9 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented.
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Implement a read barrier in GenCAS below and re-enable
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
// this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
@@ -2278,6 +2225,15 @@
}
void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCAS(Primitive::kPrimNot, invoke, codegen_);
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 67c2f3a..c5b44d4 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -54,19 +54,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathX86_64 slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathX86_64 for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathX86_64 for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -891,49 +878,6 @@
GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
}
-void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
- // The inputs plus one temp.
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::SameAsFirstInput());
- locations->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array.
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count.
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
- CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
- codegen_->AddSlowPath(slow_path);
-
- X86_64Assembler* assembler = GetAssembler();
-
- __ cmpl(idx, Address(obj, count_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ j(kAboveEqual, slow_path->GetEntryLabel());
-
- // out = out[2*idx].
- __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset));
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) {
// Check to see if we have known failures that will cause us to have to bail out
// to the runtime, and just generate the runtime call directly.
@@ -1122,14 +1066,20 @@
void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ if (kEmitCompilerReadBarrier) {
+ return;
+ }
+
CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
}
-// TODO: Implement read barriers in the SystemArrayCopy intrinsic.
-// Note that this code path is not used (yet) because we do not
-// intrinsify methods that can go into the IntrinsicSlowPathX86_64
-// slow path.
void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ DCHECK(!kEmitCompilerReadBarrier);
+
X86_64Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -1953,8 +1903,9 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ Address src(base, offset, ScaleFactor::TIMES_1, 0);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke, output_loc, base, src, temp, /* needs_null_check */ false);
} else {
__ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
codegen->GenerateReadBarrierSlow(
@@ -1991,10 +1942,11 @@
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in InstructionCodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier.
+ // path in InstructionCodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -2178,9 +2130,9 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented.
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Implement a read barrier in GenCAS below and re-enable
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
// this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
@@ -2296,6 +2248,15 @@
}
void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCAS(Primitive::kPrimNot, invoke, codegen_);
}
@@ -2484,7 +2445,7 @@
: CTZ(static_cast<uint32_t>(value));
}
if (is_long) {
- codegen->Load64BitValue(out, 1L << value);
+ codegen->Load64BitValue(out, 1ULL << value);
} else {
codegen->Load32BitValue(out, 1 << value);
}
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 8a75a90..7347686 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -65,6 +65,16 @@
is_singleton_and_not_returned_ = false;
return;
}
+ if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) ||
+ (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) {
+ // The field is accessed in an unresolved way. We mark the object as a singleton to
+ // disable load/store optimizations on it.
+ // Note that we could optimize this case and still perform some optimizations until
+ // we hit the unresolved access, but disabling is the simplest.
+ is_singleton_ = false;
+ is_singleton_and_not_returned_ = false;
+ return;
+ }
if (user->IsReturn()) {
is_singleton_and_not_returned_ = false;
}
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 63bbc2c..3f27c91 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -38,7 +38,13 @@
class Location : public ValueObject {
public:
enum OutputOverlap {
+ // The liveness of the output overlaps the liveness of one or
+ // several input(s); the register allocator cannot reuse an
+ // input's location for the output's location.
kOutputOverlap,
+ // The liveness of the output does not overlap the liveness of any
+ // input; the register allocator is allowed to reuse an input's
+ // location for the output's location.
kNoOutputOverlap
};
@@ -494,6 +500,10 @@
return inputs_.size();
}
+ // Set the output location. Argument `overlaps` tells whether the
+ // output overlaps any of the inputs (if so, it cannot share the
+ // same register as one of the inputs); it is set to
+ // `Location::kOutputOverlap` by default for safety.
void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) {
DCHECK(output_.IsInvalid());
output_overlaps_ = overlaps;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ae3c4b0..c2c212b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2283,8 +2283,6 @@
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsValidHandle(type_handle));
- DCHECK(!type_handle->IsErroneous());
- DCHECK(!type_handle->IsArrayClass() || !type_handle->GetComponentType()->IsErroneous());
if (!is_exact) {
DCHECK(!type_handle->CannotBeAssignedFromOtherTypes())
<< "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed";
@@ -2432,8 +2430,69 @@
}
}
+bool HLoadClass::InstructionDataEquals(const HInstruction* other) const {
+ const HLoadClass* other_load_class = other->AsLoadClass();
+ // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type
+ // names rather than type indexes. However, we shall also have to re-think the hash code.
+ if (type_index_ != other_load_class->type_index_ ||
+ GetPackedFields() != other_load_class->GetPackedFields()) {
+ return false;
+ }
+ LoadKind load_kind = GetLoadKind();
+ if (HasAddress(load_kind)) {
+ return GetAddress() == other_load_class->GetAddress();
+ } else if (HasTypeReference(load_kind)) {
+ return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
+ } else {
+ DCHECK(HasDexCacheReference(load_kind)) << load_kind;
+ // If the type indexes and dex files are the same, dex cache element offsets
+ // must also be the same, so we don't need to compare them.
+ return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile());
+ }
+}
+
+void HLoadClass::SetLoadKindInternal(LoadKind load_kind) {
+ // Once sharpened, the load kind should not be changed again.
+ // Also, kReferrersClass should never be overwritten.
+ DCHECK_EQ(GetLoadKind(), LoadKind::kDexCacheViaMethod);
+ SetPackedField<LoadKindField>(load_kind);
+
+ if (load_kind != LoadKind::kDexCacheViaMethod) {
+ RemoveAsUserOfInput(0u);
+ SetRawInputAt(0u, nullptr);
+ }
+ if (!NeedsEnvironment()) {
+ RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) {
+ switch (rhs) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ return os << "ReferrersClass";
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ return os << "BootImageLinkTimeAddress";
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ return os << "BootImageLinkTimePcRelative";
+ case HLoadClass::LoadKind::kBootImageAddress:
+ return os << "BootImageAddress";
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ return os << "DexCacheAddress";
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ return os << "DexCachePcRelative";
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ return os << "DexCacheViaMethod";
+ default:
+ LOG(FATAL) << "Unknown HLoadClass::LoadKind: " << static_cast<int>(rhs);
+ UNREACHABLE();
+ }
+}
+
bool HLoadString::InstructionDataEquals(const HInstruction* other) const {
const HLoadString* other_load_string = other->AsLoadString();
+ // TODO: To allow GVN for HLoadString from different dex files, we should compare the strings
+ // rather than their indexes. However, we shall also have to re-think the hash code.
if (string_index_ != other_load_string->string_index_ ||
GetPackedFields() != other_load_string->GetPackedFields()) {
return false;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 711a6c1..6b2c33e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
#include "base/arena_object.h"
#include "base/stl_util.h"
#include "dex/compiler_enums.h"
+#include "dex_file.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
@@ -85,6 +86,16 @@
static constexpr uint32_t kNoDexPc = -1;
+inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) {
+ // For the purposes of the compiler, the dex files must actually be the same object
+ // if we want to safely treat them as the same. This is especially important for JIT
+ // as custom class loaders can open the same underlying file (or memory) multiple
+ // times and provide different class resolution but no two class loaders should ever
+ // use the same DexFile object - doing so is an unsupported hack that can lead to
+ // all sorts of weird failures.
+ return &lhs == &rhs;
+}
+
enum IfCondition {
// All types.
kCondEQ, // ==
@@ -161,6 +172,10 @@
static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact);
+ static ReferenceTypeInfo Create(TypeHandle type_handle) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return Create(type_handle, type_handle->CannotBeAssignedFromOtherTypes());
+ }
+
static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) {
return ReferenceTypeInfo(type_handle, is_exact);
}
@@ -1920,6 +1935,14 @@
environment_ = environment;
}
+ void InsertRawEnvironment(HEnvironment* environment) {
+ DCHECK(environment_ != nullptr);
+ DCHECK_EQ(environment->GetHolder(), this);
+ DCHECK(environment->GetParent() == nullptr);
+ environment->parent_ = environment_;
+ environment_ = environment;
+ }
+
void RemoveEnvironment();
// Set the environment of this instruction, copying it from `environment`. While
@@ -5006,7 +5029,7 @@
}
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
- return (obj == InputAt(0)) && GetFieldOffset().Uint32Value() < kPageSize;
+ return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value());
}
size_t ComputeHashCode() const OVERRIDE {
@@ -5053,7 +5076,7 @@
}
bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
- return (obj == InputAt(0)) && GetFieldOffset().Uint32Value() < kPageSize;
+ return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value());
}
const FieldInfo& GetFieldInfo() const { return field_info_; }
@@ -5079,8 +5102,13 @@
class HArrayGet FINAL : public HExpression<2> {
public:
- HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc)
+ HArrayGet(HInstruction* array,
+ HInstruction* index,
+ Primitive::Type type,
+ uint32_t dex_pc,
+ bool is_string_char_at = false)
: HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
+ SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
@@ -5114,12 +5142,24 @@
return result;
}
+ bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); }
+
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
DECLARE_INSTRUCTION(ArrayGet);
private:
+ // We treat a String as an array, creating the HArrayGet from String.charAt()
+ // intrinsic in the instruction simplifier. We can always determine whether
+ // a particular HArrayGet is actually a String.charAt() by looking at the type
+ // of the input but that requires holding the mutator lock, so we prefer to use
+ // a flag, so that code generators don't need to do the locking.
+ static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
+ static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1;
+ static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
DISALLOW_COPY_AND_ASSIGN(HArrayGet);
};
@@ -5225,8 +5265,9 @@
class HArrayLength FINAL : public HExpression<1> {
public:
- HArrayLength(HInstruction* array, uint32_t dex_pc)
+ HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false)
: HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+ SetPackedFlag<kFlagIsStringLength>(is_string_length);
// Note that arrays do not change length, so the instruction does not
// depend on any write.
SetRawInputAt(0, array);
@@ -5240,7 +5281,6 @@
return obj == InputAt(0);
}
- void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); }
bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); }
DECLARE_INSTRUCTION(ArrayLength);
@@ -5263,8 +5303,12 @@
public:
// `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException`
// constructor.
- HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
- : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ HBoundsCheck(HInstruction* index,
+ HInstruction* length,
+ uint32_t dex_pc,
+ uint32_t string_char_at_method_index = DexFile::kDexNoIndex)
+ : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc),
+ string_char_at_method_index_(string_char_at_method_index) {
DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
SetRawInputAt(0, index);
SetRawInputAt(1, length);
@@ -5279,11 +5323,23 @@
bool CanThrow() const OVERRIDE { return true; }
+ bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; }
+ uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; }
+
HInstruction* GetIndex() const { return InputAt(0); }
DECLARE_INSTRUCTION(BoundsCheck);
private:
+ // We treat a String as an array, creating the HBoundsCheck from String.charAt()
+ // intrinsic in the instruction simplifier. We want to include the String.charAt()
+ // in the stack trace if we actually throw the StringIndexOutOfBoundsException,
+ // so we need to create an HEnvironment which will be translated to an InlineInfo
+ // indicating the extra stack frame. Since we add this HEnvironment quite late,
+ // in the PrepareForRegisterAllocation pass, we need to remember the method index
+ // from the invoke as we don't want to look again at the dex bytecode.
+ uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array.
+
DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
};
@@ -5329,8 +5385,44 @@
/**
* Instruction to load a Class object.
*/
-class HLoadClass FINAL : public HExpression<1> {
+class HLoadClass FINAL : public HInstruction {
public:
+ // Determines how to load the Class.
+ enum class LoadKind {
+ // Use the Class* from the method's own ArtMethod*.
+ kReferrersClass,
+
+ // Use boot image Class* address that will be known at link time.
+ // Used for boot image classes referenced by boot image code in non-PIC mode.
+ kBootImageLinkTimeAddress,
+
+ // Use PC-relative boot image Class* address that will be known at link time.
+ // Used for boot image classes referenced by boot image code in PIC mode.
+ kBootImageLinkTimePcRelative,
+
+ // Use a known boot image Class* address, embedded in the code by the codegen.
+ // Used for boot image classes referenced by apps in AOT- and JIT-compiled code.
+ // Note: codegen needs to emit a linker patch if indicated by compiler options'
+ // GetIncludePatchInformation().
+ kBootImageAddress,
+
+ // Load from the resolved types array at an absolute address.
+ // Used for classes outside the boot image referenced by JIT-compiled code.
+ kDexCacheAddress,
+
+ // Load from resolved types array in the dex cache using a PC-relative load.
+ // Used for classes outside boot image when we know that we can access
+ // the dex cache arrays using a PC-relative load.
+ kDexCachePcRelative,
+
+ // Load from resolved types array accessed through the class loaded from
+ // the compiled method's own ArtMethod*. This is the default access type when
+ // all other types are unavailable.
+ kDexCacheViaMethod,
+
+ kLast = kDexCacheViaMethod
+ };
+
HLoadClass(HCurrentMethod* current_method,
uint16_t type_index,
const DexFile& dex_file,
@@ -5338,7 +5430,8 @@
uint32_t dex_pc,
bool needs_access_check,
bool is_in_dex_cache)
- : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
+ : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
+ special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
dex_file_(dex_file),
loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
@@ -5346,26 +5439,47 @@
// methods so we can't possibly end up in this situation.
DCHECK(!is_referrers_class || !needs_access_check);
- SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class);
+ SetPackedField<LoadKindField>(
+ is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
SetPackedFlag<kFlagGenerateClInitCheck>(false);
- SetRawInputAt(0, current_method);
+ }
+
+ void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) {
+ DCHECK(HasAddress(load_kind));
+ load_data_.address = address;
+ SetLoadKindInternal(load_kind);
+ }
+
+ void SetLoadKindWithTypeReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t type_index) {
+ DCHECK(HasTypeReference(load_kind));
+ DCHECK(IsSameDexFile(dex_file_, dex_file));
+ DCHECK_EQ(type_index_, type_index);
+ SetLoadKindInternal(load_kind);
+ }
+
+ void SetLoadKindWithDexCacheReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t element_index) {
+ DCHECK(HasDexCacheReference(load_kind));
+ DCHECK(IsSameDexFile(dex_file_, dex_file));
+ load_data_.dex_cache_element_index = element_index;
+ SetLoadKindInternal(load_kind);
+ }
+
+ LoadKind GetLoadKind() const {
+ return GetPackedField<LoadKindField>();
}
bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- // Note that we don't need to test for generate_clinit_check_.
- // Whether or not we need to generate the clinit check is processed in
- // prepare_for_register_allocator based on existing HInvokes and HClinitChecks.
- return other->AsLoadClass()->type_index_ == type_index_ &&
- other->AsLoadClass()->GetPackedFields() == GetPackedFields();
- }
+ bool InstructionDataEquals(const HInstruction* other) const;
size_t ComputeHashCode() const OVERRIDE { return type_index_; }
- uint16_t GetTypeIndex() const { return type_index_; }
bool CanBeNull() const OVERRIDE { return false; }
bool NeedsEnvironment() const OVERRIDE {
@@ -5400,7 +5514,15 @@
loaded_class_rti_ = rti;
}
- const DexFile& GetDexFile() { return dex_file_; }
+ uint32_t GetTypeIndex() const { return type_index_; }
+ const DexFile& GetDexFile() const { return dex_file_; }
+
+ uint32_t GetDexCacheElementOffset() const;
+
+ uint64_t GetAddress() const {
+ DCHECK(HasAddress(GetLoadKind()));
+ return load_data_.address;
+ }
bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); }
@@ -5408,30 +5530,96 @@
return SideEffects::CanTriggerGC();
}
- bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); }
+ bool IsReferrersClass() const { return GetLoadKind() == LoadKind::kReferrersClass; }
bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); }
+ void MarkInDexCache() {
+ SetPackedFlag<kFlagIsInDexCache>(true);
+ DCHECK(!NeedsEnvironment());
+ RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
+ }
+
+ void AddSpecialInput(HInstruction* special_input);
+
+ using HInstruction::GetInputRecords; // Keep the const version visible.
+ ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
+ return ArrayRef<HUserRecord<HInstruction*>>(
+ &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
+ }
+
+ Primitive::Type GetType() const OVERRIDE {
+ return Primitive::kPrimNot;
+ }
+
DECLARE_INSTRUCTION(LoadClass);
private:
- static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits;
- static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1;
+ static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits;
static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1;
// Whether this instruction must generate the initialization check.
// Used for code generation.
static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1;
- static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1;
+ static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1;
+ static constexpr size_t kFieldLoadKindSize =
+ MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
+ static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize;
static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
+ using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
+
+ static bool HasTypeReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageLinkTimeAddress ||
+ load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == LoadKind::kDexCacheViaMethod ||
+ load_kind == LoadKind::kReferrersClass;
+ }
+
+ static bool HasAddress(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageAddress || load_kind == LoadKind::kDexCacheAddress;
+ }
+
+ static bool HasDexCacheReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kDexCachePcRelative;
+ }
+
+ void SetLoadKindInternal(LoadKind load_kind);
+
+ // The special input is the HCurrentMethod for kDexCacheViaMethod or kReferrersClass.
+ // For other load kinds it's empty or possibly some architecture-specific instruction
+ // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative.
+ HUserRecord<HInstruction*> special_input_;
const uint16_t type_index_;
const DexFile& dex_file_;
+ union {
+ uint32_t dex_cache_element_index; // Only for dex cache reference.
+ uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets.
+ } load_data_;
+
ReferenceTypeInfo loaded_class_rti_;
DISALLOW_COPY_AND_ASSIGN(HLoadClass);
};
+std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
+
+// Note: defined outside class to see operator<<(., HLoadClass::LoadKind).
+inline uint32_t HLoadClass::GetDexCacheElementOffset() const {
+ DCHECK(HasDexCacheReference(GetLoadKind())) << GetLoadKind();
+ return load_data_.dex_cache_element_index;
+}
+
+// Note: defined outside class to see operator<<(., HLoadClass::LoadKind).
+inline void HLoadClass::AddSpecialInput(HInstruction* special_input) {
+ // The special input is used for PC-relative loads on some architectures.
+ DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
+ GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind();
+ DCHECK(special_input_.GetInstruction() == nullptr);
+ special_input_ = HUserRecord<HInstruction*>(special_input);
+ special_input->AddUseAt(this, 0);
+}
class HLoadString FINAL : public HInstruction {
public:
@@ -5599,6 +5787,9 @@
void SetLoadKindInternal(LoadKind load_kind);
+ // The special input is the HCurrentMethod for kDexCacheViaMethod.
+ // For other load kinds it's empty or possibly some architecture-specific instruction
+ // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative.
HUserRecord<HInstruction*> special_input_;
// String index serves also as the hash code and it's also needed for slow-paths,
@@ -6572,16 +6763,6 @@
}
}
-inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) {
- // For the purposes of the compiler, the dex files must actually be the same object
- // if we want to safely treat them as the same. This is especially important for JIT
- // as custom class loaders can open the same underlying file (or memory) multiple
- // times and provide different class resolution but no two class loaders should ever
- // use the same DexFile object - doing so is an unsupported hack that can lead to
- // all sorts of weird failures.
- return &lhs == &rhs;
-}
-
#define INSTRUCTION_TYPE_CHECK(type, super) \
inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \
inline const H##type* HInstruction::As##type() const { \
diff --git a/compiler/optimizing/optimizing_cfi_test_expected.inc b/compiler/optimizing/optimizing_cfi_test_expected.inc
index 764160a..05eb063 100644
--- a/compiler/optimizing/optimizing_cfi_test_expected.inc
+++ b/compiler/optimizing/optimizing_cfi_test_expected.inc
@@ -32,21 +32,21 @@
// 0x00000012: .cfi_def_cfa_offset: 64
static constexpr uint8_t expected_asm_kArm64[] = {
- 0xE0, 0x0F, 0x1C, 0xF8, 0xF4, 0xD7, 0x02, 0xA9, 0xFE, 0x1F, 0x00, 0xF9,
- 0xE8, 0xA7, 0x01, 0x6D, 0xE8, 0xA7, 0x41, 0x6D, 0xF4, 0xD7, 0x42, 0xA9,
- 0xFE, 0x1F, 0x40, 0xF9, 0xFF, 0x03, 0x01, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
+ 0xE0, 0x0F, 0x1C, 0xF8, 0xF4, 0x17, 0x00, 0xF9, 0xF5, 0x7B, 0x03, 0xA9,
+ 0xE8, 0xA7, 0x01, 0x6D, 0xE8, 0xA7, 0x41, 0x6D, 0xF4, 0x17, 0x40, 0xF9,
+ 0xF5, 0x7B, 0x43, 0xA9, 0xFF, 0x03, 0x01, 0x91, 0xC0, 0x03, 0x5F, 0xD6,
};
static constexpr uint8_t expected_cfi_kArm64[] = {
- 0x44, 0x0E, 0x40, 0x44, 0x94, 0x06, 0x95, 0x04, 0x44, 0x9E, 0x02, 0x44,
+ 0x44, 0x0E, 0x40, 0x44, 0x94, 0x06, 0x44, 0x95, 0x04, 0x9E, 0x02, 0x44,
0x05, 0x48, 0x0A, 0x05, 0x49, 0x08, 0x0A, 0x44, 0x06, 0x48, 0x06, 0x49,
- 0x44, 0xD4, 0xD5, 0x44, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
+ 0x44, 0xD4, 0x44, 0xD5, 0xDE, 0x44, 0x0E, 0x00, 0x44, 0x0B, 0x0E, 0x40,
};
// 0x00000000: str x0, [sp, #-64]!
// 0x00000004: .cfi_def_cfa_offset: 64
-// 0x00000004: stp x20, x21, [sp, #40]
+// 0x00000004: str x20, [sp, #40]
// 0x00000008: .cfi_offset: r20 at cfa-24
-// 0x00000008: .cfi_offset: r21 at cfa-16
-// 0x00000008: str lr, [sp, #56]
+// 0x00000008: stp x21, lr, [sp, #48]
+// 0x0000000c: .cfi_offset: r21 at cfa-16
// 0x0000000c: .cfi_offset: r30 at cfa-8
// 0x0000000c: stp d8, d9, [sp, #24]
// 0x00000010: .cfi_offset_extended: r72 at cfa-40
@@ -55,10 +55,10 @@
// 0x00000010: ldp d8, d9, [sp, #24]
// 0x00000014: .cfi_restore_extended: r72
// 0x00000014: .cfi_restore_extended: r73
-// 0x00000014: ldp x20, x21, [sp, #40]
+// 0x00000014: ldr x20, [sp, #40]
// 0x00000018: .cfi_restore: r20
-// 0x00000018: .cfi_restore: r21
-// 0x00000018: ldr lr, [sp, #56]
+// 0x00000018: ldp x21, lr, [sp, #48]
+// 0x0000001c: .cfi_restore: r21
// 0x0000001c: .cfi_restore: r30
// 0x0000001c: add sp, sp, #0x40 (64)
// 0x00000020: .cfi_def_cfa_offset: 0
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index cb2fc0a..93116f8 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -80,6 +80,15 @@
HandleInvoke(invoke);
}
+ void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
+ HLoadClass::LoadKind load_kind = load_class->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == HLoadClass::LoadKind::kDexCachePcRelative) {
+ InitializePCRelativeBasePointer();
+ load_class->AddSpecialInput(base_);
+ }
+ }
+
void VisitLoadString(HLoadString* load_string) OVERRIDE {
HLoadString::LoadKind load_kind = load_string->GetLoadKind();
if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative ||
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index c941c0c..8fb5396 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -40,6 +40,22 @@
void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) {
check->ReplaceWith(check->InputAt(0));
+ if (check->IsStringCharAt()) {
+ // Add a fake environment for String.charAt() inline info as we want
+ // the exception to appear as being thrown from there.
+ const DexFile& dex_file = check->GetEnvironment()->GetDexFile();
+ DCHECK_STREQ(PrettyMethod(check->GetStringCharAtMethodIndex(), dex_file).c_str(),
+ "char java.lang.String.charAt(int)");
+ ArenaAllocator* arena = GetGraph()->GetArena();
+ HEnvironment* environment = new (arena) HEnvironment(arena,
+ /* number_of_vregs */ 0u,
+ dex_file,
+ check->GetStringCharAtMethodIndex(),
+ /* dex_pc */ DexFile::kDexNoIndex,
+ kVirtual,
+ check);
+ check->InsertRawEnvironment(environment);
+ }
}
void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) {
@@ -130,7 +146,11 @@
instruction->ReplaceInput(GetGraph()->GetIntConstant(load_class->GetTypeIndex()), 0);
// The allocation entry point that deals with access checks does not work with inlined
// methods, so we need to check whether this allocation comes from an inlined method.
- if (has_only_one_use && !instruction->GetEnvironment()->IsFromInlinedInvoke()) {
+ // We also need to make the same check as for moving clinit check, whether the HLoadClass
+ // has the clinit check responsibility or not (HLoadClass can throw anyway).
+ if (has_only_one_use &&
+ !instruction->GetEnvironment()->IsFromInlinedInvoke() &&
+ CanMoveClinitCheck(load_class, instruction)) {
// We can remove the load class from the graph. If it needed access checks, we delegate
// the access check to the allocation.
if (load_class->NeedsAccessCheck()) {
@@ -187,7 +207,8 @@
HInstruction* user) const {
// Determine if input and user come from the same dex instruction, so that we can move
// the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
- // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user).
+ // to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user),
+ // or from HLoadClass (input) to HNewInstance (user).
// Start with a quick dex pc check.
if (user->GetDexPc() != input->GetDexPc()) {
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 2a281dd..3dfd728 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -46,13 +46,6 @@
return *cache;
}
-// Returns true if klass is admissible to the propagation: non-null and non-erroneous.
-// For an array type, we also check if the component type is admissible.
-static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
- return klass != nullptr && !klass->IsErroneous() &&
- (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
-}
-
ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() {
return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_);
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 2106be6..edd83bf 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -42,6 +42,14 @@
void Run() OVERRIDE;
+ // Returns true if klass is admissible to the propagation: non-null and resolved.
+ // For an array type, we also check if the component type is admissible.
+ static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return klass != nullptr &&
+ klass->IsResolved() &&
+ (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
+ }
+
static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation";
private:
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 08bd35f..97f34e6 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -40,13 +40,14 @@
HInstruction* instruction = it.Current();
if (instruction->IsInvokeStaticOrDirect()) {
ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect());
+ } else if (instruction->IsLoadClass()) {
+ ProcessLoadClass(instruction->AsLoadClass());
} else if (instruction->IsLoadString()) {
ProcessLoadString(instruction->AsLoadString());
}
// TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder
// here. Rewrite it to avoid the CompilerDriver's reliance on verifier data
// because we know the type better when inlining.
- // TODO: HLoadClass - select better load kind if available.
}
}
}
@@ -153,6 +154,123 @@
invoke->SetDispatchInfo(dispatch_info);
}
+void HSharpening::ProcessLoadClass(HLoadClass* load_class) {
+ if (load_class->NeedsAccessCheck()) {
+ // We need to call the runtime anyway, so we simply get the class as that call's return value.
+ return;
+ }
+ if (load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) {
+ // Loading from the ArtMethod* is the most efficient retrieval.
+ // TODO: This may not actually be true for all architectures and
+ // locations of target classes. The additional register pressure
+ // for using the ArtMethod* should be considered.
+ return;
+ }
+
+ DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kDexCacheViaMethod);
+ DCHECK(!load_class->IsInDexCache()) << "HLoadClass should not be optimized before sharpening.";
+
+ const DexFile& dex_file = load_class->GetDexFile();
+ uint32_t type_index = load_class->GetTypeIndex();
+
+ bool is_in_dex_cache = false;
+ HLoadClass::LoadKind desired_load_kind;
+ uint64_t address = 0u; // Class or dex cache element address.
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
+ ? compilation_unit_.GetDexCache()
+ : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+ mirror::Class* klass = dex_cache->GetResolvedType(type_index);
+
+ if (compiler_driver_->IsBootImage()) {
+ // Compiling boot image. Check if the class is a boot image class.
+ DCHECK(!runtime->UseJitCompilation());
+ if (!compiler_driver_->GetSupportBootImageFixup()) {
+ // MIPS/MIPS64 or compiler_driver_test. Do not sharpen.
+ desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+ } else {
+ if (klass != nullptr &&
+ compiler_driver_->IsImageClass(
+ dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
+ is_in_dex_cache = true;
+ desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
+ ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
+ : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
+ } else {
+ // Not a boot image class. We must go through the dex cache.
+ DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
+ desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
+ }
+ }
+ } else if (runtime->UseJitCompilation()) {
+ // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+ // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+ is_in_dex_cache = (klass != nullptr);
+ if (klass != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
+ desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(klass);
+ } else {
+ // Note: If the class is not in the dex cache or isn't initialized, the
+ // instruction needs environment and will not be inlined across dex files.
+ // Within a dex file, the slow-path helper loads the correct class and
+ // inlined frames are used correctly for OOM stack trace.
+ // TODO: Write a test for this. Bug: 29416588
+ desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress;
+ void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index];
+ address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
+ }
+ } else {
+ // AOT app compilation. Check if the class is in the boot image.
+ if ((klass != nullptr) &&
+ runtime->GetHeap()->ObjectIsInBootImageSpace(klass) &&
+ !codegen_->GetCompilerOptions().GetCompilePic()) {
+ desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(klass);
+ } else {
+ // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
+ // Use PC-relative load from the dex cache if the dex file belongs
+ // to the oat file that we're currently compiling.
+ desired_load_kind =
+ ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_class->GetDexFile())
+ ? HLoadClass::LoadKind::kDexCachePcRelative
+ : HLoadClass::LoadKind::kDexCacheViaMethod;
+ }
+ }
+ }
+ if (is_in_dex_cache) {
+ load_class->MarkInDexCache();
+ }
+
+ HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind);
+ switch (load_kind) {
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ load_class->SetLoadKindWithTypeReference(load_kind, dex_file, type_index);
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ case HLoadClass::LoadKind::kDexCacheAddress:
+ DCHECK_NE(address, 0u);
+ load_class->SetLoadKindWithAddress(load_kind, address);
+ break;
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ DexCacheArraysLayout layout(pointer_size, &dex_file);
+ size_t element_index = layout.TypeOffset(type_index);
+ load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected load kind: " << load_kind;
+ UNREACHABLE();
+ }
+}
+
void HSharpening::ProcessLoadString(HLoadString* load_string) {
DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod);
DCHECK(!load_string->IsInDexCache());
@@ -193,13 +311,14 @@
mirror::String* string = dex_cache->GetResolvedString(string_index);
is_in_dex_cache = (string != nullptr);
if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
+ // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
address = reinterpret_cast64<uint64_t>(string);
} else {
// Note: If the string is not in the dex cache, the instruction needs environment
// and will not be inlined across dex files. Within a dex file, the slow-path helper
// loads the correct string and inlined frames are used correctly for OOM stack trace.
- // TODO: Write a test for this.
+ // TODO: Write a test for this. Bug: 29416588
desired_load_kind = HLoadString::LoadKind::kDexCacheAddress;
void* dex_cache_element_address = &dex_cache->GetStrings()[string_index];
address = reinterpret_cast64<uint64_t>(dex_cache_element_address);
@@ -207,20 +326,18 @@
} else {
// AOT app compilation. Try to lookup the string without allocating if not found.
mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache);
- if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) {
- if (codegen_->GetCompilerOptions().GetCompilePic()) {
- // Use PC-relative load from the dex cache if the dex file belongs
- // to the oat file that we're currently compiling.
- desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)
- ? HLoadString::LoadKind::kDexCachePcRelative
- : HLoadString::LoadKind::kDexCacheViaMethod;
- } else {
- desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
- address = reinterpret_cast64<uint64_t>(string);
- }
+ if (string != nullptr &&
+ runtime->GetHeap()->ObjectIsInBootImageSpace(string) &&
+ !codegen_->GetCompilerOptions().GetCompilePic()) {
+ desired_load_kind = HLoadString::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(string);
} else {
- // Not JIT and the string is not in boot image.
- desired_load_kind = HLoadString::LoadKind::kDexCachePcRelative;
+ // Not JIT and either the string is not in boot image or we are compiling in PIC mode.
+ // Use PC-relative load from the dex cache if the dex file belongs
+ // to the oat file that we're currently compiling.
+ desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)
+ ? HLoadString::LoadKind::kDexCachePcRelative
+ : HLoadString::LoadKind::kDexCacheViaMethod;
}
}
}
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 24152f6..d35ae66 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -47,6 +47,7 @@
private:
void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
+ void ProcessLoadClass(HLoadClass* load_class);
void ProcessLoadString(HLoadString* load_string);
CodeGenerator* codegen_;
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 11a254e..fc8af64 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -228,7 +228,7 @@
void StackMapStream::ComputeInlineInfoEncoding() {
uint32_t method_index_max = 0;
- uint32_t dex_pc_max = 0;
+ uint32_t dex_pc_max = DexFile::kDexNoIndex;
uint32_t invoke_type_max = 0;
uint32_t inline_info_index = 0;
@@ -236,7 +236,10 @@
for (size_t j = 0; j < entry.inlining_depth; ++j) {
InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
method_index_max = std::max(method_index_max, inline_entry.method_index);
- dex_pc_max = std::max(dex_pc_max, inline_entry.dex_pc);
+ if (inline_entry.dex_pc != DexFile::kDexNoIndex &&
+ (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) {
+ dex_pc_max = inline_entry.dex_pc;
+ }
invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type));
}
}
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 41f72f5..53a9795 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -108,7 +108,7 @@
};
struct InlineInfoEntry {
- uint32_t dex_pc;
+ uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods.
uint32_t method_index;
InvokeType invoke_type;
uint32_t num_dex_registers;
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 274d0de..a571d14 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -671,6 +671,9 @@
virtual void vcmpdz(DRegister dd, Condition cond = AL) = 0;
virtual void vmstat(Condition cond = AL) = 0; // VMRS APSR_nzcv, FPSCR
+ virtual void vcntd(DRegister dd, DRegister dm) = 0;
+ virtual void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) = 0;
+
virtual void vpushs(SRegister reg, int nregs, Condition cond = AL) = 0;
virtual void vpushd(DRegister reg, int nregs, Condition cond = AL) = 0;
virtual void vpops(SRegister reg, int nregs, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 0a227b2..6f7119d 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1264,6 +1264,31 @@
Emit(encoding);
}
+void Arm32Assembler::vcntd(DRegister dd, DRegister dm) {
+ uint32_t encoding = (B31 | B30 | B29 | B28 | B25 | B24 | B23 | B21 | B20) |
+ ((static_cast<int32_t>(dd) >> 4) * B22) |
+ ((static_cast<uint32_t>(dd) & 0xf) * B12) |
+ (B10 | B8) |
+ ((static_cast<int32_t>(dm) >> 4) * B5) |
+ (static_cast<uint32_t>(dm) & 0xf);
+
+ Emit(encoding);
+}
+
+void Arm32Assembler::vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) {
+ CHECK(size == 8 || size == 16 || size == 32) << size;
+ uint32_t encoding = (B31 | B30 | B29 | B28 | B25 | B24 | B23 | B21 | B20) |
+ ((static_cast<uint32_t>(size >> 4) & 0x3) * B18) |
+ ((static_cast<int32_t>(dd) >> 4) * B22) |
+ ((static_cast<uint32_t>(dd) & 0xf) * B12) |
+ (B9) |
+ (is_unsigned ? B7 : 0) |
+ ((static_cast<int32_t>(dm) >> 4) * B5) |
+ (static_cast<uint32_t>(dm) & 0xf);
+
+ Emit(encoding);
+}
+
void Arm32Assembler::svc(uint32_t imm24) {
CHECK(IsUint<24>(imm24)) << imm24;
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index bc6020e..8726ac8 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -205,6 +205,9 @@
void vcmpdz(DRegister dd, Condition cond = AL) OVERRIDE;
void vmstat(Condition cond = AL) OVERRIDE; // VMRS APSR_nzcv, FPSCR
+ void vcntd(DRegister dd, DRegister dm) OVERRIDE;
+ void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) OVERRIDE;
+
void vpushs(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index e570e22..b214062 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -899,4 +899,43 @@
T3Helper(&arm::Arm32Assembler::revsh, true, "revsh{cond} {reg1}, {reg2}", "revsh");
}
+TEST_F(AssemblerArm32Test, vcnt) {
+ // Different D register numbers are used here, to test register encoding.
+ // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
+ // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
+ // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
+ GetAssembler()->vcntd(arm::D0, arm::D1);
+ GetAssembler()->vcntd(arm::D19, arm::D20);
+ GetAssembler()->vcntd(arm::D0, arm::D9);
+ GetAssembler()->vcntd(arm::D16, arm::D20);
+
+ std::string expected =
+ "vcnt.8 d0, d1\n"
+ "vcnt.8 d19, d20\n"
+ "vcnt.8 d0, d9\n"
+ "vcnt.8 d16, d20\n";
+
+ DriverStr(expected, "vcnt");
+}
+
+TEST_F(AssemblerArm32Test, vpaddl) {
+ // Different D register numbers are used here, to test register encoding.
+ // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
+ // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
+ // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
+ // Different data types (signed and unsigned) are also tested.
+ GetAssembler()->vpaddld(arm::D0, arm::D0, 8, true);
+ GetAssembler()->vpaddld(arm::D20, arm::D20, 8, false);
+ GetAssembler()->vpaddld(arm::D0, arm::D20, 16, false);
+ GetAssembler()->vpaddld(arm::D20, arm::D0, 32, true);
+
+ std::string expected =
+ "vpaddl.u8 d0, d0\n"
+ "vpaddl.s8 d20, d20\n"
+ "vpaddl.s16 d0, d20\n"
+ "vpaddl.u32 d20, d0\n";
+
+ DriverStr(expected, "vpaddl");
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 546dd65..a72ea41 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -3117,6 +3117,30 @@
Emit32(encoding);
}
+void Thumb2Assembler::vcntd(DRegister dd, DRegister dm) {
+ uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) |
+ ((static_cast<int32_t>(dd) >> 4) * B22) |
+ ((static_cast<uint32_t>(dd) & 0xf) * B12) |
+ (B10 | B8) |
+ ((static_cast<int32_t>(dm) >> 4) * B5) |
+ (static_cast<uint32_t>(dm) & 0xf);
+
+ Emit32(encoding);
+}
+
+void Thumb2Assembler::vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) {
+ CHECK(size == 8 || size == 16 || size == 32) << size;
+ uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) |
+ ((static_cast<uint32_t>(size >> 4) & 0x3) * B18) |
+ ((static_cast<int32_t>(dd) >> 4) * B22) |
+ ((static_cast<uint32_t>(dd) & 0xf) * B12) |
+ (B9) |
+ (is_unsigned ? B7 : 0) |
+ ((static_cast<int32_t>(dm) >> 4) * B5) |
+ (static_cast<uint32_t>(dm) & 0xf);
+
+ Emit32(encoding);
+}
void Thumb2Assembler::svc(uint32_t imm8) {
CHECK(IsUint<8>(imm8)) << imm8;
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index ce310a4..2ca74fc 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -250,6 +250,9 @@
void vcmpdz(DRegister dd, Condition cond = AL) OVERRIDE;
void vmstat(Condition cond = AL) OVERRIDE; // VMRS APSR_nzcv, FPSCR
+ void vcntd(DRegister dd, DRegister dm) OVERRIDE;
+ void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) OVERRIDE;
+
void vpushs(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE;
void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index b5cafcb..7f1dc49 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1380,4 +1380,43 @@
DriverStr(expected, "revsh");
}
+TEST_F(AssemblerThumb2Test, vcnt) {
+ // Different D register numbers are used here, to test register encoding.
+ // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
+ // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
+ // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
+ __ vcntd(arm::D0, arm::D1);
+ __ vcntd(arm::D19, arm::D20);
+ __ vcntd(arm::D0, arm::D9);
+ __ vcntd(arm::D16, arm::D20);
+
+ std::string expected =
+ "vcnt.8 d0, d1\n"
+ "vcnt.8 d19, d20\n"
+ "vcnt.8 d0, d9\n"
+ "vcnt.8 d16, d20\n";
+
+ DriverStr(expected, "vcnt");
+}
+
+TEST_F(AssemblerThumb2Test, vpaddl) {
+ // Different D register numbers are used here, to test register encoding.
+ // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd,
+ // For source and destination registers which use D0..D15, the M bit and D bit should be 0.
+ // For source and destination registers which use D16..D32, the M bit and D bit should be 1.
+ // Different data types (signed and unsigned) are also tested.
+ __ vpaddld(arm::D0, arm::D0, 8, true);
+ __ vpaddld(arm::D20, arm::D20, 8, false);
+ __ vpaddld(arm::D0, arm::D20, 16, false);
+ __ vpaddld(arm::D20, arm::D0, 32, true);
+
+ std::string expected =
+ "vpaddl.u8 d0, d0\n"
+ "vpaddl.s8 d20, d20\n"
+ "vpaddl.s16 d0, d20\n"
+ "vpaddl.u32 d20, d0\n";
+
+ DriverStr(expected, "vpaddl");
+}
+
} // namespace art
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 1842f00..54ed62b 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -648,6 +648,15 @@
void Arm64Assembler::SpillRegisters(vixl::CPURegList registers, int offset) {
int size = registers.RegisterSizeInBytes();
const Register sp = vixl_masm_->StackPointer();
+ // Since we are operating on register pairs, we would like to align on
+ // double the standard size; on the other hand, we don't want to insert
+ // an extra store, which will happen if the number of registers is even.
+ if (!IsAlignedParam(offset, 2 * size) && registers.Count() % 2 != 0) {
+ const CPURegister& dst0 = registers.PopLowestIndex();
+ ___ Str(dst0, MemOperand(sp, offset));
+ cfi_.RelOffset(DWARFReg(dst0), offset);
+ offset += size;
+ }
while (registers.Count() >= 2) {
const CPURegister& dst0 = registers.PopLowestIndex();
const CPURegister& dst1 = registers.PopLowestIndex();
@@ -667,6 +676,13 @@
void Arm64Assembler::UnspillRegisters(vixl::CPURegList registers, int offset) {
int size = registers.RegisterSizeInBytes();
const Register sp = vixl_masm_->StackPointer();
+ // Be consistent with the logic for spilling registers.
+ if (!IsAlignedParam(offset, 2 * size) && registers.Count() % 2 != 0) {
+ const CPURegister& dst0 = registers.PopLowestIndex();
+ ___ Ldr(dst0, MemOperand(sp, offset));
+ cfi_.Restore(DWARFReg(dst0));
+ offset += size;
+ }
while (registers.Count() >= 2) {
const CPURegister& dst0 = registers.PopLowestIndex();
const CPURegister& dst1 = registers.PopLowestIndex();
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 084e901..92b4c8e 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -344,6 +344,17 @@
}
template <typename ImmType>
+ std::string RepeatFFIb(void (Ass::*f)(FPReg, FPReg, ImmType), int imm_bits, std::string fmt) {
+ return RepeatTemplatedRegistersImmBits<FPReg, FPReg, ImmType>(f,
+ imm_bits,
+ GetFPRegisters(),
+ GetFPRegisters(),
+ &AssemblerTest::GetFPRegName,
+ &AssemblerTest::GetFPRegName,
+ fmt);
+ }
+
+ template <typename ImmType>
std::string RepeatIbFF(void (Ass::*f)(ImmType, FPReg, FPReg), int imm_bits, std::string fmt) {
return RepeatTemplatedImmBitsRegisters<ImmType, FPReg, FPReg>(f,
GetFPRegisters(),
@@ -461,7 +472,7 @@
void SetUp() OVERRIDE {
arena_.reset(new ArenaAllocator(&pool_));
- assembler_.reset(new (arena_.get()) Ass(arena_.get()));
+ assembler_.reset(CreateAssembler(arena_.get()));
test_helper_.reset(
new AssemblerTestInfrastructure(GetArchitectureString(),
GetAssemblerCmdName(),
@@ -481,6 +492,11 @@
arena_.reset();
}
+ // Override this to set up any architecture-specific things, e.g., CPU revision.
+ virtual Ass* CreateAssembler(ArenaAllocator* arena) {
+ return new (arena) Ass(arena);
+ }
+
// Override this to set up any architecture-specific things, e.g., register vectors.
virtual void SetUpHelpers() {}
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 9368301..ac93083 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -448,6 +448,11 @@
EmitI(0xf, static_cast<Register>(0), rt, imm16);
}
+void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
+ CHECK(IsR6());
+ EmitI(0xf, rs, rt, imm16);
+}
+
void MipsAssembler::Sync(uint32_t stype) {
EmitR(0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0),
stype & 0x1f, 0xf);
@@ -1385,13 +1390,8 @@
Register base,
int32_t offset,
Register temp) {
- if (!IsInt<16>(offset)) {
- CHECK_NE(temp, AT); // Must not use AT as temp, as not to overwrite the loaded value.
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
+ CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ false);
if (value == 0) {
temp = ZERO;
} else {
@@ -1404,14 +1404,8 @@
Register base,
int32_t offset,
Register temp) {
- // IsInt<16> must be passed a signed value.
- if (!IsInt<16>(offset) || !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize))) {
- CHECK_NE(temp, AT); // Must not use AT as temp, as not to overwrite the loaded value.
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
+ CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ true);
uint32_t low = Low32Bits(value);
uint32_t high = High32Bits(value);
if (low == 0) {
@@ -1457,11 +1451,35 @@
}
void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
+ CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
if (IsInt<16>(value)) {
Addiu(rt, rs, value);
+ } else if (IsR6()) {
+ int16_t high = High16Bits(value);
+ int16_t low = Low16Bits(value);
+ high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
+ if (low != 0) {
+ Aui(temp, rs, high);
+ Addiu(rt, temp, low);
+ } else {
+ Aui(rt, rs, high);
+ }
} else {
- LoadConst32(temp, value);
- Addu(rt, rs, temp);
+ // Do not load the whole 32-bit `value` if it can be represented as
+ // a sum of two 16-bit signed values. This can save an instruction.
+ constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
+ constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
+ if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
+ Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
+ Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
+ } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
+ Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
+ Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
+ } else {
+ // Now that all shorter options have been exhausted, load the full 32-bit value.
+ LoadConst32(temp, value);
+ Addu(rt, rs, temp);
+ }
}
}
@@ -2262,17 +2280,103 @@
Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
}
-void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
- int32_t offset) {
- // IsInt<16> must be passed a signed value.
- if (!IsInt<16>(offset) ||
- (type == kLoadDoubleword && !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
+void MipsAssembler::AdjustBaseAndOffset(Register& base,
+ int32_t& offset,
+ bool is_doubleword,
+ bool is_float) {
+ // This method is used to adjust the base register and offset pair
+ // for a load/store when the offset doesn't fit into int16_t.
+ // It is assumed that `base + offset` is sufficiently aligned for memory
+ // operands that are machine word in size or smaller. For doubleword-sized
+ // operands it's assumed that `base` is a multiple of 8, while `offset`
+ // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
+ // and spilled variables on the stack accessed relative to the stack
+ // pointer register).
+ // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
+ CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
+
+ bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
+ bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
+
+ // IsInt<16> must be passed a signed value, hence the static cast below.
+ if (IsInt<16>(offset) &&
+ (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
+ // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
+ return;
}
+ // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
+ uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
+
+ // Do not load the whole 32-bit `offset` if it can be represented as
+ // a sum of two 16-bit signed offsets. This can save an instruction or two.
+ // To simplify matters, only do this for a symmetric range of offsets from
+ // about -64KB to about +64KB, allowing further addition of 4 when accessing
+ // 64-bit variables with two 32-bit accesses.
+ constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
+ constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
+ if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
+ Addiu(AT, base, kMinOffsetForSimpleAdjustment);
+ offset -= kMinOffsetForSimpleAdjustment;
+ } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
+ Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
+ offset += kMinOffsetForSimpleAdjustment;
+ } else if (IsR6()) {
+ // On R6 take advantage of the aui instruction, e.g.:
+ // aui AT, base, offset_high
+ // lw reg_lo, offset_low(AT)
+ // lw reg_hi, (offset_low+4)(AT)
+ // or when offset_low+4 overflows int16_t:
+ // aui AT, base, offset_high
+ // addiu AT, AT, 8
+ // lw reg_lo, (offset_low-8)(AT)
+ // lw reg_hi, (offset_low-4)(AT)
+ int16_t offset_high = High16Bits(offset);
+ int16_t offset_low = Low16Bits(offset);
+ offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
+ Aui(AT, base, offset_high);
+ if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
+ // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
+ Addiu(AT, AT, kMipsDoublewordSize);
+ offset_low -= kMipsDoublewordSize;
+ }
+ offset = offset_low;
+ } else {
+ // Do not load the whole 32-bit `offset` if it can be represented as
+ // a sum of three 16-bit signed offsets. This can save an instruction.
+ // To simplify matters, only do this for a symmetric range of offsets from
+ // about -96KB to about +96KB, allowing further addition of 4 when accessing
+ // 64-bit variables with two 32-bit accesses.
+ constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
+ constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
+ if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
+ Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
+ Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
+ offset -= kMinOffsetForMediumAdjustment;
+ } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
+ Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
+ Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
+ offset += kMinOffsetForMediumAdjustment;
+ } else {
+ // Now that all shorter options have been exhausted, load the full 32-bit offset.
+ int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
+ LoadConst32(AT, loaded_offset);
+ Addu(AT, AT, base);
+ offset -= loaded_offset;
+ }
+ }
+ base = AT;
+
+ CHECK(IsInt<16>(offset));
+ if (two_accesses) {
+ CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
+ }
+ CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
+}
+
+void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
+ int32_t offset) {
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kLoadDoubleword));
switch (type) {
case kLoadSignedByte:
Lb(reg, base, offset);
@@ -2306,27 +2410,12 @@
}
void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
- if (!IsInt<16>(offset)) {
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
-
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
Lwc1(reg, base, offset);
}
void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
- // IsInt<16> must be passed a signed value.
- if (!IsInt<16>(offset) ||
- (!IsAligned<kMipsDoublewordSize>(offset) &&
- !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
-
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
if (offset & 0x7) {
if (Is32BitFPU()) {
Lwc1(reg, base, offset);
@@ -2365,15 +2454,10 @@
void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base,
int32_t offset) {
- // IsInt<16> must be passed a signed value.
- if (!IsInt<16>(offset) ||
- (type == kStoreDoubleword && !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
-
+ // Must not use AT as `reg`, so as not to overwrite the value being stored
+ // with the adjusted `base`.
+ CHECK_NE(reg, AT);
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kStoreDoubleword));
switch (type) {
case kStoreByte:
Sb(reg, base, offset);
@@ -2396,27 +2480,12 @@
}
void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
- if (!IsInt<16>(offset)) {
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
-
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
Swc1(reg, base, offset);
}
void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
- // IsInt<16> must be passed a signed value.
- if (!IsInt<16>(offset) ||
- (!IsAligned<kMipsDoublewordSize>(offset) &&
- !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
- LoadConst32(AT, offset);
- Addu(AT, AT, base);
- base = AT;
- offset = 0;
- }
-
+ AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
if (offset & 0x7) {
if (Is32BitFPU()) {
Swc1(reg, base, offset);
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index d5e6285..31b3b31 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -183,6 +183,7 @@
void Lbu(Register rt, Register rs, uint16_t imm16);
void Lhu(Register rt, Register rs, uint16_t imm16);
void Lui(Register rt, uint16_t imm16);
+ void Aui(Register rt, Register rs, uint16_t imm16); // R6
void Sync(uint32_t stype);
void Mfhi(Register rd); // R2
void Mflo(Register rd); // R2
@@ -385,6 +386,10 @@
void Bc1nez(FRegister ft, MipsLabel* label); // R6
void EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, size_t size);
+ void AdjustBaseAndOffset(Register& base,
+ int32_t& offset,
+ bool is_doubleword,
+ bool is_float = false);
void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset);
void LoadSFromOffset(FRegister reg, Register base, int32_t offset);
void LoadDFromOffset(FRegister reg, Register base, int32_t offset);
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
new file mode 100644
index 0000000..ce92d60
--- /dev/null
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "assembler_mips.h"
+
+#include <map>
+
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+#define __ GetAssembler()->
+
+namespace art {
+
+struct MIPSCpuRegisterCompare {
+ bool operator()(const mips::Register& a, const mips::Register& b) const {
+ return a < b;
+ }
+};
+
+class AssemblerMIPS32r6Test : public AssemblerTest<mips::MipsAssembler,
+ mips::Register,
+ mips::FRegister,
+ uint32_t> {
+ public:
+ typedef AssemblerTest<mips::MipsAssembler, mips::Register, mips::FRegister, uint32_t> Base;
+
+ AssemblerMIPS32r6Test() :
+ instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r6", nullptr)) {
+ }
+
+ protected:
+ // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+ std::string GetArchitectureString() OVERRIDE {
+ return "mips";
+ }
+
+ std::string GetAssemblerParameters() OVERRIDE {
+ return " --no-warn -32 -march=mips32r6";
+ }
+
+ std::string GetDisassembleParameters() OVERRIDE {
+ return " -D -bbinary -mmips:isa32r6";
+ }
+
+ mips::MipsAssembler* CreateAssembler(ArenaAllocator* arena) OVERRIDE {
+ return new (arena) mips::MipsAssembler(arena, instruction_set_features_.get());
+ }
+
+ void SetUpHelpers() OVERRIDE {
+ if (registers_.size() == 0) {
+ registers_.push_back(new mips::Register(mips::ZERO));
+ registers_.push_back(new mips::Register(mips::AT));
+ registers_.push_back(new mips::Register(mips::V0));
+ registers_.push_back(new mips::Register(mips::V1));
+ registers_.push_back(new mips::Register(mips::A0));
+ registers_.push_back(new mips::Register(mips::A1));
+ registers_.push_back(new mips::Register(mips::A2));
+ registers_.push_back(new mips::Register(mips::A3));
+ registers_.push_back(new mips::Register(mips::T0));
+ registers_.push_back(new mips::Register(mips::T1));
+ registers_.push_back(new mips::Register(mips::T2));
+ registers_.push_back(new mips::Register(mips::T3));
+ registers_.push_back(new mips::Register(mips::T4));
+ registers_.push_back(new mips::Register(mips::T5));
+ registers_.push_back(new mips::Register(mips::T6));
+ registers_.push_back(new mips::Register(mips::T7));
+ registers_.push_back(new mips::Register(mips::S0));
+ registers_.push_back(new mips::Register(mips::S1));
+ registers_.push_back(new mips::Register(mips::S2));
+ registers_.push_back(new mips::Register(mips::S3));
+ registers_.push_back(new mips::Register(mips::S4));
+ registers_.push_back(new mips::Register(mips::S5));
+ registers_.push_back(new mips::Register(mips::S6));
+ registers_.push_back(new mips::Register(mips::S7));
+ registers_.push_back(new mips::Register(mips::T8));
+ registers_.push_back(new mips::Register(mips::T9));
+ registers_.push_back(new mips::Register(mips::K0));
+ registers_.push_back(new mips::Register(mips::K1));
+ registers_.push_back(new mips::Register(mips::GP));
+ registers_.push_back(new mips::Register(mips::SP));
+ registers_.push_back(new mips::Register(mips::FP));
+ registers_.push_back(new mips::Register(mips::RA));
+
+ secondary_register_names_.emplace(mips::Register(mips::ZERO), "zero");
+ secondary_register_names_.emplace(mips::Register(mips::AT), "at");
+ secondary_register_names_.emplace(mips::Register(mips::V0), "v0");
+ secondary_register_names_.emplace(mips::Register(mips::V1), "v1");
+ secondary_register_names_.emplace(mips::Register(mips::A0), "a0");
+ secondary_register_names_.emplace(mips::Register(mips::A1), "a1");
+ secondary_register_names_.emplace(mips::Register(mips::A2), "a2");
+ secondary_register_names_.emplace(mips::Register(mips::A3), "a3");
+ secondary_register_names_.emplace(mips::Register(mips::T0), "t0");
+ secondary_register_names_.emplace(mips::Register(mips::T1), "t1");
+ secondary_register_names_.emplace(mips::Register(mips::T2), "t2");
+ secondary_register_names_.emplace(mips::Register(mips::T3), "t3");
+ secondary_register_names_.emplace(mips::Register(mips::T4), "t4");
+ secondary_register_names_.emplace(mips::Register(mips::T5), "t5");
+ secondary_register_names_.emplace(mips::Register(mips::T6), "t6");
+ secondary_register_names_.emplace(mips::Register(mips::T7), "t7");
+ secondary_register_names_.emplace(mips::Register(mips::S0), "s0");
+ secondary_register_names_.emplace(mips::Register(mips::S1), "s1");
+ secondary_register_names_.emplace(mips::Register(mips::S2), "s2");
+ secondary_register_names_.emplace(mips::Register(mips::S3), "s3");
+ secondary_register_names_.emplace(mips::Register(mips::S4), "s4");
+ secondary_register_names_.emplace(mips::Register(mips::S5), "s5");
+ secondary_register_names_.emplace(mips::Register(mips::S6), "s6");
+ secondary_register_names_.emplace(mips::Register(mips::S7), "s7");
+ secondary_register_names_.emplace(mips::Register(mips::T8), "t8");
+ secondary_register_names_.emplace(mips::Register(mips::T9), "t9");
+ secondary_register_names_.emplace(mips::Register(mips::K0), "k0");
+ secondary_register_names_.emplace(mips::Register(mips::K1), "k1");
+ secondary_register_names_.emplace(mips::Register(mips::GP), "gp");
+ secondary_register_names_.emplace(mips::Register(mips::SP), "sp");
+ secondary_register_names_.emplace(mips::Register(mips::FP), "fp");
+ secondary_register_names_.emplace(mips::Register(mips::RA), "ra");
+
+ fp_registers_.push_back(new mips::FRegister(mips::F0));
+ fp_registers_.push_back(new mips::FRegister(mips::F1));
+ fp_registers_.push_back(new mips::FRegister(mips::F2));
+ fp_registers_.push_back(new mips::FRegister(mips::F3));
+ fp_registers_.push_back(new mips::FRegister(mips::F4));
+ fp_registers_.push_back(new mips::FRegister(mips::F5));
+ fp_registers_.push_back(new mips::FRegister(mips::F6));
+ fp_registers_.push_back(new mips::FRegister(mips::F7));
+ fp_registers_.push_back(new mips::FRegister(mips::F8));
+ fp_registers_.push_back(new mips::FRegister(mips::F9));
+ fp_registers_.push_back(new mips::FRegister(mips::F10));
+ fp_registers_.push_back(new mips::FRegister(mips::F11));
+ fp_registers_.push_back(new mips::FRegister(mips::F12));
+ fp_registers_.push_back(new mips::FRegister(mips::F13));
+ fp_registers_.push_back(new mips::FRegister(mips::F14));
+ fp_registers_.push_back(new mips::FRegister(mips::F15));
+ fp_registers_.push_back(new mips::FRegister(mips::F16));
+ fp_registers_.push_back(new mips::FRegister(mips::F17));
+ fp_registers_.push_back(new mips::FRegister(mips::F18));
+ fp_registers_.push_back(new mips::FRegister(mips::F19));
+ fp_registers_.push_back(new mips::FRegister(mips::F20));
+ fp_registers_.push_back(new mips::FRegister(mips::F21));
+ fp_registers_.push_back(new mips::FRegister(mips::F22));
+ fp_registers_.push_back(new mips::FRegister(mips::F23));
+ fp_registers_.push_back(new mips::FRegister(mips::F24));
+ fp_registers_.push_back(new mips::FRegister(mips::F25));
+ fp_registers_.push_back(new mips::FRegister(mips::F26));
+ fp_registers_.push_back(new mips::FRegister(mips::F27));
+ fp_registers_.push_back(new mips::FRegister(mips::F28));
+ fp_registers_.push_back(new mips::FRegister(mips::F29));
+ fp_registers_.push_back(new mips::FRegister(mips::F30));
+ fp_registers_.push_back(new mips::FRegister(mips::F31));
+ }
+ }
+
+ void TearDown() OVERRIDE {
+ AssemblerTest::TearDown();
+ STLDeleteElements(®isters_);
+ STLDeleteElements(&fp_registers_);
+ }
+
+ std::vector<mips::Register*> GetRegisters() OVERRIDE {
+ return registers_;
+ }
+
+ std::vector<mips::FRegister*> GetFPRegisters() OVERRIDE {
+ return fp_registers_;
+ }
+
+ uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+ return imm_value;
+ }
+
+ std::string GetSecondaryRegisterName(const mips::Register& reg) OVERRIDE {
+ CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+ return secondary_register_names_[reg];
+ }
+
+ std::string RepeatInsn(size_t count, const std::string& insn) {
+ std::string result;
+ for (; count != 0u; --count) {
+ result += insn;
+ }
+ return result;
+ }
+
+ void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register,
+ mips::Register,
+ mips::MipsLabel*),
+ std::string instr_name) {
+ mips::MipsLabel label;
+ (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label);
+ constexpr size_t kAdduCount1 = 63;
+ for (size_t i = 0; i != kAdduCount1; ++i) {
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+ }
+ __ Bind(&label);
+ constexpr size_t kAdduCount2 = 64;
+ for (size_t i = 0; i != kAdduCount2; ++i) {
+ __ Addu(mips::ZERO, mips::ZERO, mips::ZERO);
+ }
+ (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label);
+
+ std::string expected =
+ ".set noreorder\n" +
+ instr_name + " $a0, $a1, 1f\n"
+ "nop\n" +
+ RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") +
+ "1:\n" +
+ RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") +
+ instr_name + " $a2, $a3, 1b\n"
+ "nop\n";
+ DriverStr(expected, instr_name);
+ }
+
+ private:
+ std::vector<mips::Register*> registers_;
+ std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_;
+
+ std::vector<mips::FRegister*> fp_registers_;
+ std::unique_ptr<const MipsInstructionSetFeatures> instruction_set_features_;
+};
+
+
+TEST_F(AssemblerMIPS32r6Test, Toolchain) {
+ EXPECT_TRUE(CheckTools());
+}
+
+TEST_F(AssemblerMIPS32r6Test, MulR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::MulR6, "mul ${reg1}, ${reg2}, ${reg3}"), "MulR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MuhR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::MuhR6, "muh ${reg1}, ${reg2}, ${reg3}"), "MuhR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MuhuR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::MuhuR6, "muhu ${reg1}, ${reg2}, ${reg3}"), "MuhuR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, DivR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::DivR6, "div ${reg1}, ${reg2}, ${reg3}"), "DivR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, ModR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::ModR6, "mod ${reg1}, ${reg2}, ${reg3}"), "ModR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, DivuR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::DivuR6, "divu ${reg1}, ${reg2}, ${reg3}"), "DivuR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, ModuR6) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::ModuR6, "modu ${reg1}, ${reg2}, ${reg3}"), "ModuR6");
+}
+
+//////////
+// MISC //
+//////////
+
+TEST_F(AssemblerMIPS32r6Test, Aui) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Aui, 16, "aui ${reg1}, ${reg2}, {imm}"), "Aui");
+}
+
+TEST_F(AssemblerMIPS32r6Test, Bitswap) {
+ DriverStr(RepeatRR(&mips::MipsAssembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap");
+}
+
+TEST_F(AssemblerMIPS32r6Test, Seleqz) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"),
+ "seleqz");
+}
+
+TEST_F(AssemblerMIPS32r6Test, Selnez) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::Selnez, "selnez ${reg1}, ${reg2}, ${reg3}"),
+ "selnez");
+}
+
+TEST_F(AssemblerMIPS32r6Test, ClzR6) {
+ DriverStr(RepeatRR(&mips::MipsAssembler::ClzR6, "clz ${reg1}, ${reg2}"), "clzR6");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CloR6) {
+ DriverStr(RepeatRR(&mips::MipsAssembler::CloR6, "clo ${reg1}, ${reg2}"), "cloR6");
+}
+
+////////////////////
+// FLOATING POINT //
+////////////////////
+
+TEST_F(AssemblerMIPS32r6Test, SelS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::SelS, "sel.s ${reg1}, ${reg2}, ${reg3}"), "sel.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, SelD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::SelD, "sel.d ${reg1}, ${reg2}, ${reg3}"), "sel.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, ClassS) {
+ DriverStr(RepeatFF(&mips::MipsAssembler::ClassS, "class.s ${reg1}, ${reg2}"), "class.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, ClassD) {
+ DriverStr(RepeatFF(&mips::MipsAssembler::ClassD, "class.d ${reg1}, ${reg2}"), "class.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MinS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::MinS, "min.s ${reg1}, ${reg2}, ${reg3}"), "min.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MinD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::MinD, "min.d ${reg1}, ${reg2}, ${reg3}"), "min.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaxS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::MaxS, "max.s ${reg1}, ${reg2}, ${reg3}"), "max.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, MaxD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::MaxD, "max.d ${reg1}, ${reg2}, ${reg3}"), "max.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUnS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUnS, "cmp.un.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.un.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpEqS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpEqS, "cmp.eq.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.eq.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUeqS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUeqS, "cmp.ueq.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ueq.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpLtS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLtS, "cmp.lt.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.lt.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUltS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUltS, "cmp.ult.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ult.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpLeS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLeS, "cmp.le.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.le.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUleS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUleS, "cmp.ule.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ule.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpOrS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpOrS, "cmp.or.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.or.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUneS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUneS, "cmp.une.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.une.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpNeS) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpNeS, "cmp.ne.s ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ne.s");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUnD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUnD, "cmp.un.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.un.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpEqD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpEqD, "cmp.eq.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.eq.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUeqD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUeqD, "cmp.ueq.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ueq.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpLtD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLtD, "cmp.lt.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.lt.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUltD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUltD, "cmp.ult.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ult.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpLeD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLeD, "cmp.le.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.le.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUleD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUleD, "cmp.ule.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ule.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpOrD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpOrD, "cmp.or.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.or.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpUneD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUneD, "cmp.une.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.une.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, CmpNeD) {
+ DriverStr(RepeatFFF(&mips::MipsAssembler::CmpNeD, "cmp.ne.d ${reg1}, ${reg2}, ${reg3}"),
+ "cmp.ne.d");
+}
+
+TEST_F(AssemblerMIPS32r6Test, LoadDFromOffset) {
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x8000);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FF8);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFB);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFC);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFF);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0xFFF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x8008);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x8001);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x8000);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0xFFF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x17FE8);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x0FFF8);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x0FFF1);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x0FFF1);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x0FFF8);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x17FE8);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x17FF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x17FE9);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x17FE9);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x17FF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x12345678);
+
+ const char* expected =
+ "ldc1 $f0, -0x8000($a0)\n"
+ "ldc1 $f0, 0($a0)\n"
+ "ldc1 $f0, 0x7FF8($a0)\n"
+ "lwc1 $f0, 0x7FFB($a0)\n"
+ "lw $t8, 0x7FFF($a0)\n"
+ "mthc1 $t8, $f0\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "lwc1 $f0, 4($at)\n"
+ "lw $t8, 8($at)\n"
+ "mthc1 $t8, $f0\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "lwc1 $f0, 7($at)\n"
+ "lw $t8, 11($at)\n"
+ "mthc1 $t8, $f0\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "ldc1 $f0, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "ldc1 $f0, -0x10($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "lwc1 $f0, -9($at)\n"
+ "lw $t8, -5($at)\n"
+ "mthc1 $t8, $f0\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "ldc1 $f0, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "ldc1 $f0, 0x7FF8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "ldc1 $f0, -0x7FE8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "ldc1 $f0, 0x8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "lwc1 $f0, 0xF($at)\n"
+ "lw $t8, 0x13($at)\n"
+ "mthc1 $t8, $f0\n"
+ "aui $at, $a0, 0x1\n"
+ "lwc1 $f0, -0xF($at)\n"
+ "lw $t8, -0xB($at)\n"
+ "mthc1 $t8, $f0\n"
+ "aui $at, $a0, 0x1\n"
+ "ldc1 $f0, -0x8($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "ldc1 $f0, 0x7FE8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "ldc1 $f0, -0x7FF0($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "lwc1 $f0, -0x7FE9($at)\n"
+ "lw $t8, -0x7FE5($at)\n"
+ "mthc1 $t8, $f0\n"
+ "aui $at, $a0, 0x1\n"
+ "lwc1 $f0, 0x7FE9($at)\n"
+ "lw $t8, 0x7FED($at)\n"
+ "mthc1 $t8, $f0\n"
+ "aui $at, $a0, 0x1\n"
+ "ldc1 $f0, 0x7FF0($at)\n"
+ "aui $at, $a0, 0x1234\n"
+ "ldc1 $f0, 0x5678($at)\n";
+ DriverStr(expected, "LoadDFromOffset");
+}
+
+TEST_F(AssemblerMIPS32r6Test, StoreDToOffset) {
+ __ StoreDToOffset(mips::F0, mips::A0, -0x8000);
+ __ StoreDToOffset(mips::F0, mips::A0, +0);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FF8);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FFB);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FFC);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FFF);
+ __ StoreDToOffset(mips::F0, mips::A0, -0xFFF0);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x8008);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x8001);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x8000);
+ __ StoreDToOffset(mips::F0, mips::A0, +0xFFF0);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x17FE8);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x0FFF8);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x0FFF1);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x0FFF1);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x0FFF8);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x17FE8);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x17FF0);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x17FE9);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x17FE9);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x17FF0);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x12345678);
+
+ const char* expected =
+ "sdc1 $f0, -0x8000($a0)\n"
+ "sdc1 $f0, 0($a0)\n"
+ "sdc1 $f0, 0x7FF8($a0)\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, 0x7FFB($a0)\n"
+ "sw $t8, 0x7FFF($a0)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, 4($at)\n"
+ "sw $t8, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, 7($at)\n"
+ "sw $t8, 11($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "sdc1 $f0, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "sdc1 $f0, -0x10($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, -9($at)\n"
+ "sw $t8, -5($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "sdc1 $f0, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "sdc1 $f0, 0x7FF8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "sdc1 $f0, -0x7FE8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "sdc1 $f0, 0x8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, 0xF($at)\n"
+ "sw $t8, 0x13($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, -0xF($at)\n"
+ "sw $t8, -0xB($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "sdc1 $f0, -0x8($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "sdc1 $f0, 0x7FE8($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "sdc1 $f0, -0x7FF0($at)\n"
+ "aui $at, $a0, 0xFFFF\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, -0x7FE9($at)\n"
+ "sw $t8, -0x7FE5($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "mfhc1 $t8, $f0\n"
+ "swc1 $f0, 0x7FE9($at)\n"
+ "sw $t8, 0x7FED($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "sdc1 $f0, 0x7FF0($at)\n"
+ "aui $at, $a0, 0x1234\n"
+ "sdc1 $f0, 0x5678($at)\n";
+ DriverStr(expected, "StoreDToOffset");
+}
+
+//////////////
+// BRANCHES //
+//////////////
+
+// TODO: MipsAssembler::Auipc
+// MipsAssembler::Addiupc
+// MipsAssembler::Bc
+// MipsAssembler::Jic
+// MipsAssembler::Jialc
+// MipsAssembler::Bltc
+// MipsAssembler::Bltzc
+// MipsAssembler::Bgtzc
+// MipsAssembler::Bgec
+// MipsAssembler::Bgezc
+// MipsAssembler::Blezc
+// MipsAssembler::Bltuc
+// MipsAssembler::Bgeuc
+// MipsAssembler::Beqc
+// MipsAssembler::Bnec
+// MipsAssembler::Beqzc
+// MipsAssembler::Bnezc
+// MipsAssembler::Bc1eqz
+// MipsAssembler::Bc1nez
+// MipsAssembler::Buncond
+// MipsAssembler::Bcond
+// MipsAssembler::Call
+
+// TODO: AssemblerMIPS32r6Test.B
+// AssemblerMIPS32r6Test.Beq
+// AssemblerMIPS32r6Test.Bne
+// AssemblerMIPS32r6Test.Beqz
+// AssemblerMIPS32r6Test.Bnez
+// AssemblerMIPS32r6Test.Bltz
+// AssemblerMIPS32r6Test.Bgez
+// AssemblerMIPS32r6Test.Blez
+// AssemblerMIPS32r6Test.Bgtz
+// AssemblerMIPS32r6Test.Blt
+// AssemblerMIPS32r6Test.Bge
+// AssemblerMIPS32r6Test.Bltu
+// AssemblerMIPS32r6Test.Bgeu
+
+#undef __
+
+} // namespace art
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index 56e5884..a1d6ad6 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -647,6 +647,26 @@
DriverStr(RepeatRRIb(&mips::MipsAssembler::Movt, 3, "movt ${reg1}, ${reg2}, $fcc{imm}"), "Movt");
}
+TEST_F(AssemblerMIPSTest, MovfS) {
+ DriverStr(RepeatFFIb(&mips::MipsAssembler::MovfS, 3, "movf.s ${reg1}, ${reg2}, $fcc{imm}"),
+ "MovfS");
+}
+
+TEST_F(AssemblerMIPSTest, MovfD) {
+ DriverStr(RepeatFFIb(&mips::MipsAssembler::MovfD, 3, "movf.d ${reg1}, ${reg2}, $fcc{imm}"),
+ "MovfD");
+}
+
+TEST_F(AssemblerMIPSTest, MovtS) {
+ DriverStr(RepeatFFIb(&mips::MipsAssembler::MovtS, 3, "movt.s ${reg1}, ${reg2}, $fcc{imm}"),
+ "MovtS");
+}
+
+TEST_F(AssemblerMIPSTest, MovtD) {
+ DriverStr(RepeatFFIb(&mips::MipsAssembler::MovtD, 3, "movt.d ${reg1}, ${reg2}, $fcc{imm}"),
+ "MovtD");
+}
+
TEST_F(AssemblerMIPSTest, CvtSW) {
DriverStr(RepeatFF(&mips::MipsAssembler::Cvtsw, "cvt.s.w ${reg1}, ${reg2}"), "CvtSW");
}
@@ -731,212 +751,538 @@
DriverStr(RepeatRR(&mips::MipsAssembler::Not, "nor ${reg1}, ${reg2}, $zero"), "Not");
}
-TEST_F(AssemblerMIPSTest, LoadFromOffset) {
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 256);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 1000);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0x8000);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0x10000);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0x12345678);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, -256);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0xFFFF8000);
- __ LoadFromOffset(mips::kLoadSignedByte, mips::A0, mips::A1, 0xABCDEF00);
-
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 256);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 1000);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0x8000);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0x10000);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0x12345678);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, -256);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0xFFFF8000);
- __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A0, mips::A1, 0xABCDEF00);
-
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 256);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 1000);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0x8000);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0x10000);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0x12345678);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, -256);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0xFFFF8000);
- __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A0, mips::A1, 0xABCDEF00);
-
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 256);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 1000);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0x8000);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0x10000);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0x12345678);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, -256);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0xFFFF8000);
- __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A0, mips::A1, 0xABCDEF00);
-
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 256);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 1000);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0x8000);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0x10000);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0x12345678);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, -256);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0xFFFF8000);
- __ LoadFromOffset(mips::kLoadWord, mips::A0, mips::A1, 0xABCDEF00);
-
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A1, 0);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A1, mips::A0, 0);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 256);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 1000);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0x8000);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0x10000);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0x12345678);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -256);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0xFFFF8000);
- __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, 0xABCDEF00);
+TEST_F(AssemblerMIPSTest, Addiu32) {
+ __ Addiu32(mips::A1, mips::A2, -0x8000);
+ __ Addiu32(mips::A1, mips::A2, +0);
+ __ Addiu32(mips::A1, mips::A2, +0x7FFF);
+ __ Addiu32(mips::A1, mips::A2, -0x10000);
+ __ Addiu32(mips::A1, mips::A2, -0x8001);
+ __ Addiu32(mips::A1, mips::A2, +0x8000);
+ __ Addiu32(mips::A1, mips::A2, +0xFFFE);
+ __ Addiu32(mips::A1, mips::A2, -0x10001);
+ __ Addiu32(mips::A1, mips::A2, +0xFFFF);
+ __ Addiu32(mips::A1, mips::A2, +0x10000);
+ __ Addiu32(mips::A1, mips::A2, +0x10001);
+ __ Addiu32(mips::A1, mips::A2, +0x12345678);
const char* expected =
- "lb $a0, 0($a0)\n"
- "lb $a0, 0($a1)\n"
- "lb $a0, 256($a1)\n"
- "lb $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
- "addu $at, $at, $a1\n"
- "lb $a0, 0($at)\n"
+ "addiu $a1, $a2, -0x8000\n"
+ "addiu $a1, $a2, 0\n"
+ "addiu $a1, $a2, 0x7FFF\n"
+ "addiu $at, $a2, -0x8000\n"
+ "addiu $a1, $at, -0x8000\n"
+ "addiu $at, $a2, -0x8000\n"
+ "addiu $a1, $at, -1\n"
+ "addiu $at, $a2, 0x7FFF\n"
+ "addiu $a1, $at, 1\n"
+ "addiu $at, $a2, 0x7FFF\n"
+ "addiu $a1, $at, 0x7FFF\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0xFFFF\n"
+ "addu $a1, $a2, $at\n"
+ "ori $at, $zero, 0xFFFF\n"
+ "addu $a1, $a2, $at\n"
"lui $at, 1\n"
- "addu $at, $at, $a1\n"
- "lb $a0, 0($at)\n"
- "lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
- "addu $at, $at, $a1\n"
- "lb $a0, 0($at)\n"
- "lb $a0, -256($a1)\n"
- "lb $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "lb $a0, 0($at)\n"
-
- "lbu $a0, 0($a0)\n"
- "lbu $a0, 0($a1)\n"
- "lbu $a0, 256($a1)\n"
- "lbu $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
- "addu $at, $at, $a1\n"
- "lbu $a0, 0($at)\n"
+ "addu $a1, $a2, $at\n"
"lui $at, 1\n"
- "addu $at, $at, $a1\n"
- "lbu $a0, 0($at)\n"
+ "ori $at, $at, 1\n"
+ "addu $a1, $a2, $at\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
- "addu $at, $at, $a1\n"
- "lbu $a0, 0($at)\n"
- "lbu $a0, -256($a1)\n"
- "lbu $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "lbu $a0, 0($at)\n"
+ "ori $at, $at, 0x5678\n"
+ "addu $a1, $a2, $at\n";
+ DriverStr(expected, "Addiu32");
+}
- "lh $a0, 0($a0)\n"
- "lh $a0, 0($a1)\n"
- "lh $a0, 256($a1)\n"
- "lh $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
+TEST_F(AssemblerMIPSTest, LoadFromOffset) {
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x8000);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x7FF8);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x7FFB);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x7FFC);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x7FFF);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0xFFF0);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x8008);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x8001);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x8000);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0xFFF0);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x17FE8);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x0FFF8);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x0FFF1);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x0FFF1);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x0FFF8);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x17FE8);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x17FF0);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, -0x17FE9);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x17FE9);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x17FF0);
+ __ LoadFromOffset(mips::kLoadSignedByte, mips::A3, mips::A1, +0x12345678);
+
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x8000);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x7FF8);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x7FFB);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x7FFC);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x7FFF);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0xFFF0);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x8008);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x8001);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x8000);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0xFFF0);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x17FE8);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x0FFF8);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x0FFF1);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x0FFF1);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x0FFF8);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x17FE8);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x17FF0);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, -0x17FE9);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x17FE9);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x17FF0);
+ __ LoadFromOffset(mips::kLoadUnsignedByte, mips::A3, mips::A1, +0x12345678);
+
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x8000);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x7FF8);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x7FFB);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x7FFC);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x7FFF);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0xFFF0);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x8008);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x8001);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x8000);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0xFFF0);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x17FE8);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x0FFF8);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x0FFF1);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x0FFF1);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x0FFF8);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x17FE8);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x17FF0);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, -0x17FE9);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x17FE9);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x17FF0);
+ __ LoadFromOffset(mips::kLoadSignedHalfword, mips::A3, mips::A1, +0x12345678);
+
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x8000);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x7FF8);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x7FFB);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x7FFC);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x7FFF);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0xFFF0);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x8008);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x8001);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x8000);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0xFFF0);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x17FE8);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x0FFF8);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x0FFF1);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x0FFF1);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x0FFF8);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x17FE8);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x17FF0);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, -0x17FE9);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x17FE9);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x17FF0);
+ __ LoadFromOffset(mips::kLoadUnsignedHalfword, mips::A3, mips::A1, +0x12345678);
+
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x8000);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x7FF8);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x7FFB);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x7FFC);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x7FFF);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0xFFF0);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x8008);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x8001);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x8000);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0xFFF0);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x17FE8);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x0FFF8);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x0FFF1);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x0FFF1);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x0FFF8);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x17FE8);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x17FF0);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, -0x17FE9);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x17FE9);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x17FF0);
+ __ LoadFromOffset(mips::kLoadWord, mips::A3, mips::A1, +0x12345678);
+
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x8000);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x7FF8);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x7FFB);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x7FFC);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x7FFF);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0xFFF0);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x8008);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x8001);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x8000);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0xFFF0);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x17FE8);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x0FFF8);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x0FFF1);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x0FFF1);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x0FFF8);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x17FE8);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x17FF0);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, -0x17FE9);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x17FE9);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x17FF0);
+ __ LoadFromOffset(mips::kLoadDoubleword, mips::A0, mips::A2, +0x12345678);
+
+ const char* expected =
+ "lb $a3, -0x8000($a1)\n"
+ "lb $a3, 0($a1)\n"
+ "lb $a3, 0x7FF8($a1)\n"
+ "lb $a3, 0x7FFB($a1)\n"
+ "lb $a3, 0x7FFC($a1)\n"
+ "lb $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lb $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lb $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lb $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lb $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lb $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lb $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lb $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lb $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lb $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lb $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lb $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "lh $a0, 0($at)\n"
- "lui $at, 1\n"
+ "lb $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "lh $a0, 0($at)\n"
+ "lb $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "lb $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "lb $a3, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a1\n"
- "lh $a0, 0($at)\n"
- "lh $a0, -256($a1)\n"
- "lh $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "lh $a0, 0($at)\n"
+ "lb $a3, 0($at)\n"
- "lhu $a0, 0($a0)\n"
- "lhu $a0, 0($a1)\n"
- "lhu $a0, 256($a1)\n"
- "lhu $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
+ "lbu $a3, -0x8000($a1)\n"
+ "lbu $a3, 0($a1)\n"
+ "lbu $a3, 0x7FF8($a1)\n"
+ "lbu $a3, 0x7FFB($a1)\n"
+ "lbu $a3, 0x7FFC($a1)\n"
+ "lbu $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lbu $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lbu $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lbu $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lbu $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lbu $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lbu $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lbu $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lbu $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lbu $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lbu $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lbu $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "lhu $a0, 0($at)\n"
- "lui $at, 1\n"
+ "lbu $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "lhu $a0, 0($at)\n"
+ "lbu $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "lbu $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "lbu $a3, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a1\n"
- "lhu $a0, 0($at)\n"
- "lhu $a0, -256($a1)\n"
- "lhu $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "lhu $a0, 0($at)\n"
+ "lbu $a3, 0($at)\n"
- "lw $a0, 0($a0)\n"
- "lw $a0, 0($a1)\n"
- "lw $a0, 256($a1)\n"
- "lw $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
+ "lh $a3, -0x8000($a1)\n"
+ "lh $a3, 0($a1)\n"
+ "lh $a3, 0x7FF8($a1)\n"
+ "lh $a3, 0x7FFB($a1)\n"
+ "lh $a3, 0x7FFC($a1)\n"
+ "lh $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lh $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lh $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lh $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lh $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lh $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lh $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lh $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lh $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lh $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lh $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lh $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "lw $a0, 0($at)\n"
- "lui $at, 1\n"
+ "lh $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "lw $a0, 0($at)\n"
+ "lh $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "lh $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "lh $a3, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a1\n"
- "lw $a0, 0($at)\n"
- "lw $a0, -256($a1)\n"
- "lw $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "lw $a0, 0($at)\n"
+ "lh $a3, 0($at)\n"
- "lw $a1, 4($a0)\n"
- "lw $a0, 0($a0)\n"
- "lw $a0, 0($a1)\n"
- "lw $a1, 4($a1)\n"
- "lw $a1, 0($a0)\n"
- "lw $a2, 4($a0)\n"
+ "lhu $a3, -0x8000($a1)\n"
+ "lhu $a3, 0($a1)\n"
+ "lhu $a3, 0x7FF8($a1)\n"
+ "lhu $a3, 0x7FFB($a1)\n"
+ "lhu $a3, 0x7FFC($a1)\n"
+ "lhu $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lhu $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lhu $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lhu $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lhu $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lhu $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lhu $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lhu $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lhu $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lhu $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lhu $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lhu $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a1\n"
+ "lhu $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a1\n"
+ "lhu $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "lhu $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "lhu $a3, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, $at, 0x5678\n"
+ "addu $at, $at, $a1\n"
+ "lhu $a3, 0($at)\n"
+
+ "lw $a3, -0x8000($a1)\n"
+ "lw $a3, 0($a1)\n"
+ "lw $a3, 0x7FF8($a1)\n"
+ "lw $a3, 0x7FFB($a1)\n"
+ "lw $a3, 0x7FFC($a1)\n"
+ "lw $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lw $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lw $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "lw $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lw $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "lw $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lw $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lw $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lw $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lw $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lw $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lw $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a1\n"
+ "lw $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a1\n"
+ "lw $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "lw $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "lw $a3, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, $at, 0x5678\n"
+ "addu $at, $at, $a1\n"
+ "lw $a3, 0($at)\n"
+
+ "lw $a0, -0x8000($a2)\n"
+ "lw $a1, -0x7FFC($a2)\n"
"lw $a0, 0($a2)\n"
"lw $a1, 4($a2)\n"
- "lw $a0, 256($a2)\n"
- "lw $a1, 260($a2)\n"
- "lw $a0, 1000($a2)\n"
- "lw $a1, 1004($a2)\n"
- "ori $at, $zero, 0x8000\n"
+ "lw $a0, 0x7FF8($a2)\n"
+ "lw $a1, 0x7FFC($a2)\n"
+ "lw $a0, 0x7FFB($a2)\n"
+ "lw $a1, 0x7FFF($a2)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "lw $a0, 4($at)\n"
+ "lw $a1, 8($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "lw $a0, 7($at)\n"
+ "lw $a1, 11($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "lw $a0, -0x7FF8($at)\n"
+ "lw $a1, -0x7FF4($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "lw $a0, -0x10($at)\n"
+ "lw $a1, -0xC($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "lw $a0, -9($at)\n"
+ "lw $a1, -5($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "lw $a0, 8($at)\n"
+ "lw $a1, 12($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "lw $a0, 0x7FF8($at)\n"
+ "lw $a1, 0x7FFC($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lw $a0, -0x7FF8($at)\n"
+ "lw $a1, -0x7FF4($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lw $a0, -8($at)\n"
+ "lw $a1, -4($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lw $a0, -1($at)\n"
+ "lw $a1, 3($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lw $a0, 1($at)\n"
+ "lw $a1, 5($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lw $a0, 8($at)\n"
+ "lw $a1, 12($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lw $a0, 0x7FF8($at)\n"
+ "lw $a1, 0x7FFC($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a2\n"
"lw $a0, 0($at)\n"
"lw $a1, 4($at)\n"
- "lui $at, 1\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a2\n"
+ "lw $a0, 7($at)\n"
+ "lw $a1, 11($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a2\n"
+ "lw $a0, 1($at)\n"
+ "lw $a1, 5($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
"addu $at, $at, $a2\n"
"lw $a0, 0($at)\n"
"lw $a1, 4($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
- "addu $at, $at, $a2\n"
- "lw $a0, 0($at)\n"
- "lw $a1, 4($at)\n"
- "lw $a0, -256($a2)\n"
- "lw $a1, -252($a2)\n"
- "lw $a0, 0xFFFF8000($a2)\n"
- "lw $a1, 0xFFFF8004($a2)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a2\n"
"lw $a0, 0($at)\n"
"lw $a1, 4($at)\n";
@@ -944,208 +1290,513 @@
}
TEST_F(AssemblerMIPSTest, LoadSFromOffset) {
- __ LoadSFromOffset(mips::F0, mips::A0, 0);
- __ LoadSFromOffset(mips::F0, mips::A0, 4);
- __ LoadSFromOffset(mips::F0, mips::A0, 256);
- __ LoadSFromOffset(mips::F0, mips::A0, 0x8000);
- __ LoadSFromOffset(mips::F0, mips::A0, 0x10000);
- __ LoadSFromOffset(mips::F0, mips::A0, 0x12345678);
- __ LoadSFromOffset(mips::F0, mips::A0, -256);
- __ LoadSFromOffset(mips::F0, mips::A0, 0xFFFF8000);
- __ LoadSFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x8000);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x7FF8);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x7FFB);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x7FFC);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x7FFF);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0xFFF0);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x8008);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x8001);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x8000);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0xFFF0);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x17FE8);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x0FFF8);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x0FFF1);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x0FFF1);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x0FFF8);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x17FE8);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x17FF0);
+ __ LoadSFromOffset(mips::F2, mips::A0, -0x17FE9);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x17FE9);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x17FF0);
+ __ LoadSFromOffset(mips::F2, mips::A0, +0x12345678);
const char* expected =
- "lwc1 $f0, 0($a0)\n"
- "lwc1 $f0, 4($a0)\n"
- "lwc1 $f0, 256($a0)\n"
- "ori $at, $zero, 0x8000\n"
+ "lwc1 $f2, -0x8000($a0)\n"
+ "lwc1 $f2, 0($a0)\n"
+ "lwc1 $f2, 0x7FF8($a0)\n"
+ "lwc1 $f2, 0x7FFB($a0)\n"
+ "lwc1 $f2, 0x7FFC($a0)\n"
+ "lwc1 $f2, 0x7FFF($a0)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "lwc1 $f2, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "lwc1 $f2, -0x10($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "lwc1 $f2, -9($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "lwc1 $f2, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "lwc1 $f2, 0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lwc1 $f2, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lwc1 $f2, -8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lwc1 $f2, -1($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lwc1 $f2, 1($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lwc1 $f2, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lwc1 $f2, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a0\n"
- "lwc1 $f0, 0($at)\n"
- "lui $at, 1\n"
+ "lwc1 $f2, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a0\n"
- "lwc1 $f0, 0($at)\n"
+ "lwc1 $f2, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a0\n"
+ "lwc1 $f2, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a0\n"
+ "lwc1 $f2, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a0\n"
- "lwc1 $f0, 0($at)\n"
- "lwc1 $f0, -256($a0)\n"
- "lwc1 $f0, 0xFFFF8000($a0)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a0\n"
- "lwc1 $f0, 0($at)\n";
+ "lwc1 $f2, 0($at)\n";
DriverStr(expected, "LoadSFromOffset");
}
-
TEST_F(AssemblerMIPSTest, LoadDFromOffset) {
- __ LoadDFromOffset(mips::F0, mips::A0, 0);
- __ LoadDFromOffset(mips::F0, mips::A0, 4);
- __ LoadDFromOffset(mips::F0, mips::A0, 256);
- __ LoadDFromOffset(mips::F0, mips::A0, 0x8000);
- __ LoadDFromOffset(mips::F0, mips::A0, 0x10000);
- __ LoadDFromOffset(mips::F0, mips::A0, 0x12345678);
- __ LoadDFromOffset(mips::F0, mips::A0, -256);
- __ LoadDFromOffset(mips::F0, mips::A0, 0xFFFF8000);
- __ LoadDFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x8000);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FF8);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFB);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFC);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFF);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0xFFF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x8008);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x8001);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x8000);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0xFFF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x17FE8);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x0FFF8);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x0FFF1);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x0FFF1);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x0FFF8);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x17FE8);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x17FF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, -0x17FE9);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x17FE9);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x17FF0);
+ __ LoadDFromOffset(mips::F0, mips::A0, +0x12345678);
const char* expected =
+ "ldc1 $f0, -0x8000($a0)\n"
"ldc1 $f0, 0($a0)\n"
- "lwc1 $f0, 4($a0)\n"
- "lwc1 $f1, 8($a0)\n"
- "ldc1 $f0, 256($a0)\n"
- "ori $at, $zero, 0x8000\n"
+ "ldc1 $f0, 0x7FF8($a0)\n"
+ "lwc1 $f0, 0x7FFB($a0)\n"
+ "lwc1 $f1, 0x7FFF($a0)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "lwc1 $f0, 4($at)\n"
+ "lwc1 $f1, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "lwc1 $f0, 7($at)\n"
+ "lwc1 $f1, 11($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "ldc1 $f0, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "ldc1 $f0, -0x10($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "lwc1 $f0, -9($at)\n"
+ "lwc1 $f1, -5($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "ldc1 $f0, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "ldc1 $f0, 0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "ldc1 $f0, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "ldc1 $f0, -8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "lwc1 $f0, -1($at)\n"
+ "lwc1 $f1, 3($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "lwc1 $f0, 1($at)\n"
+ "lwc1 $f1, 5($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "ldc1 $f0, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "ldc1 $f0, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a0\n"
"ldc1 $f0, 0($at)\n"
- "lui $at, 1\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a0\n"
+ "lwc1 $f0, 7($at)\n"
+ "lwc1 $f1, 11($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a0\n"
+ "lwc1 $f0, 1($at)\n"
+ "lwc1 $f1, 5($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
"addu $at, $at, $a0\n"
"ldc1 $f0, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
- "addu $at, $at, $a0\n"
- "ldc1 $f0, 0($at)\n"
- "ldc1 $f0, -256($a0)\n"
- "ldc1 $f0, 0xFFFF8000($a0)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a0\n"
"ldc1 $f0, 0($at)\n";
DriverStr(expected, "LoadDFromOffset");
}
TEST_F(AssemblerMIPSTest, StoreToOffset) {
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A0, 0);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 256);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 1000);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0x8000);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0x10000);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0x12345678);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, -256);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0xFFFF8000);
- __ StoreToOffset(mips::kStoreByte, mips::A0, mips::A1, 0xABCDEF00);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x8000);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x7FF8);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x7FFB);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x7FFC);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x7FFF);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0xFFF0);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x8008);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x8001);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x8000);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0xFFF0);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x17FE8);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x0FFF8);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x0FFF1);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x0FFF1);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x0FFF8);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x17FE8);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x17FF0);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, -0x17FE9);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x17FE9);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x17FF0);
+ __ StoreToOffset(mips::kStoreByte, mips::A3, mips::A1, +0x12345678);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A0, 0);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 256);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 1000);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0x8000);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0x10000);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0x12345678);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, -256);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0xFFFF8000);
- __ StoreToOffset(mips::kStoreHalfword, mips::A0, mips::A1, 0xABCDEF00);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x8000);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x7FF8);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x7FFB);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x7FFC);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x7FFF);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0xFFF0);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x8008);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x8001);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x8000);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0xFFF0);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x17FE8);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x0FFF8);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x0FFF1);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x0FFF1);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x0FFF8);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x17FE8);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x17FF0);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, -0x17FE9);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x17FE9);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x17FF0);
+ __ StoreToOffset(mips::kStoreHalfword, mips::A3, mips::A1, +0x12345678);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A0, 0);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 256);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 1000);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0x8000);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0x10000);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0x12345678);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, -256);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0xFFFF8000);
- __ StoreToOffset(mips::kStoreWord, mips::A0, mips::A1, 0xABCDEF00);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x8000);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x7FF8);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x7FFB);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x7FFC);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x7FFF);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0xFFF0);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x8008);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x8001);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x8000);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0xFFF0);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x17FE8);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x0FFF8);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x0FFF1);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x0FFF1);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x0FFF8);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x17FE8);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x17FF0);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, -0x17FE9);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x17FE9);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x17FF0);
+ __ StoreToOffset(mips::kStoreWord, mips::A3, mips::A1, +0x12345678);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 256);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 1000);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0x8000);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0x10000);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0x12345678);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -256);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0xFFFF8000);
- __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, 0xABCDEF00);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x8000);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x7FF8);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x7FFB);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x7FFC);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x7FFF);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0xFFF0);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x8008);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x8001);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x8000);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0xFFF0);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x17FE8);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x0FFF8);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x0FFF1);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x0FFF1);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x0FFF8);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x17FE8);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x17FF0);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, -0x17FE9);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x17FE9);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x17FF0);
+ __ StoreToOffset(mips::kStoreDoubleword, mips::A0, mips::A2, +0x12345678);
const char* expected =
- "sb $a0, 0($a0)\n"
- "sb $a0, 0($a1)\n"
- "sb $a0, 256($a1)\n"
- "sb $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
+ "sb $a3, -0x8000($a1)\n"
+ "sb $a3, 0($a1)\n"
+ "sb $a3, 0x7FF8($a1)\n"
+ "sb $a3, 0x7FFB($a1)\n"
+ "sb $a3, 0x7FFC($a1)\n"
+ "sb $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sb $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sb $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sb $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "sb $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "sb $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sb $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sb $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sb $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sb $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sb $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sb $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "sb $a0, 0($at)\n"
- "lui $at, 1\n"
+ "sb $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "sb $a0, 0($at)\n"
+ "sb $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "sb $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "sb $a3, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a1\n"
- "sb $a0, 0($at)\n"
- "sb $a0, -256($a1)\n"
- "sb $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "sb $a0, 0($at)\n"
+ "sb $a3, 0($at)\n"
- "sh $a0, 0($a0)\n"
- "sh $a0, 0($a1)\n"
- "sh $a0, 256($a1)\n"
- "sh $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
+ "sh $a3, -0x8000($a1)\n"
+ "sh $a3, 0($a1)\n"
+ "sh $a3, 0x7FF8($a1)\n"
+ "sh $a3, 0x7FFB($a1)\n"
+ "sh $a3, 0x7FFC($a1)\n"
+ "sh $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sh $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sh $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sh $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "sh $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "sh $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sh $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sh $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sh $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sh $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sh $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sh $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "sh $a0, 0($at)\n"
- "lui $at, 1\n"
+ "sh $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "sh $a0, 0($at)\n"
+ "sh $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "sh $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "sh $a3, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a1\n"
- "sh $a0, 0($at)\n"
- "sh $a0, -256($a1)\n"
- "sh $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "sh $a0, 0($at)\n"
+ "sh $a3, 0($at)\n"
- "sw $a0, 0($a0)\n"
- "sw $a0, 0($a1)\n"
- "sw $a0, 256($a1)\n"
- "sw $a0, 1000($a1)\n"
- "ori $at, $zero, 0x8000\n"
+ "sw $a3, -0x8000($a1)\n"
+ "sw $a3, 0($a1)\n"
+ "sw $a3, 0x7FF8($a1)\n"
+ "sw $a3, 0x7FFB($a1)\n"
+ "sw $a3, 0x7FFC($a1)\n"
+ "sw $a3, 0x7FFF($a1)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sw $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sw $a3, -0x10($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "sw $a3, -9($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "sw $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "sw $a3, 0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sw $a3, -0x7FF8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sw $a3, -8($at)\n"
+ "addiu $at, $a1, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sw $a3, -1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sw $a3, 1($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sw $a3, 8($at)\n"
+ "addiu $at, $a1, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sw $a3, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "sw $a0, 0($at)\n"
- "lui $at, 1\n"
+ "sw $a3, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a1\n"
- "sw $a0, 0($at)\n"
+ "sw $a3, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a1\n"
+ "sw $a3, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a1\n"
+ "sw $a3, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a1\n"
- "sw $a0, 0($at)\n"
- "sw $a0, -256($a1)\n"
- "sw $a0, 0xFFFF8000($a1)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a1\n"
- "sw $a0, 0($at)\n"
+ "sw $a3, 0($at)\n"
+ "sw $a0, -0x8000($a2)\n"
+ "sw $a1, -0x7FFC($a2)\n"
"sw $a0, 0($a2)\n"
"sw $a1, 4($a2)\n"
- "sw $a0, 256($a2)\n"
- "sw $a1, 260($a2)\n"
- "sw $a0, 1000($a2)\n"
- "sw $a1, 1004($a2)\n"
- "ori $at, $zero, 0x8000\n"
+ "sw $a0, 0x7FF8($a2)\n"
+ "sw $a1, 0x7FFC($a2)\n"
+ "sw $a0, 0x7FFB($a2)\n"
+ "sw $a1, 0x7FFF($a2)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "sw $a0, 4($at)\n"
+ "sw $a1, 8($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "sw $a0, 7($at)\n"
+ "sw $a1, 11($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "sw $a0, -0x7FF8($at)\n"
+ "sw $a1, -0x7FF4($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "sw $a0, -0x10($at)\n"
+ "sw $a1, -0xC($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "sw $a0, -9($at)\n"
+ "sw $a1, -5($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "sw $a0, 8($at)\n"
+ "sw $a1, 12($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "sw $a0, 0x7FF8($at)\n"
+ "sw $a1, 0x7FFC($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sw $a0, -0x7FF8($at)\n"
+ "sw $a1, -0x7FF4($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sw $a0, -8($at)\n"
+ "sw $a1, -4($at)\n"
+ "addiu $at, $a2, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sw $a0, -1($at)\n"
+ "sw $a1, 3($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sw $a0, 1($at)\n"
+ "sw $a1, 5($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sw $a0, 8($at)\n"
+ "sw $a1, 12($at)\n"
+ "addiu $at, $a2, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sw $a0, 0x7FF8($at)\n"
+ "sw $a1, 0x7FFC($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a2\n"
"sw $a0, 0($at)\n"
"sw $a1, 4($at)\n"
- "lui $at, 1\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a2\n"
+ "sw $a0, 7($at)\n"
+ "sw $a1, 11($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a2\n"
+ "sw $a0, 1($at)\n"
+ "sw $a1, 5($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
"addu $at, $at, $a2\n"
"sw $a0, 0($at)\n"
"sw $a1, 4($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
- "addu $at, $at, $a2\n"
- "sw $a0, 0($at)\n"
- "sw $a1, 4($at)\n"
- "sw $a0, -256($a2)\n"
- "sw $a1, -252($a2)\n"
- "sw $a0, 0xFFFF8000($a2)\n"
- "sw $a1, 0xFFFF8004($a2)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a2\n"
"sw $a0, 0($at)\n"
"sw $a1, 4($at)\n";
@@ -1153,69 +1804,174 @@
}
TEST_F(AssemblerMIPSTest, StoreSToOffset) {
- __ StoreSToOffset(mips::F0, mips::A0, 0);
- __ StoreSToOffset(mips::F0, mips::A0, 4);
- __ StoreSToOffset(mips::F0, mips::A0, 256);
- __ StoreSToOffset(mips::F0, mips::A0, 0x8000);
- __ StoreSToOffset(mips::F0, mips::A0, 0x10000);
- __ StoreSToOffset(mips::F0, mips::A0, 0x12345678);
- __ StoreSToOffset(mips::F0, mips::A0, -256);
- __ StoreSToOffset(mips::F0, mips::A0, 0xFFFF8000);
- __ StoreSToOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x8000);
+ __ StoreSToOffset(mips::F2, mips::A0, +0);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x7FF8);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x7FFB);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x7FFC);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x7FFF);
+ __ StoreSToOffset(mips::F2, mips::A0, -0xFFF0);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x8008);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x8001);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x8000);
+ __ StoreSToOffset(mips::F2, mips::A0, +0xFFF0);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x17FE8);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x0FFF8);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x0FFF1);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x0FFF1);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x0FFF8);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x17FE8);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x17FF0);
+ __ StoreSToOffset(mips::F2, mips::A0, -0x17FE9);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x17FE9);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x17FF0);
+ __ StoreSToOffset(mips::F2, mips::A0, +0x12345678);
const char* expected =
- "swc1 $f0, 0($a0)\n"
- "swc1 $f0, 4($a0)\n"
- "swc1 $f0, 256($a0)\n"
- "ori $at, $zero, 0x8000\n"
+ "swc1 $f2, -0x8000($a0)\n"
+ "swc1 $f2, 0($a0)\n"
+ "swc1 $f2, 0x7FF8($a0)\n"
+ "swc1 $f2, 0x7FFB($a0)\n"
+ "swc1 $f2, 0x7FFC($a0)\n"
+ "swc1 $f2, 0x7FFF($a0)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "swc1 $f2, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "swc1 $f2, -0x10($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "swc1 $f2, -9($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "swc1 $f2, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "swc1 $f2, 0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "swc1 $f2, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "swc1 $f2, -8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "swc1 $f2, -1($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "swc1 $f2, 1($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "swc1 $f2, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "swc1 $f2, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a0\n"
- "swc1 $f0, 0($at)\n"
- "lui $at, 1\n"
+ "swc1 $f2, 0($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a0\n"
- "swc1 $f0, 0($at)\n"
+ "swc1 $f2, 7($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a0\n"
+ "swc1 $f2, 1($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
+ "addu $at, $at, $a0\n"
+ "swc1 $f2, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a0\n"
- "swc1 $f0, 0($at)\n"
- "swc1 $f0, -256($a0)\n"
- "swc1 $f0, 0xFFFF8000($a0)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
- "addu $at, $at, $a0\n"
- "swc1 $f0, 0($at)\n";
+ "swc1 $f2, 0($at)\n";
DriverStr(expected, "StoreSToOffset");
}
TEST_F(AssemblerMIPSTest, StoreDToOffset) {
- __ StoreDToOffset(mips::F0, mips::A0, 0);
- __ StoreDToOffset(mips::F0, mips::A0, 4);
- __ StoreDToOffset(mips::F0, mips::A0, 256);
- __ StoreDToOffset(mips::F0, mips::A0, 0x8000);
- __ StoreDToOffset(mips::F0, mips::A0, 0x10000);
- __ StoreDToOffset(mips::F0, mips::A0, 0x12345678);
- __ StoreDToOffset(mips::F0, mips::A0, -256);
- __ StoreDToOffset(mips::F0, mips::A0, 0xFFFF8000);
- __ StoreDToOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x8000);
+ __ StoreDToOffset(mips::F0, mips::A0, +0);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FF8);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FFB);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FFC);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x7FFF);
+ __ StoreDToOffset(mips::F0, mips::A0, -0xFFF0);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x8008);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x8001);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x8000);
+ __ StoreDToOffset(mips::F0, mips::A0, +0xFFF0);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x17FE8);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x0FFF8);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x0FFF1);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x0FFF1);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x0FFF8);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x17FE8);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x17FF0);
+ __ StoreDToOffset(mips::F0, mips::A0, -0x17FE9);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x17FE9);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x17FF0);
+ __ StoreDToOffset(mips::F0, mips::A0, +0x12345678);
const char* expected =
+ "sdc1 $f0, -0x8000($a0)\n"
"sdc1 $f0, 0($a0)\n"
- "swc1 $f0, 4($a0)\n"
- "swc1 $f1, 8($a0)\n"
- "sdc1 $f0, 256($a0)\n"
- "ori $at, $zero, 0x8000\n"
+ "sdc1 $f0, 0x7FF8($a0)\n"
+ "swc1 $f0, 0x7FFB($a0)\n"
+ "swc1 $f1, 0x7FFF($a0)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "swc1 $f0, 4($at)\n"
+ "swc1 $f1, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "swc1 $f0, 7($at)\n"
+ "swc1 $f1, 11($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "sdc1 $f0, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "sdc1 $f0, -0x10($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "swc1 $f0, -9($at)\n"
+ "swc1 $f1, -5($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "sdc1 $f0, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "sdc1 $f0, 0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sdc1 $f0, -0x7FF8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "sdc1 $f0, -8($at)\n"
+ "addiu $at, $a0, -0x7FF8\n"
+ "addiu $at, $at, -0x7FF8\n"
+ "swc1 $f0, -1($at)\n"
+ "swc1 $f1, 3($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "swc1 $f0, 1($at)\n"
+ "swc1 $f1, 5($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sdc1 $f0, 8($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "addiu $at, $at, 0x7FF8\n"
+ "sdc1 $f0, 0x7FF8($at)\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
"addu $at, $at, $a0\n"
"sdc1 $f0, 0($at)\n"
- "lui $at, 1\n"
+ "lui $at, 0xFFFE\n"
+ "ori $at, $at, 0x8010\n"
+ "addu $at, $at, $a0\n"
+ "swc1 $f0, 7($at)\n"
+ "swc1 $f1, 11($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FE8\n"
+ "addu $at, $at, $a0\n"
+ "swc1 $f0, 1($at)\n"
+ "swc1 $f1, 5($at)\n"
+ "lui $at, 0x1\n"
+ "ori $at, $at, 0x7FF0\n"
"addu $at, $at, $a0\n"
"sdc1 $f0, 0($at)\n"
"lui $at, 0x1234\n"
- "ori $at, 0x5678\n"
- "addu $at, $at, $a0\n"
- "sdc1 $f0, 0($at)\n"
- "sdc1 $f0, -256($a0)\n"
- "sdc1 $f0, 0xFFFF8000($a0)\n"
- "lui $at, 0xABCD\n"
- "ori $at, 0xEF00\n"
+ "ori $at, $at, 0x5678\n"
"addu $at, $at, $a0\n"
"sdc1 $f0, 0($at)\n";
DriverStr(expected, "StoreDToOffset");
@@ -1492,6 +2248,51 @@
DriverStr(expected, "Bc1t");
}
+///////////////////////
+// Loading Constants //
+///////////////////////
+
+TEST_F(AssemblerMIPSTest, LoadConst32) {
+ // IsUint<16>(value)
+ __ LoadConst32(mips::V0, 0);
+ __ LoadConst32(mips::V0, 65535);
+ // IsInt<16>(value)
+ __ LoadConst32(mips::V0, -1);
+ __ LoadConst32(mips::V0, -32768);
+ // Everything else
+ __ LoadConst32(mips::V0, 65536);
+ __ LoadConst32(mips::V0, 65537);
+ __ LoadConst32(mips::V0, 2147483647);
+ __ LoadConst32(mips::V0, -32769);
+ __ LoadConst32(mips::V0, -65536);
+ __ LoadConst32(mips::V0, -65537);
+ __ LoadConst32(mips::V0, -2147483647);
+ __ LoadConst32(mips::V0, -2147483648);
+
+ const char* expected =
+ // IsUint<16>(value)
+ "ori $v0, $zero, 0\n" // __ LoadConst32(mips::V0, 0);
+ "ori $v0, $zero, 65535\n" // __ LoadConst32(mips::V0, 65535);
+ // IsInt<16>(value)
+ "addiu $v0, $zero, -1\n" // __ LoadConst32(mips::V0, -1);
+ "addiu $v0, $zero, -32768\n" // __ LoadConst32(mips::V0, -32768);
+ // Everything else
+ "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65536);
+ "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65537);
+ "ori $v0, 1\n" // "
+ "lui $v0, 32767\n" // __ LoadConst32(mips::V0, 2147483647);
+ "ori $v0, 65535\n" // "
+ "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -32769);
+ "ori $v0, 32767\n" // "
+ "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -65536);
+ "lui $v0, 65534\n" // __ LoadConst32(mips::V0, -65537);
+ "ori $v0, 65535\n" // "
+ "lui $v0, 32768\n" // __ LoadConst32(mips::V0, -2147483647);
+ "ori $v0, 1\n" // "
+ "lui $v0, 32768\n"; // __ LoadConst32(mips::V0, -2147483648);
+ DriverStr(expected, "LoadConst32");
+}
+
#undef __
} // namespace art
diff --git a/compiler/utils/string_reference.h b/compiler/utils/string_reference.h
index 72552f2..e4c34ca 100644
--- a/compiler/utils/string_reference.h
+++ b/compiler/utils/string_reference.h
@@ -20,16 +20,19 @@
#include <stdint.h>
#include "base/logging.h"
+#include "dex_file-inl.h"
#include "utf-inl.h"
namespace art {
-class DexFile;
-
-// A string is uniquely located by its DexFile and the string_ids_ table index into that DexFile.
+// A string is located by its DexFile and the string_ids_ table index into that DexFile.
struct StringReference {
StringReference(const DexFile* file, uint32_t index) : dex_file(file), string_index(index) { }
+ const char* GetStringData() const {
+ return dex_file->GetStringData(dex_file->GetStringId(string_index));
+ }
+
const DexFile* dex_file;
uint32_t string_index;
};
@@ -46,15 +49,13 @@
// Use the string order enforced by the dex file verifier.
DCHECK_EQ(
sr1.string_index < sr2.string_index,
- CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
- sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)),
- sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0);
+ CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(sr1.GetStringData(),
+ sr2.GetStringData()) < 0);
return sr1.string_index < sr2.string_index;
} else {
// Cannot compare indexes, so do the string comparison.
- return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
- sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)),
- sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0;
+ return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(sr1.GetStringData(),
+ sr2.GetStringData()) < 0;
}
}
};
diff --git a/compiler/utils/string_reference_test.cc b/compiler/utils/string_reference_test.cc
new file mode 100644
index 0000000..df5080e
--- /dev/null
+++ b/compiler/utils/string_reference_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "utils/string_reference.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "utils/test_dex_file_builder.h"
+
+namespace art {
+
+TEST(StringReference, ValueComparator) {
+ // This is a regression test for the StringReferenceValueComparator using the wrong
+ // dex file to get the string data from a StringId. We construct two dex files with
+ // just a single string with the same length but different value. This creates dex
+ // files that have the same layout, so the byte offset read from the StringId in one
+ // dex file, when used in the other dex file still points to valid string data, except
+ // that it's the wrong string. Without the fix the strings would then compare equal.
+ TestDexFileBuilder builder1;
+ builder1.AddString("String1");
+ std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1");
+ ASSERT_EQ(1u, dex_file1->NumStringIds());
+ ASSERT_STREQ("String1", dex_file1->GetStringData(dex_file1->GetStringId(0)));
+ StringReference sr1(dex_file1.get(), 0);
+
+ TestDexFileBuilder builder2;
+ builder2.AddString("String2");
+ std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 2");
+ ASSERT_EQ(1u, dex_file2->NumStringIds());
+ ASSERT_STREQ("String2", dex_file2->GetStringData(dex_file2->GetStringId(0)));
+ StringReference sr2(dex_file2.get(), 0);
+
+ StringReferenceValueComparator cmp;
+ EXPECT_TRUE(cmp(sr1, sr2)); // "String1" < "String2" is true.
+ EXPECT_FALSE(cmp(sr2, sr1)); // "String2" < "String1" is false.
+}
+
+TEST(StringReference, ValueComparator2) {
+ const char* const kDexFile1Strings[] = {
+ "",
+ "abc",
+ "abcxyz",
+ };
+ const char* const kDexFile2Strings[] = {
+ "a",
+ "abc",
+ "abcdef",
+ "def",
+ };
+ const bool expectedCmp12[arraysize(kDexFile1Strings)][arraysize(kDexFile2Strings)] = {
+ { true, true, true, true },
+ { false, false, true, true },
+ { false, false, false, true },
+ };
+ const bool expectedCmp21[arraysize(kDexFile2Strings)][arraysize(kDexFile1Strings)] = {
+ { false, true, true },
+ { false, false, true },
+ { false, false, true },
+ { false, false, false },
+ };
+
+ TestDexFileBuilder builder1;
+ for (const char* s : kDexFile1Strings) {
+ builder1.AddString(s);
+ }
+ std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1");
+ ASSERT_EQ(arraysize(kDexFile1Strings), dex_file1->NumStringIds());
+ for (size_t index = 0; index != arraysize(kDexFile1Strings); ++index) {
+ ASSERT_STREQ(kDexFile1Strings[index], dex_file1->GetStringData(dex_file1->GetStringId(index)));
+ }
+
+ TestDexFileBuilder builder2;
+ for (const char* s : kDexFile2Strings) {
+ builder2.AddString(s);
+ }
+ std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 1");
+ ASSERT_EQ(arraysize(kDexFile2Strings), dex_file2->NumStringIds());
+ for (size_t index = 0; index != arraysize(kDexFile2Strings); ++index) {
+ ASSERT_STREQ(kDexFile2Strings[index], dex_file2->GetStringData(dex_file2->GetStringId(index)));
+ }
+
+ StringReferenceValueComparator cmp;
+ for (size_t index1 = 0; index1 != arraysize(kDexFile1Strings); ++index1) {
+ for (size_t index2 = 0; index2 != arraysize(kDexFile2Strings); ++index2) {
+ StringReference sr1(dex_file1.get(), index1);
+ StringReference sr2(dex_file2.get(), index2);
+ EXPECT_EQ(expectedCmp12[index1][index2], cmp(sr1, sr2)) << index1 << " " << index2;
+ EXPECT_EQ(expectedCmp21[index2][index1], cmp(sr2, sr1)) << index1 << " " << index2;
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index fb37804..6921780 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -227,9 +227,18 @@
// Write the complete header again, just simpler that way.
std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
+ static constexpr bool kVerify = false;
+ static constexpr bool kVerifyChecksum = false;
std::string error_msg;
std::unique_ptr<const DexFile> dex_file(DexFile::Open(
- &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, false, &error_msg));
+ &dex_file_data_[0],
+ dex_file_data_.size(),
+ dex_location,
+ 0u,
+ nullptr,
+ kVerify,
+ kVerifyChecksum,
+ &error_msg));
CHECK(dex_file != nullptr) << error_msg;
return dex_file;
}
diff --git a/compiler/utils/type_reference.h b/compiler/utils/type_reference.h
new file mode 100644
index 0000000..bd0739f
--- /dev/null
+++ b/compiler/utils/type_reference.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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_COMPILER_UTILS_TYPE_REFERENCE_H_
+#define ART_COMPILER_UTILS_TYPE_REFERENCE_H_
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "utils/string_reference.h"
+
+namespace art {
+
+class DexFile;
+
+// A type is located by its DexFile and the string_ids_ table index into that DexFile.
+struct TypeReference {
+ TypeReference(const DexFile* file, uint32_t index) : dex_file(file), type_index(index) { }
+
+ const DexFile* dex_file;
+ uint32_t type_index;
+};
+
+// Compare the actual referenced type names. Used for type reference deduplication.
+struct TypeReferenceValueComparator {
+ bool operator()(TypeReference tr1, TypeReference tr2) const {
+ // Note that we want to deduplicate identical boot image types even if they are
+ // referenced by different dex files, so we simply compare the descriptors.
+ StringReference sr1(tr1.dex_file, tr1.dex_file->GetTypeId(tr1.type_index).descriptor_idx_);
+ StringReference sr2(tr2.dex_file, tr2.dex_file->GetTypeId(tr2.type_index).descriptor_idx_);
+ return StringReferenceValueComparator()(sr1, sr2);
+ }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_TYPE_REFERENCE_H_
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 3db85c1..c133980 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -22,6 +22,7 @@
#include <fstream>
#include <iostream>
+#include <limits>
#include <sstream>
#include <string>
#include <unordered_set>
@@ -81,6 +82,9 @@
namespace art {
+static constexpr size_t kDefaultMinDexFilesForSwap = 2;
+static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
+
static int original_argc;
static char** original_argv;
@@ -351,6 +355,20 @@
UsageError(" --swap-fd=<file-descriptor>: specifies a file to use for swap (by descriptor).");
UsageError(" Example: --swap-fd=10");
UsageError("");
+ UsageError(" --swap-dex-size-threshold=<size>: specifies the minimum total dex file size in");
+ UsageError(" bytes to allow the use of swap.");
+ UsageError(" Example: --swap-dex-size-threshold=1000000");
+ UsageError(" Default: %zu", kDefaultMinDexFileCumulativeSizeForSwap);
+ UsageError("");
+ UsageError(" --swap-dex-count-threshold=<count>: specifies the minimum number of dex files to");
+ UsageError(" allow the use of swap.");
+ UsageError(" Example: --swap-dex-count-threshold=10");
+ UsageError(" Default: %zu", kDefaultMinDexFilesForSwap);
+ UsageError("");
+ UsageError(" --very-large-app-threshold=<size>: specifies the minimum total dex file size in");
+ UsageError(" bytes to consider the input \"very large\" and punt on the compilation.");
+ UsageError(" Example: --very-large-app-threshold=100000000");
+ UsageError("");
UsageError(" --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
UsageError(" Example: --app-image-fd=10");
UsageError("");
@@ -473,25 +491,6 @@
pthread_t pthread_;
};
-static constexpr size_t kMinDexFilesForSwap = 2;
-static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
-
-static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
- if (is_image) {
- // Don't use swap, we know generation should succeed, and we don't want to slow it down.
- return false;
- }
- if (dex_files.size() < kMinDexFilesForSwap) {
- // If there are less dex files than the threshold, assume it's gonna be fine.
- return false;
- }
- size_t dex_files_size = 0;
- for (const auto* dex_file : dex_files) {
- dex_files_size += dex_file->GetHeader().file_size_;
- }
- return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
-}
-
class Dex2Oat FINAL {
public:
explicit Dex2Oat(TimingLogger* timings) :
@@ -1132,6 +1131,21 @@
swap_file_name_ = option.substr(strlen("--swap-file=")).data();
} else if (option.starts_with("--swap-fd=")) {
ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
+ } else if (option.starts_with("--swap-dex-size-threshold=")) {
+ ParseUintOption(option,
+ "--swap-dex-size-threshold",
+ &min_dex_file_cumulative_size_for_swap_,
+ Usage);
+ } else if (option.starts_with("--swap-dex-count-threshold=")) {
+ ParseUintOption(option,
+ "--swap-dex-count-threshold",
+ &min_dex_files_for_swap_,
+ Usage);
+ } else if (option.starts_with("--very-large-app-threshold=")) {
+ ParseUintOption(option,
+ "--very-large-app-threshold",
+ &very_large_threshold_,
+ Usage);
} else if (option.starts_with("--app-image-file=")) {
app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
} else if (option.starts_with("--app-image-fd=")) {
@@ -1226,18 +1240,6 @@
unlink(swap_file_name_.c_str());
}
- // If we use a swap file, ensure we are above the threshold to make it necessary.
- if (swap_fd_ != -1) {
- if (!UseSwap(IsBootImage(), dex_files_)) {
- close(swap_fd_);
- swap_fd_ = -1;
- VLOG(compiler) << "Decided to run without swap.";
- } else {
- LOG(INFO) << "Large app, accepted running with swap.";
- }
- }
- // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that.
-
return true;
}
@@ -1408,6 +1410,37 @@
}
dex_files_ = MakeNonOwningPointerVector(opened_dex_files_);
+
+ // We had to postpone the swap decision till now, as this is the point when we actually
+ // know about the dex files we're going to use.
+
+ // Make sure that we didn't create the driver, yet.
+ CHECK(driver_ == nullptr);
+ // If we use a swap file, ensure we are above the threshold to make it necessary.
+ if (swap_fd_ != -1) {
+ if (!UseSwap(IsBootImage(), dex_files_)) {
+ close(swap_fd_);
+ swap_fd_ = -1;
+ VLOG(compiler) << "Decided to run without swap.";
+ } else {
+ LOG(INFO) << "Large app, accepted running with swap.";
+ }
+ }
+ // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that.
+
+ // If we need to downgrade the compiler-filter for size reasons, do that check now.
+ if (!IsBootImage() && IsVeryLarge(dex_files_)) {
+ if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime,
+ compiler_options_->GetCompilerFilter())) {
+ LOG(INFO) << "Very large app, downgrading to verify-at-runtime.";
+ // Note: this change won't be reflected in the key-value store, as that had to be
+ // finalized before loading the dex files. This setup is currently required
+ // to get the size from the DexFile objects.
+ // TODO: refactor. b/29790079
+ compiler_options_->SetCompilerFilter(CompilerFilter::kVerifyAtRuntime);
+ }
+ }
+
if (IsBootImage()) {
// For boot image, pass opened dex files to the Runtime::Create().
// Note: Runtime acquires ownership of these dex files.
@@ -1836,10 +1869,6 @@
}
}
- CompilerOptions* GetCompilerOptions() const {
- return compiler_options_.get();
- }
-
bool IsImage() const {
return IsAppImage() || IsBootImage();
}
@@ -1891,6 +1920,30 @@
}
private:
+ bool UseSwap(bool is_image, const std::vector<const DexFile*>& dex_files) {
+ if (is_image) {
+ // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+ return false;
+ }
+ if (dex_files.size() < min_dex_files_for_swap_) {
+ // If there are less dex files than the threshold, assume it's gonna be fine.
+ return false;
+ }
+ size_t dex_files_size = 0;
+ for (const auto* dex_file : dex_files) {
+ dex_files_size += dex_file->GetHeader().file_size_;
+ }
+ return dex_files_size >= min_dex_file_cumulative_size_for_swap_;
+ }
+
+ bool IsVeryLarge(std::vector<const DexFile*>& dex_files) {
+ size_t dex_files_size = 0;
+ for (const auto* dex_file : dex_files) {
+ dex_files_size += dex_file->GetHeader().file_size_;
+ }
+ return dex_files_size >= very_large_threshold_;
+ }
+
template <typename T>
static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
std::vector<T*> result;
@@ -1979,8 +2032,10 @@
if (location == OatFile::kSpecialSharedLibrary) {
break;
}
+ static constexpr bool kVerifyChecksum = true;
std::string error_msg;
- if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) {
+ if (!DexFile::Open(
+ location.c_str(), location.c_str(), kVerifyChecksum, &error_msg, opened_dex_files)) {
// If we fail to open the dex file because it's been stripped, try to open the dex file
// from its corresponding oat file.
OatFileAssistant oat_file_assistant(location.c_str(), isa, false, false);
@@ -2478,6 +2533,9 @@
bool dump_slow_timing_;
std::string swap_file_name_;
int swap_fd_;
+ size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
+ size_t min_dex_file_cumulative_size_for_swap_ = kDefaultMinDexFileCumulativeSizeForSwap;
+ size_t very_large_threshold_ = std::numeric_limits<size_t>::max();
std::string app_image_file_name_;
int app_image_fd_;
std::string profile_file_;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
new file mode 100644
index 0000000..6188883
--- /dev/null
+++ b/dex2oat/dex2oat_test.cc
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "common_runtime_test.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stringprintf.h"
+#include "dex2oat_environment_test.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "utils.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+namespace art {
+
+class Dex2oatTest : public Dex2oatEnvironmentTest {
+ public:
+ virtual void TearDown() OVERRIDE {
+ Dex2oatEnvironmentTest::TearDown();
+
+ output_ = "";
+ error_msg_ = "";
+ success_ = false;
+ }
+
+ protected:
+ void GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter,
+ const std::vector<std::string>& extra_args = {},
+ bool expect_success = true) {
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+ args.push_back("--runtime-arg");
+ args.push_back("-Xnorelocate");
+
+ args.insert(args.end(), extra_args.begin(), extra_args.end());
+
+ std::string error_msg;
+ bool success = Dex2Oat(args, &error_msg);
+
+ if (expect_success) {
+ ASSERT_TRUE(success) << error_msg;
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+
+ CheckFilter(filter, odex_file->GetCompilerFilter());
+ } else {
+ ASSERT_FALSE(success) << output_;
+
+ error_msg_ = error_msg;
+
+ // Verify there's no loadable odex file.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file.get() == nullptr);
+ }
+ }
+
+ // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
+ // in subclasses when equality is not expected.
+ virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
+ EXPECT_EQ(expected, actual);
+ }
+
+ bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
+ Runtime* runtime = Runtime::Current();
+
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ runtime->GetHeap()->GetBootImageSpaces();
+ if (image_spaces.empty()) {
+ *error_msg = "No image location found for Dex2Oat.";
+ return false;
+ }
+ std::string image_location = image_spaces[0]->GetImageLocation();
+
+ std::vector<std::string> argv;
+ argv.push_back(runtime->GetCompilerExecutable());
+ argv.push_back("--runtime-arg");
+ argv.push_back("-classpath");
+ argv.push_back("--runtime-arg");
+ std::string class_path = runtime->GetClassPathString();
+ if (class_path == "") {
+ class_path = OatFile::kSpecialSharedLibrary;
+ }
+ argv.push_back(class_path);
+ if (runtime->IsDebuggable()) {
+ argv.push_back("--debuggable");
+ }
+ runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+ if (!runtime->IsVerificationEnabled()) {
+ argv.push_back("--compiler-filter=verify-none");
+ }
+
+ if (runtime->MustRelocateIfPossible()) {
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xrelocate");
+ } else {
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xnorelocate");
+ }
+
+ if (!kIsTargetBuild) {
+ argv.push_back("--host");
+ }
+
+ argv.push_back("--boot-image=" + image_location);
+
+ std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+ argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+ argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end());
+
+ // We must set --android-root.
+ const char* android_root = getenv("ANDROID_ROOT");
+ CHECK(android_root != nullptr);
+ argv.push_back("--android-root=" + std::string(android_root));
+
+ std::string command_line(Join(argv, ' '));
+
+ // We need to fix up the '&' being used for "do not check classpath."
+ size_t ampersand = command_line.find(" &");
+ CHECK_NE(ampersand, std::string::npos);
+ command_line = command_line.replace(ampersand, 2, " \\&");
+
+ command_line += " 2>&1";
+
+ // We need dex2oat to actually log things.
+ setenv("ANDROID_LOG_TAGS", "*:d", 1);
+
+ FILE* pipe = popen(command_line.c_str(), "r");
+
+ setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
+ if (pipe == nullptr) {
+ success_ = false;
+ } else {
+ char buffer[128];
+
+ while (fgets(buffer, 128, pipe) != nullptr) {
+ output_ += buffer;
+ }
+
+ int result = pclose(pipe);
+ success_ = result == 0;
+ }
+ return success_;
+ }
+
+ std::string output_ = "";
+ std::string error_msg_ = "";
+ bool success_ = false;
+};
+
+class Dex2oatSwapTest : public Dex2oatTest {
+ protected:
+ void RunTest(bool use_fd, bool expect_use, const std::vector<std::string>& extra_args = {}) {
+ std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+ std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+
+ std::vector<std::string> copy(extra_args);
+
+ std::unique_ptr<ScratchFile> sf;
+ if (use_fd) {
+ sf.reset(new ScratchFile());
+ copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
+ } else {
+ std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
+ copy.push_back("--swap-file=" + swap_location);
+ }
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy);
+
+ CheckValidity();
+ ASSERT_TRUE(success_);
+ CheckResult(expect_use);
+ }
+
+ void CheckResult(bool expect_use) {
+ if (kIsTargetBuild) {
+ CheckTargetResult(expect_use);
+ } else {
+ CheckHostResult(expect_use);
+ }
+ }
+
+ void CheckTargetResult(bool expect_use ATTRIBUTE_UNUSED) {
+ // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
+ // something for variants with file descriptor where we can control the lifetime of
+ // the swap file and thus take a look at it.
+ }
+
+ void CheckHostResult(bool expect_use) {
+ if (!kIsTargetBuild) {
+ if (expect_use) {
+ EXPECT_NE(output_.find("Large app, accepted running with swap."), std::string::npos)
+ << output_;
+ } else {
+ EXPECT_EQ(output_.find("Large app, accepted running with swap."), std::string::npos)
+ << output_;
+ }
+ }
+ }
+
+ // Check whether the dex2oat run was really successful.
+ void CheckValidity() {
+ if (kIsTargetBuild) {
+ CheckTargetValidity();
+ } else {
+ CheckHostValidity();
+ }
+ }
+
+ void CheckTargetValidity() {
+ // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
+ // something for variants with file descriptor where we can control the lifetime of
+ // the swap file and thus take a look at it.
+ }
+
+ // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
+ void CheckHostValidity() {
+ EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
+ }
+};
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapDefaultSingleSmall) {
+ RunTest(false /* use_fd */, false /* expect_use */);
+ RunTest(true /* use_fd */, false /* expect_use */);
+}
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapSingle) {
+ RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
+ RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
+}
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapSmall) {
+ RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
+ RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
+}
+
+TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) {
+ RunTest(false /* use_fd */,
+ true /* expect_use */,
+ { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
+ RunTest(true /* use_fd */,
+ true /* expect_use */,
+ { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
+}
+
+class Dex2oatVeryLargeTest : public Dex2oatTest {
+ protected:
+ void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED,
+ CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE {
+ // Ignore, we'll do our own checks.
+ }
+
+ void RunTest(CompilerFilter::Filter filter,
+ bool expect_large,
+ const std::vector<std::string>& extra_args = {}) {
+ std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+
+ std::vector<std::string> copy(extra_args);
+
+ GenerateOdexForTest(dex_location, odex_location, filter, copy);
+
+ CheckValidity();
+ ASSERT_TRUE(success_);
+ CheckResult(dex_location, odex_location, filter, expect_large);
+ }
+
+ void CheckResult(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter,
+ bool expect_large) {
+ // Host/target independent checks.
+ std::string error_msg;
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ if (expect_large) {
+ // Note: we cannot check the following:
+ // EXPECT_TRUE(CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime,
+ // odex_file->GetCompilerFilter()));
+ // The reason is that the filter override currently happens when the dex files are
+ // loaded in dex2oat, which is after the oat file has been started. Thus, the header
+ // store cannot be changed, and the original filter is set in stone.
+
+ for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+ ASSERT_TRUE(dex_file != nullptr);
+ uint32_t class_def_count = dex_file->NumClassDefs();
+ ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max());
+ for (uint16_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+ OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+ EXPECT_EQ(oat_class.GetType(), OatClassType::kOatClassNoneCompiled);
+ }
+ }
+
+ // If the input filter was "below," it should have been used.
+ if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime, filter)) {
+ EXPECT_EQ(odex_file->GetCompilerFilter(), filter);
+ }
+ } else {
+ EXPECT_EQ(odex_file->GetCompilerFilter(), filter);
+ }
+
+ // Host/target dependent checks.
+ if (kIsTargetBuild) {
+ CheckTargetResult(expect_large);
+ } else {
+ CheckHostResult(expect_large);
+ }
+ }
+
+ void CheckTargetResult(bool expect_large ATTRIBUTE_UNUSED) {
+ // TODO: Ignore for now. May do something for fd things.
+ }
+
+ void CheckHostResult(bool expect_large) {
+ if (!kIsTargetBuild) {
+ if (expect_large) {
+ EXPECT_NE(output_.find("Very large app, downgrading to verify-at-runtime."),
+ std::string::npos)
+ << output_;
+ } else {
+ EXPECT_EQ(output_.find("Very large app, downgrading to verify-at-runtime."),
+ std::string::npos)
+ << output_;
+ }
+ }
+ }
+
+ // Check whether the dex2oat run was really successful.
+ void CheckValidity() {
+ if (kIsTargetBuild) {
+ CheckTargetValidity();
+ } else {
+ CheckHostValidity();
+ }
+ }
+
+ void CheckTargetValidity() {
+ // TODO: Ignore for now.
+ }
+
+ // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
+ void CheckHostValidity() {
+ EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
+ }
+};
+
+TEST_F(Dex2oatVeryLargeTest, DontUseVeryLarge) {
+ RunTest(CompilerFilter::kVerifyNone, false);
+ RunTest(CompilerFilter::kVerifyAtRuntime, false);
+ RunTest(CompilerFilter::kInterpretOnly, false);
+ RunTest(CompilerFilter::kSpeed, false);
+
+ RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=1000000" });
+ RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=1000000" });
+ RunTest(CompilerFilter::kInterpretOnly, false, { "--very-large-app-threshold=1000000" });
+ RunTest(CompilerFilter::kSpeed, false, { "--very-large-app-threshold=1000000" });
+}
+
+TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) {
+ RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=100" });
+ RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=100" });
+ RunTest(CompilerFilter::kInterpretOnly, true, { "--very-large-app-threshold=100" });
+ RunTest(CompilerFilter::kSpeed, true, { "--very-large-app-threshold=100" });
+}
+
+} // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 1a2f2c2..48b773e 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -17,8 +17,8 @@
*
* This is a re-implementation of the original dexdump utility that was
* based on Dalvik functions in libdex into a new dexdump that is now
- * based on Art functions in libart instead. The output is identical to
- * the original for correct DEX files. Error messages may differ, however.
+ * based on Art functions in libart instead. The output is very similar to
+ * to the original for correct DEX files. Error messages may differ, however.
* Also, ODEX files are no longer supported.
*
* The dexdump tool is intended to mimic objdump. When possible, use
@@ -65,6 +65,8 @@
typedef uint16_t u2;
typedef uint32_t u4;
typedef uint64_t u8;
+typedef int8_t s1;
+typedef int16_t s2;
typedef int32_t s4;
typedef int64_t s8;
@@ -187,6 +189,13 @@
}
/*
+ * Returns string representing the boolean value.
+ */
+static const char* strBool(bool val) {
+ return val ? "true" : "false";
+}
+
+/*
* Returns a quoted string representing the boolean value.
*/
static const char* quotedBool(bool val) {
@@ -346,10 +355,197 @@
}
/*
+ * Dumps a string value with some escape characters.
+ */
+static void dumpEscapedString(const char* p) {
+ fputs("\"", gOutFile);
+ for (; *p; p++) {
+ switch (*p) {
+ case '\\':
+ fputs("\\\\", gOutFile);
+ break;
+ case '\"':
+ fputs("\\\"", gOutFile);
+ break;
+ case '\t':
+ fputs("\\t", gOutFile);
+ break;
+ case '\n':
+ fputs("\\n", gOutFile);
+ break;
+ case '\r':
+ fputs("\\r", gOutFile);
+ break;
+ default:
+ putc(*p, gOutFile);
+ } // switch
+ } // for
+ fputs("\"", gOutFile);
+}
+
+/*
+ * Dumps a string as an XML attribute value.
+ */
+static void dumpXmlAttribute(const char* p) {
+ for (; *p; p++) {
+ switch (*p) {
+ case '&':
+ fputs("&", gOutFile);
+ break;
+ case '<':
+ fputs("<", gOutFile);
+ break;
+ case '>':
+ fputs(">", gOutFile);
+ break;
+ case '"':
+ fputs(""", gOutFile);
+ break;
+ case '\t':
+ fputs("	", gOutFile);
+ break;
+ case '\n':
+ fputs("
", gOutFile);
+ break;
+ case '\r':
+ fputs("
", gOutFile);
+ break;
+ default:
+ putc(*p, gOutFile);
+ } // switch
+ } // for
+}
+
+/*
+ * Reads variable width value, possibly sign extended at the last defined byte.
+ */
+static u8 readVarWidth(const u1** data, u1 arg, bool sign_extend) {
+ u8 value = 0;
+ for (u4 i = 0; i <= arg; i++) {
+ value |= static_cast<u8>(*(*data)++) << (i * 8);
+ }
+ if (sign_extend) {
+ int shift = (7 - arg) * 8;
+ return (static_cast<s8>(value) << shift) >> shift;
+ }
+ return value;
+}
+
+/*
+ * Dumps encoded value.
+ */
+static void dumpEncodedValue(const DexFile* pDexFile, const u1** data); // forward
+static void dumpEncodedValue(const DexFile* pDexFile, const u1** data, u1 type, u1 arg) {
+ switch (type) {
+ case DexFile::kDexAnnotationByte:
+ fprintf(gOutFile, "%" PRId8, static_cast<s1>(readVarWidth(data, arg, false)));
+ break;
+ case DexFile::kDexAnnotationShort:
+ fprintf(gOutFile, "%" PRId16, static_cast<s2>(readVarWidth(data, arg, true)));
+ break;
+ case DexFile::kDexAnnotationChar:
+ fprintf(gOutFile, "%" PRIu16, static_cast<u2>(readVarWidth(data, arg, false)));
+ break;
+ case DexFile::kDexAnnotationInt:
+ fprintf(gOutFile, "%" PRId32, static_cast<s4>(readVarWidth(data, arg, true)));
+ break;
+ case DexFile::kDexAnnotationLong:
+ fprintf(gOutFile, "%" PRId64, static_cast<s8>(readVarWidth(data, arg, true)));
+ break;
+ case DexFile::kDexAnnotationFloat: {
+ // Fill on right.
+ union {
+ float f;
+ u4 data;
+ } conv;
+ conv.data = static_cast<u4>(readVarWidth(data, arg, false)) << (3 - arg) * 8;
+ fprintf(gOutFile, "%g", conv.f);
+ break;
+ }
+ case DexFile::kDexAnnotationDouble: {
+ // Fill on right.
+ union {
+ double d;
+ u8 data;
+ } conv;
+ conv.data = readVarWidth(data, arg, false) << (7 - arg) * 8;
+ fprintf(gOutFile, "%g", conv.d);
+ break;
+ }
+ case DexFile::kDexAnnotationString: {
+ const u4 idx = static_cast<u4>(readVarWidth(data, arg, false));
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ dumpEscapedString(pDexFile->StringDataByIdx(idx));
+ } else {
+ dumpXmlAttribute(pDexFile->StringDataByIdx(idx));
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationType: {
+ const u4 str_idx = static_cast<u4>(readVarWidth(data, arg, false));
+ fputs(pDexFile->StringByTypeIdx(str_idx), gOutFile);
+ break;
+ }
+ case DexFile::kDexAnnotationField:
+ case DexFile::kDexAnnotationEnum: {
+ const u4 field_idx = static_cast<u4>(readVarWidth(data, arg, false));
+ const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(field_idx);
+ fputs(pDexFile->StringDataByIdx(pFieldId.name_idx_), gOutFile);
+ break;
+ }
+ case DexFile::kDexAnnotationMethod: {
+ const u4 method_idx = static_cast<u4>(readVarWidth(data, arg, false));
+ const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(method_idx);
+ fputs(pDexFile->StringDataByIdx(pMethodId.name_idx_), gOutFile);
+ break;
+ }
+ case DexFile::kDexAnnotationArray: {
+ fputc('{', gOutFile);
+ // Decode and display all elements.
+ const u4 size = DecodeUnsignedLeb128(data);
+ for (u4 i = 0; i < size; i++) {
+ fputc(' ', gOutFile);
+ dumpEncodedValue(pDexFile, data);
+ }
+ fputs(" }", gOutFile);
+ break;
+ }
+ case DexFile::kDexAnnotationAnnotation: {
+ const u4 type_idx = DecodeUnsignedLeb128(data);
+ fputs(pDexFile->StringByTypeIdx(type_idx), gOutFile);
+ // Decode and display all name=value pairs.
+ const u4 size = DecodeUnsignedLeb128(data);
+ for (u4 i = 0; i < size; i++) {
+ const u4 name_idx = DecodeUnsignedLeb128(data);
+ fputc(' ', gOutFile);
+ fputs(pDexFile->StringDataByIdx(name_idx), gOutFile);
+ fputc('=', gOutFile);
+ dumpEncodedValue(pDexFile, data);
+ }
+ break;
+ }
+ case DexFile::kDexAnnotationNull:
+ fputs("null", gOutFile);
+ break;
+ case DexFile::kDexAnnotationBoolean:
+ fputs(strBool(arg), gOutFile);
+ break;
+ default:
+ fputs("????", gOutFile);
+ break;
+ } // switch
+}
+
+/*
+ * Dumps encoded value with prefix.
+ */
+static void dumpEncodedValue(const DexFile* pDexFile, const u1** data) {
+ const u1 enc = *(*data)++;
+ dumpEncodedValue(pDexFile, data, enc & 0x1f, enc >> 5);
+}
+
+/*
* Dumps the file header.
- *
- * Note that some of the : are misaligned on purpose to preserve
- * the exact output of the original Dalvik dexdump.
*/
static void dumpFileHeader(const DexFile* pDexFile) {
const DexFile::Header& pHeader = pDexFile->GetHeader();
@@ -373,8 +569,8 @@
fprintf(gOutFile, "type_ids_size : %d\n", pHeader.type_ids_size_);
fprintf(gOutFile, "type_ids_off : %d (0x%06x)\n",
pHeader.type_ids_off_, pHeader.type_ids_off_);
- fprintf(gOutFile, "proto_ids_size : %d\n", pHeader.proto_ids_size_);
- fprintf(gOutFile, "proto_ids_off : %d (0x%06x)\n",
+ fprintf(gOutFile, "proto_ids_size : %d\n", pHeader.proto_ids_size_);
+ fprintf(gOutFile, "proto_ids_off : %d (0x%06x)\n",
pHeader.proto_ids_off_, pHeader.proto_ids_off_);
fprintf(gOutFile, "field_ids_size : %d\n", pHeader.field_ids_size_);
fprintf(gOutFile, "field_ids_off : %d (0x%06x)\n",
@@ -426,6 +622,99 @@
fprintf(gOutFile, "\n");
}
+/**
+ * Dumps an annotation set item.
+ */
+static void dumpAnnotationSetItem(const DexFile* pDexFile, const DexFile::AnnotationSetItem* set_item) {
+ if (set_item == nullptr || set_item->size_ == 0) {
+ fputs(" empty-annotation-set\n", gOutFile);
+ return;
+ }
+ for (u4 i = 0; i < set_item->size_; i++) {
+ const DexFile::AnnotationItem* annotation = pDexFile->GetAnnotationItem(set_item, i);
+ if (annotation == nullptr) {
+ continue;
+ }
+ fputs(" ", gOutFile);
+ switch (annotation->visibility_) {
+ case DexFile::kDexVisibilityBuild: fputs("VISIBILITY_BUILD ", gOutFile); break;
+ case DexFile::kDexVisibilityRuntime: fputs("VISIBILITY_RUNTIME ", gOutFile); break;
+ case DexFile::kDexVisibilitySystem: fputs("VISIBILITY_SYSTEM ", gOutFile); break;
+ default: fputs("VISIBILITY_UNKNOWN ", gOutFile); break;
+ } // switch
+ // Decode raw bytes in annotation.
+ const u1* rData = annotation->annotation_;
+ dumpEncodedValue(pDexFile, &rData, DexFile::kDexAnnotationAnnotation, 0);
+ fputc('\n', gOutFile);
+ }
+}
+
+/*
+ * Dumps class annotations.
+ */
+static void dumpClassAnnotations(const DexFile* pDexFile, int idx) {
+ const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
+ const DexFile::AnnotationsDirectoryItem* dir = pDexFile->GetAnnotationsDirectory(pClassDef);
+ if (dir == nullptr) {
+ return; // none
+ }
+
+ fprintf(gOutFile, "Class #%d annotations:\n", idx);
+
+ const DexFile::AnnotationSetItem* class_set_item = pDexFile->GetClassAnnotationSet(dir);
+ const DexFile::FieldAnnotationsItem* fields = pDexFile->GetFieldAnnotations(dir);
+ const DexFile::MethodAnnotationsItem* methods = pDexFile->GetMethodAnnotations(dir);
+ const DexFile::ParameterAnnotationsItem* pars = pDexFile->GetParameterAnnotations(dir);
+
+ // Annotations on the class itself.
+ if (class_set_item != nullptr) {
+ fprintf(gOutFile, "Annotations on class\n");
+ dumpAnnotationSetItem(pDexFile, class_set_item);
+ }
+
+ // Annotations on fields.
+ if (fields != nullptr) {
+ for (u4 i = 0; i < dir->fields_size_; i++) {
+ const u4 field_idx = fields[i].field_idx_;
+ const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(field_idx);
+ const char* field_name = pDexFile->StringDataByIdx(pFieldId.name_idx_);
+ fprintf(gOutFile, "Annotations on field #%u '%s'\n", field_idx, field_name);
+ dumpAnnotationSetItem(pDexFile, pDexFile->GetFieldAnnotationSetItem(fields[i]));
+ }
+ }
+
+ // Annotations on methods.
+ if (methods != nullptr) {
+ for (u4 i = 0; i < dir->methods_size_; i++) {
+ const u4 method_idx = methods[i].method_idx_;
+ const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(method_idx);
+ const char* method_name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+ fprintf(gOutFile, "Annotations on method #%u '%s'\n", method_idx, method_name);
+ dumpAnnotationSetItem(pDexFile, pDexFile->GetMethodAnnotationSetItem(methods[i]));
+ }
+ }
+
+ // Annotations on method parameters.
+ if (pars != nullptr) {
+ for (u4 i = 0; i < dir->parameters_size_; i++) {
+ const u4 method_idx = pars[i].method_idx_;
+ const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(method_idx);
+ const char* method_name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+ fprintf(gOutFile, "Annotations on method #%u '%s' parameters\n", method_idx, method_name);
+ const DexFile::AnnotationSetRefList*
+ list = pDexFile->GetParameterAnnotationSetRefList(&pars[i]);
+ if (list != nullptr) {
+ for (u4 j = 0; j < list->size_; j++) {
+ fprintf(gOutFile, "#%u\n", j);
+ dumpAnnotationSetItem(pDexFile, pDexFile->GetSetRefItemItem(&list->list_[j]));
+ }
+ }
+ }
+ }
+
+ fputc('\n', gOutFile);
+}
+
/*
* Dumps an interface that a class declares to implement.
*/
@@ -677,27 +966,25 @@
fprintf(gOutFile, " v%d", pDecInsn->VRegA());
break;
case Instruction::k10t: // op +AA
- case Instruction::k20t: // op +AAAA
- {
- const s4 targ = (s4) pDecInsn->VRegA();
- fprintf(gOutFile, " %04x // %c%04x",
- insnIdx + targ,
- (targ < 0) ? '-' : '+',
- (targ < 0) ? -targ : targ);
- }
+ case Instruction::k20t: { // op +AAAA
+ const s4 targ = (s4) pDecInsn->VRegA();
+ fprintf(gOutFile, " %04x // %c%04x",
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
break;
+ }
case Instruction::k22x: // op vAA, vBBBB
fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB());
break;
- case Instruction::k21t: // op vAA, +BBBB
- {
- const s4 targ = (s4) pDecInsn->VRegB();
- fprintf(gOutFile, " v%d, %04x // %c%04x", pDecInsn->VRegA(),
- insnIdx + targ,
- (targ < 0) ? '-' : '+',
- (targ < 0) ? -targ : targ);
- }
+ case Instruction::k21t: { // op vAA, +BBBB
+ const s4 targ = (s4) pDecInsn->VRegB();
+ fprintf(gOutFile, " v%d, %04x // %c%04x", pDecInsn->VRegA(),
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
break;
+ }
case Instruction::k21s: // op vAA, #+BBBB
fprintf(gOutFile, " v%d, #int %d // #%x",
pDecInsn->VRegA(), (s4) pDecInsn->VRegB(), (u2)pDecInsn->VRegB());
@@ -727,16 +1014,15 @@
pDecInsn->VRegA(), pDecInsn->VRegB(),
(s4) pDecInsn->VRegC(), (u1) pDecInsn->VRegC());
break;
- case Instruction::k22t: // op vA, vB, +CCCC
- {
- const s4 targ = (s4) pDecInsn->VRegC();
- fprintf(gOutFile, " v%d, v%d, %04x // %c%04x",
- pDecInsn->VRegA(), pDecInsn->VRegB(),
- insnIdx + targ,
- (targ < 0) ? '-' : '+',
- (targ < 0) ? -targ : targ);
- }
+ case Instruction::k22t: { // op vA, vB, +CCCC
+ const s4 targ = (s4) pDecInsn->VRegC();
+ fprintf(gOutFile, " v%d, v%d, %04x // %c%04x",
+ pDecInsn->VRegA(), pDecInsn->VRegB(),
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
break;
+ }
case Instruction::k22s: // op vA, vB, #+CCCC
fprintf(gOutFile, " v%d, v%d, #int %d // #%04x",
pDecInsn->VRegA(), pDecInsn->VRegB(),
@@ -751,18 +1037,17 @@
case Instruction::k30t:
fprintf(gOutFile, " #%08x", pDecInsn->VRegA());
break;
- case Instruction::k31i: // op vAA, #+BBBBBBBB
- {
- // This is often, but not always, a float.
- union {
- float f;
- u4 i;
- } conv;
- conv.i = pDecInsn->VRegB();
- fprintf(gOutFile, " v%d, #float %f // #%08x",
- pDecInsn->VRegA(), conv.f, pDecInsn->VRegB());
- }
+ case Instruction::k31i: { // op vAA, #+BBBBBBBB
+ // This is often, but not always, a float.
+ union {
+ float f;
+ u4 i;
+ } conv;
+ conv.i = pDecInsn->VRegB();
+ fprintf(gOutFile, " v%d, #float %g // #%08x",
+ pDecInsn->VRegA(), conv.f, pDecInsn->VRegB());
break;
+ }
case Instruction::k31t: // op vAA, offset +BBBBBBBB
fprintf(gOutFile, " v%d, %08x // +%08x",
pDecInsn->VRegA(), insnIdx + pDecInsn->VRegB(), pDecInsn->VRegB());
@@ -770,39 +1055,37 @@
case Instruction::k32x: // op vAAAA, vBBBB
fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB());
break;
- case Instruction::k35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
+ case Instruction::k35c: { // op {vC, vD, vE, vF, vG}, thing@BBBB
// NOT SUPPORTED:
// case Instruction::k35ms: // [opt] invoke-virtual+super
// case Instruction::k35mi: // [opt] inline invoke
- {
- u4 arg[Instruction::kMaxVarArgRegs];
- pDecInsn->GetVarArgs(arg);
- fputs(" {", gOutFile);
- for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) {
- if (i == 0) {
- fprintf(gOutFile, "v%d", arg[i]);
- } else {
- fprintf(gOutFile, ", v%d", arg[i]);
- }
- } // for
- fprintf(gOutFile, "}, %s", indexBuf);
- }
+ u4 arg[Instruction::kMaxVarArgRegs];
+ pDecInsn->GetVarArgs(arg);
+ fputs(" {", gOutFile);
+ for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) {
+ if (i == 0) {
+ fprintf(gOutFile, "v%d", arg[i]);
+ } else {
+ fprintf(gOutFile, ", v%d", arg[i]);
+ }
+ } // for
+ fprintf(gOutFile, "}, %s", indexBuf);
break;
- case Instruction::k25x: // op vC, {vD, vE, vF, vG} (B: count)
- {
- u4 arg[Instruction::kMaxVarArgRegs25x];
- pDecInsn->GetAllArgs25x(arg);
- fprintf(gOutFile, " v%d, {", arg[0]);
- for (int i = 0, n = pDecInsn->VRegB(); i < n; i++) {
- if (i == 0) {
- fprintf(gOutFile, "v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]);
- } else {
- fprintf(gOutFile, ", v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]);
- }
- } // for
- fputc('}', gOutFile);
- }
+ }
+ case Instruction::k25x: { // op vC, {vD, vE, vF, vG} (B: count)
+ u4 arg[Instruction::kMaxVarArgRegs25x];
+ pDecInsn->GetAllArgs25x(arg);
+ fprintf(gOutFile, " v%d, {", arg[0]);
+ for (int i = 0, n = pDecInsn->VRegB(); i < n; i++) {
+ if (i == 0) {
+ fprintf(gOutFile, "v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]);
+ } else {
+ fprintf(gOutFile, ", v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]);
+ }
+ } // for
+ fputc('}', gOutFile);
break;
+ }
case Instruction::k3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
// NOT SUPPORTED:
// case Instruction::k3rms: // [opt] invoke-virtual+super/range
@@ -821,18 +1104,17 @@
fprintf(gOutFile, "}, %s", indexBuf);
}
break;
- case Instruction::k51l: // op vAA, #+BBBBBBBBBBBBBBBB
- {
- // This is often, but not always, a double.
- union {
- double d;
- u8 j;
- } conv;
- conv.j = pDecInsn->WideVRegB();
- fprintf(gOutFile, " v%d, #double %f // #%016" PRIx64,
- pDecInsn->VRegA(), conv.d, pDecInsn->WideVRegB());
- }
+ case Instruction::k51l: { // op vAA, #+BBBBBBBBBBBBBBBB
+ // This is often, but not always, a double.
+ union {
+ double d;
+ u8 j;
+ } conv;
+ conv.j = pDecInsn->WideVRegB();
+ fprintf(gOutFile, " v%d, #double %g // #%016" PRIx64,
+ pDecInsn->VRegA(), conv.d, pDecInsn->WideVRegB());
break;
+ }
// NOT SUPPORTED:
// case Instruction::k00x: // unknown op or breakpoint
// break;
@@ -1017,126 +1299,9 @@
}
/*
- * Dumps a string value with some escape characters.
- */
-static void dumpEscapedString(const char* p) {
- for (; *p; p++) {
- switch (*p) {
- case '\\':
- fputs("\\\\", gOutFile);
- break;
- case '\"':
- fputs("\\\"", gOutFile);
- break;
- case '\t':
- fputs("\\t", gOutFile);
- break;
- case '\n':
- fputs("\\n", gOutFile);
- break;
- case '\r':
- fputs("\\r", gOutFile);
- break;
- default:
- putc(*p, gOutFile);
- }
- }
-}
-
-/*
- * Dumps an XML attribute value between double-quotes.
- */
-static void dumpXmlAttribute(const char* p) {
- for (; *p; p++) {
- switch (*p) {
- case '&':
- fputs("&", gOutFile);
- break;
- case '<':
- fputs("<", gOutFile);
- break;
- case '"':
- fputs(""", gOutFile);
- break;
- case '\t':
- fputs("	", gOutFile);
- break;
- case '\n':
- fputs("
", gOutFile);
- break;
- case '\r':
- fputs("
", gOutFile);
- break;
- default:
- putc(*p, gOutFile);
- }
- }
-}
-
-/*
- * Dumps a value of static (class) field.
- */
-static void dumpSFieldValue(const DexFile* pDexFile,
- EncodedStaticFieldValueIterator::ValueType valueType,
- const jvalue* pValue) {
- switch (valueType) {
- case EncodedStaticFieldValueIterator::kByte:
- fprintf(gOutFile, "%" PRIu8, pValue->b);
- break;
- case EncodedStaticFieldValueIterator::kShort:
- fprintf(gOutFile, "%" PRId16, pValue->s);
- break;
- case EncodedStaticFieldValueIterator::kChar:
- fprintf(gOutFile, "%" PRIu16, pValue->c);
- break;
- case EncodedStaticFieldValueIterator::kInt:
- fprintf(gOutFile, "%" PRId32, pValue->i);
- break;
- case EncodedStaticFieldValueIterator::kLong:
- fprintf(gOutFile, "%" PRId64, pValue->j);
- break;
- case EncodedStaticFieldValueIterator::kFloat:
- fprintf(gOutFile, "%f", pValue->f);
- break;
- case EncodedStaticFieldValueIterator::kDouble:
- fprintf(gOutFile, "%f", pValue->d);
- break;
- case EncodedStaticFieldValueIterator::kString: {
- const char* str =
- pDexFile->GetStringData(pDexFile->GetStringId(pValue->i));
- if (gOptions.outputFormat == OUTPUT_PLAIN) {
- fputs("\"", gOutFile);
- dumpEscapedString(str);
- fputs("\"", gOutFile);
- } else {
- dumpXmlAttribute(str);
- }
- break;
- }
- case EncodedStaticFieldValueIterator::kNull:
- fputs("null", gOutFile);
- break;
- case EncodedStaticFieldValueIterator::kBoolean:
- fputs(pValue->z ? "true" : "false", gOutFile);
- break;
-
- case EncodedStaticFieldValueIterator::kAnnotation:
- case EncodedStaticFieldValueIterator::kArray:
- case EncodedStaticFieldValueIterator::kEnum:
- case EncodedStaticFieldValueIterator::kField:
- case EncodedStaticFieldValueIterator::kMethod:
- case EncodedStaticFieldValueIterator::kType:
- default:
- fprintf(gOutFile, "Unexpected static field type: %d", valueType);
- }
-}
-
-/*
* Dumps a static (class) field.
*/
-static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i,
- EncodedStaticFieldValueIterator::ValueType valueType,
- const jvalue* pValue) {
+static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, const u1** data) {
// Bail for anything private if export only requested.
if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) {
return;
@@ -1153,9 +1318,9 @@
fprintf(gOutFile, " name : '%s'\n", name);
fprintf(gOutFile, " type : '%s'\n", typeDescriptor);
fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr);
- if (pValue != nullptr) {
+ if (data != nullptr) {
fputs(" value : ", gOutFile);
- dumpSFieldValue(pDexFile, valueType, pValue);
+ dumpEncodedValue(pDexFile, data);
fputs("\n", gOutFile);
}
} else if (gOptions.outputFormat == OUTPUT_XML) {
@@ -1170,9 +1335,9 @@
fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0));
// The "deprecated=" is not knowable w/o parsing annotations.
fprintf(gOutFile, " visibility=%s\n", quotedVisibility(flags));
- if (pValue != nullptr) {
+ if (data != nullptr) {
fputs(" value=\"", gOutFile);
- dumpSFieldValue(pDexFile, valueType, pValue);
+ dumpEncodedValue(pDexFile, data);
fputs("\"\n", gOutFile);
}
fputs(">\n</field>\n", gOutFile);
@@ -1185,8 +1350,7 @@
* Dumps an instance field.
*/
static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) {
- dumpSField(pDexFile, idx, flags, i,
- EncodedStaticFieldValueIterator::kByte, nullptr);
+ dumpSField(pDexFile, idx, flags, i, nullptr);
}
/*
@@ -1196,7 +1360,7 @@
*/
static void dumpCfg(const DexFile* dex_file,
- uint32_t dex_method_idx,
+ u4 dex_method_idx,
const DexFile::CodeItem* code_item) {
if (code_item != nullptr) {
std::ostringstream oss;
@@ -1207,7 +1371,7 @@
static void dumpCfg(const DexFile* dex_file, int idx) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(idx);
- const uint8_t* class_data = dex_file->GetClassData(class_def);
+ const u1* class_data = dex_file->GetClassData(class_def);
if (class_data == nullptr) { // empty class such as a marker interface?
return;
}
@@ -1248,7 +1412,15 @@
return;
}
- if (gOptions.cfg) {
+ if (gOptions.showSectionHeaders) {
+ dumpClassDef(pDexFile, idx);
+ }
+
+ if (gOptions.showAnnotations) {
+ dumpClassAnnotations(pDexFile, idx);
+ }
+
+ if (gOptions.showCfg) {
dumpCfg(pDexFile, idx);
return;
}
@@ -1347,33 +1519,35 @@
}
} else {
ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
+
+ // Prepare data for static fields.
+ const u1* sData = pDexFile->GetEncodedStaticFieldValuesArray(pClassDef);
+ const u4 sSize = sData != nullptr ? DecodeUnsignedLeb128(&sData) : 0;
+
+ // Static fields.
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " Static fields -\n");
}
- EncodedStaticFieldValueIterator staticFieldValues(*pDexFile, pClassDef);
- for (int i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) {
- EncodedStaticFieldValueIterator::ValueType valueType =
- EncodedStaticFieldValueIterator::kByte;
- const jvalue* pValue = nullptr;
- if (staticFieldValues.HasNext()) {
- valueType = staticFieldValues.GetValueType();
- pValue = &staticFieldValues.GetJavaValue();
- }
- dumpSField(pDexFile, pClassData.GetMemberIndex(),
- pClassData.GetRawMemberAccessFlags(), i,
- valueType, pValue);
- if (staticFieldValues.HasNext()) {
- staticFieldValues.Next();
- }
+ for (u4 i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) {
+ dumpSField(pDexFile,
+ pClassData.GetMemberIndex(),
+ pClassData.GetRawMemberAccessFlags(),
+ i,
+ i < sSize ? &sData : nullptr);
} // for
- DCHECK(!staticFieldValues.HasNext());
+
+ // Instance fields.
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " Instance fields -\n");
}
- for (int i = 0; pClassData.HasNextInstanceField(); i++, pClassData.Next()) {
- dumpIField(pDexFile, pClassData.GetMemberIndex(),
- pClassData.GetRawMemberAccessFlags(), i);
+ for (u4 i = 0; pClassData.HasNextInstanceField(); i++, pClassData.Next()) {
+ dumpIField(pDexFile,
+ pClassData.GetMemberIndex(),
+ pClassData.GetRawMemberAccessFlags(),
+ i);
} // for
+
+ // Direct methods.
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " Direct methods -\n");
}
@@ -1383,6 +1557,8 @@
pClassData.GetMethodCodeItem(),
pClassData.GetMethodCodeItemOffset(), i);
} // for
+
+ // Virtual methods.
if (gOptions.outputFormat == OUTPUT_PLAIN) {
fprintf(gOutFile, " Virtual methods -\n");
}
@@ -1434,9 +1610,6 @@
char* package = nullptr;
const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
for (u4 i = 0; i < classDefsSize; i++) {
- if (gOptions.showSectionHeaders) {
- dumpClassDef(pDexFile, i);
- }
dumpClass(pDexFile, i, &package);
} // for
@@ -1461,17 +1634,11 @@
}
// If the file is not a .dex file, the function tries .zip/.jar/.apk files,
- // all of which are Zip archives with "classes.dex" inside. The compressed
- // data needs to be extracted to a temp file, the location of which varies.
- //
- // TODO(ajcbik): fix following issues
- //
- // (1) gOptions.tempFileName is not accounted for
- // (2) gOptions.ignoreBadChecksum is not accounted for
- //
+ // all of which are Zip archives with "classes.dex" inside.
+ const bool kVerifyChecksum = !gOptions.ignoreBadChecksum;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) {
+ if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
// Display returned error message to user. Note that this error behavior
// differs from the error messages shown by the original Dalvik dexdump.
fputs(error_msg.c_str(), stderr);
diff --git a/dexdump/dexdump.h b/dexdump/dexdump.h
index 50280a9..6939f90 100644
--- a/dexdump/dexdump.h
+++ b/dexdump/dexdump.h
@@ -42,13 +42,13 @@
bool disassemble;
bool exportsOnly;
bool ignoreBadChecksum;
+ bool showAnnotations;
+ bool showCfg;
bool showFileHeaders;
bool showSectionHeaders;
bool verbose;
- bool cfg;
OutputFormat outputFormat;
const char* outputFileName;
- const char* tempFileName;
};
/* Prototypes. */
diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc
index dd1002c..f716ba8 100644
--- a/dexdump/dexdump_main.cc
+++ b/dexdump/dexdump_main.cc
@@ -17,8 +17,8 @@
*
* This is a re-implementation of the original dexdump utility that was
* based on Dalvik functions in libdex into a new dexdump that is now
- * based on Art functions in libart instead. The output is identical to
- * the original for correct DEX files. Error messages may differ, however.
+ * based on Art functions in libart instead. The output is very similar to
+ * to the original for correct DEX files. Error messages may differ, however.
* Also, ODEX files are no longer supported.
*/
@@ -40,19 +40,18 @@
*/
static void usage(void) {
fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
- fprintf(stderr, "%s: [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]"
- " [-t tempfile] dexfile...\n", gProgName);
- fprintf(stderr, "\n");
+ fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]"
+ " dexfile...\n\n", gProgName);
+ fprintf(stderr, " -a : display annotations\n");
fprintf(stderr, " -c : verify checksum and exit\n");
fprintf(stderr, " -d : disassemble code sections\n");
fprintf(stderr, " -e : display exported items only\n");
fprintf(stderr, " -f : display summary information from file header\n");
- fprintf(stderr, " -g : dump CFG for dex\n");
+ fprintf(stderr, " -g : display CFG for dex\n");
fprintf(stderr, " -h : display file header details\n");
fprintf(stderr, " -i : ignore checksum failures\n");
fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
fprintf(stderr, " -o : output file name (defaults to stdout)\n");
- fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
}
/*
@@ -70,11 +69,14 @@
// Parse all arguments.
while (1) {
- const int ic = getopt(argc, argv, "cdefghil:t:o:");
+ const int ic = getopt(argc, argv, "acdefghil:o:");
if (ic < 0) {
break; // done
}
switch (ic) {
+ case 'a': // display annotations
+ gOptions.showAnnotations = true;
+ break;
case 'c': // verify the checksum then exit
gOptions.checksumOnly = true;
break;
@@ -84,13 +86,13 @@
case 'e': // exported items only
gOptions.exportsOnly = true;
break;
- case 'f': // dump outer file header
+ case 'f': // display outer file header
gOptions.showFileHeaders = true;
break;
- case 'g': // dump cfg
- gOptions.cfg = true;
+ case 'g': // display cfg
+ gOptions.showCfg = true;
break;
- case 'h': // dump section headers, i.e. all meta-data
+ case 'h': // display section headers, i.e. all meta-data
gOptions.showSectionHeaders = true;
break;
case 'i': // continue even if checksum is bad
@@ -106,9 +108,6 @@
wantUsage = true;
}
break;
- case 't': // temp file, used when opening compressed Jar
- gOptions.tempFileName = optarg;
- break;
case 'o': // output file
gOptions.outputFileName = optarg;
break;
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index d20c169..6f19df5 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -180,9 +180,10 @@
static int processFile(const char* fileName) {
// If the file is not a .dex file, the function tries .zip/.jar/.apk files,
// all of which are Zip archives with "classes.dex" inside.
+ static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) {
+ if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) {
fputs(error_msg.c_str(), stderr);
fputc('\n', stderr);
return -1;
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 286faf2..1a3e3f5 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -1497,6 +1497,25 @@
}
break;
}
+ case 0x7B: case 0x7F: {
+ FpRegister d(instr, 12, 22);
+ FpRegister m(instr, 0, 5);
+ uint32_t sz = (instr >> 18) & 0x3; // Decode size bits.
+ uint32_t size = (sz == 0) ? 8 : sz << 4;
+ uint32_t opc2 = (instr >> 7) & 0xF;
+ uint32_t Q = (instr >> 6) & 1;
+ if (Q == 0 && opc2 == 0xA && size == 8) { // 1010, VCNT
+ opcode << "vcnt." << size;
+ args << d << ", " << m;
+ } else if (Q == 0 && (opc2 == 0x4 || opc2 == 0x5) && size <= 32) { // 010x, VPADDL
+ bool op = HasBitSet(instr, 7);
+ opcode << "vpaddl." << (op ? "u" : "s") << size;
+ args << d << ", " << m;
+ } else {
+ opcode << "UNKNOWN " << op2;
+ }
+ break;
+ }
default: // more formats
if ((op2 >> 4) == 2) { // 010xxxx
// data processing (register)
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index a95ea64..1f51311 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -384,6 +384,8 @@
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 13, "trunc.w", "fad" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 14, "ceil.w", "fad" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 15, "floor.w", "fad" },
+ { kFpMask | (0x201 << 16), kCop1 | (0x200 << 16) | 17, "movf", "fadc" },
+ { kFpMask | (0x201 << 16), kCop1 | (0x201 << 16) | 17, "movt", "fadc" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 26, "rint", "fad" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 27, "class", "fad" },
{ kFpMask | (0x21f << 16), kCop1 | (0x200 << 16) | 32, "cvt.s", "fad" },
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7239a47..bb35f8d 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -35,7 +35,7 @@
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "dex_file-inl.h"
-#include "dex_instruction.h"
+#include "dex_instruction-inl.h"
#include "disassembler.h"
#include "elf_builder.h"
#include "gc/space/image_space.h"
@@ -578,6 +578,7 @@
// Print embedded dex file data range.
const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin();
const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
+ std::set<uint32_t> string_ids;
uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - oat_file_begin);
os << StringPrintf("dex-file: 0x%08x..0x%08x\n",
dex_offset,
@@ -623,7 +624,7 @@
<< " (" << oat_class.GetType() << ")\n";
// TODO: include bitmap here if type is kOatClassSomeCompiled?
if (options_.list_classes_) continue;
- if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis)) {
+ if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis, string_ids)) {
success = false;
}
if (stop_analysis) {
@@ -631,7 +632,7 @@
return success;
}
}
-
+ os << "Number of unique strings loaded from dex code: " << string_ids.size() << "\n";
os << std::flush;
return success;
}
@@ -725,7 +726,8 @@
bool DumpOatClass(VariableIndentationOutputStream* vios,
const OatFile::OatClass& oat_class, const DexFile& dex_file,
- const DexFile::ClassDef& class_def, bool* stop_analysis) {
+ const DexFile::ClassDef& class_def, bool* stop_analysis,
+ std::set<uint32_t>& string_ids) {
bool success = true;
bool addr_found = false;
const uint8_t* class_data = dex_file.GetClassData(class_def);
@@ -739,7 +741,7 @@
while (it.HasNextDirectMethod()) {
if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file,
it.GetMemberIndex(), it.GetMethodCodeItem(),
- it.GetRawMemberAccessFlags(), &addr_found)) {
+ it.GetRawMemberAccessFlags(), &addr_found, string_ids)) {
success = false;
}
if (addr_found) {
@@ -752,7 +754,7 @@
while (it.HasNextVirtualMethod()) {
if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file,
it.GetMemberIndex(), it.GetMethodCodeItem(),
- it.GetRawMemberAccessFlags(), &addr_found)) {
+ it.GetRawMemberAccessFlags(), &addr_found, string_ids)) {
success = false;
}
if (addr_found) {
@@ -777,9 +779,35 @@
uint32_t class_method_index,
const OatFile::OatClass& oat_class, const DexFile& dex_file,
uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
- uint32_t method_access_flags, bool* addr_found) {
+ uint32_t method_access_flags, bool* addr_found,
+ std::set<uint32_t>& string_ids) {
bool success = true;
+ if (code_item != nullptr) {
+ const uint16_t* code_ptr = code_item->insns_;
+ const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
+
+ while (code_ptr < code_end) {
+ const Instruction* inst = Instruction::At(code_ptr);
+ switch (inst->Opcode()) {
+ case Instruction::CONST_STRING: {
+ uint32_t string_index = inst->VRegB_21c();
+ string_ids.insert(string_index);
+ break;
+ }
+ case Instruction::CONST_STRING_JUMBO: {
+ uint32_t string_index = inst->VRegB_31c();
+ string_ids.insert(string_index);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ code_ptr += inst->SizeInCodeUnits();
+ }
+ }
// TODO: Support regex
std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
if (method_name.find(options_.method_filter_) == std::string::npos) {
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 462c397..cd0aa6f 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -61,17 +61,21 @@
ASSERT_TRUE(file_info.Equals(info));
}
- // Runs test with given arguments.
- int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) {
+ std::string GetProfmanCmd() {
std::string file_path = GetTestAndroidRoot();
file_path += "/bin/profman";
if (kIsDebugBuild) {
file_path += "d";
}
-
- EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ EXPECT_TRUE(OS::FileExists(file_path.c_str()))
+ << file_path << " should be a valid file path";
+ return file_path;
+ }
+ // Runs test with given arguments.
+ int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) {
+ std::string profman_cmd = GetProfmanCmd();
std::vector<std::string> argv_str;
- argv_str.push_back(file_path);
+ argv_str.push_back(profman_cmd);
for (size_t k = 0; k < profiles_fd.size(); k++) {
argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k]));
}
@@ -80,6 +84,15 @@
std::string error;
return ExecAndReturnCode(argv_str, &error);
}
+
+ bool GenerateTestProfile(const std::string& filename) {
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--generate-test-profile=" + filename);
+ std::string error;
+ return ExecAndReturnCode(argv_str, &error);
+ }
};
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
@@ -282,4 +295,15 @@
CheckProfileInfo(profile1, info1);
}
+TEST_F(ProfileAssistantTest, TestProfileGeneration) {
+ ScratchFile profile;
+ // Generate a test profile.
+ GenerateTestProfile(profile.GetFilename());
+
+ // Verify that the generated profile is valid and can be loaded.
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ProfileCompilationInfo info;
+ ASSERT_TRUE(info.Load(GetFd(profile)));
+}
+
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index 754e431..a5fefa7 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -100,6 +100,14 @@
UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
UsageError(" accepts a file descriptor. Cannot be used together with");
UsageError(" --reference-profile-file.");
+ UsageError(" --generate-test-profile=<filename>: generates a random profile file for testing.");
+ UsageError(" --generate-test-profile-num-dex=<number>: number of dex files that should be");
+ UsageError(" included in the generated profile. Defaults to 20.");
+ UsageError(" --generate-test-profile-method-ratio=<number>: the percentage from the maximum");
+ UsageError(" number of methods that should be generated. Defaults to 5.");
+ UsageError(" --generate-test-profile-class-ratio=<number>: the percentage from the maximum");
+ UsageError(" number of classes that should be generated. Defaults to 5.");
+ UsageError("");
UsageError("");
UsageError(" --dex-location=<string>: location string to use with corresponding");
UsageError(" apk-fd to find dex files");
@@ -111,12 +119,20 @@
exit(EXIT_FAILURE);
}
+// Note: make sure you update the Usage if you change these values.
+static constexpr uint16_t kDefaultTestProfileNumDex = 20;
+static constexpr uint16_t kDefaultTestProfileMethodRatio = 5;
+static constexpr uint16_t kDefaultTestProfileClassRatio = 5;
+
class ProfMan FINAL {
public:
ProfMan() :
reference_profile_file_fd_(kInvalidFd),
dump_only_(false),
dump_output_to_fd_(kInvalidFd),
+ test_profile_num_dex_(kDefaultTestProfileNumDex),
+ test_profile_method_ratio_(kDefaultTestProfileMethodRatio),
+ test_profile_class_ratio_(kDefaultTestProfileClassRatio),
start_ns_(NanoTime()) {}
~ProfMan() {
@@ -159,6 +175,23 @@
dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString());
} else if (option.starts_with("--apk-fd=")) {
ParseFdForCollection(option, "--apk-fd", &apks_fd_);
+ } else if (option.starts_with("--generate-test-profile=")) {
+ test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString();
+ } else if (option.starts_with("--generate-test-profile-num-dex=")) {
+ ParseUintOption(option,
+ "--generate-test-profile-num-dex",
+ &test_profile_num_dex_,
+ Usage);
+ } else if (option.starts_with("--generate-test-profile-method-ratio")) {
+ ParseUintOption(option,
+ "--generate-test-profile-method-ratio",
+ &test_profile_method_ratio_,
+ Usage);
+ } else if (option.starts_with("--generate-test-profile-class-ratio")) {
+ ParseUintOption(option,
+ "--generate-test-profile-class-ratio",
+ &test_profile_class_ratio_,
+ Usage);
} else {
Usage("Unknown argument '%s'", option.data());
}
@@ -168,6 +201,15 @@
bool has_reference_profile = !reference_profile_file_.empty() ||
FdIsValid(reference_profile_file_fd_);
+ if (!test_profile_.empty()) {
+ if (test_profile_method_ratio_ > 100) {
+ Usage("Invalid ratio for --generate-test-profile-method-ratio");
+ }
+ if (test_profile_class_ratio_ > 100) {
+ Usage("Invalid ratio for --generate-test-profile-class-ratio");
+ }
+ return;
+ }
// --dump-only may be specified with only --reference-profiles present.
if (!dump_only_ && !has_profiles) {
Usage("No profile files specified.");
@@ -234,6 +276,7 @@
MemMap::Init(); // for ZipArchive::OpenFromFd
std::vector<const DexFile*> dex_files;
assert(dex_locations_.size() == apks_fd_.size());
+ static constexpr bool kVerifyChecksum = true;
for (size_t i = 0; i < dex_locations_.size(); ++i) {
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files_for_location;
@@ -246,6 +289,7 @@
}
if (DexFile::OpenFromZip(*zip_archive,
dex_locations_[i],
+ kVerifyChecksum,
&error_msg,
&dex_files_for_location)) {
} else {
@@ -315,6 +359,25 @@
return dump_only_;
}
+ int GenerateTestProfile() {
+ int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY);
+ if (profile_test_fd < 0) {
+ std::cerr << "Cannot open " << test_profile_ << strerror(errno);
+ return -1;
+ }
+
+ bool result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd,
+ test_profile_num_dex_,
+ test_profile_method_ratio_,
+ test_profile_class_ratio_);
+ close(profile_test_fd); // ignore close result.
+ return result ? 0 : -1;
+ }
+
+ bool ShouldGenerateTestProfile() {
+ return !test_profile_.empty();
+ }
+
private:
static void ParseFdForCollection(const StringPiece& option,
const char* arg_name,
@@ -348,6 +411,10 @@
int reference_profile_file_fd_;
bool dump_only_;
int dump_output_to_fd_;
+ std::string test_profile_;
+ uint16_t test_profile_num_dex_;
+ uint16_t test_profile_method_ratio_;
+ uint16_t test_profile_class_ratio_;
uint64_t start_ns_;
};
@@ -358,6 +425,9 @@
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
profman.ParseArgs(argc, argv);
+ if (profman.ShouldGenerateTestProfile()) {
+ return profman.GenerateTestProfile();
+ }
if (profman.ShouldOnlyDumpProfile()) {
return profman.DumpProfileInfo();
}
diff --git a/runtime/Android.mk b/runtime/Android.mk
index aa12c83..1c442fc 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -169,7 +169,6 @@
os_linux.cc \
parsed_options.cc \
primitive.cc \
- profiler.cc \
quick_exception_handler.cc \
quick/inline_method_analyser.cc \
reference_table.cc \
@@ -369,7 +368,6 @@
oat.h \
object_callbacks.h \
process_state.h \
- profiler_options.h \
quick/inline_method_analyser.h \
runtime.h \
stack.h \
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index 44c7649..38ca76a 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -30,18 +30,17 @@
.arch armv7-a
.thumb
-// Macro to generate the value of Runtime::Current into rDest clobbering rTemp. As it uses labels
+// Macro to generate the value of Runtime::Current into rDest. As it uses labels
// then the labels need to be unique. We bind these to the function name in the ENTRY macros.
-.macro RUNTIME_CURRENT name, num, rDest, rTemp
+.macro RUNTIME_CURRENT name, num, rDest
.if .Lruntime_current\num\()_used
.error
.endif
.set .Lruntime_current\num\()_used, 1
- ldr \rDest, .Lgot_\name\()_\num @ Load offset of the GOT.
- ldr \rTemp, .Lruntime_instance_\name\()_\num @ Load GOT offset of Runtime::instance_.
+ ldr \rDest, .Lruntime_instance_\name\()_\num @ Load GOT_PREL offset of Runtime::instance_.
.Lload_got_\name\()_\num\():
- add \rDest, pc @ Fixup GOT address.
- ldr \rDest, [\rDest, \rTemp] @ Load address of Runtime::instance_.
+ add \rDest, pc @ Fixup GOT_PREL address.
+ ldr \rDest, [\rDest] @ Load address of Runtime::instance_.
ldr \rDest, [\rDest] @ Load Runtime::instance_.
.endm
@@ -90,26 +89,20 @@
DEF_ENTRY .arm, \name
.endm
-// Terminate an ENTRY and generate GOT references.
+// Terminate an ENTRY and generate GOT_PREL references.
.macro END name
// Generate offsets of GOT and Runtime::instance_ used in RUNTIME_CURRENT.
.if .Lruntime_current1_used
- .Lgot_\name\()_1:
- .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_1+4)
.Lruntime_instance_\name\()_1:
- .word _ZN3art7Runtime9instance_E(GOT)
+ .word _ZN3art7Runtime9instance_E(GOT_PREL)-(.Lload_got_\name\()_1+4)
.endif
.if .Lruntime_current2_used
- .Lgot_\name\()_2:
- .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_2+4)
.Lruntime_instance_\name\()_2:
- .word _ZN3art7Runtime9instance_E(GOT)
+ .word _ZN3art7Runtime9instance_E(GOT_PREL)-(.Lload_got_\name\()_2+4)
.endif
.if .Lruntime_current3_used
- .Lgot_\name\()_3:
- .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_3+4)
.Lruntime_instance_\name\()_3:
- .word _ZN3art7Runtime9instance_E(GOT)
+ .word _ZN3art7Runtime9instance_E(GOT_PREL)-(.Lload_got_\name\()_3+4)
.endif
// Remove the RUNTIME_CURRENTx macros so they get rebound in the next function entry.
.purgem RUNTIME_CURRENT1
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index d81e0a9..d105c67 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -34,7 +34,7 @@
namespace art {
-extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_null_pointer_exception_from_signal();
extern "C" void art_quick_throw_stack_overflow();
extern "C" void art_quick_implicit_suspend();
@@ -107,8 +107,10 @@
*out_return_pc = (sc->arm_pc + instr_size) | 1;
}
-bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
+ if (!IsValidImplicitCheck(info)) {
+ return false;
+ }
// The code that looks for the catch location needs to know the value of the
// ARM PC at the point of call. For Null checks we insert a GC map that is immediately after
// the load/store instruction that might cause the fault. However the mapping table has
@@ -122,7 +124,10 @@
uint32_t instr_size = GetInstructionSize(ptr);
sc->arm_lr = (sc->arm_pc + instr_size) | 1; // LR needs to point to gc map location
- sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception);
+ sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
+ // Pass the faulting address as the first argument of
+ // art_quick_throw_null_pointer_exception_from_signal.
+ sc->arm_r0 = reinterpret_cast<uintptr_t>(info->si_addr);
VLOG(signals) << "Generating null pointer exception";
return true;
}
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index ffac030..c3a5829 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -206,6 +206,7 @@
struct sigaction sa, osa;
sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
sa.sa_sigaction = bad_divide_inst_handle;
+ sigemptyset(&sa.sa_mask);
sigaction(SIGILL, &sa, &osa);
bool has_div = false;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 5209bb6..0797def 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -44,15 +44,15 @@
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kSaveAll)
*/
-.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp1, rTemp2
+.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp
SPILL_ALL_CALLEE_SAVE_GPRS @ 9 words (36 bytes) of callee saves.
vpush {s16-s31} @ 16 words (64 bytes) of floats.
.cfi_adjust_cfa_offset 64
sub sp, #12 @ 3 words of space, bottom word will hold Method*
.cfi_adjust_cfa_offset 12
- RUNTIME_CURRENT1 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
- ldr \rTemp1, [\rTemp1, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kSaveAll Method*.
- str \rTemp1, [sp, #0] @ Place Method* at bottom of stack.
+ RUNTIME_CURRENT1 \rTemp @ Load Runtime::Current into rTemp.
+ ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp is kSaveAll Method*.
+ str \rTemp, [sp, #0] @ Place Method* at bottom of stack.
str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame.
// Ugly compile-time check, but we only have the preprocessor.
@@ -65,7 +65,7 @@
* Macro that sets up the callee save frame to conform with
* Runtime::CreateCalleeSaveMethod(kRefsOnly).
*/
-.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp1, rTemp2
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp
push {r5-r8, r10-r11, lr} @ 7 words of callee saves
.cfi_adjust_cfa_offset 28
.cfi_rel_offset r5, 0
@@ -77,9 +77,9 @@
.cfi_rel_offset lr, 24
sub sp, #4 @ bottom word will hold Method*
.cfi_adjust_cfa_offset 4
- RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
- ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*.
- str \rTemp1, [sp, #0] @ Place Method* at bottom of stack.
+ RUNTIME_CURRENT2 \rTemp @ Load Runtime::Current into rTemp.
+ ldr \rTemp, [\rTemp, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp is kRefsOnly Method*.
+ str \rTemp, [sp, #0] @ Place Method* at bottom of stack.
str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame.
// Ugly compile-time check, but we only have the preprocessor.
@@ -88,30 +88,6 @@
#endif
.endm
- /*
- * Macro that sets up the callee save frame to conform with
- * Runtime::CreateCalleeSaveMethod(kRefsOnly)
- * and preserves the value of rTemp2 at entry.
- */
-.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_RTEMP2 rTemp1, rTemp2
- push {r5-r8, r10-r11, lr} @ 7 words of callee saves
- .cfi_adjust_cfa_offset 28
- .cfi_rel_offset r5, 0
- .cfi_rel_offset r6, 4
- .cfi_rel_offset r7, 8
- .cfi_rel_offset r8, 12
- .cfi_rel_offset r10, 16
- .cfi_rel_offset r11, 20
- .cfi_rel_offset lr, 24
- sub sp, #4 @ bottom word will hold Method*
- .cfi_adjust_cfa_offset 4
- str \rTemp2, [sp, #0] @ save rTemp2
- RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
- ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*.
- ldr \rTemp2, [sp, #0] @ restore rTemp2
- str \rTemp1, [sp, #0] @ Place Method* at bottom of stack.
- str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame.
-
// Ugly compile-time check, but we only have the preprocessor.
#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 28 + 4)
#error "REFS_ONLY_CALLEE_SAVE_FRAME(ARM) size not as expected."
@@ -164,12 +140,12 @@
#endif
.endm
-.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp1, rTemp2
+.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY
- RUNTIME_CURRENT3 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
- @ rTemp1 is kRefsAndArgs Method*.
- ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET]
- str \rTemp1, [sp, #0] @ Place Method* at bottom of stack.
+ RUNTIME_CURRENT3 \rTemp @ Load Runtime::Current into rTemp.
+ @ rTemp is kRefsAndArgs Method*.
+ ldr \rTemp, [\rTemp, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET]
+ str \rTemp, [sp, #0] @ Place Method* at bottom of stack.
str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame.
.endm
@@ -217,7 +193,7 @@
.macro DELIVER_PENDING_EXCEPTION
.fnend
.fnstart
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 @ save callee saves for throw
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0 @ save callee saves for throw
mov r0, r9 @ pass Thread::Current
b artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*)
.endm
@@ -225,7 +201,7 @@
.macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
.extern \cxx_name
ENTRY \c_name
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 // save all registers as basis for long jump context
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0 @ save all registers as basis for long jump context
mov r0, r9 @ pass Thread::Current
b \cxx_name @ \cxx_name(Thread*)
END \c_name
@@ -234,7 +210,7 @@
.macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name
.extern \cxx_name
ENTRY \c_name
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r1, r2 // save all registers as basis for long jump context
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r1 @ save all registers as basis for long jump context
mov r1, r9 @ pass Thread::Current
b \cxx_name @ \cxx_name(Thread*)
END \c_name
@@ -243,7 +219,7 @@
.macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
.extern \cxx_name
ENTRY \c_name
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2 @ save all registers as basis for long jump context
mov r2, r9 @ pass Thread::Current
b \cxx_name @ \cxx_name(Thread*)
END \c_name
@@ -275,7 +251,7 @@
.macro ONE_ARG_REF_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case of GC
ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
mov r2, r9 @ pass Thread::Current
bl \entrypoint @ (uint32_t field_idx, const Method* referrer, Thread*)
@@ -287,7 +263,7 @@
.macro TWO_ARG_REF_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC
ldr r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
mov r3, r9 @ pass Thread::Current
bl \entrypoint @ (field_idx, Object*, referrer, Thread*)
@@ -299,7 +275,7 @@
.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3 @ save callee saves in case of GC
ldr r3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
.cfi_adjust_cfa_offset 16
@@ -323,6 +299,11 @@
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
/*
+ * Call installed by a signal handler to create and deliver a NullPointerException.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception_from_signal, artThrowNullPointerExceptionFromSignal
+
+ /*
* Called by managed code to create and deliver an ArithmeticException.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
@@ -334,6 +315,12 @@
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode
@@ -360,7 +347,7 @@
*/
.macro INVOKE_TRAMPOLINE_BODY cxx_name
.extern \cxx_name
- SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case allocation triggers GC
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2 @ save callee saves in case allocation triggers GC
mov r2, r9 @ pass Thread::Current
mov r3, sp
bl \cxx_name @ (method_idx, this, Thread*, SP)
@@ -566,7 +553,7 @@
.Llock_strex_fail:
b .Lretry_lock @ retry
.Lslow_lock:
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case we block
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case we block
mov r1, r9 @ pass Thread::Current
bl artLockObjectFromCode @ (Object* obj, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -575,7 +562,7 @@
END art_quick_lock_object
ENTRY art_quick_lock_object_no_inline
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case we block
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case we block
mov r1, r9 @ pass Thread::Current
bl artLockObjectFromCode @ (Object* obj, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -632,7 +619,7 @@
b .Lretry_unlock @ retry
.Lslow_unlock:
@ save callee saves in case exception allocation triggers GC
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1
mov r1, r9 @ pass Thread::Current
bl artUnlockObjectFromCode @ (Object* obj, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -642,7 +629,7 @@
ENTRY art_quick_unlock_object_no_inline
@ save callee saves in case exception allocation triggers GC
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1
mov r1, r9 @ pass Thread::Current
bl artUnlockObjectFromCode @ (Object* obj, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -677,7 +664,7 @@
.cfi_restore r0
.cfi_restore r1
.cfi_restore lr
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2 @ save all registers as basis for long jump context
mov r2, r9 @ pass Thread::Current
b artThrowClassCastException @ (Class*, Class*, Thread*)
bkpt
@@ -813,7 +800,7 @@
.Lthrow_array_store_exception:
pop {r0-r2, lr}
/* No need to repeat restore cfi directives, the ones above apply here. */
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3, ip
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3
mov r1, r2
mov r2, r9 @ pass Thread::Current
b artThrowArrayStoreException @ (Class*, Class*, Thread*)
@@ -824,7 +811,7 @@
.macro ONE_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case of GC
mov r1, r9 @ pass Thread::Current
bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -836,7 +823,7 @@
.macro TWO_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC
mov r2, r9 @ pass Thread::Current
bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -848,7 +835,7 @@
.macro THREE_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3 @ save callee saves in case of GC
mov r3, r9 @ pass Thread::Current
@ (uint32_t type_idx, Method* method, int32_t component_count, Thread*)
bl \entrypoint
@@ -861,7 +848,7 @@
.macro FOUR_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_RTEMP2 r12, r3 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12 @ save callee saves in case of GC
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
.cfi_adjust_cfa_offset 16
bl \entrypoint
@@ -890,7 +877,7 @@
*/
.extern artGet64StaticFromCode
ENTRY art_quick_get64_static
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC
ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
mov r2, r9 @ pass Thread::Current
bl artGet64StaticFromCode @ (uint32_t field_idx, const Method* referrer, Thread*)
@@ -916,7 +903,7 @@
*/
.extern artGet64InstanceFromCode
ENTRY art_quick_get64_instance
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC
ldr r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
mov r3, r9 @ pass Thread::Current
bl artGet64InstanceFromCode @ (field_idx, Object*, referrer, Thread*)
@@ -941,7 +928,7 @@
*/
.extern artSet64StaticFromCode
ENTRY art_quick_set64_static
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r12 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case of GC
@ r2:r3 contain the wide argument
ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
@@ -966,7 +953,7 @@
*/
.extern artSet64InstanceFromCode
ENTRY art_quick_set64_instance
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12 @ save callee saves in case of GC
@ r2:r3 contain the wide argument
ldr r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer
str r9, [sp, #-12]! @ expand the frame and pass Thread::Current
@@ -1087,7 +1074,7 @@
bx lr
.Lart_quick_alloc_object_rosalloc_slow_path:
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC
mov r2, r9 @ pass Thread::Current
bl artAllocObjectFromCodeRosAlloc @ (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -1172,7 +1159,7 @@
ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path
.Lart_quick_alloc_object_tlab_slow_path:
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC.
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 // Save callee saves in case of GC.
mov r2, r9 // Pass Thread::Current.
bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -1207,7 +1194,7 @@
pop {r0, r1, r3, lr}
b .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
.Lart_quick_alloc_object_region_tlab_slow_path:
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC.
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 // Save callee saves in case of GC.
mov r2, r9 // Pass Thread::Current.
bl artAllocObjectFromCodeRegionTLAB // (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -1227,7 +1214,7 @@
1:
#endif
mov r0, rSELF
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves for GC stack crawl
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves for GC stack crawl
@ TODO: save FPRs to enable access in the debugger?
bl artTestSuspendFromCode @ (Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
@@ -1235,7 +1222,7 @@
ENTRY art_quick_implicit_suspend
mov r0, rSELF
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves for stack crawl
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves for stack crawl
bl artTestSuspendFromCode @ (Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
END art_quick_implicit_suspend
@@ -1298,7 +1285,7 @@
.extern artQuickResolutionTrampoline
ENTRY art_quick_resolution_trampoline
- SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2
mov r2, r9 @ pass Thread::Current
mov r3, sp @ pass SP
blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP)
@@ -1403,7 +1390,7 @@
.extern artQuickToInterpreterBridge
ENTRY art_quick_to_interpreter_bridge
- SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r1, r2
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r1
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP)
@@ -1426,7 +1413,7 @@
.extern artInstrumentationMethodExitFromCode
ENTRY art_quick_instrumentation_entry
@ Make stack crawlable and clobber r2 and r3 (post saving)
- SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2
@ preserve r0 (not normally an arg) knowing there is a spare slot in kRefsAndArgs.
str r0, [sp, #4]
mov r2, r9 @ pass Thread::Current
@@ -1441,7 +1428,7 @@
.global art_quick_instrumentation_exit
art_quick_instrumentation_exit:
mov lr, #0 @ link register is to here, so clobber with 0 for later checks
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ set up frame knowing r2 and r3 must be dead on exit
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ set up frame knowing r2 and r3 must be dead on exit
mov r12, sp @ remember bottom of caller's frame
push {r0-r1} @ save return value
.cfi_adjust_cfa_offset 8
@@ -1480,7 +1467,7 @@
*/
.extern artDeoptimize
ENTRY art_quick_deoptimize
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0
mov r0, r9 @ Set up args.
blx artDeoptimize @ artDeoptimize(Thread*)
END art_quick_deoptimize
@@ -1491,7 +1478,7 @@
*/
.extern artDeoptimizeFromCompiledCode
ENTRY art_quick_deoptimize_from_compiled_code
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0
mov r0, r9 @ Set up args.
blx artDeoptimizeFromCompiledCode @ artDeoptimizeFromCompiledCode(Thread*)
END art_quick_deoptimize_from_compiled_code
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index 3e9ad0d..f591fcc 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -29,7 +29,7 @@
#include "thread-inl.h"
extern "C" void art_quick_throw_stack_overflow();
-extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_null_pointer_exception_from_signal();
extern "C" void art_quick_implicit_suspend();
//
@@ -84,8 +84,10 @@
*out_return_pc = sc->pc + 4;
}
-bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
+ if (!IsValidImplicitCheck(info)) {
+ return false;
+ }
// The code that looks for the catch location needs to know the value of the
// PC at the point of call. For Null checks we insert a GC map that is immediately after
// the load/store instruction that might cause the fault.
@@ -95,7 +97,10 @@
sc->regs[30] = sc->pc + 4; // LR needs to point to gc map location
- sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception);
+ sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
+ // Pass the faulting address as the first argument of
+ // art_quick_throw_null_pointer_exception_from_signal.
+ sc->regs[0] = reinterpret_cast<uintptr_t>(info->si_addr);
VLOG(signals) << "Generating null pointer exception";
return true;
}
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 1fba09b..10ee63f 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -406,6 +406,11 @@
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
/*
+ * Call installed by a signal handler to create and deliver a NullPointerException.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception_from_signal, artThrowNullPointerExceptionFromSignal
+
+ /*
* Called by managed code to create and deliver an ArithmeticException.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
@@ -417,6 +422,12 @@
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 45e33a8..833ba1b 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -268,6 +268,8 @@
static_assert(!IsDirectEntrypoint(kQuickThrowNullPointer), "Non-direct C stub marked direct.");
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
static_assert(!IsDirectEntrypoint(kQuickThrowStackOverflow), "Non-direct C stub marked direct.");
+ qpoints->pThrowStringBounds = art_quick_throw_string_bounds;
+ static_assert(!IsDirectEntrypoint(kQuickThrowStringBounds), "Non-direct C stub marked direct.");
// Deoptimization from compiled code.
qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index 8ea78eb..754284c 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -27,7 +27,7 @@
#include "thread-inl.h"
extern "C" void art_quick_throw_stack_overflow();
-extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_null_pointer_exception_from_signal();
//
// Mips specific fault handler functions.
@@ -71,8 +71,10 @@
*out_return_pc = sc->sc_pc + 4;
}
-bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
+ if (!IsValidImplicitCheck(info)) {
+ return false;
+ }
// The code that looks for the catch location needs to know the value of the
// PC at the point of call. For Null checks we insert a GC map that is immediately after
// the load/store instruction that might cause the fault.
@@ -81,8 +83,11 @@
struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
sc->sc_regs[31] = sc->sc_pc + 4; // RA needs to point to gc map location
- sc->sc_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception);
+ sc->sc_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
sc->sc_regs[25] = sc->sc_pc; // make sure T9 points to the function
+ // Pass the faulting address as the first argument of
+ // art_quick_throw_null_pointer_exception_from_signal.
+ sc->sc_regs[0] = reinterpret_cast<uintptr_t>(info->si_addr);
VLOG(signals) << "Generating null pointer exception";
return true;
}
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3ee26af..c1b8044 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -506,6 +506,18 @@
move $a0, rSELF # pass Thread::Current
END art_quick_throw_null_pointer_exception
+
+ /*
+ * Call installed by a signal handler to create and deliver a NullPointerException.
+ */
+ .extern artThrowNullPointerExceptionFromSignal
+ENTRY art_quick_throw_null_pointer_exception_from_signal
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ la $t9, artThrowNullPointerExceptionFromSignal
+ jalr $zero, $t9 # artThrowNullPointerExceptionFromSignal(uintptr_t, Thread*)
+ move $a1, rSELF # pass Thread::Current
+END art_quick_throw_null_pointer_exception_from_signal
+
/*
* Called by managed code to create and deliver an ArithmeticException
*/
@@ -529,6 +541,18 @@
END art_quick_throw_array_bounds
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt().
+ */
+ .extern artThrowStringBoundsFromCode
+ENTRY art_quick_throw_string_bounds
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ la $t9, artThrowStringBoundsFromCode
+ jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*)
+ move $a2, rSELF # pass Thread::Current
+END art_quick_throw_string_bounds
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
.extern artThrowStackOverflowFromCode
diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc
index 4abfcf1..c9a32ad 100644
--- a/runtime/arch/mips64/fault_handler_mips64.cc
+++ b/runtime/arch/mips64/fault_handler_mips64.cc
@@ -27,7 +27,7 @@
#include "thread-inl.h"
extern "C" void art_quick_throw_stack_overflow();
-extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_null_pointer_exception_from_signal();
//
// Mips64 specific fault handler functions.
@@ -71,8 +71,11 @@
*out_return_pc = sc->sc_pc + 4;
}
-bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
- void* context) {
+bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
+ if (!IsValidImplicitCheck(info)) {
+ return false;
+ }
+
// The code that looks for the catch location needs to know the value of the
// PC at the point of call. For Null checks we insert a GC map that is immediately after
// the load/store instruction that might cause the fault.
@@ -81,8 +84,11 @@
struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
sc->sc_regs[31] = sc->sc_pc + 4; // RA needs to point to gc map location
- sc->sc_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception);
+ sc->sc_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
sc->sc_regs[25] = sc->sc_pc; // make sure T9 points to the function
+ // Pass the faulting address as the first argument of
+ // art_quick_throw_null_pointer_exception_from_signal.
+ sc->sc_regs[0] = reinterpret_cast<uintptr_t>(info->si_addr);
VLOG(signals) << "Generating null pointer exception";
return true;
}
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 8f1a35a..ae69620 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -593,6 +593,17 @@
END art_quick_throw_null_pointer_exception
/*
+ * Call installed by a signal handler to create and deliver a NullPointerException
+ */
+ .extern artThrowNullPointerExceptionFromSignal
+ENTRY art_quick_throw_null_pointer_exception_from_signal
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ dla $t9, artThrowNullPointerExceptionFromSignal
+ jalr $zero, $t9 # artThrowNullPointerExceptionFromSignal(uinptr_t, Thread*)
+ move $a1, rSELF # pass Thread::Current
+END art_quick_throw_null_pointer_exception
+
+ /*
* Called by managed code to create and deliver an ArithmeticException
*/
.extern artThrowDivZeroFromCode
@@ -617,6 +628,19 @@
END art_quick_throw_array_bounds
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt().
+ */
+ .extern artThrowStringBoundsFromCode
+ENTRY art_quick_throw_string_bounds
+.Lart_quick_throw_string_bounds_gp_set:
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ dla $t9, artThrowStringBoundsFromCode
+ jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*)
+ move $a2, rSELF # pass Thread::Current
+END art_quick_throw_string_bounds
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
.extern artThrowStackOverflowFromCode
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index d7c4cb1..24e3a0d 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -36,6 +36,7 @@
#define CTX_EIP uc_mcontext->__ss.__rip
#define CTX_EAX uc_mcontext->__ss.__rax
#define CTX_METHOD uc_mcontext->__ss.__rdi
+#define CTX_RDI uc_mcontext->__ss.__rdi
#define CTX_JMP_BUF uc_mcontext->__ss.__rdi
#else
// 32 bit mac build.
@@ -71,12 +72,12 @@
#if defined(__APPLE__) && defined(__x86_64__)
// mac symbols have a prefix of _ on x86_64
-extern "C" void _art_quick_throw_null_pointer_exception();
+extern "C" void _art_quick_throw_null_pointer_exception_from_signal();
extern "C" void _art_quick_throw_stack_overflow();
extern "C" void _art_quick_test_suspend();
#define EXT_SYM(sym) _ ## sym
#else
-extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_null_pointer_exception_from_signal();
extern "C" void art_quick_throw_stack_overflow();
extern "C" void art_quick_test_suspend();
#define EXT_SYM(sym) sym
@@ -292,7 +293,10 @@
*out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
}
-bool NullPointerHandler::Action(int, siginfo_t*, void* context) {
+bool NullPointerHandler::Action(int, siginfo_t* sig, void* context) {
+ if (!IsValidImplicitCheck(sig)) {
+ return false;
+ }
struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
@@ -314,7 +318,15 @@
*next_sp = retaddr;
uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
- uc->CTX_EIP = reinterpret_cast<uintptr_t>(EXT_SYM(art_quick_throw_null_pointer_exception));
+ uc->CTX_EIP = reinterpret_cast<uintptr_t>(
+ EXT_SYM(art_quick_throw_null_pointer_exception_from_signal));
+ // Pass the faulting address as the first argument of
+ // art_quick_throw_null_pointer_exception_from_signal.
+#if defined(__x86_64__)
+ uc->CTX_RDI = reinterpret_cast<uintptr_t>(sig->si_addr);
+#else
+ uc->CTX_EAX = reinterpret_cast<uintptr_t>(sig->si_addr);
+#endif
VLOG(signals) << "Generating null pointer exception";
return true;
}
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 2d7f664..5851fbd 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -82,7 +82,7 @@
PUSH edi // Save callee saves (ebx is saved/restored by the upcall)
PUSH esi
PUSH ebp
- pushl REG_VAR(got_reg) // Save got_reg
+ PUSH RAW_VAR(got_reg) // Save got_reg
subl MACRO_LITERAL(8), %esp // Grow stack by 2 words.
CFI_ADJUST_CFA_OFFSET(8)
@@ -97,6 +97,7 @@
movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
// Restore got_reg.
movl 12(%esp), REG_VAR(got_reg)
+ CFI_RESTORE(RAW_VAR(got_reg))
// Ugly compile-time check, but we only have the preprocessor.
// Last +4: implicit return address pushed on stack when caller made call.
@@ -283,6 +284,11 @@
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
/*
+ * Call installed by a signal handler to create and deliver a NullPointerException.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception_from_signal, artThrowNullPointerExceptionFromSignal
+
+ /*
* Called by managed code to create and deliver an ArithmeticException.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
@@ -310,6 +316,12 @@
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
* the method_idx. This wrapper will save arg1-arg3 and call the appropriate C helper.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 8064ed6..e777e6c 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -309,6 +309,11 @@
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode
/*
+ * Call installed by a signal handler to create and deliver a NullPointerException.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception_from_signal, artThrowNullPointerExceptionFromSignal
+
+ /*
* Called by managed code to create and deliver an ArithmeticException.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
@@ -336,6 +341,12 @@
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain
* the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper.
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 98d3345..a102858 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -33,9 +33,10 @@
namespace art {
+template<ReadBarrierOption kReadBarrierOption>
inline mirror::Class* ArtField::GetDeclaringClass() {
GcRootSource gc_root_source(this);
- mirror::Class* result = declaring_class_.Read(&gc_root_source);
+ mirror::Class* result = declaring_class_.Read<kReadBarrierOption>(&gc_root_source);
DCHECK(result != nullptr);
DCHECK(result->IsLoaded() || result->IsErroneous()) << result->GetStatus();
return result;
diff --git a/runtime/art_field.h b/runtime/art_field.h
index b64b70f..aaccbf3 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -41,6 +41,7 @@
public:
ArtField();
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
mirror::Class* GetDeclaringClass() SHARED_REQUIRES(Locks::mutator_lock_);
void SetDeclaringClass(mirror::Class *new_declaring_class)
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 7647ad6..26450c4 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -395,8 +395,9 @@
return GetDeclaringClass()->GetDexCache();
}
+template<ReadBarrierOption kReadBarrierOption>
inline bool ArtMethod::IsProxyMethod() {
- return GetDeclaringClass()->IsProxyClass();
+ return GetDeclaringClass<kReadBarrierOption>()->IsProxyClass();
}
inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(size_t pointer_size) {
@@ -438,24 +439,24 @@
return type;
}
-template<typename RootVisitorType>
+template<ReadBarrierOption kReadBarrierOption, typename RootVisitorType>
void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
- ArtMethod* interface_method = nullptr;
- mirror::Class* klass = declaring_class_.Read();
- if (LIKELY(klass != nullptr)) {
+ if (LIKELY(!declaring_class_.IsNull())) {
+ visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
+ mirror::Class* klass = declaring_class_.Read<kReadBarrierOption>();
if (UNLIKELY(klass->IsProxyClass())) {
// For normal methods, dex cache shortcuts will be visited through the declaring class.
// However, for proxies we need to keep the interface method alive, so we visit its roots.
- interface_method = mirror::DexCache::GetElementPtrSize(
+ ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize(
GetDexCacheResolvedMethods(pointer_size),
GetDexMethodIndex(),
pointer_size);
DCHECK(interface_method != nullptr);
DCHECK_EQ(interface_method,
- Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this));
+ Runtime::Current()->GetClassLinker()->FindMethodForProxy<kReadBarrierOption>(
+ klass, this));
interface_method->VisitRoots(visitor, pointer_size);
}
- visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
// We know we don't have profiling information if the class hasn't been verified. Note
// that this check also ensures the IsNative call can be made, as IsNative expects a fully
// created class (and not a retired one).
@@ -463,7 +464,7 @@
// Runtime methods and native methods use the same field as the profiling info for
// storing their own data (jni entrypoint for native methods, and ImtConflictTable for
// some runtime methods).
- if (!IsNative() && !IsRuntimeMethod()) {
+ if (!IsNative<kReadBarrierOption>() && !IsRuntimeMethod()) {
ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
if (profiling_info != nullptr) {
profiling_info->VisitRoots(visitor);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index a012a5a..2b025f8 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -340,6 +340,7 @@
return (GetAccessFlags() & kAccSynthetic) != 0;
}
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_);
bool SkipAccessChecks() {
@@ -564,7 +565,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
- template<typename RootVisitorType>
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename RootVisitorType>
void VisitRoots(RootVisitorType& visitor, size_t pointer_size) NO_THREAD_SAFETY_ANALYSIS;
const DexFile* GetDexFile() SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 8eb3742..2d702f6 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -127,32 +127,32 @@
ADD_TEST_EQ(THREAD_SELF_OFFSET,
art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
-// Offset of field Thread::tlsPtr_.thread_local_objects.
-#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
- art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + 2 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_end.
#define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET,
art::Thread::ThreadLocalEndOffset<__SIZEOF_POINTER__>().Int32Value())
+// Offset of field Thread::tlsPtr_.thread_local_objects.
+#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__)
+ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
+ art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_current_ibase.
-#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__)
+#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
art::Thread::MterpCurrentIBaseOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_default_ibase.
-#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__)
+#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_CURRENT_IBASE_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET,
art::Thread::MterpDefaultIBaseOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_alt_ibase.
-#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__)
+#define THREAD_ALT_IBASE_OFFSET (THREAD_DEFAULT_IBASE_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET,
art::Thread::MterpAltIBaseOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.rosalloc_runs.
-#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__)
+#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_ALT_IBASE_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET,
art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top.
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index bd8de87..1c32024 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -73,6 +73,11 @@
level == kThreadListLock ||
// Ignore logging which may or may not have set up thread data structures.
level == kLoggingLock ||
+ // When transitioning from suspended to runnable, a daemon thread might be in
+ // a situation where the runtime is shutting down. To not crash our debug locking
+ // mechanism we just pass null Thread* to the MutexLock during that transition
+ // (see Thread::TransitionFromSuspendedToRunnable).
+ level == kThreadSuspendCountLock ||
// Avoid recursive death.
level == kAbortLock) << level;
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index f3e260b..f2575f7 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -225,6 +225,34 @@
return klass;
}
+template<ReadBarrierOption kReadBarrierOption>
+ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) {
+ DCHECK(proxy_class->IsProxyClass());
+ DCHECK(proxy_method->IsProxyMethod<kReadBarrierOption>());
+ {
+ Thread* const self = Thread::Current();
+ ReaderMutexLock mu(self, dex_lock_);
+ // Locate the dex cache of the original interface/Object
+ for (const DexCacheData& data : dex_caches_) {
+ if (!self->IsJWeakCleared(data.weak_root) &&
+ proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types,
+ image_pointer_size_)) {
+ mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(
+ self->DecodeJObject(data.weak_root));
+ if (dex_cache != nullptr) {
+ ArtMethod* resolved_method = dex_cache->GetResolvedMethod(
+ proxy_method->GetDexMethodIndex(), image_pointer_size_);
+ CHECK(resolved_method != nullptr);
+ return resolved_method;
+ }
+ }
+ }
+ }
+ LOG(FATAL) << "Didn't find dex cache for " << PrettyClass(proxy_class) << " "
+ << PrettyMethod(proxy_method);
+ UNREACHABLE();
+}
+
} // namespace art
#endif // ART_RUNTIME_CLASS_LINKER_INL_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index db0e9ac..fe7448f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4280,33 +4280,6 @@
return DotToDescriptor(name->ToModifiedUtf8().c_str());
}
-ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) {
- DCHECK(proxy_class->IsProxyClass());
- DCHECK(proxy_method->IsProxyMethod());
- {
- Thread* const self = Thread::Current();
- ReaderMutexLock mu(self, dex_lock_);
- // Locate the dex cache of the original interface/Object
- for (const DexCacheData& data : dex_caches_) {
- if (!self->IsJWeakCleared(data.weak_root) &&
- proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types,
- image_pointer_size_)) {
- mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(
- self->DecodeJObject(data.weak_root));
- if (dex_cache != nullptr) {
- ArtMethod* resolved_method = dex_cache->GetResolvedMethod(
- proxy_method->GetDexMethodIndex(), image_pointer_size_);
- CHECK(resolved_method != nullptr);
- return resolved_method;
- }
- }
- }
- }
- LOG(FATAL) << "Didn't find dex cache for " << PrettyClass(proxy_class) << " "
- << PrettyMethod(proxy_method);
- UNREACHABLE();
-}
-
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cd1ca7f..ca5af19 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -483,6 +483,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
std::string GetDescriptorForProxy(mirror::Class* proxy_class)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ArtMethod* FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method)
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f58af5a..741b682 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -284,7 +284,8 @@
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::string error_msg;
MemMap::Init();
- if (!DexFile::Open(location, location, &error_msg, &dex_files)) {
+ static constexpr bool kVerifyChecksum = true;
+ if (!DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files)) {
LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
UNREACHABLE();
} else {
@@ -418,26 +419,6 @@
(*icu_cleanup_fn)();
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
-
- // Manually closing the JNI libraries.
- // Runtime does not support repeatedly doing JNI->CreateVM, thus we need to manually clean up the
- // dynamic linking loader so that gtests would not fail.
- // Bug: 25785594
- if (runtime_->IsStarted()) {
- {
- // We retrieve the handle by calling dlopen on the library. To close it, we need to call
- // dlclose twice, the first time to undo our dlopen and the second time to actually unload it.
- // See man dlopen.
- void* handle = dlopen("libjavacore.so", RTLD_LAZY);
- dlclose(handle);
- CHECK_EQ(0, dlclose(handle));
- }
- {
- void* handle = dlopen("libopenjdkd.so", RTLD_LAZY);
- dlclose(handle);
- CHECK_EQ(0, dlclose(handle));
- }
- }
}
static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
@@ -482,7 +463,7 @@
#define ART_TARGET_NATIVETEST_DIR_STRING ""
#endif
-std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) {
+std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) const {
CHECK(name != nullptr);
std::string filename;
if (IsHost()) {
@@ -500,9 +481,11 @@
std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTestImpl::OpenTestDexFiles(
const char* name) {
std::string filename = GetTestDexFileName(name);
+ static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> dex_files;
- bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files);
+ bool success = DexFile::Open(
+ filename.c_str(), filename.c_str(), kVerifyChecksum, &error_msg, &dex_files);
CHECK(success) << "Failed to open '" << filename << "': " << error_msg;
for (auto& dex_file : dex_files) {
CHECK_EQ(PROT_READ, dex_file->GetPermissions());
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 0ce40e8..b68eb19 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -111,7 +111,7 @@
std::string GetTestAndroidRoot();
- std::string GetTestDexFileName(const char* name);
+ std::string GetTestDexFileName(const char* name) const;
std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 75cce42..912a74a 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -402,12 +402,117 @@
dex_file, type);
}
-void ThrowNullPointerExceptionFromDexPC() {
+static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instruction& instr)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (!CanDoImplicitNullCheckOn(addr)) {
+ return false;
+ }
+
+ switch (instr.Opcode()) {
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ // Without inlining, we could just check that the offset is the class offset.
+ // However, when inlining, the compiler can (validly) merge the null check with a field access
+ // on the same object. Note that the stack map at the NPE will reflect the invoke's location,
+ // which is the caller.
+ return true;
+ }
+
+ case Instruction::IGET:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT:
+ case Instruction::IPUT:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT: {
+ ArtField* field =
+ Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false);
+ return (addr == 0) ||
+ (addr == field->GetOffset().Uint32Value()) ||
+ (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
+ }
+
+ case Instruction::IGET_QUICK:
+ case Instruction::IGET_BOOLEAN_QUICK:
+ case Instruction::IGET_BYTE_QUICK:
+ case Instruction::IGET_CHAR_QUICK:
+ case Instruction::IGET_SHORT_QUICK:
+ case Instruction::IGET_WIDE_QUICK:
+ case Instruction::IGET_OBJECT_QUICK:
+ case Instruction::IPUT_QUICK:
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ case Instruction::IPUT_BYTE_QUICK:
+ case Instruction::IPUT_CHAR_QUICK:
+ case Instruction::IPUT_SHORT_QUICK:
+ case Instruction::IPUT_WIDE_QUICK:
+ case Instruction::IPUT_OBJECT_QUICK: {
+ return (addr == 0u) ||
+ (addr == instr.VRegC_22c()) ||
+ (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
+ }
+
+ case Instruction::AGET:
+ case Instruction::AGET_WIDE:
+ case Instruction::AGET_OBJECT:
+ case Instruction::AGET_BOOLEAN:
+ case Instruction::AGET_BYTE:
+ case Instruction::AGET_CHAR:
+ case Instruction::AGET_SHORT:
+ case Instruction::APUT:
+ case Instruction::APUT_WIDE:
+ case Instruction::APUT_OBJECT:
+ case Instruction::APUT_BOOLEAN:
+ case Instruction::APUT_BYTE:
+ case Instruction::APUT_CHAR:
+ case Instruction::APUT_SHORT:
+ case Instruction::FILL_ARRAY_DATA:
+ case Instruction::ARRAY_LENGTH: {
+ // The length access should crash. We currently do not do implicit checks on
+ // the array access itself.
+ return (addr == 0u) ||
+ (addr == mirror::Array::LengthOffset().Uint32Value()) ||
+ (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
+ }
+
+ default: {
+ // We have covered all the cases where an NPE could occur.
+ // Note that this must be kept in sync with the compiler, and adding
+ // any new way to do implicit checks in the compiler should also update
+ // this code.
+ return false;
+ }
+ }
+}
+
+void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) {
uint32_t throw_dex_pc;
ArtMethod* method = Thread::Current()->GetCurrentMethod(&throw_dex_pc);
const DexFile::CodeItem* code = method->GetCodeItem();
CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_);
const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]);
+ if (check_address && !IsValidImplicitCheck(addr, method, *instr)) {
+ const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ LOG(FATAL) << "Invalid address for an implicit NullPointerException check: "
+ << "0x" << std::hex << addr << std::dec
+ << ", at "
+ << instr->DumpString(dex_file)
+ << " in "
+ << PrettyMethod(method);
+ }
+
switch (instr->Opcode()) {
case Instruction::INVOKE_DIRECT:
ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kDirect);
@@ -530,14 +635,32 @@
ThrowException("Ljava/lang/NullPointerException;", nullptr,
"Attempt to get length of null array");
break;
+ case Instruction::FILL_ARRAY_DATA: {
+ ThrowException("Ljava/lang/NullPointerException;", nullptr,
+ "Attempt to write to null array");
+ break;
+ }
+ case Instruction::INVOKE_LAMBDA:
+ case Instruction::BOX_LAMBDA:
+ case Instruction::UNBOX_LAMBDA:
+ case Instruction::LIBERATE_VARIABLE: {
+ ThrowException("Ljava/lang/NullPointerException;", nullptr,
+ "Using a null lambda");
+ break;
+ }
+ case Instruction::MONITOR_ENTER:
+ case Instruction::MONITOR_EXIT: {
+ ThrowException("Ljava/lang/NullPointerException;", nullptr,
+ "Attempt to do a synchronize operation on a null object");
+ break;
+ }
default: {
- // TODO: We should have covered all the cases where we expect a NPE above, this
- // message/logging is so we can improve any cases we've missed in the future.
const DexFile* dex_file =
method->GetDeclaringClass()->GetDexCache()->GetDexFile();
- ThrowException("Ljava/lang/NullPointerException;", nullptr,
- StringPrintf("Null pointer exception during instruction '%s'",
- instr->DumpString(dex_file).c_str()).c_str());
+ LOG(FATAL) << "NullPointerException at an unexpected instruction: "
+ << instr->DumpString(dex_file)
+ << " in "
+ << PrettyMethod(method);
break;
}
}
@@ -654,6 +777,13 @@
}
}
+// StringIndexOutOfBoundsException
+
+void ThrowStringIndexOutOfBoundsException(int index, int length) {
+ ThrowException("Ljava/lang/StringIndexOutOfBoundsException;", nullptr,
+ StringPrintf("length=%d; index=%d", length, index).c_str());
+}
+
// VerifyError
void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index c3a1f09..cbd338d 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -195,7 +195,7 @@
InvokeType type)
SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR;
-void ThrowNullPointerExceptionFromDexPC()
+void ThrowNullPointerExceptionFromDexPC(bool check_address = false, uintptr_t addr = 0)
SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerException(const char* msg)
@@ -211,6 +211,11 @@
void ThrowStackOverflowError(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR;
+// StringIndexOutOfBoundsException
+
+void ThrowStringIndexOutOfBoundsException(int index, int length)
+ SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR;
+
// VerifyError
void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...)
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
new file mode 100644
index 0000000..743fbb9
--- /dev/null
+++ b/runtime/dex2oat_environment_test.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014 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_DEX2OAT_ENVIRONMENT_TEST_H_
+#define ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "oat_file_assistant.h"
+#include "os.h"
+#include "runtime.h"
+#include "utils.h"
+
+namespace art {
+
+// Test class that provides some helpers to set a test up for compilation using dex2oat.
+class Dex2oatEnvironmentTest : public CommonRuntimeTest {
+ public:
+ virtual void SetUp() OVERRIDE {
+ CommonRuntimeTest::SetUp();
+
+ // Create a scratch directory to work from.
+ scratch_dir_ = android_data_ + "/Dex2oatEnvironmentTest";
+ ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
+
+ // Create a subdirectory in scratch for odex files.
+ odex_oat_dir_ = scratch_dir_ + "/oat";
+ ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
+
+ odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
+ ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
+
+ // Verify the environment is as we expect
+ uint32_t checksum;
+ std::string error_msg;
+ ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
+ << "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
+ ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
+ << "Expected dex file to be at: " << GetDexSrc1();
+ ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
+ << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
+ ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+ << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
+ ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
+ << "Expected dex file to be at: " << GetDexSrc2();
+
+ // GetMultiDexSrc2 should have the same primary dex checksum as
+ // GetMultiDexSrc1, but a different secondary dex checksum.
+ static constexpr bool kVerifyChecksum = true;
+ std::vector<std::unique_ptr<const DexFile>> multi1;
+ ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
+ GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg;
+ ASSERT_GT(multi1.size(), 1u);
+
+ std::vector<std::unique_ptr<const DexFile>> multi2;
+ ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
+ GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg;
+ ASSERT_GT(multi2.size(), 1u);
+
+ ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
+ ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+ }
+
+ virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+ // options->push_back(std::make_pair("-verbose:oat", nullptr));
+
+ // Set up the image location.
+ options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
+ nullptr));
+ // Make sure compilercallbacks are not set so that relocation will be
+ // enabled.
+ callbacks_.reset();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ ClearDirectory(odex_dir_.c_str());
+ ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
+
+ ClearDirectory(odex_oat_dir_.c_str());
+ ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
+
+ ClearDirectory(scratch_dir_.c_str());
+ ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
+
+ CommonRuntimeTest::TearDown();
+ }
+
+ static void Copy(const std::string& src, const std::string& dst) {
+ std::ifstream src_stream(src, std::ios::binary);
+ std::ofstream dst_stream(dst, std::ios::binary);
+
+ dst_stream << src_stream.rdbuf();
+ }
+
+ // Returns the directory where the pre-compiled core.art can be found.
+ // TODO: We should factor out this into common tests somewhere rather than
+ // re-hardcoding it here (This was copied originally from the elf writer
+ // test).
+ std::string GetImageDirectory() const {
+ if (IsHost()) {
+ const char* host_dir = getenv("ANDROID_HOST_OUT");
+ CHECK(host_dir != nullptr);
+ return std::string(host_dir) + "/framework";
+ } else {
+ return std::string("/data/art-test");
+ }
+ }
+
+ std::string GetImageLocation() const {
+ return GetImageDirectory() + "/core.art";
+ }
+
+ std::string GetSystemImageFile() const {
+ return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
+ + "/core.art";
+ }
+
+ bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+ std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
+ return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+ }
+
+ std::string GetDexSrc1() const {
+ return GetTestDexFileName("Main");
+ }
+
+ // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
+ // file stripped.
+ std::string GetStrippedDexSrc1() const {
+ return GetTestDexFileName("MainStripped");
+ }
+
+ std::string GetMultiDexSrc1() const {
+ return GetTestDexFileName("MultiDex");
+ }
+
+ // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
+ // with the contents of the secondary dex file changed.
+ std::string GetMultiDexSrc2() const {
+ return GetTestDexFileName("MultiDexModifiedSecondary");
+ }
+
+ std::string GetDexSrc2() const {
+ return GetTestDexFileName("Nested");
+ }
+
+ // Scratch directory, for dex and odex files (oat files will go in the
+ // dalvik cache).
+ const std::string& GetScratchDir() const {
+ return scratch_dir_;
+ }
+
+ // Odex directory is the subdirectory in the scratch directory where odex
+ // files should be located.
+ const std::string& GetOdexDir() const {
+ return odex_dir_;
+ }
+
+ private:
+ std::string scratch_dir_;
+ std::string odex_oat_dir_;
+ std::string odex_dir_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index 4e6c3ca..108a5af 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -38,10 +38,88 @@
return reinterpret_cast<const char*>(ptr);
}
+inline const char* DexFile::GetStringData(const StringId& string_id) const {
+ uint32_t ignored;
+ return GetStringDataAndUtf16Length(string_id, &ignored);
+}
+
+inline const char* DexFile::StringDataAndUtf16LengthByIdx(uint32_t idx,
+ uint32_t* utf16_length) const {
+ if (idx == kDexNoIndex) {
+ *utf16_length = 0;
+ return nullptr;
+ }
+ const StringId& string_id = GetStringId(idx);
+ return GetStringDataAndUtf16Length(string_id, utf16_length);
+}
+
+inline const char* DexFile::StringDataByIdx(uint32_t idx) const {
+ uint32_t unicode_length;
+ return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
+}
+
+inline const char* DexFile::StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const {
+ const TypeId& type_id = GetTypeId(idx);
+ return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
+}
+
+inline const char* DexFile::StringByTypeIdx(uint32_t idx) const {
+ const TypeId& type_id = GetTypeId(idx);
+ return StringDataByIdx(type_id.descriptor_idx_);
+}
+
+inline const char* DexFile::GetTypeDescriptor(const TypeId& type_id) const {
+ return StringDataByIdx(type_id.descriptor_idx_);
+}
+
+inline const char* DexFile::GetFieldTypeDescriptor(const FieldId& field_id) const {
+ const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_);
+ return GetTypeDescriptor(type_id);
+}
+
+inline const char* DexFile::GetFieldName(const FieldId& field_id) const {
+ return StringDataByIdx(field_id.name_idx_);
+}
+
+inline const char* DexFile::GetMethodDeclaringClassDescriptor(const MethodId& method_id) const {
+ const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_);
+ return GetTypeDescriptor(type_id);
+}
+
inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const {
return Signature(this, GetProtoId(method_id.proto_idx_));
}
+inline const char* DexFile::GetMethodName(const MethodId& method_id) const {
+ return StringDataByIdx(method_id.name_idx_);
+}
+
+inline const char* DexFile::GetMethodShorty(uint32_t idx) const {
+ return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_);
+}
+
+inline const char* DexFile::GetMethodShorty(const MethodId& method_id) const {
+ return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_);
+}
+
+inline const char* DexFile::GetMethodShorty(const MethodId& method_id, uint32_t* length) const {
+ // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
+ return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
+}
+
+inline const char* DexFile::GetClassDescriptor(const ClassDef& class_def) const {
+ return StringByTypeIdx(class_def.class_idx_);
+}
+
+inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) const {
+ return StringByTypeIdx(proto_id.return_type_idx_);
+}
+
+inline const char* DexFile::GetShorty(uint32_t proto_idx) const {
+ const ProtoId& proto_id = GetProtoId(proto_idx);
+ return StringDataByIdx(proto_id.shorty_idx_);
+}
+
inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) {
const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_];
return reinterpret_cast<const TryItem*>
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 05c95e0..5a203af 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -109,7 +109,7 @@
}
if (IsDexMagic(magic)) {
std::unique_ptr<const DexFile> dex_file(
- DexFile::OpenFile(fd.release(), filename, false, error_msg));
+ DexFile::OpenFile(fd.release(), filename, false, false, error_msg));
if (dex_file.get() == nullptr) {
return false;
}
@@ -120,7 +120,10 @@
return false;
}
-bool DexFile::Open(const char* filename, const char* location, std::string* error_msg,
+bool DexFile::Open(const char* filename,
+ const char* location,
+ bool verify_checksum,
+ std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
ScopedTrace trace(std::string("Open dex file ") + location);
DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
@@ -131,10 +134,13 @@
return false;
}
if (IsZipMagic(magic)) {
- return DexFile::OpenZip(fd.release(), location, error_msg, dex_files);
+ return DexFile::OpenZip(fd.release(), location, verify_checksum, error_msg, dex_files);
}
if (IsDexMagic(magic)) {
- std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true,
+ std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(),
+ location,
+ /* verify */ true,
+ verify_checksum,
error_msg));
if (dex_file.get() != nullptr) {
dex_files->push_back(std::move(dex_file));
@@ -207,6 +213,7 @@
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
+ bool verify_checksum,
std::string* error_msg) {
ScopedTrace trace(std::string("Open dex file from RAM ") + location);
std::unique_ptr<const DexFile> dex_file = OpenMemory(base,
@@ -220,6 +227,7 @@
dex_file->Begin(),
dex_file->Size(),
location.c_str(),
+ verify_checksum,
error_msg)) {
return nullptr;
}
@@ -227,7 +235,10 @@
return dex_file;
}
-std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
+std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
+ const char* location,
+ bool verify,
+ bool verify_checksum,
std::string* error_msg) {
ScopedTrace trace(std::string("Open dex file ") + location);
CHECK(location != nullptr);
@@ -276,7 +287,9 @@
}
if (verify && !DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
- location, error_msg)) {
+ location,
+ verify_checksum,
+ error_msg)) {
return nullptr;
}
@@ -285,7 +298,10 @@
const char* DexFile::kClassesDex = "classes.dex";
-bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg,
+bool DexFile::OpenZip(int fd,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
ScopedTrace trace("Dex file open Zip " + std::string(location));
DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr";
@@ -294,7 +310,7 @@
DCHECK(!error_msg->empty());
return false;
}
- return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files);
+ return DexFile::OpenFromZip(*zip_archive, location, verify_checksum, error_msg, dex_files);
}
std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location,
@@ -310,8 +326,11 @@
error_msg);
}
-std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, const char* entry_name,
- const std::string& location, std::string* error_msg,
+std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive,
+ const char* entry_name,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
ZipOpenErrorCode* error_code) {
ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
CHECK(!location.empty());
@@ -342,7 +361,9 @@
}
CHECK(dex_file->IsReadOnly()) << location;
if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
- location.c_str(), error_msg)) {
+ location.c_str(),
+ verify_checksum,
+ error_msg)) {
*error_code = ZipOpenErrorCode::kVerifyError;
return nullptr;
}
@@ -356,14 +377,16 @@
// seems an excessive number.
static constexpr size_t kWarnOnManyDexFilesThreshold = 100;
-bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
+bool DexFile::OpenFromZip(const ZipArchive& zip_archive,
+ const std::string& location,
+ bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
ScopedTrace trace("Dex file open from Zip " + std::string(location));
DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr";
ZipOpenErrorCode error_code;
- std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg,
- &error_code));
+ std::unique_ptr<const DexFile> dex_file(
+ Open(zip_archive, kClassesDex, location, verify_checksum, error_msg, &error_code));
if (dex_file.get() == nullptr) {
return false;
} else {
@@ -378,8 +401,8 @@
for (size_t i = 1; ; ++i) {
std::string name = GetMultiDexClassesDexName(i);
std::string fake_location = GetMultiDexLocation(i, location.c_str());
- std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location,
- error_msg, &error_code));
+ std::unique_ptr<const DexFile> next_dex_file(
+ Open(zip_archive, name.c_str(), fake_location, verify_checksum, error_msg, &error_code));
if (next_dex_file.get() == nullptr) {
if (error_code != ZipOpenErrorCode::kEntryNotFound) {
LOG(WARNING) << error_msg;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 638821b..3dffe4b 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -416,7 +416,10 @@
static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
// Opens .dex files found in the container, guessing the container format based on file extension.
- static bool Open(const char* filename, const char* location, std::string* error_msg,
+ static bool Open(const char* filename,
+ const char* location,
+ bool verify_checksum,
+ std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files);
// Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry.
@@ -429,10 +432,13 @@
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
+ bool verify_checksum,
std::string* error_msg);
// Open all classesXXX.dex files from a zip archive.
- static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
+ static bool OpenFromZip(const ZipArchive& zip_archive,
+ const std::string& location,
+ bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files);
@@ -522,25 +528,12 @@
// as the string length of the string data.
const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const;
- const char* GetStringData(const StringId& string_id) const {
- uint32_t ignored;
- return GetStringDataAndUtf16Length(string_id, &ignored);
- }
+ const char* GetStringData(const StringId& string_id) const;
// Index version of GetStringDataAndUtf16Length.
- const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const {
- if (idx == kDexNoIndex) {
- *utf16_length = 0;
- return nullptr;
- }
- const StringId& string_id = GetStringId(idx);
- return GetStringDataAndUtf16Length(string_id, utf16_length);
- }
+ const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const;
- const char* StringDataByIdx(uint32_t idx) const {
- uint32_t unicode_length;
- return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
- }
+ const char* StringDataByIdx(uint32_t idx) const;
// Looks up a string id for a given modified utf8 string.
const StringId* FindStringId(const char* string) const;
@@ -571,20 +564,12 @@
}
// Get the descriptor string associated with a given type index.
- const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const {
- const TypeId& type_id = GetTypeId(idx);
- return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
- }
+ const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const;
- const char* StringByTypeIdx(uint32_t idx) const {
- const TypeId& type_id = GetTypeId(idx);
- return StringDataByIdx(type_id.descriptor_idx_);
- }
+ const char* StringByTypeIdx(uint32_t idx) const;
// Returns the type descriptor string of a type id.
- const char* GetTypeDescriptor(const TypeId& type_id) const {
- return StringDataByIdx(type_id.descriptor_idx_);
- }
+ const char* GetTypeDescriptor(const TypeId& type_id) const;
// Looks up a type for the given string index
const TypeId* FindTypeId(uint32_t string_idx) const;
@@ -619,15 +604,10 @@
}
// Returns the class descriptor string of a field id.
- const char* GetFieldTypeDescriptor(const FieldId& field_id) const {
- const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_);
- return GetTypeDescriptor(type_id);
- }
+ const char* GetFieldTypeDescriptor(const FieldId& field_id) const;
// Returns the name of a field id.
- const char* GetFieldName(const FieldId& field_id) const {
- return StringDataByIdx(field_id.name_idx_);
- }
+ const char* GetFieldName(const FieldId& field_id) const;
// Returns the number of method identifiers in the .dex file.
size_t NumMethodIds() const {
@@ -653,10 +633,7 @@
const DexFile::ProtoId& signature) const;
// Returns the declaring class descriptor string of a method id.
- const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const {
- const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_);
- return GetTypeDescriptor(type_id);
- }
+ const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const;
// Returns the prototype of a method id.
const ProtoId& GetMethodPrototype(const MethodId& method_id) const {
@@ -667,23 +644,15 @@
const Signature GetMethodSignature(const MethodId& method_id) const;
// Returns the name of a method id.
- const char* GetMethodName(const MethodId& method_id) const {
- return StringDataByIdx(method_id.name_idx_);
- }
+ const char* GetMethodName(const MethodId& method_id) const;
// Returns the shorty of a method by its index.
- const char* GetMethodShorty(uint32_t idx) const {
- return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_);
- }
+ const char* GetMethodShorty(uint32_t idx) const;
// Returns the shorty of a method id.
- const char* GetMethodShorty(const MethodId& method_id) const {
- return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_);
- }
- const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const {
- // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
- return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
- }
+ const char* GetMethodShorty(const MethodId& method_id) const;
+ const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const;
+
// Returns the number of class definitions in the .dex file.
uint32_t NumClassDefs() const {
DCHECK(header_ != nullptr) << GetLocation();
@@ -703,9 +672,7 @@
}
// Returns the class descriptor string of a class definition.
- const char* GetClassDescriptor(const ClassDef& class_def) const {
- return StringByTypeIdx(class_def.class_idx_);
- }
+ const char* GetClassDescriptor(const ClassDef& class_def) const;
// Looks up a class definition by its class descriptor. Hash must be
// ComputeModifiedUtf8Hash(descriptor).
@@ -743,9 +710,7 @@
}
}
- const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const {
- return StringByTypeIdx(proto_id.return_type_idx_);
- }
+ const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const;
// Returns the number of prototype identifiers in the .dex file.
size_t NumProtoIds() const {
@@ -782,10 +747,7 @@
const Signature CreateSignature(const StringPiece& signature) const;
// Returns the short form method descriptor for the given prototype.
- const char* GetShorty(uint32_t proto_idx) const {
- const ProtoId& proto_id = GetProtoId(proto_idx);
- return StringDataByIdx(proto_id.shorty_idx_);
- }
+ const char* GetShorty(uint32_t proto_idx) const;
const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
if (proto_id.parameters_off_ == 0) {
@@ -1177,11 +1139,17 @@
private:
// Opens a .dex file
- static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location,
- bool verify, std::string* error_msg);
+ static std::unique_ptr<const DexFile> OpenFile(int fd,
+ const char* location,
+ bool verify,
+ bool verify_checksum,
+ std::string* error_msg);
// Opens dex files from within a .jar, .zip, or .apk file
- static bool OpenZip(int fd, const std::string& location, std::string* error_msg,
+ static bool OpenZip(int fd,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files);
enum class ZipOpenErrorCode { // private
@@ -1195,8 +1163,11 @@
// Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null
// return.
- static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, const char* entry_name,
- const std::string& location, std::string* error_msg,
+ static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive,
+ const char* entry_name,
+ const std::string& location,
+ bool verify_checksum,
+ std::string* error_msg,
ZipOpenErrorCode* error_code);
// Opens a .dex file at the given address backed by a MemMap
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 796701d..4f8e6f1 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -154,9 +154,10 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
+ static constexpr bool kVerifyChecksum = true;
std::string error_msg;
std::vector<std::unique_ptr<const DexFile>> tmp;
- bool success = DexFile::Open(location, location, &error_msg, &tmp);
+ bool success = DexFile::Open(location, location, kVerifyChecksum, &error_msg, &tmp);
CHECK(success) << error_msg;
EXPECT_EQ(1U, tmp.size());
std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 1d24349..5132efc 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -128,9 +128,14 @@
error_stmt; \
}
-bool DexFileVerifier::Verify(const DexFile* dex_file, const uint8_t* begin, size_t size,
- const char* location, std::string* error_msg) {
- std::unique_ptr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location));
+bool DexFileVerifier::Verify(const DexFile* dex_file,
+ const uint8_t* begin,
+ size_t size,
+ const char* location,
+ bool verify_checksum,
+ std::string* error_msg) {
+ std::unique_ptr<DexFileVerifier> verifier(
+ new DexFileVerifier(dex_file, begin, size, location, verify_checksum));
if (!verifier->Verify()) {
*error_msg = verifier->FailureReason();
return false;
@@ -273,8 +278,13 @@
const uint8_t* non_sum_ptr = reinterpret_cast<const uint8_t*>(header_) + non_sum;
adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
if (adler_checksum != header_->checksum_) {
- ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
- return false;
+ if (verify_checksum_) {
+ ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
+ return false;
+ } else {
+ LOG(WARNING) << StringPrintf(
+ "Ignoring bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
+ }
}
// Check the contents of the header.
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 90409db..133e432 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -26,17 +26,31 @@
class DexFileVerifier {
public:
- static bool Verify(const DexFile* dex_file, const uint8_t* begin, size_t size,
- const char* location, std::string* error_msg);
+ static bool Verify(const DexFile* dex_file,
+ const uint8_t* begin,
+ size_t size,
+ const char* location,
+ bool verify_checksum,
+ std::string* error_msg);
const std::string& FailureReason() const {
return failure_reason_;
}
private:
- DexFileVerifier(const DexFile* dex_file, const uint8_t* begin, size_t size, const char* location)
- : dex_file_(dex_file), begin_(begin), size_(size), location_(location),
- header_(&dex_file->GetHeader()), ptr_(nullptr), previous_item_(nullptr) {
+ DexFileVerifier(const DexFile* dex_file,
+ const uint8_t* begin,
+ size_t size,
+ const char* location,
+ bool verify_checksum)
+ : dex_file_(dex_file),
+ begin_(begin),
+ size_(size),
+ location_(location),
+ verify_checksum_(verify_checksum),
+ header_(&dex_file->GetHeader()),
+ ptr_(nullptr),
+ previous_item_(nullptr) {
}
bool Verify();
@@ -176,6 +190,7 @@
const uint8_t* const begin_;
const size_t size_;
const char* const location_;
+ const bool verify_checksum_;
const DexFile::Header* const header_;
struct OffsetTypeMapEmptyFn {
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 4e53914..71c0ad9 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -122,6 +122,10 @@
class DexFileVerifierTest : public CommonRuntimeTest {
protected:
+ DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) {
+ return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
+ }
+
void VerifyModification(const char* dex_file_base64_content,
const char* location,
std::function<void(DexFile*)> f,
@@ -130,16 +134,17 @@
std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(dex_file_base64_content, &length);
CHECK(dex_bytes != nullptr);
// Note: `dex_file` will be destroyed before `dex_bytes`.
- std::unique_ptr<DexFile> dex_file(
- new DexFile(dex_bytes.get(), length, "tmp", 0, nullptr, nullptr));
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
f(dex_file.get());
FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin()));
+ static constexpr bool kVerifyChecksum = true;
std::string error_msg;
bool success = DexFileVerifier::Verify(dex_file.get(),
dex_file->Begin(),
dex_file->Size(),
location,
+ kVerifyChecksum,
&error_msg);
if (expected_error == nullptr) {
EXPECT_TRUE(success) << error_msg;
@@ -175,7 +180,7 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
std::vector<std::unique_ptr<const DexFile>> tmp;
- bool success = DexFile::Open(location, location, error_msg, &tmp);
+ bool success = DexFile::Open(location, location, true, error_msg, &tmp);
CHECK(success) << error_msg;
EXPECT_EQ(1U, tmp.size());
std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]);
@@ -1697,4 +1702,45 @@
" implemented interface with type idx: '0'");
}
+TEST_F(DexFileVerifierTest, Checksum) {
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(kGoodTestDex, &length);
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+
+ // Good checksum: all pass.
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good checksum, no verify",
+ /*verify_checksum*/ false,
+ &error_msg));
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "good checksum, verify",
+ /*verify_checksum*/ true,
+ &error_msg));
+
+ // Bad checksum: !verify_checksum passes verify_checksum fails.
+ DexFile::Header* header = reinterpret_cast<DexFile::Header*>(
+ const_cast<uint8_t*>(dex_file->Begin()));
+ header->checksum_ = 0;
+ EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad checksum, no verify",
+ /*verify_checksum*/ false,
+ &error_msg));
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad checksum, verify",
+ /*verify_checksum*/ true,
+ &error_msg));
+ EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg;
+}
+
} // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index fc62573..1b55d2f 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -448,23 +448,10 @@
: ClassLinker::kNoICCECheckForCache;
resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
}
+ // Resolution and access check.
if (UNLIKELY(resolved_method == nullptr)) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
- } else if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
- if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
- resolved_method->IsConstructor())) {
- // Hack for String init:
- //
- // We assume that the input of String.<init> in verified code is always
- // an unitialized reference. If it is a null constant, it must have been
- // optimized out by the compiler. Do not throw NullPointerException.
- } else {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowNullPointerExceptionForMethodAccess(method_idx, type);
- return nullptr; // Failure.
- }
} else if (access_check) {
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
bool can_access_resolved_method =
@@ -482,6 +469,22 @@
return nullptr; // Failure.
}
}
+ // Next, null pointer check.
+ if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
+ if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
+ resolved_method->IsConstructor())) {
+ // Hack for String init:
+ //
+ // We assume that the input of String.<init> in verified code is always
+ // an unitialized reference. If it is a null constant, it must have been
+ // optimized out by the compiler. Do not throw NullPointerException.
+ } else {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ ThrowNullPointerExceptionForMethodAccess(method_idx, type);
+ return nullptr; // Failure.
+ }
+ }
switch (type) {
case kStatic:
case kDirect:
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index f3a0d2f..d0dad34 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -120,6 +120,8 @@
extern "C" void art_quick_throw_div_zero();
extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
extern "C" void art_quick_throw_null_pointer_exception();
+extern "C" void art_quick_throw_null_pointer_exception_from_signal(uintptr_t address);
extern "C" void art_quick_throw_stack_overflow(void*);
+extern "C" void art_quick_throw_string_bounds(int32_t index, int32_t limit);
#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 5dafa8b..f98de95 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -114,6 +114,7 @@
qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+ qpoints->pThrowStringBounds = art_quick_throw_string_bounds;
// Deoptimize
qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 79d1c13..30b639e 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -140,6 +140,7 @@
V(ThrowNoSuchMethod, void, int32_t) \
V(ThrowNullPointer, void, void) \
V(ThrowStackOverflow, void, void*) \
+ V(ThrowStringBounds, void, int32_t, int32_t) \
V(Deoptimize, void, void) \
\
V(A64Load, int64_t, volatile const int64_t *) \
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 5256fea..ea9f7b0 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -29,7 +29,7 @@
self->QuickDeliverException();
}
-// Called by generated call to throw an exception.
+// Called by generated code to throw an exception.
extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
/*
@@ -48,17 +48,27 @@
self->QuickDeliverException();
}
-// Called by generated call to throw a NPE exception.
+// Called by generated code to throw a NPE exception.
extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ // We come from an explicit check in the generated code. This path is triggered
+ // only if the object is indeed null.
+ ThrowNullPointerExceptionFromDexPC(/* check_address */ false, 0U);
+ self->QuickDeliverException();
+}
+
+// Installed by a signal handler to throw a NPE exception.
+extern "C" NO_RETURN void artThrowNullPointerExceptionFromSignal(uintptr_t addr, Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ScopedQuickEntrypointChecks sqec(self);
self->NoteSignalBeingHandled();
- ThrowNullPointerExceptionFromDexPC();
+ ThrowNullPointerExceptionFromDexPC(/* check_address */ true, addr);
self->NoteSignalHandlerDone();
self->QuickDeliverException();
}
-// Called by generated call to throw an arithmetic divide by zero exception.
+// Called by generated code to throw an arithmetic divide by zero exception.
extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -66,7 +76,7 @@
self->QuickDeliverException();
}
-// Called by generated call to throw an array index out of bounds exception.
+// Called by generated code to throw an array index out of bounds exception.
extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -74,6 +84,14 @@
self->QuickDeliverException();
}
+// Called by generated code to throw a string index out of bounds exception.
+extern "C" NO_RETURN void artThrowStringBoundsFromCode(int index, int length, Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ScopedQuickEntrypointChecks sqec(self);
+ ThrowStringIndexOutOfBoundsException(index, length);
+ self->QuickDeliverException();
+}
+
extern "C" NO_RETURN void artThrowStackOverflowFromCode(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index c621672..7a624b2 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -112,17 +112,19 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, name, pthread_self, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, pthread_self, last_no_thread_suspension_cause,
sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, checkpoint_functions,
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, checkpoint_function,
sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_functions, jni_entrypoints,
- sizeof(void*) * 6);
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, active_suspend_barriers,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, jni_entrypoints,
+ sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers));
// Skip across the entrypoints structures.
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, thread_local_start, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, mterp_current_ibase, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*));
@@ -285,7 +287,8 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pDeoptimize, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pThrowStringBounds, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStringBounds, pDeoptimize, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeoptimize, pA64Load, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, sizeof(void*));
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 625b1e8..56e0fb7 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -96,6 +96,14 @@
bool Action(int sig, siginfo_t* siginfo, void* context) OVERRIDE;
+ static bool IsValidImplicitCheck(siginfo_t* siginfo) {
+ // Our implicit NPE checks always limit the range to a page.
+ // Note that the runtime will do more exhaustive checks (that we cannot
+ // reasonably do in signal processing code) based on the dex instruction
+ // faulting.
+ return CanDoImplicitNullCheckOn(reinterpret_cast<uintptr_t>(siginfo->si_addr));
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(NullPointerHandler);
};
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 64fa434..3011112 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -28,7 +28,7 @@
namespace gc {
namespace collector {
-inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegionOrImmuneSpace(
+inline mirror::Object* ConcurrentCopying::MarkUnevacFromSpaceRegion(
mirror::Object* ref, accounting::ContinuousSpaceBitmap* bitmap) {
// For the Baker-style RB, in a rare case, we could incorrectly change the object from white
// to gray even though the object has already been marked through. This happens if a mutator
@@ -69,6 +69,37 @@
return ref;
}
+template<bool kGrayImmuneObject>
+inline mirror::Object* ConcurrentCopying::MarkImmuneSpace(mirror::Object* ref) {
+ if (kUseBakerReadBarrier) {
+ // The GC-running thread doesn't (need to) gray immune objects except when updating thread roots
+ // in the thread flip on behalf of suspended threads (when gc_grays_immune_objects_ is
+ // true). Also, a mutator doesn't (need to) gray an immune object after GC has updated all
+ // immune space objects (when updated_all_immune_objects_ is true).
+ if (kIsDebugBuild) {
+ if (Thread::Current() == thread_running_gc_) {
+ DCHECK(!kGrayImmuneObject ||
+ updated_all_immune_objects_.LoadRelaxed() ||
+ gc_grays_immune_objects_);
+ } else {
+ DCHECK(kGrayImmuneObject);
+ }
+ }
+ if (!kGrayImmuneObject || updated_all_immune_objects_.LoadRelaxed()) {
+ return ref;
+ }
+ // This may or may not succeed, which is ok because the object may already be gray.
+ bool success = ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(),
+ ReadBarrier::GrayPtr());
+ if (success) {
+ MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
+ immune_gray_stack_.push_back(ref);
+ }
+ }
+ return ref;
+}
+
+template<bool kGrayImmuneObject>
inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
if (from_ref == nullptr) {
return nullptr;
@@ -109,10 +140,14 @@
return to_ref;
}
case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace: {
- return MarkUnevacFromSpaceRegionOrImmuneSpace(from_ref, region_space_bitmap_);
+ return MarkUnevacFromSpaceRegion(from_ref, region_space_bitmap_);
}
case space::RegionSpace::RegionType::kRegionTypeNone:
- return MarkNonMoving(from_ref);
+ if (immune_spaces_.ContainsObject(from_ref)) {
+ return MarkImmuneSpace<kGrayImmuneObject>(from_ref);
+ } else {
+ return MarkNonMoving(from_ref);
+ }
default:
UNREACHABLE();
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 3f8f628..b7b5aa0 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -50,14 +50,16 @@
mark_stack_lock_("concurrent copying mark stack lock", kMarkSweepMarkStackLock),
thread_running_gc_(nullptr),
is_marking_(false), is_active_(false), is_asserting_to_space_invariant_(false),
+ region_space_bitmap_(nullptr),
heap_mark_bitmap_(nullptr), live_stack_freeze_size_(0), mark_stack_mode_(kMarkStackModeOff),
weak_ref_access_enabled_(true),
skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
rb_table_(heap_->GetReadBarrierTable()),
- force_evacuate_all_(false) {
+ force_evacuate_all_(false),
+ immune_gray_stack_lock_("concurrent copying immune gray stack lock",
+ kMarkSweepMarkStackLock) {
static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
"The region space size and the read barrier table region size must match");
- cc_heap_bitmap_.reset(new accounting::HeapBitmap(heap));
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
@@ -139,19 +141,10 @@
space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
CHECK(space->IsZygoteSpace() || space->IsImageSpace());
immune_spaces_.AddSpace(space);
- const char* bitmap_name = space->IsImageSpace() ? "cc image space bitmap" :
- "cc zygote space bitmap";
- // TODO: try avoiding using bitmaps for image/zygote to save space.
- accounting::ContinuousSpaceBitmap* bitmap =
- accounting::ContinuousSpaceBitmap::Create(bitmap_name, space->Begin(), space->Capacity());
- cc_heap_bitmap_->AddContinuousSpaceBitmap(bitmap);
- cc_bitmaps_.push_back(bitmap);
} else if (space == region_space_) {
accounting::ContinuousSpaceBitmap* bitmap =
accounting::ContinuousSpaceBitmap::Create("cc region space bitmap",
space->Begin(), space->Capacity());
- cc_heap_bitmap_->AddContinuousSpaceBitmap(bitmap);
- cc_bitmaps_.push_back(bitmap);
region_space_bitmap_ = bitmap;
}
}
@@ -179,6 +172,15 @@
} else {
force_evacuate_all_ = false;
}
+ if (kUseBakerReadBarrier) {
+ updated_all_immune_objects_.StoreRelaxed(false);
+ // GC may gray immune objects in the thread flip.
+ gc_grays_immune_objects_ = true;
+ if (kIsDebugBuild) {
+ MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
+ DCHECK(immune_gray_stack_.empty());
+ }
+ }
BindBitmaps();
if (kVerboseMode) {
LOG(INFO) << "force_evacuate_all=" << force_evacuate_all_;
@@ -192,7 +194,7 @@
}
// Used to switch the thread roots of a thread from from-space refs to to-space refs.
-class ThreadFlipVisitor : public Closure {
+class ConcurrentCopying::ThreadFlipVisitor : public Closure {
public:
ThreadFlipVisitor(ConcurrentCopying* concurrent_copying, bool use_tlab)
: concurrent_copying_(concurrent_copying), use_tlab_(use_tlab) {
@@ -229,7 +231,7 @@
};
// Called back from Runtime::FlipThreadRoots() during a pause.
-class FlipCallback : public Closure {
+class ConcurrentCopying::FlipCallback : public Closure {
public:
explicit FlipCallback(ConcurrentCopying* concurrent_copying)
: concurrent_copying_(concurrent_copying) {
@@ -303,31 +305,6 @@
live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
}
-// Used to visit objects in the immune spaces.
-class ConcurrentCopyingImmuneSpaceObjVisitor {
- public:
- explicit ConcurrentCopyingImmuneSpaceObjVisitor(ConcurrentCopying* cc)
- : collector_(cc) {}
-
- void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_)
- SHARED_REQUIRES(Locks::heap_bitmap_lock_) {
- DCHECK(obj != nullptr);
- DCHECK(collector_->immune_spaces_.ContainsObject(obj));
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- collector_->cc_heap_bitmap_->GetContinuousSpaceBitmap(obj);
- DCHECK(cc_bitmap != nullptr)
- << "An immune space object must have a bitmap";
- if (kIsDebugBuild) {
- DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj))
- << "Immune space object must be already marked";
- }
- collector_->MarkUnevacFromSpaceRegionOrImmuneSpace(obj, cc_bitmap);
- }
-
- private:
- ConcurrentCopying* const collector_;
-};
-
class EmptyCheckpoint : public Closure {
public:
explicit EmptyCheckpoint(ConcurrentCopying* concurrent_copying)
@@ -348,6 +325,27 @@
ConcurrentCopying* const concurrent_copying_;
};
+// Used to visit objects in the immune spaces.
+inline void ConcurrentCopying::ScanImmuneObject(mirror::Object* obj) {
+ DCHECK(obj != nullptr);
+ DCHECK(immune_spaces_.ContainsObject(obj));
+ // Update the fields without graying it or pushing it onto the mark stack.
+ Scan(obj);
+}
+
+class ConcurrentCopying::ImmuneSpaceScanObjVisitor {
+ public:
+ explicit ImmuneSpaceScanObjVisitor(ConcurrentCopying* cc)
+ : collector_(cc) {}
+
+ void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ collector_->ScanImmuneObject(obj);
+ }
+
+ private:
+ ConcurrentCopying* const collector_;
+};
+
// Concurrently mark roots that are guarded by read barriers and process the mark stack.
void ConcurrentCopying::MarkingPhase() {
TimingLogger::ScopedTiming split("MarkingPhase", GetTimings());
@@ -355,25 +353,46 @@
LOG(INFO) << "GC MarkingPhase";
}
CHECK(weak_ref_access_enabled_);
- {
- // Mark the image root. The WB-based collectors do not need to
- // scan the image objects from roots by relying on the card table,
- // but it's necessary for the RB to-space invariant to hold.
- TimingLogger::ScopedTiming split1("VisitImageRoots", GetTimings());
- for (space::ContinuousSpace* space : heap_->GetContinuousSpaces()) {
- if (space->IsImageSpace()) {
- gc::space::ImageSpace* image = space->AsImageSpace();
- if (image != nullptr) {
- mirror::ObjectArray<mirror::Object>* image_root = image->GetImageHeader().GetImageRoots();
- mirror::Object* marked_image_root = Mark(image_root);
- CHECK_EQ(image_root, marked_image_root) << "An image object does not move";
- if (ReadBarrier::kEnableToSpaceInvariantChecks) {
- AssertToSpaceInvariant(nullptr, MemberOffset(0), marked_image_root);
- }
- }
- }
- }
+
+ // Scan immune spaces.
+ // Update all the fields in the immune spaces first without graying the objects so that we
+ // minimize dirty pages in the immune spaces. Note mutators can concurrently access and gray some
+ // of the objects.
+ if (kUseBakerReadBarrier) {
+ gc_grays_immune_objects_ = false;
}
+ for (auto& space : immune_spaces_.GetSpaces()) {
+ DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
+ accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+ ImmuneSpaceScanObjVisitor visitor(this);
+ live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
+ reinterpret_cast<uintptr_t>(space->Limit()),
+ visitor);
+ }
+ if (kUseBakerReadBarrier) {
+ // This release fence makes the field updates in the above loop visible before allowing mutator
+ // getting access to immune objects without graying it first.
+ updated_all_immune_objects_.StoreRelease(true);
+ // Now whiten immune objects concurrently accessed and grayed by mutators. We can't do this in
+ // the above loop because we would incorrectly disable the read barrier by whitening an object
+ // which may point to an unscanned, white object, breaking the to-space invariant.
+ //
+ // Make sure no mutators are in the middle of marking an immune object before whitening immune
+ // objects.
+ IssueEmptyCheckpoint();
+ MutexLock mu(Thread::Current(), immune_gray_stack_lock_);
+ if (kVerboseMode) {
+ LOG(INFO) << "immune gray stack size=" << immune_gray_stack_.size();
+ }
+ for (mirror::Object* obj : immune_gray_stack_) {
+ DCHECK(obj->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ bool success = obj->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
+ ReadBarrier::WhitePtr());
+ DCHECK(success);
+ }
+ immune_gray_stack_.clear();
+ }
+
{
TimingLogger::ScopedTiming split2("VisitConcurrentRoots", GetTimings());
Runtime::Current()->VisitConcurrentRoots(this, kVisitRootFlagAllRoots);
@@ -384,16 +403,6 @@
Runtime::Current()->VisitNonThreadRoots(this);
}
- // Immune spaces.
- for (auto& space : immune_spaces_.GetSpaces()) {
- DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
- accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
- ConcurrentCopyingImmuneSpaceObjVisitor visitor(this);
- live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
- reinterpret_cast<uintptr_t>(space->Limit()),
- visitor);
- }
-
Thread* self = Thread::Current();
{
TimingLogger::ScopedTiming split7("ProcessMarkStack", GetTimings());
@@ -487,7 +496,7 @@
Runtime::Current()->BroadcastForNewSystemWeaks();
}
-class DisableMarkingCheckpoint : public Closure {
+class ConcurrentCopying::DisableMarkingCheckpoint : public Closure {
public:
explicit DisableMarkingCheckpoint(ConcurrentCopying* concurrent_copying)
: concurrent_copying_(concurrent_copying) {
@@ -683,9 +692,9 @@
// The following visitors are used to verify that there's no references to the from-space left after
// marking.
-class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor : public SingleRootVisitor {
+class ConcurrentCopying::VerifyNoFromSpaceRefsVisitor : public SingleRootVisitor {
public:
- explicit ConcurrentCopyingVerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector)
+ explicit VerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* ref) const
@@ -712,16 +721,16 @@
ConcurrentCopying* const collector_;
};
-class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor {
+class ConcurrentCopying::VerifyNoFromSpaceRefsFieldVisitor {
public:
- explicit ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector)
+ explicit VerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
mirror::Object* ref =
obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
- ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_);
+ VerifyNoFromSpaceRefsVisitor visitor(collector_);
visitor(ref);
}
void operator()(mirror::Class* klass, mirror::Reference* ref) const
@@ -739,7 +748,7 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
SHARED_REQUIRES(Locks::mutator_lock_) {
- ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_);
+ VerifyNoFromSpaceRefsVisitor visitor(collector_);
visitor(root->AsMirrorPtr());
}
@@ -747,9 +756,9 @@
ConcurrentCopying* const collector_;
};
-class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor {
+class ConcurrentCopying::VerifyNoFromSpaceRefsObjectVisitor {
public:
- explicit ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector)
+ explicit VerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* obj) const
SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -761,7 +770,7 @@
ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg);
space::RegionSpace* region_space = collector->RegionSpace();
CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
- ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor visitor(collector);
+ VerifyNoFromSpaceRefsFieldVisitor visitor(collector);
obj->VisitReferences(visitor, visitor);
if (kUseBakerReadBarrier) {
CHECK(obj->GetReadBarrierPointer() == ReadBarrier::WhitePtr())
@@ -785,16 +794,15 @@
CHECK(!thread->GetIsGcMarking());
}
}
- ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor visitor(this);
+ VerifyNoFromSpaceRefsObjectVisitor visitor(this);
// Roots.
{
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this);
+ VerifyNoFromSpaceRefsVisitor ref_visitor(this);
Runtime::Current()->VisitRoots(&ref_visitor);
}
// The to-space.
- region_space_->WalkToSpace(ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor::ObjectCallback,
- this);
+ region_space_->WalkToSpace(VerifyNoFromSpaceRefsObjectVisitor::ObjectCallback, this);
// Non-moving spaces.
{
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
@@ -802,7 +810,7 @@
}
// The alloc stack.
{
- ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this);
+ VerifyNoFromSpaceRefsVisitor ref_visitor(this);
for (auto* it = heap_->allocation_stack_->Begin(), *end = heap_->allocation_stack_->End();
it < end; ++it) {
mirror::Object* const obj = it->AsMirrorPtr();
@@ -817,9 +825,9 @@
}
// The following visitors are used to assert the to-space invariant.
-class ConcurrentCopyingAssertToSpaceInvariantRefsVisitor {
+class ConcurrentCopying::AssertToSpaceInvariantRefsVisitor {
public:
- explicit ConcurrentCopyingAssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector)
+ explicit AssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* ref) const
@@ -835,16 +843,16 @@
ConcurrentCopying* const collector_;
};
-class ConcurrentCopyingAssertToSpaceInvariantFieldVisitor {
+class ConcurrentCopying::AssertToSpaceInvariantFieldVisitor {
public:
- explicit ConcurrentCopyingAssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
+ explicit AssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
mirror::Object* ref =
obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
- ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_);
+ AssertToSpaceInvariantRefsVisitor visitor(collector_);
visitor(ref);
}
void operator()(mirror::Class* klass, mirror::Reference* ref ATTRIBUTE_UNUSED) const
@@ -861,7 +869,7 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
SHARED_REQUIRES(Locks::mutator_lock_) {
- ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_);
+ AssertToSpaceInvariantRefsVisitor visitor(collector_);
visitor(root->AsMirrorPtr());
}
@@ -869,9 +877,9 @@
ConcurrentCopying* const collector_;
};
-class ConcurrentCopyingAssertToSpaceInvariantObjectVisitor {
+class ConcurrentCopying::AssertToSpaceInvariantObjectVisitor {
public:
- explicit ConcurrentCopyingAssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector)
+ explicit AssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* obj) const
SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -884,7 +892,7 @@
space::RegionSpace* region_space = collector->RegionSpace();
CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space";
collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj);
- ConcurrentCopyingAssertToSpaceInvariantFieldVisitor visitor(collector);
+ AssertToSpaceInvariantFieldVisitor visitor(collector);
obj->VisitReferences(visitor, visitor);
}
@@ -892,7 +900,7 @@
ConcurrentCopying* const collector_;
};
-class RevokeThreadLocalMarkStackCheckpoint : public Closure {
+class ConcurrentCopying::RevokeThreadLocalMarkStackCheckpoint : public Closure {
public:
RevokeThreadLocalMarkStackCheckpoint(ConcurrentCopying* concurrent_copying,
bool disable_weak_ref_access)
@@ -1112,7 +1120,7 @@
region_space_->AddLiveBytes(to_ref, alloc_size);
}
if (ReadBarrier::kEnableToSpaceInvariantChecks || kIsDebugBuild) {
- ConcurrentCopyingAssertToSpaceInvariantObjectVisitor visitor(this);
+ AssertToSpaceInvariantObjectVisitor visitor(this);
visitor(to_ref);
}
}
@@ -1241,6 +1249,9 @@
IssueEmptyCheckpoint();
// Disable the check.
is_mark_stack_push_disallowed_.StoreSequentiallyConsistent(0);
+ if (kUseBakerReadBarrier) {
+ updated_all_immune_objects_.StoreSequentiallyConsistent(false);
+ }
CheckEmptyMarkStack();
}
@@ -1290,13 +1301,9 @@
SwapBitmaps();
heap_->UnBindBitmaps();
- // Remove bitmaps for the immune spaces.
- while (!cc_bitmaps_.empty()) {
- accounting::ContinuousSpaceBitmap* cc_bitmap = cc_bitmaps_.back();
- cc_heap_bitmap_->RemoveContinuousSpaceBitmap(cc_bitmap);
- delete cc_bitmap;
- cc_bitmaps_.pop_back();
- }
+ // Delete the region bitmap.
+ DCHECK(region_space_bitmap_ != nullptr);
+ delete region_space_bitmap_;
region_space_bitmap_ = nullptr;
}
@@ -1412,15 +1419,6 @@
// In a non-moving space.
if (immune_spaces_.ContainsObject(obj)) {
LOG(INFO) << "holder is in an immune image or the zygote space.";
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- cc_heap_bitmap_->GetContinuousSpaceBitmap(obj);
- CHECK(cc_bitmap != nullptr)
- << "An immune space object must have a bitmap.";
- if (cc_bitmap->Test(obj)) {
- LOG(INFO) << "holder is marked in the bit map.";
- } else {
- LOG(INFO) << "holder is NOT marked in the bit map.";
- }
} else {
LOG(INFO) << "holder is in a non-immune, non-moving (or main) space.";
accounting::ContinuousSpaceBitmap* mark_bitmap =
@@ -1451,17 +1449,17 @@
mirror::Object* ref) {
// In a non-moving spaces. Check that the ref is marked.
if (immune_spaces_.ContainsObject(ref)) {
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- cc_heap_bitmap_->GetContinuousSpaceBitmap(ref);
- CHECK(cc_bitmap != nullptr)
- << "An immune space ref must have a bitmap. " << ref;
if (kUseBakerReadBarrier) {
- CHECK(cc_bitmap->Test(ref))
+ // Immune object may not be gray if called from the GC.
+ if (Thread::Current() == thread_running_gc_ && !gc_grays_immune_objects_) {
+ return;
+ }
+ bool updated_all_immune_objects = updated_all_immune_objects_.LoadSequentiallyConsistent();
+ CHECK(updated_all_immune_objects || ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
<< "Unmarked immune space ref. obj=" << obj << " rb_ptr="
- << obj->GetReadBarrierPointer() << " ref=" << ref;
- } else {
- CHECK(cc_bitmap->Test(ref))
- << "Unmarked immune space ref. obj=" << obj << " ref=" << ref;
+ << (obj != nullptr ? obj->GetReadBarrierPointer() : nullptr)
+ << " ref=" << ref << " ref rb_ptr=" << ref->GetReadBarrierPointer()
+ << " updated_all_immune_objects=" << updated_all_immune_objects;
}
} else {
accounting::ContinuousSpaceBitmap* mark_bitmap =
@@ -1484,9 +1482,9 @@
}
// Used to scan ref fields of an object.
-class ConcurrentCopyingRefFieldsVisitor {
+class ConcurrentCopying::RefFieldsVisitor {
public:
- explicit ConcurrentCopyingRefFieldsVisitor(ConcurrentCopying* collector)
+ explicit RefFieldsVisitor(ConcurrentCopying* collector)
: collector_(collector) {}
void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */)
@@ -1512,7 +1510,7 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
ALWAYS_INLINE
SHARED_REQUIRES(Locks::mutator_lock_) {
- collector_->MarkRoot(root);
+ collector_->MarkRoot</*kGrayImmuneObject*/false>(root);
}
private:
@@ -1522,7 +1520,8 @@
// Scan ref fields of an object.
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
- ConcurrentCopyingRefFieldsVisitor visitor(this);
+ DCHECK_EQ(Thread::Current(), thread_running_gc_);
+ RefFieldsVisitor visitor(this);
// Disable the read barrier for a performance reason.
to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
visitor, visitor);
@@ -1530,9 +1529,10 @@
// Process a field.
inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
+ DCHECK_EQ(Thread::Current(), thread_running_gc_);
mirror::Object* ref = obj->GetFieldObject<
mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
- mirror::Object* to_ref = Mark(ref);
+ mirror::Object* to_ref = Mark</*kGrayImmuneObject*/false>(ref);
if (to_ref == ref) {
return;
}
@@ -1571,10 +1571,11 @@
}
}
+template<bool kGrayImmuneObject>
inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
DCHECK(!root->IsNull());
mirror::Object* const ref = root->AsMirrorPtr();
- mirror::Object* to_ref = Mark(ref);
+ mirror::Object* to_ref = Mark<kGrayImmuneObject>(ref);
if (to_ref != ref) {
auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
auto expected_ref = mirror::CompressedReference<mirror::Object>::FromMirrorPtr(ref);
@@ -1595,14 +1596,46 @@
for (size_t i = 0; i < count; ++i) {
mirror::CompressedReference<mirror::Object>* const root = roots[i];
if (!root->IsNull()) {
- MarkRoot(root);
+ // kGrayImmuneObject is true because this is used for the thread flip.
+ MarkRoot</*kGrayImmuneObject*/true>(root);
}
}
}
+// Temporary set gc_grays_immune_objects_ to true in a scope if the current thread is GC.
+class ConcurrentCopying::ScopedGcGraysImmuneObjects {
+ public:
+ explicit ScopedGcGraysImmuneObjects(ConcurrentCopying* collector)
+ : collector_(collector), enabled_(false) {
+ if (kUseBakerReadBarrier &&
+ collector_->thread_running_gc_ == Thread::Current() &&
+ !collector_->gc_grays_immune_objects_) {
+ collector_->gc_grays_immune_objects_ = true;
+ enabled_ = true;
+ }
+ }
+
+ ~ScopedGcGraysImmuneObjects() {
+ if (kUseBakerReadBarrier &&
+ collector_->thread_running_gc_ == Thread::Current() &&
+ enabled_) {
+ DCHECK(collector_->gc_grays_immune_objects_);
+ collector_->gc_grays_immune_objects_ = false;
+ }
+ }
+
+ private:
+ ConcurrentCopying* const collector_;
+ bool enabled_;
+};
+
// Fill the given memory block with a dummy object. Used to fill in a
// copy of objects that was lost in race.
void ConcurrentCopying::FillWithDummyObject(mirror::Object* dummy_obj, size_t byte_size) {
+ // GC doesn't gray immune objects while scanning immune objects. But we need to trigger the read
+ // barriers here because we need the updated reference to the int array class, etc. Temporary set
+ // gc_grays_immune_objects_ to true so that we won't cause a DCHECK failure in MarkImmuneSpace().
+ ScopedGcGraysImmuneObjects scoped_gc_gray_immune_objects(this);
CHECK_ALIGNED(byte_size, kObjectAlignment);
memset(dummy_obj, 0, byte_size);
mirror::Class* int_array_class = mirror::IntArray::GetArrayClass();
@@ -1838,21 +1871,8 @@
} else {
// from_ref is in a non-moving space.
if (immune_spaces_.ContainsObject(from_ref)) {
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
- DCHECK(cc_bitmap != nullptr)
- << "An immune space object must have a bitmap";
- if (kIsDebugBuild) {
- DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
- << "Immune space object must be already marked";
- }
- if (cc_bitmap->Test(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- } else {
- // Newly marked.
- to_ref = nullptr;
- }
+ // An immune object is alive.
+ to_ref = from_ref;
} else {
// Non-immune non-moving space. Use the mark bitmap.
accounting::ContinuousSpaceBitmap* mark_bitmap =
@@ -1891,85 +1911,74 @@
mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref) {
// ref is in a non-moving space (from_ref == to_ref).
DCHECK(!region_space_->HasAddress(ref)) << ref;
- if (immune_spaces_.ContainsObject(ref)) {
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- cc_heap_bitmap_->GetContinuousSpaceBitmap(ref);
- DCHECK(cc_bitmap != nullptr)
- << "An immune space object must have a bitmap";
- if (kIsDebugBuild) {
- DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(ref)->Test(ref))
- << "Immune space object must be already marked";
+ DCHECK(!immune_spaces_.ContainsObject(ref));
+ // Use the mark bitmap.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(ref);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if (!is_los && mark_bitmap->Test(ref)) {
+ // Already marked.
+ if (kUseBakerReadBarrier) {
+ DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
}
- MarkUnevacFromSpaceRegionOrImmuneSpace(ref, cc_bitmap);
+ } else if (is_los && los_bitmap->Test(ref)) {
+ // Already marked in LOS.
+ if (kUseBakerReadBarrier) {
+ DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ }
} else {
- // Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
- bool is_los = mark_bitmap == nullptr;
- if (!is_los && mark_bitmap->Test(ref)) {
- // Already marked.
- if (kUseBakerReadBarrier) {
- DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
- ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ // Not marked.
+ if (IsOnAllocStack(ref)) {
+ // If it's on the allocation stack, it's considered marked. Keep it white.
+ // Objects on the allocation stack need not be marked.
+ if (!is_los) {
+ DCHECK(!mark_bitmap->Test(ref));
+ } else {
+ DCHECK(!los_bitmap->Test(ref));
}
- } else if (is_los && los_bitmap->Test(ref)) {
- // Already marked in LOS.
if (kUseBakerReadBarrier) {
- DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
- ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
}
} else {
- // Not marked.
- if (IsOnAllocStack(ref)) {
- // If it's on the allocation stack, it's considered marked. Keep it white.
- // Objects on the allocation stack need not be marked.
- if (!is_los) {
- DCHECK(!mark_bitmap->Test(ref));
- } else {
- DCHECK(!los_bitmap->Test(ref));
+ // For the baker-style RB, we need to handle 'false-gray' cases. See the
+ // kRegionTypeUnevacFromSpace-case comment in Mark().
+ if (kUseBakerReadBarrier) {
+ // Test the bitmap first to reduce the chance of false gray cases.
+ if ((!is_los && mark_bitmap->Test(ref)) ||
+ (is_los && los_bitmap->Test(ref))) {
+ return ref;
}
- if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
+ }
+ // Not marked or on the allocation stack. Try to mark it.
+ // This may or may not succeed, which is ok.
+ bool cas_success = false;
+ if (kUseBakerReadBarrier) {
+ cas_success = ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(),
+ ReadBarrier::GrayPtr());
+ }
+ if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
+ // Already marked.
+ if (kUseBakerReadBarrier && cas_success &&
+ ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
+ PushOntoFalseGrayStack(ref);
+ }
+ } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
+ // Already marked in LOS.
+ if (kUseBakerReadBarrier && cas_success &&
+ ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
+ PushOntoFalseGrayStack(ref);
}
} else {
- // For the baker-style RB, we need to handle 'false-gray' cases. See the
- // kRegionTypeUnevacFromSpace-case comment in Mark().
+ // Newly marked.
if (kUseBakerReadBarrier) {
- // Test the bitmap first to reduce the chance of false gray cases.
- if ((!is_los && mark_bitmap->Test(ref)) ||
- (is_los && los_bitmap->Test(ref))) {
- return ref;
- }
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
}
- // Not marked or on the allocation stack. Try to mark it.
- // This may or may not succeed, which is ok.
- bool cas_success = false;
- if (kUseBakerReadBarrier) {
- cas_success = ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(),
- ReadBarrier::GrayPtr());
- }
- if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
- // Already marked.
- if (kUseBakerReadBarrier && cas_success &&
- ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
- PushOntoFalseGrayStack(ref);
- }
- } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
- // Already marked in LOS.
- if (kUseBakerReadBarrier && cas_success &&
- ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr()) {
- PushOntoFalseGrayStack(ref);
- }
- } else {
- // Newly marked.
- if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
- }
- PushOntoMarkStack(ref);
- }
+ PushOntoMarkStack(ref);
}
}
}
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index e9ff618..166a1f0 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -51,21 +51,22 @@
class ConcurrentCopying : public GarbageCollector {
public:
- // TODO: disable thse flags for production use.
// Enable the no-from-space-refs verification at the pause.
- static constexpr bool kEnableNoFromSpaceRefsVerification = true;
+ static constexpr bool kEnableNoFromSpaceRefsVerification = kIsDebugBuild;
// Enable the from-space bytes/objects check.
- static constexpr bool kEnableFromSpaceAccountingCheck = true;
+ static constexpr bool kEnableFromSpaceAccountingCheck = kIsDebugBuild;
// Enable verbose mode.
static constexpr bool kVerboseMode = false;
ConcurrentCopying(Heap* heap, const std::string& name_prefix = "");
~ConcurrentCopying();
- virtual void RunPhases() OVERRIDE REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
- void InitializePhase() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
+ virtual void RunPhases() OVERRIDE
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
+ void InitializePhase() SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_, !immune_gray_stack_lock_);
void MarkingPhase() SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
void ReclaimPhase() SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
void FinishPhase() REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
@@ -93,8 +94,9 @@
DCHECK(ref != nullptr);
return IsMarked(ref) == ref;
}
+ template<bool kGrayImmuneObject = true>
ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
bool IsMarking() const {
return is_marking_;
}
@@ -118,16 +120,19 @@
void Scan(mirror::Object* to_ref) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
void Process(mirror::Object* obj, MemberOffset offset)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_ , !skipped_blocks_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_ , !skipped_blocks_lock_, !immune_gray_stack_lock_);
virtual void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info)
OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
+ template<bool kGrayImmuneObject>
void MarkRoot(mirror::CompressedReference<mirror::Object>* root)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
virtual void VisitRoots(mirror::CompressedReference<mirror::Object>** roots, size_t count,
const RootInfo& info)
OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
void VerifyNoFromSpaceReferences() REQUIRES(Locks::mutator_lock_);
accounting::ObjectStack* GetAllocationStack();
accounting::ObjectStack* GetLiveStack();
@@ -147,9 +152,11 @@
SHARED_REQUIRES(Locks::mutator_lock_);
void ProcessReferences(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_);
virtual mirror::Object* MarkObject(mirror::Object* from_ref) OVERRIDE
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
virtual void MarkHeapReference(mirror::HeapReference<mirror::Object>* from_ref) OVERRIDE
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
virtual mirror::Object* IsMarked(mirror::Object* from_ref) OVERRIDE
SHARED_REQUIRES(Locks::mutator_lock_);
virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) OVERRIDE
@@ -183,14 +190,19 @@
void ExpandGcMarkStack() SHARED_REQUIRES(Locks::mutator_lock_);
mirror::Object* MarkNonMoving(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
- ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegionOrImmuneSpace(mirror::Object* from_ref,
+ ALWAYS_INLINE mirror::Object* MarkUnevacFromSpaceRegion(mirror::Object* from_ref,
accounting::SpaceBitmap<kObjectAlignment>* bitmap)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
+ template<bool kGrayImmuneObject>
+ ALWAYS_INLINE mirror::Object* MarkImmuneSpace(mirror::Object* from_ref)
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_);
void PushOntoFalseGrayStack(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
void ProcessFalseGrayStack() SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_);
+ void ScanImmuneObject(mirror::Object* obj)
+ SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
space::RegionSpace* region_space_; // The underlying region space.
std::unique_ptr<Barrier> gc_barrier_;
@@ -208,8 +220,6 @@
bool is_active_; // True while the collection is ongoing.
bool is_asserting_to_space_invariant_; // True while asserting the to-space invariant.
ImmuneSpaces immune_spaces_;
- std::unique_ptr<accounting::HeapBitmap> cc_heap_bitmap_;
- std::vector<accounting::SpaceBitmap<kObjectAlignment>*> cc_bitmaps_;
accounting::SpaceBitmap<kObjectAlignment>* region_space_bitmap_;
// A cache of Heap::GetMarkBitmap().
accounting::HeapBitmap* heap_mark_bitmap_;
@@ -243,17 +253,27 @@
accounting::ReadBarrierTable* rb_table_;
bool force_evacuate_all_; // True if all regions are evacuated.
+ Atomic<bool> updated_all_immune_objects_;
+ bool gc_grays_immune_objects_;
+ Mutex immune_gray_stack_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::vector<mirror::Object*> immune_gray_stack_ GUARDED_BY(immune_gray_stack_lock_);
- friend class ConcurrentCopyingRefFieldsVisitor;
- friend class ConcurrentCopyingImmuneSpaceObjVisitor;
- friend class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor;
- friend class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor;
- friend class ConcurrentCopyingClearBlackPtrsVisitor;
- friend class ConcurrentCopyingLostCopyVisitor;
- friend class ThreadFlipVisitor;
- friend class FlipCallback;
- friend class ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor;
- friend class RevokeThreadLocalMarkStackCheckpoint;
+ class AssertToSpaceInvariantFieldVisitor;
+ class AssertToSpaceInvariantObjectVisitor;
+ class AssertToSpaceInvariantRefsVisitor;
+ class ClearBlackPtrsVisitor;
+ class ComputeUnevacFromSpaceLiveRatioVisitor;
+ class DisableMarkingCheckpoint;
+ class FlipCallback;
+ class ImmuneSpaceScanObjVisitor;
+ class LostCopyVisitor;
+ class RefFieldsVisitor;
+ class RevokeThreadLocalMarkStackCheckpoint;
+ class ScopedGcGraysImmuneObjects;
+ class ThreadFlipVisitor;
+ class VerifyNoFromSpaceRefsFieldVisitor;
+ class VerifyNoFromSpaceRefsObjectVisitor;
+ class VerifyNoFromSpaceRefsVisitor;
DISALLOW_IMPLICIT_CONSTRUCTORS(ConcurrentCopying);
};
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 6beb606..43482eb 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -52,8 +52,9 @@
MarkCompact::MarkCompact(Heap* heap, const std::string& name_prefix)
: GarbageCollector(heap, name_prefix + (name_prefix.empty() ? "" : " ") + "mark compact"),
- space_(nullptr), collector_name_(name_), updating_references_(false) {
-}
+ space_(nullptr),
+ collector_name_(name_),
+ updating_references_(false) {}
void MarkCompact::RunPhases() {
Thread* self = Thread::Current();
@@ -85,30 +86,20 @@
++live_objects_in_space_;
}
-class CalculateObjectForwardingAddressVisitor {
- public:
- explicit CalculateObjectForwardingAddressVisitor(MarkCompact* collector)
- : collector_(collector) {}
- void operator()(mirror::Object* obj) const REQUIRES(Locks::mutator_lock_,
- Locks::heap_bitmap_lock_) {
- DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment);
- DCHECK(collector_->IsMarked(obj) != nullptr);
- collector_->ForwardObject(obj);
- }
-
- private:
- MarkCompact* const collector_;
-};
void MarkCompact::CalculateObjectForwardingAddresses() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
// The bump pointer in the space where the next forwarding address will be.
bump_pointer_ = reinterpret_cast<uint8_t*>(space_->Begin());
// Visit all the marked objects in the bitmap.
- CalculateObjectForwardingAddressVisitor visitor(this);
objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()),
reinterpret_cast<uintptr_t>(space_->End()),
- visitor);
+ [this](mirror::Object* obj)
+ REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment);
+ DCHECK(IsMarked(obj) != nullptr);
+ ForwardObject(obj);
+ });
}
void MarkCompact::InitializePhase() {
@@ -129,17 +120,6 @@
false, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(), this);
}
-class BitmapSetSlowPathVisitor {
- public:
- void operator()(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) {
- // Marking a large object, make sure its aligned as a sanity check.
- if (!IsAligned<kPageSize>(obj)) {
- Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- LOG(FATAL) << obj;
- }
- }
-};
-
inline mirror::Object* MarkCompact::MarkObject(mirror::Object* obj) {
if (obj == nullptr) {
return nullptr;
@@ -155,8 +135,15 @@
}
} else {
DCHECK(!space_->HasAddress(obj));
- BitmapSetSlowPathVisitor visitor;
- if (!mark_bitmap_->Set(obj, visitor)) {
+ auto slow_path = [this](const mirror::Object* ref)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // Marking a large object, make sure its aligned as a sanity check.
+ if (!IsAligned<kPageSize>(ref)) {
+ Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
+ LOG(FATAL) << ref;
+ }
+ };
+ if (!mark_bitmap_->Set(obj, slow_path)) {
// This object was not previously marked.
MarkStackPush(obj);
}
@@ -296,10 +283,9 @@
}
}
-class UpdateRootVisitor : public RootVisitor {
+class MarkCompact::UpdateRootVisitor : public RootVisitor {
public:
- explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) {
- }
+ explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) {}
void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES(Locks::mutator_lock_)
@@ -332,10 +318,10 @@
MarkCompact* const collector_;
};
-class UpdateObjectReferencesVisitor {
+class MarkCompact::UpdateObjectReferencesVisitor {
public:
- explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {
- }
+ explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {}
+
void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::heap_bitmap_lock_)
REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
collector_->UpdateObjectReferences(obj);
@@ -423,10 +409,9 @@
}
}
-class UpdateReferenceVisitor {
+class MarkCompact::UpdateReferenceVisitor {
public:
- explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) {
- }
+ explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) {}
void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const
ALWAYS_INLINE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
@@ -501,19 +486,6 @@
return space != space_ && !immune_spaces_.ContainsSpace(space);
}
-class MoveObjectVisitor {
- public:
- explicit MoveObjectVisitor(MarkCompact* collector) : collector_(collector) {
- }
- void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::heap_bitmap_lock_)
- REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
- collector_->MoveObject(obj, obj->SizeOf());
- }
-
- private:
- MarkCompact* const collector_;
-};
-
void MarkCompact::MoveObject(mirror::Object* obj, size_t len) {
// Look at the forwarding address stored in the lock word to know where to copy.
DCHECK(space_->HasAddress(obj)) << obj;
@@ -534,10 +506,13 @@
void MarkCompact::MoveObjects() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
// Move the objects in the before forwarding bitmap.
- MoveObjectVisitor visitor(this);
objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()),
reinterpret_cast<uintptr_t>(space_->End()),
- visitor);
+ [this](mirror::Object* obj)
+ SHARED_REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
+ MoveObject(obj, obj->SizeOf());
+ });
CHECK(lock_words_to_restore_.empty());
}
@@ -572,10 +547,9 @@
heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, this);
}
-class MarkCompactMarkObjectVisitor {
+class MarkCompact::MarkObjectVisitor {
public:
- explicit MarkCompactMarkObjectVisitor(MarkCompact* collector) : collector_(collector) {
- }
+ explicit MarkObjectVisitor(MarkCompact* collector) : collector_(collector) {}
void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const ALWAYS_INLINE
REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
@@ -608,7 +582,7 @@
// Visit all of the references of an object and update.
void MarkCompact::ScanObject(mirror::Object* obj) {
- MarkCompactMarkObjectVisitor visitor(this);
+ MarkObjectVisitor visitor(this);
obj->VisitReferences(visitor, visitor);
}
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 4831157..16abfb7 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -222,13 +222,10 @@
bool updating_references_;
private:
- friend class BitmapSetSlowPathVisitor;
- friend class CalculateObjectForwardingAddressVisitor;
- friend class MarkCompactMarkObjectVisitor;
- friend class MoveObjectVisitor;
- friend class UpdateObjectReferencesVisitor;
- friend class UpdateReferenceVisitor;
- friend class UpdateRootVisitor;
+ class MarkObjectVisitor;
+ class UpdateObjectReferencesVisitor;
+ class UpdateReferenceVisitor;
+ class UpdateRootVisitor;
DISALLOW_IMPLICIT_CONSTRUCTORS(MarkCompact);
};
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 894ceba..9f54f1c 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -266,7 +266,7 @@
PreCleanCards();
}
-class ScanObjectVisitor {
+class MarkSweep::ScanObjectVisitor {
public:
explicit ScanObjectVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE
: mark_sweep_(mark_sweep) {}
@@ -393,12 +393,14 @@
return IsMarked(ref->AsMirrorPtr());
}
-class MarkSweepMarkObjectSlowPath {
+class MarkSweep::MarkObjectSlowPath {
public:
- explicit MarkSweepMarkObjectSlowPath(MarkSweep* mark_sweep,
- mirror::Object* holder = nullptr,
- MemberOffset offset = MemberOffset(0))
- : mark_sweep_(mark_sweep), holder_(holder), offset_(offset) {}
+ explicit MarkObjectSlowPath(MarkSweep* mark_sweep,
+ mirror::Object* holder = nullptr,
+ MemberOffset offset = MemberOffset(0))
+ : mark_sweep_(mark_sweep),
+ holder_(holder),
+ offset_(offset) {}
void operator()(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
if (kProfileLargeObjects) {
@@ -444,27 +446,8 @@
}
PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL);
MemMap::DumpMaps(LOG(INTERNAL_FATAL), true);
- {
- LOG(INTERNAL_FATAL) << "Attempting see if it's a bad root";
- Thread* self = Thread::Current();
- if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
- mark_sweep_->VerifyRoots();
- } else {
- const bool heap_bitmap_exclusive_locked =
- Locks::heap_bitmap_lock_->IsExclusiveHeld(self);
- if (heap_bitmap_exclusive_locked) {
- Locks::heap_bitmap_lock_->ExclusiveUnlock(self);
- }
- {
- ScopedThreadSuspension(self, kSuspended);
- ScopedSuspendAll ssa(__FUNCTION__);
- mark_sweep_->VerifyRoots();
- }
- if (heap_bitmap_exclusive_locked) {
- Locks::heap_bitmap_lock_->ExclusiveLock(self);
- }
- }
- }
+ LOG(INTERNAL_FATAL) << "Attempting see if it's a bad thread root\n";
+ mark_sweep_->VerifySuspendedThreadRoots();
LOG(FATAL) << "Can't mark invalid object";
}
}
@@ -499,7 +482,7 @@
if (kCountMarkedObjects) {
++mark_slowpath_count_;
}
- MarkSweepMarkObjectSlowPath visitor(this, holder, offset);
+ MarkObjectSlowPath visitor(this, holder, offset);
// TODO: We already know that the object is not in the current_space_bitmap_ but MarkBitmap::Set
// will check again.
if (!mark_bitmap_->Set(obj, visitor)) {
@@ -534,7 +517,7 @@
if (LIKELY(object_bitmap->HasAddress(obj))) {
return !object_bitmap->AtomicTestAndSet(obj);
}
- MarkSweepMarkObjectSlowPath visitor(this);
+ MarkObjectSlowPath visitor(this);
return !mark_bitmap_->AtomicTestAndSet(obj, visitor);
}
@@ -553,7 +536,7 @@
}
}
-class VerifyRootMarkedVisitor : public SingleRootVisitor {
+class MarkSweep::VerifyRootMarkedVisitor : public SingleRootVisitor {
public:
explicit VerifyRootMarkedVisitor(MarkSweep* collector) : collector_(collector) { }
@@ -582,7 +565,7 @@
}
}
-class VerifyRootVisitor : public SingleRootVisitor {
+class MarkSweep::VerifyRootVisitor : public SingleRootVisitor {
public:
void VisitRoot(mirror::Object* root, const RootInfo& info) OVERRIDE
SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
@@ -591,15 +574,15 @@
if (heap->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
space::LargeObjectSpace* large_object_space = heap->GetLargeObjectsSpace();
if (large_object_space != nullptr && !large_object_space->Contains(root)) {
- LOG(INTERNAL_FATAL) << "Found invalid root: " << root << " " << info;
+ LOG(INTERNAL_FATAL) << "Found invalid root: " << root << " " << info << "\n";
}
}
}
};
-void MarkSweep::VerifyRoots() {
+void MarkSweep::VerifySuspendedThreadRoots() {
VerifyRootVisitor visitor;
- Runtime::Current()->GetThreadList()->VisitRoots(&visitor);
+ Runtime::Current()->GetThreadList()->VisitRootsForSuspendedThreads(&visitor);
}
void MarkSweep::MarkRoots(Thread* self) {
@@ -629,7 +612,7 @@
this, static_cast<VisitRootFlags>(flags | kVisitRootFlagNonMoving));
}
-class DelayReferenceReferentVisitor {
+class MarkSweep::DelayReferenceReferentVisitor {
public:
explicit DelayReferenceReferentVisitor(MarkSweep* collector) : collector_(collector) {}
@@ -644,7 +627,7 @@
};
template <bool kUseFinger = false>
-class MarkStackTask : public Task {
+class MarkSweep::MarkStackTask : public Task {
public:
MarkStackTask(ThreadPool* thread_pool,
MarkSweep* mark_sweep,
@@ -802,7 +785,7 @@
}
};
-class CardScanTask : public MarkStackTask<false> {
+class MarkSweep::CardScanTask : public MarkStackTask<false> {
public:
CardScanTask(ThreadPool* thread_pool,
MarkSweep* mark_sweep,
@@ -967,7 +950,7 @@
}
}
-class RecursiveMarkTask : public MarkStackTask<false> {
+class MarkSweep::RecursiveMarkTask : public MarkStackTask<false> {
public:
RecursiveMarkTask(ThreadPool* thread_pool,
MarkSweep* mark_sweep,
@@ -1080,7 +1063,7 @@
Runtime::Current()->SweepSystemWeaks(this);
}
-class VerifySystemWeakVisitor : public IsMarkedVisitor {
+class MarkSweep::VerifySystemWeakVisitor : public IsMarkedVisitor {
public:
explicit VerifySystemWeakVisitor(MarkSweep* mark_sweep) : mark_sweep_(mark_sweep) {}
@@ -1109,7 +1092,7 @@
Runtime::Current()->SweepSystemWeaks(&visitor);
}
-class CheckpointMarkThreadRoots : public Closure, public RootVisitor {
+class MarkSweep::CheckpointMarkThreadRoots : public Closure, public RootVisitor {
public:
CheckpointMarkThreadRoots(MarkSweep* mark_sweep,
bool revoke_ros_alloc_thread_local_buffers_at_checkpoint)
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index c19107a..9747031 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -250,8 +250,8 @@
// Verify the roots of the heap and print out information related to any invalid roots.
// Called in MarkObject, so may we may not hold the mutator lock.
- void VerifyRoots()
- NO_THREAD_SAFETY_ANALYSIS;
+ void VerifySuspendedThreadRoots()
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Expand mark stack to 2x its current size.
void ExpandMarkStack()
@@ -353,17 +353,17 @@
std::unique_ptr<MemMap> sweep_array_free_buffer_mem_map_;
private:
- friend class CardScanTask;
- friend class CheckBitmapVisitor;
- friend class CheckReferenceVisitor;
- friend class CheckpointMarkThreadRoots;
- friend class Heap;
- friend class FifoMarkStackChunk;
- friend class MarkObjectVisitor;
- template<bool kUseFinger> friend class MarkStackTask;
- friend class MarkSweepMarkObjectSlowPath;
- friend class VerifyRootMarkedVisitor;
- friend class VerifyRootVisitor;
+ class CardScanTask;
+ class CheckpointMarkThreadRoots;
+ class DelayReferenceReferentVisitor;
+ template<bool kUseFinger> class MarkStackTask;
+ class MarkObjectSlowPath;
+ class RecursiveMarkTask;
+ class ScanObjectParallelVisitor;
+ class ScanObjectVisitor;
+ class VerifyRootMarkedVisitor;
+ class VerifyRootVisitor;
+ class VerifySystemWeakVisitor;
DISALLOW_IMPLICIT_CONSTRUCTORS(MarkSweep);
};
diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h
index e87b5ff..78fb2d2 100644
--- a/runtime/gc/collector/semi_space-inl.h
+++ b/runtime/gc/collector/semi_space-inl.h
@@ -26,21 +26,6 @@
namespace gc {
namespace collector {
-class BitmapSetSlowPathVisitor {
- public:
- explicit BitmapSetSlowPathVisitor(SemiSpace* semi_space) : semi_space_(semi_space) {
- }
-
- void operator()(const mirror::Object* obj) const {
- CHECK(!semi_space_->to_space_->HasAddress(obj)) << "Marking " << obj << " in to_space_";
- // Marking a large object, make sure its aligned as a sanity check.
- CHECK_ALIGNED(obj, kPageSize);
- }
-
- private:
- SemiSpace* const semi_space_;
-};
-
inline mirror::Object* SemiSpace::GetForwardingAddressInFromSpace(mirror::Object* obj) const {
DCHECK(from_space_->HasAddress(obj));
LockWord lock_word = obj->GetLockWord(false);
@@ -76,8 +61,12 @@
obj_ptr->Assign(forward_address);
} else if (!collect_from_space_only_ && !immune_spaces_.IsInImmuneRegion(obj)) {
DCHECK(!to_space_->HasAddress(obj)) << "Tried to mark " << obj << " in to-space";
- BitmapSetSlowPathVisitor visitor(this);
- if (!mark_bitmap_->Set(obj, visitor)) {
+ auto slow_path = [this](const mirror::Object* ref) {
+ CHECK(!to_space_->HasAddress(ref)) << "Marking " << ref << " in to_space_";
+ // Marking a large object, make sure its aligned as a sanity check.
+ CHECK_ALIGNED(ref, kPageSize);
+ };
+ if (!mark_bitmap_->Set(obj, slow_path)) {
// This object was not previously marked.
MarkStackPush(obj);
}
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index f37daa5..7a4c025 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -282,22 +282,11 @@
}
}
-class SemiSpaceScanObjectVisitor {
- public:
- explicit SemiSpaceScanObjectVisitor(SemiSpace* ss) : semi_space_(ss) {}
- void operator()(Object* obj) const REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
- DCHECK(obj != nullptr);
- semi_space_->ScanObject(obj);
- }
- private:
- SemiSpace* const semi_space_;
-};
-
// Used to verify that there's no references to the from-space.
-class SemiSpaceVerifyNoFromSpaceReferencesVisitor {
+class SemiSpace::VerifyNoFromSpaceReferencesVisitor {
public:
- explicit SemiSpaceVerifyNoFromSpaceReferencesVisitor(space::ContinuousMemMapAllocSpace* from_space) :
- from_space_(from_space) {}
+ explicit VerifyNoFromSpaceReferencesVisitor(space::ContinuousMemMapAllocSpace* from_space)
+ : from_space_(from_space) {}
void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const
SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE {
@@ -331,23 +320,10 @@
void SemiSpace::VerifyNoFromSpaceReferences(Object* obj) {
DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space";
- SemiSpaceVerifyNoFromSpaceReferencesVisitor visitor(from_space_);
+ VerifyNoFromSpaceReferencesVisitor visitor(from_space_);
obj->VisitReferences(visitor, VoidFunctor());
}
-class SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor {
- public:
- explicit SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor(SemiSpace* ss) : semi_space_(ss) {}
- void operator()(Object* obj) const
- SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
- DCHECK(obj != nullptr);
- semi_space_->VerifyNoFromSpaceReferences(obj);
- }
-
- private:
- SemiSpace* const semi_space_;
-};
-
void SemiSpace::MarkReachableObjects() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
{
@@ -390,10 +366,12 @@
} else {
TimingLogger::ScopedTiming t2("VisitLiveBits", GetTimings());
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
- SemiSpaceScanObjectVisitor visitor(this);
live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
reinterpret_cast<uintptr_t>(space->End()),
- visitor);
+ [this](mirror::Object* obj)
+ REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ ScanObject(obj);
+ });
}
if (kIsDebugBuild) {
// Verify that there are no from-space references that
@@ -401,10 +379,13 @@
// card table) didn't miss any from-space references in the
// space.
accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
- SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor visitor(this);
live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()),
reinterpret_cast<uintptr_t>(space->End()),
- visitor);
+ [this](Object* obj)
+ SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
+ DCHECK(obj != nullptr);
+ VerifyNoFromSpaceReferences(obj);
+ });
}
}
}
@@ -424,10 +405,12 @@
// classes (primitive array classes) that could move though they
// don't contain any other references.
accounting::LargeObjectBitmap* large_live_bitmap = los->GetLiveBitmap();
- SemiSpaceScanObjectVisitor visitor(this);
large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()),
reinterpret_cast<uintptr_t>(los->End()),
- visitor);
+ [this](mirror::Object* obj)
+ REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ ScanObject(obj);
+ });
}
// Recursively process the mark stack.
ProcessMarkStack();
@@ -697,10 +680,9 @@
heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, this);
}
-class SemiSpaceMarkObjectVisitor {
+class SemiSpace::MarkObjectVisitor {
public:
- explicit SemiSpaceMarkObjectVisitor(SemiSpace* collector) : collector_(collector) {
- }
+ explicit MarkObjectVisitor(SemiSpace* collector) : collector_(collector) {}
void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const ALWAYS_INLINE
REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
@@ -739,7 +721,7 @@
// Visit all of the references of an object and update.
void SemiSpace::ScanObject(Object* obj) {
DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space";
- SemiSpaceMarkObjectVisitor visitor(this);
+ MarkObjectVisitor visitor(this);
obj->VisitReferences(visitor, visitor);
}
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 0199e1a..694e536 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -272,7 +272,9 @@
bool swap_semi_spaces_;
private:
- friend class BitmapSetSlowPathVisitor;
+ class BitmapSetSlowPathVisitor;
+ class MarkObjectVisitor;
+ class VerifyNoFromSpaceReferencesVisitor;
DISALLOW_IMPLICIT_CONSTRUCTORS(SemiSpace);
};
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index cdd5f2e..8da1493 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -442,7 +442,7 @@
}
// Create other spaces based on whether or not we have a moving GC.
if (foreground_collector_type_ == kCollectorTypeCC) {
- region_space_ = space::RegionSpace::Create("Region space", capacity_ * 2, request_begin);
+ region_space_ = space::RegionSpace::Create("main space (region space)", capacity_ * 2, request_begin);
AddSpace(region_space_);
} else if (IsMovingGc(foreground_collector_type_) &&
foreground_collector_type_ != kCollectorTypeGSS) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 78c570f..e896c7a 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1154,6 +1154,80 @@
return true;
}
+static MemMap* LoadImageFile(const char* image_filename,
+ const char* image_location,
+ const ImageHeader& image_header,
+ uint8_t* address,
+ int fd,
+ TimingLogger& logger,
+ std::string* error_msg) {
+ TimingLogger::ScopedTiming timing("MapImageFile", &logger);
+ const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode();
+ if (storage_mode == ImageHeader::kStorageModeUncompressed) {
+ return MemMap::MapFileAtAddress(address,
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ fd,
+ 0,
+ /*low_4gb*/true,
+ /*reuse*/false,
+ image_filename,
+ error_msg);
+ }
+
+ if (storage_mode != ImageHeader::kStorageModeLZ4 &&
+ storage_mode != ImageHeader::kStorageModeLZ4HC) {
+ *error_msg = StringPrintf("Invalid storage mode in image header %d",
+ static_cast<int>(storage_mode));
+ return nullptr;
+ }
+
+ // Reserve output and decompress into it.
+ std::unique_ptr<MemMap> map(MemMap::MapAnonymous(image_location,
+ address,
+ image_header.GetImageSize(),
+ PROT_READ | PROT_WRITE,
+ /*low_4gb*/true,
+ /*reuse*/false,
+ error_msg));
+ if (map != nullptr) {
+ const size_t stored_size = image_header.GetDataSize();
+ const size_t decompress_offset = sizeof(ImageHeader); // Skip the header.
+ std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ /*offset*/0,
+ /*low_4gb*/false,
+ image_filename,
+ error_msg));
+ if (temp_map == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+ memcpy(map->Begin(), &image_header, sizeof(ImageHeader));
+ const uint64_t start = NanoTime();
+ // LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
+ TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
+ const size_t decompressed_size = LZ4_decompress_safe(
+ reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
+ reinterpret_cast<char*>(map->Begin()) + decompress_offset,
+ stored_size,
+ map->Size() - decompress_offset);
+ VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
+ if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) {
+ *error_msg = StringPrintf(
+ "Decompressed size does not match expected image size %zu vs %zu",
+ decompressed_size + sizeof(ImageHeader),
+ image_header.GetImageSize());
+ return nullptr;
+ }
+ }
+
+ return map.release();
+}
+
ImageSpace* ImageSpace::Init(const char* image_filename,
const char* image_location,
bool validate_oat_file,
@@ -1231,91 +1305,30 @@
return nullptr;
}
- // The preferred address to map the image, null specifies any address. If we manage to map the
- // image at the image begin, the amount of fixup work required is minimized.
- std::vector<uint8_t*> addresses(1, image_header->GetImageBegin());
- if (image_header->IsPic()) {
- // Can also map at a random low_4gb address since we can relocate in-place.
- addresses.push_back(nullptr);
- }
-
- // Note: The image header is part of the image due to mmap page alignment required of offset.
std::unique_ptr<MemMap> map;
- std::string temp_error_msg;
- for (uint8_t* address : addresses) {
- TimingLogger::ScopedTiming timing("MapImageFile", &logger);
- // Only care about the error message for the last address in addresses. We want to avoid the
- // overhead of printing the process maps if we can relocate.
- std::string* out_error_msg = (address == addresses.back()) ? &temp_error_msg : nullptr;
- const ImageHeader::StorageMode storage_mode = image_header->GetStorageMode();
- if (storage_mode == ImageHeader::kStorageModeUncompressed) {
- map.reset(MemMap::MapFileAtAddress(address,
- image_header->GetImageSize(),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- file->Fd(),
- 0,
- /*low_4gb*/true,
- /*reuse*/false,
- image_filename,
- /*out*/out_error_msg));
- } else {
- if (storage_mode != ImageHeader::kStorageModeLZ4 &&
- storage_mode != ImageHeader::kStorageModeLZ4HC) {
- *error_msg = StringPrintf("Invalid storage mode in image header %d",
- static_cast<int>(storage_mode));
- return nullptr;
- }
- // Reserve output and decompress into it.
- map.reset(MemMap::MapAnonymous(image_location,
- address,
- image_header->GetImageSize(),
- PROT_READ | PROT_WRITE,
- /*low_4gb*/true,
- /*reuse*/false,
- /*out*/out_error_msg));
- if (map != nullptr) {
- const size_t stored_size = image_header->GetDataSize();
- const size_t decompress_offset = sizeof(ImageHeader); // Skip the header.
- std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
- PROT_READ,
- MAP_PRIVATE,
- file->Fd(),
- /*offset*/0,
- /*low_4gb*/false,
- image_filename,
- out_error_msg));
- if (temp_map == nullptr) {
- DCHECK(!out_error_msg->empty());
- return nullptr;
- }
- memcpy(map->Begin(), image_header, sizeof(ImageHeader));
- const uint64_t start = NanoTime();
- // LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
- TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
- const size_t decompressed_size = LZ4_decompress_safe(
- reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
- reinterpret_cast<char*>(map->Begin()) + decompress_offset,
- stored_size,
- map->Size() - decompress_offset);
- VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
- if (decompressed_size + sizeof(ImageHeader) != image_header->GetImageSize()) {
- *error_msg = StringPrintf(
- "Decompressed size does not match expected image size %zu vs %zu",
- decompressed_size + sizeof(ImageHeader),
- image_header->GetImageSize());
- return nullptr;
- }
- }
- }
- if (map != nullptr) {
- break;
- }
+ // GetImageBegin is the preferred address to map the image. If we manage to map the
+ // image at the image begin, the amount of fixup work required is minimized.
+ map.reset(LoadImageFile(image_filename,
+ image_location,
+ *image_header,
+ image_header->GetImageBegin(),
+ file->Fd(),
+ logger,
+ error_msg));
+ // If the header specifies PIC mode, we can also map at a random low_4gb address since we can
+ // relocate in-place.
+ if (map == nullptr && image_header->IsPic()) {
+ map.reset(LoadImageFile(image_filename,
+ image_location,
+ *image_header,
+ /* address */ nullptr,
+ file->Fd(),
+ logger,
+ error_msg));
}
-
+ // Were we able to load something and continue?
if (map == nullptr) {
- DCHECK(!temp_error_msg.empty());
- *error_msg = temp_error_msg;
+ DCHECK(!error_msg->empty());
return nullptr;
}
DCHECK_EQ(0, memcmp(image_header, map->Begin(), sizeof(ImageHeader)));
diff --git a/runtime/globals.h b/runtime/globals.h
index 477cbdf..0b44c47 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -40,6 +40,12 @@
// compile-time constant so the compiler can generate better code.
static constexpr int kPageSize = 4096;
+// Returns whether the given memory offset can be used for generating
+// an implicit null check.
+static inline bool CanDoImplicitNullCheckOn(uintptr_t offset) {
+ return offset < kPageSize;
+}
+
// Required object alignment
static constexpr size_t kObjectAlignment = 8;
static constexpr size_t kLargeObjectAlignment = kPageSize;
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index d983a9f..c644cde 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -74,6 +74,10 @@
if (self != nullptr) {
self->GetJniEnv()->DeleteWeakGlobalRef(class_loader_);
}
+
+ if (!needs_native_bridge_) {
+ android::CloseNativeLibrary(handle_);
+ }
}
jweak GetClassLoader() const {
@@ -271,8 +275,7 @@
REQUIRES(!Locks::jni_libraries_lock_)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- typedef void (*JNI_OnUnloadFn)(JavaVM*, void*);
- std::vector<JNI_OnUnloadFn> unload_functions;
+ std::vector<SharedLibrary*> unload_libraries;
{
MutexLock mu(soa.Self(), *Locks::jni_libraries_lock_);
for (auto it = libraries_.begin(); it != libraries_.end(); ) {
@@ -283,15 +286,7 @@
// the native libraries of the boot class loader.
if (class_loader != nullptr &&
soa.Self()->IsJWeakCleared(class_loader)) {
- void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
- if (sym == nullptr) {
- VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
- } else {
- VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]";
- JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
- unload_functions.push_back(jni_on_unload);
- }
- delete library;
+ unload_libraries.push_back(library);
it = libraries_.erase(it);
} else {
++it;
@@ -299,9 +294,17 @@
}
}
// Do this without holding the jni libraries lock to prevent possible deadlocks.
- for (JNI_OnUnloadFn fn : unload_functions) {
- VLOG(jni) << "Calling JNI_OnUnload";
- (*fn)(soa.Vm(), nullptr);
+ typedef void (*JNI_OnUnloadFn)(JavaVM*, void*);
+ for (auto library : unload_libraries) {
+ void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
+ if (sym == nullptr) {
+ VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
+ } else {
+ VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling...";
+ JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
+ jni_on_unload(soa.Vm(), nullptr);
+ }
+ delete library;
}
}
@@ -739,8 +742,14 @@
// As the incoming class loader is reachable/alive during the call of this function,
// it's okay to decode it without worrying about unexpectedly marking it alive.
mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(class_loader);
- class_loader_allocator =
- Runtime::Current()->GetClassLinker()->GetAllocatorForClassLoader(loader);
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (class_linker->IsBootClassLoader(soa, loader)) {
+ loader = nullptr;
+ class_loader = nullptr;
+ }
+
+ class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader);
CHECK(class_loader_allocator != nullptr);
}
if (library != nullptr) {
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index ae5a0f6..cfe6cd1 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -59,8 +59,8 @@
options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheMaxCapacity);
jit_options->dump_info_on_shutdown_ =
options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
- jit_options->save_profiling_info_ =
- options.GetOrDefault(RuntimeArgumentMap::JITSaveProfilingInfo);
+ jit_options->profile_saver_options_ =
+ options.GetOrDefault(RuntimeArgumentMap::ProfileSaverOpts);
jit_options->compile_threshold_ = options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
if (jit_options->compile_threshold_ > std::numeric_limits<uint16_t>::max()) {
@@ -144,11 +144,10 @@
cumulative_timings_("JIT timings"),
memory_use_("Memory used for compilation", 16),
lock_("JIT memory use lock"),
- use_jit_compilation_(true),
- save_profiling_info_(false) {}
+ use_jit_compilation_(true) {}
Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
- DCHECK(options->UseJitCompilation() || options->GetSaveProfilingInfo());
+ DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled());
std::unique_ptr<Jit> jit(new Jit);
jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
@@ -163,12 +162,12 @@
return nullptr;
}
jit->use_jit_compilation_ = options->UseJitCompilation();
- jit->save_profiling_info_ = options->GetSaveProfilingInfo();
+ jit->profile_saver_options_ = options->GetProfileSaverOptions();
VLOG(jit) << "JIT created with initial_capacity="
<< PrettySize(options->GetCodeCacheInitialCapacity())
<< ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
<< ", compile_threshold=" << options->GetCompileThreshold()
- << ", save_profiling_info=" << options->GetSaveProfilingInfo();
+ << ", profile_saver_options=" << options->GetProfileSaverOptions();
jit->hot_method_threshold_ = options->GetCompileThreshold();
@@ -310,13 +309,18 @@
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
const std::string& app_dir) {
- if (save_profiling_info_) {
- ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir);
+ if (profile_saver_options_.IsEnabled()) {
+ ProfileSaver::Start(profile_saver_options_,
+ filename,
+ code_cache_.get(),
+ code_paths,
+ foreign_dex_profile_path,
+ app_dir);
}
}
void Jit::StopProfileSaver() {
- if (save_profiling_info_ && ProfileSaver::IsStarted()) {
+ if (profile_saver_options_.IsEnabled() && ProfileSaver::IsStarted()) {
ProfileSaver::Stop(dump_info_on_shutdown_);
}
}
@@ -330,7 +334,7 @@
}
Jit::~Jit() {
- DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted());
+ DCHECK(!profile_saver_options_.IsEnabled() || !ProfileSaver::IsStarted());
if (dump_info_on_shutdown_) {
DumpInfo(LOG(INFO));
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index f3a6240..2aa6f3d 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -24,6 +24,7 @@
#include "base/timing_logger.h"
#include "object_callbacks.h"
#include "offline_profiling_info.h"
+#include "jit/profile_saver_options.h"
#include "thread_pool.h"
namespace art {
@@ -92,8 +93,8 @@
return use_jit_compilation_;
}
- bool SaveProfilingInfo() const {
- return save_profiling_info_;
+ bool GetSaveProfilingInfo() const {
+ return profile_saver_options_.IsEnabled();
}
// Wait until there is no more pending compilation tasks.
@@ -189,7 +190,7 @@
std::unique_ptr<jit::JitCodeCache> code_cache_;
bool use_jit_compilation_;
- bool save_profiling_info_;
+ ProfileSaverOptions profile_saver_options_;
static bool generate_debug_info_;
uint16_t hot_method_threshold_;
uint16_t warm_method_threshold_;
@@ -228,8 +229,11 @@
bool DumpJitInfoOnShutdown() const {
return dump_info_on_shutdown_;
}
+ const ProfileSaverOptions& GetProfileSaverOptions() const {
+ return profile_saver_options_;
+ }
bool GetSaveProfilingInfo() const {
- return save_profiling_info_;
+ return profile_saver_options_.IsEnabled();
}
bool UseJitCompilation() const {
return use_jit_compilation_;
@@ -237,8 +241,8 @@
void SetUseJitCompilation(bool b) {
use_jit_compilation_ = b;
}
- void SetSaveProfilingInfo(bool b) {
- save_profiling_info_ = b;
+ void SetSaveProfilingInfo(bool save_profiling_info) {
+ profile_saver_options_.SetEnabled(save_profiling_info);
}
void SetJitAtFirstUse() {
use_jit_compilation_ = true;
@@ -255,15 +259,14 @@
uint16_t priority_thread_weight_;
size_t invoke_transition_weight_;
bool dump_info_on_shutdown_;
- bool save_profiling_info_;
+ ProfileSaverOptions profile_saver_options_;
JitOptions()
: use_jit_compilation_(false),
code_cache_initial_capacity_(0),
code_cache_max_capacity_(0),
compile_threshold_(0),
- dump_info_on_shutdown_(false),
- save_profiling_info_(false) { }
+ dump_info_on_shutdown_(false) {}
DISALLOW_COPY_AND_ASSIGN(JitOptions);
};
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index c99d363..5039d2d 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -19,6 +19,7 @@
#include "errno.h"
#include <limits.h>
#include <vector>
+#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/uio.h>
@@ -40,6 +41,11 @@
static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
+// Debug flag to ignore checksums when testing if a method or a class is present in the profile.
+// Used to facilitate testing profile guided compilation across a large number of apps
+// using the same test profile.
+static constexpr bool kDebugIgnoreChecksum = false;
+
// Transform the actual dex location into relative paths.
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
@@ -547,10 +553,14 @@
return true;
}
+static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) {
+ return kDebugIgnoreChecksum || dex_file.GetLocationChecksum() == checksum;
+}
+
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
if (info_it != info_.end()) {
- if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) {
+ if (!ChecksumMatch(*method_ref.dex_file, info_it->second.checksum)) {
return false;
}
const std::set<uint16_t>& methods = info_it->second.method_set;
@@ -562,7 +572,7 @@
bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const {
auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
if (info_it != info_.end()) {
- if (dex_file.GetLocationChecksum() != info_it->second.checksum) {
+ if (!ChecksumMatch(dex_file, info_it->second.checksum)) {
return false;
}
const std::set<uint16_t>& classes = info_it->second.class_set;
@@ -659,4 +669,47 @@
}
}
+// Naive implementation to generate a random profile file suitable for testing.
+bool ProfileCompilationInfo::GenerateTestProfile(int fd,
+ uint16_t number_of_dex_files,
+ uint16_t method_ratio,
+ uint16_t class_ratio) {
+ const std::string base_dex_location = "base.apk";
+ ProfileCompilationInfo info;
+ // The limits are defined by the dex specification.
+ uint16_t max_method = std::numeric_limits<uint16_t>::max();
+ uint16_t max_classes = std::numeric_limits<uint16_t>::max();
+ uint16_t number_of_methods = max_method * method_ratio / 100;
+ uint16_t number_of_classes = max_classes * class_ratio / 100;
+
+ srand(MicroTime());
+
+ // Make sure we generate more samples with a low index value.
+ // This makes it more likely to hit valid method/class indices in small apps.
+ const uint16_t kFavorFirstN = 10000;
+ const uint16_t kFavorSplit = 2;
+
+ for (uint16_t i = 0; i < number_of_dex_files; i++) {
+ std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str());
+ std::string profile_key = GetProfileDexFileKey(dex_location);
+
+ for (uint16_t m = 0; m < number_of_methods; m++) {
+ uint16_t method_idx = rand() % max_method;
+ if (m < (number_of_methods / kFavorSplit)) {
+ method_idx %= kFavorFirstN;
+ }
+ info.AddMethodIndex(profile_key, 0, method_idx);
+ }
+
+ for (uint16_t c = 0; c < number_of_classes; c++) {
+ uint16_t class_idx = rand() % max_classes;
+ if (c < (number_of_classes / kFavorSplit)) {
+ class_idx %= kFavorFirstN;
+ }
+ info.AddClassIndex(profile_key, 0, class_idx);
+ }
+ }
+ return info.Save(fd);
+}
+
} // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index 5a07da7..0b26f9b 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -87,6 +87,11 @@
// Clears the resolved classes from the current object.
void ClearResolvedClasses();
+ static bool GenerateTestProfile(int fd,
+ uint16_t number_of_dex_files,
+ uint16_t method_ratio,
+ uint16_t class_ratio);
+
private:
enum ProfileLoadSatus {
kProfileLoadIOError,
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 9822f6e..4d4d1ea 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -30,25 +30,11 @@
namespace art {
-// TODO: read the constants from ProfileOptions,
-// Add a random delay each time we go to sleep so that we don't hammer the CPU
-// with all profile savers running at the same time.
-static constexpr const uint64_t kMinSavePeriodNs = MsToNs(20 * 1000); // 20 seconds
-static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
-// Minimum number of JIT samples during launch to include a method into the profile.
-static constexpr const size_t kStartupMethodSamples = 1;
-
-static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
-static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
-static constexpr const uint32_t kMinimumNumberOfNotificationBeforeWake =
- kMinimumNumberOfMethodsToSave;
-static constexpr const uint32_t kMaximumNumberOfNotificationBeforeWake = 50;
-
-
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
-ProfileSaver::ProfileSaver(const std::string& output_filename,
+ProfileSaver::ProfileSaver(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
@@ -72,7 +58,9 @@
total_number_of_foreign_dex_marks_(0),
max_number_of_profile_entries_cached_(0),
total_number_of_hot_spikes_(0),
- total_number_of_wake_ups_(0) {
+ total_number_of_wake_ups_(0),
+ options_(options) {
+ DCHECK(options_.IsEnabled());
AddTrackedLocations(output_filename, app_data_dir, code_paths);
if (!app_data_dir.empty()) {
// The application directory is used to determine which dex files are owned by app.
@@ -93,14 +81,13 @@
Thread* self = Thread::Current();
// Fetch the resolved classes for the app images after sleeping for
- // kSaveResolvedClassesDelayMs.
+ // options_.GetSaveResolvedClassesDelayMs().
// TODO(calin) This only considers the case of the primary profile file.
// Anything that gets loaded in the same VM will not have their resolved
// classes save (unless they started before the initial saving was done).
{
MutexLock mu(self, wait_lock_);
- constexpr uint64_t kSleepTime = kSaveResolvedClassesDelayMs;
- const uint64_t end_time = NanoTime() + MsToNs(kSleepTime);
+ const uint64_t end_time = NanoTime() + MsToNs(options_.GetSaveResolvedClassesDelayMs());
while (true) {
const uint64_t current_time = NanoTime();
if (current_time >= end_time) {
@@ -108,7 +95,7 @@
}
period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
}
- total_ms_of_sleep_ += kSaveResolvedClassesDelayMs;
+ total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
}
FetchAndCacheResolvedClassesAndMethods();
@@ -130,10 +117,11 @@
// We might have been woken up by a huge number of notifications to guarantee saving.
// If we didn't meet the minimum saving period go back to sleep (only if missed by
// a reasonable margin).
- while (kMinSavePeriodNs * 0.9 > sleep_time) {
+ uint64_t min_save_period_ns = MsToNs(options_.GetMinSavePeriodMs());
+ while (min_save_period_ns * 0.9 > sleep_time) {
{
MutexLock mu(self, wait_lock_);
- period_condition_.TimedWait(self, NsToMs(kMinSavePeriodNs - sleep_time), 0);
+ period_condition_.TimedWait(self, NsToMs(min_save_period_ns - sleep_time), 0);
sleep_time = NanoTime() - sleep_start;
}
// Check if the thread was woken up for shutdown.
@@ -183,12 +171,12 @@
jit_activity_notifications_++;
// Note that we are not as precise as we could be here but we don't want to wake the saver
// every time we see a hot method.
- if (jit_activity_notifications_ > kMinimumNumberOfNotificationBeforeWake) {
+ if (jit_activity_notifications_ > options_.GetMinNotificationBeforeWake()) {
MutexLock wait_mutex(Thread::Current(), wait_lock_);
- if ((NanoTime() - last_time_ns_saver_woke_up_) > kMinSavePeriodNs) {
+ if ((NanoTime() - last_time_ns_saver_woke_up_) > MsToNs(options_.GetMinSavePeriodMs())) {
WakeUpSaver();
}
- } else if (jit_activity_notifications_ > kMaximumNumberOfNotificationBeforeWake) {
+ } else if (jit_activity_notifications_ > options_.GetMaxNotificationBeforeWake()) {
// Make sure to wake up the saver if we see a spike in the number of notifications.
// This is a precaution to avoid "loosing" a big number of methods in case
// this is a spike with no jit after.
@@ -210,7 +198,9 @@
// Excludes native methods and classes in the boot image.
class GetMethodsVisitor : public ClassVisitor {
public:
- explicit GetMethodsVisitor(std::vector<MethodReference>* methods) : methods_(methods) {}
+ GetMethodsVisitor(std::vector<MethodReference>* methods, uint32_t startup_method_samples)
+ : methods_(methods),
+ startup_method_samples_(startup_method_samples) {}
virtual bool operator()(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
@@ -218,7 +208,7 @@
}
for (ArtMethod& method : klass->GetMethods(sizeof(void*))) {
if (!method.IsNative()) {
- if (method.GetCounter() >= kStartupMethodSamples ||
+ if (method.GetCounter() >= startup_method_samples_ ||
method.GetProfilingInfo(sizeof(void*)) != nullptr) {
// Have samples, add to profile.
const DexFile* dex_file = method.GetInterfaceMethodIfProxy(sizeof(void*))->GetDexFile();
@@ -231,6 +221,7 @@
private:
std::vector<MethodReference>* const methods_;
+ uint32_t startup_method_samples_;
};
void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
@@ -242,11 +233,11 @@
std::vector<MethodReference> methods;
{
ScopedTrace trace2("Get hot methods");
- GetMethodsVisitor visitor(&methods);
+ GetMethodsVisitor visitor(&methods, options_.GetStartupMethodSamples());
ScopedObjectAccess soa(Thread::Current());
class_linker->VisitClasses(&visitor);
VLOG(profiler) << "Methods with samples greater than "
- << kStartupMethodSamples << " = " << methods.size();
+ << options_.GetStartupMethodSamples() << " = " << methods.size();
}
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
uint64_t total_number_of_profile_entries_cached = 0;
@@ -315,11 +306,11 @@
cached_info->GetNumberOfResolvedClasses() -
static_cast<int64_t>(last_save_number_of_classes_);
- if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
- delta_number_of_classes < kMinimumNumberOfClassesToSave) {
+ if (delta_number_of_methods < options_.GetMinMethodsToSave() &&
+ delta_number_of_classes < options_.GetMinClassesToSave()) {
VLOG(profiler) << "Not enough information to save to: " << filename
- << " Nr of methods: " << delta_number_of_methods
- << " Nr of classes: " << delta_number_of_classes;
+ << " Number of methods: " << delta_number_of_methods
+ << " Number of classes: " << delta_number_of_classes;
total_number_of_skipped_writes_++;
continue;
}
@@ -398,12 +389,14 @@
return true;
}
-void ProfileSaver::Start(const std::string& output_filename,
+void ProfileSaver::Start(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
const std::string& app_data_dir) {
- DCHECK(Runtime::Current()->SaveProfileInfo());
+ DCHECK(options.IsEnabled());
+ DCHECK(Runtime::Current()->GetJit() != nullptr);
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
@@ -433,7 +426,8 @@
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
<< ". Tracking: " << Join(code_paths_to_profile, ':');
- instance_ = new ProfileSaver(output_filename,
+ instance_ = new ProfileSaver(options,
+ output_filename,
jit_code_cache,
code_paths_to_profile,
foreign_dex_profile_path,
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 9c6d0fa..59e2c94 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -20,6 +20,7 @@
#include "base/mutex.h"
#include "jit_code_cache.h"
#include "offline_profiling_info.h"
+#include "profile_saver_options.h"
#include "safe_map.h"
namespace art {
@@ -28,7 +29,8 @@
public:
// Starts the profile saver thread if not already started.
// If the saver is already running it adds (output_filename, code_paths) to its tracked locations.
- static void Start(const std::string& output_filename,
+ static void Start(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
@@ -61,7 +63,8 @@
uint16_t method_idx);
private:
- ProfileSaver(const std::string& output_filename,
+ ProfileSaver(const ProfileSaverOptions& options,
+ const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
const std::vector<std::string>& code_paths,
const std::string& foreign_dex_profile_path,
@@ -155,6 +158,7 @@
uint64_t total_number_of_hot_spikes_;
uint64_t total_number_of_wake_ups_;
+ const ProfileSaverOptions options_;
DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
};
diff --git a/runtime/jit/profile_saver_options.h b/runtime/jit/profile_saver_options.h
new file mode 100644
index 0000000..a6385d7
--- /dev/null
+++ b/runtime/jit/profile_saver_options.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 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
+ * * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
+#define ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
+
+#include <string>
+#include <ostream>
+
+namespace art {
+
+struct ProfileSaverOptions {
+ public:
+ static constexpr uint32_t kMinSavePeriodMs = 20 * 1000; // 20 seconds
+ static constexpr uint32_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
+ // Minimum number of JIT samples during launch to include a method into the profile.
+ static constexpr uint32_t kStartupMethodSamples = 1;
+ static constexpr uint32_t kMinMethodsToSave = 10;
+ static constexpr uint32_t kMinClassesToSave = 10;
+ static constexpr uint32_t kMinNotificationBeforeWake = 10;
+ static constexpr uint32_t kMaxNotificationBeforeWake = 50;
+
+ ProfileSaverOptions() :
+ enabled_(false),
+ min_save_period_ms_(kMinSavePeriodMs),
+ save_resolved_classes_delay_ms_(kSaveResolvedClassesDelayMs),
+ startup_method_samples_(kStartupMethodSamples),
+ min_methods_to_save_(kMinMethodsToSave),
+ min_classes_to_save_(kMinClassesToSave),
+ min_notification_before_wake_(kMinNotificationBeforeWake),
+ max_notification_before_wake_(kMaxNotificationBeforeWake) {}
+
+ ProfileSaverOptions(
+ bool enabled,
+ uint32_t min_save_period_ms,
+ uint32_t save_resolved_classes_delay_ms,
+ uint32_t startup_method_samples,
+ uint32_t min_methods_to_save,
+ uint32_t min_classes_to_save,
+ uint32_t min_notification_before_wake,
+ uint32_t max_notification_before_wake):
+ enabled_(enabled),
+ min_save_period_ms_(min_save_period_ms),
+ save_resolved_classes_delay_ms_(save_resolved_classes_delay_ms),
+ startup_method_samples_(startup_method_samples),
+ min_methods_to_save_(min_methods_to_save),
+ min_classes_to_save_(min_classes_to_save),
+ min_notification_before_wake_(min_notification_before_wake),
+ max_notification_before_wake_(max_notification_before_wake) {}
+
+ bool IsEnabled() const {
+ return enabled_;
+ }
+ void SetEnabled(bool enabled) {
+ enabled_ = enabled;
+ }
+
+ uint32_t GetMinSavePeriodMs() const {
+ return min_save_period_ms_;
+ }
+ uint32_t GetSaveResolvedClassesDelayMs() const {
+ return save_resolved_classes_delay_ms_;
+ }
+ uint32_t GetStartupMethodSamples() const {
+ return startup_method_samples_;
+ }
+ uint32_t GetMinMethodsToSave() const {
+ return min_methods_to_save_;
+ }
+ uint32_t GetMinClassesToSave() const {
+ return min_classes_to_save_;
+ }
+ uint32_t GetMinNotificationBeforeWake() const {
+ return min_notification_before_wake_;
+ }
+ uint32_t GetMaxNotificationBeforeWake() const {
+ return max_notification_before_wake_;
+ }
+
+ friend std::ostream & operator<<(std::ostream &os, const ProfileSaverOptions& pso) {
+ os << "enabled_" << pso.enabled_
+ << ", min_save_period_ms_" << pso.min_save_period_ms_
+ << ", save_resolved_classes_delay_ms_" << pso.save_resolved_classes_delay_ms_
+ << ", startup_method_samples_" << pso.startup_method_samples_
+ << ", min_methods_to_save_" << pso.min_methods_to_save_
+ << ", min_classes_to_save_" << pso.min_classes_to_save_
+ << ", min_notification_before_wake_" << pso.min_notification_before_wake_
+ << ", max_notification_before_wake_" << pso.max_notification_before_wake_;
+ return os;
+ }
+
+ bool enabled_;
+ uint32_t min_save_period_ms_;
+ uint32_t save_resolved_classes_delay_ms_;
+ uint32_t startup_method_samples_;
+ uint32_t min_methods_to_save_;
+ uint32_t min_classes_to_save_;
+ uint32_t min_notification_before_wake_;
+ uint32_t max_notification_before_wake_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JIT_PROFILE_SAVER_OPTIONS_H_
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 771f8ed..c047ba2 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -157,6 +157,8 @@
}
return false;
}
+
+ ScopedBacktraceMapIteratorLock lock(map.get());
for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
if ((begin >= it->start && begin < it->end) // start of new within old
&& (end > it->start && end <= it->end)) { // end of new within old
@@ -180,6 +182,7 @@
*error_msg = StringPrintf("Failed to build process map");
return false;
}
+ ScopedBacktraceMapIteratorLock(map.get());
for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
if ((begin >= it->start && begin < it->end) // start of new within old
|| (end > it->start && end < it->end) // end of new within old
@@ -339,7 +342,9 @@
if (actual == MAP_FAILED) {
if (error_msg != nullptr) {
- PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ if (kIsDebugBuild || VLOG_IS_ON(oat)) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ }
*error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. "
"See process maps in the log.",
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index fcdfc88..cefd9f0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -532,7 +532,7 @@
inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() {
Class* super_class = GetSuperClass<kVerifyFlags, kReadBarrierOption>();
return (super_class != nullptr)
- ? MemberOffset(RoundUp(super_class->GetObjectSize(),
+ ? MemberOffset(RoundUp(super_class->GetObjectSize<kVerifyFlags, kReadBarrierOption>(),
sizeof(mirror::HeapReference<mirror::Object>)))
: ClassOffset();
}
@@ -778,7 +778,8 @@
}
if (kVisitNativeRoots) {
// Since this class is reachable, we must also visit the associated roots when we scan it.
- VisitNativeRoots(visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ VisitNativeRoots<kReadBarrierOption>(
+ visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
}
}
@@ -917,24 +918,24 @@
return GetFieldPtr<GcRoot<String>*>(DexCacheStringsOffset());
}
-template<class Visitor>
+template<ReadBarrierOption kReadBarrierOption, class Visitor>
void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) {
for (ArtField& field : GetSFieldsUnchecked()) {
// Visit roots first in case the declaring class gets moved.
field.VisitRoots(visitor);
if (kIsDebugBuild && IsResolved()) {
- CHECK_EQ(field.GetDeclaringClass(), this) << GetStatus();
+ CHECK_EQ(field.GetDeclaringClass<kReadBarrierOption>(), this) << GetStatus();
}
}
for (ArtField& field : GetIFieldsUnchecked()) {
// Visit roots first in case the declaring class gets moved.
field.VisitRoots(visitor);
if (kIsDebugBuild && IsResolved()) {
- CHECK_EQ(field.GetDeclaringClass(), this) << GetStatus();
+ CHECK_EQ(field.GetDeclaringClass<kReadBarrierOption>(), this) << GetStatus();
}
}
for (ArtMethod& method : GetMethods(pointer_size)) {
- method.VisitRoots(visitor, pointer_size);
+ method.VisitRoots<kReadBarrierOption>(visitor, pointer_size);
}
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 8c20fa6..5235a3e 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1156,7 +1156,7 @@
// Visit native roots visits roots which are keyed off the native pointers such as ArtFields and
// ArtMethods.
- template<class Visitor>
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor>
void VisitNativeRoots(Visitor& visitor, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 2894b68..0b3461f 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -55,7 +55,6 @@
inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) {
DCHECK_LT(type_idx, NumResolvedTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB.
// TODO default transaction support.
- DCHECK(resolved == nullptr || !resolved->IsErroneous());
GetResolvedTypes()[type_idx] = GcRoot<Class>(resolved);
// TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 6285542..96f2098 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -20,6 +20,7 @@
#include "array.h"
#include "base/bit_utils.h"
#include "class.h"
+#include "common_throws.h"
#include "gc/heap-inl.h"
#include "globals.h"
#include "intern_table.h"
@@ -33,7 +34,7 @@
namespace mirror {
inline uint32_t String::ClassSize(size_t pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 56;
+ uint32_t vtable_entries = Object::kVTableLength + 57;
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
}
@@ -134,9 +135,7 @@
inline uint16_t String::CharAt(int32_t index) {
int32_t count = GetField32(OFFSET_OF_OBJECT_MEMBER(String, count_));
if (UNLIKELY((index < 0) || (index >= count))) {
- Thread* self = Thread::Current();
- self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
- "length=%i; index=%i", count, index);
+ ThrowStringIndexOutOfBoundsException(index, count);
return 0;
}
return GetValue()[index];
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 8c7c966..46be5e6 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -33,7 +33,6 @@
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "os.h"
-#include "profiler.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
diff --git a/runtime/oat.h b/runtime/oat.h
index 57675dc..6243660 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '7', '9', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '8', '2', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 62c723e..61dc287 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1167,12 +1167,15 @@
std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
ScopedTrace trace(__PRETTY_FUNCTION__);
+ static constexpr bool kVerify = false;
+ static constexpr bool kVerifyChecksum = false;
return DexFile::Open(dex_file_pointer_,
FileSize(),
dex_file_location_,
dex_file_location_checksum_,
this,
- false /* verify */,
+ kVerify,
+ kVerifyChecksum,
error_msg);
}
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index bb7b408..d55e373 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -28,7 +28,6 @@
#include "compiler_filter.h"
#include "oat_file.h"
#include "os.h"
-#include "profiler.h"
namespace art {
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index c79a9a6..a1d3ed9 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include "oat_file_assistant.h"
-
#include <algorithm>
#include <fstream>
#include <string>
@@ -29,8 +27,10 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "compiler_callbacks.h"
+#include "dex2oat_environment_test.h"
#include "gc/space/image_space.h"
#include "mem_map.h"
+#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "os.h"
#include "scoped_thread_state_change.h"
@@ -39,184 +39,74 @@
namespace art {
-class OatFileAssistantTest : public CommonRuntimeTest {
+class OatFileAssistantTest : public Dex2oatEnvironmentTest {
public:
- virtual void SetUp() {
+ virtual void SetUp() OVERRIDE {
ReserveImageSpace();
- CommonRuntimeTest::SetUp();
-
- // Create a scratch directory to work from.
- scratch_dir_ = android_data_ + "/OatFileAssistantTest";
- ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
-
- // Create a subdirectory in scratch for odex files.
- odex_oat_dir_ = scratch_dir_ + "/oat";
- ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
-
- odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
- ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
-
-
- // Verify the environment is as we expect
- uint32_t checksum;
- std::string error_msg;
- ASSERT_TRUE(OS::FileExists(GetImageFile().c_str()))
- << "Expected pre-compiled boot image to be at: " << GetImageFile();
- ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
- << "Expected dex file to be at: " << GetDexSrc1();
- ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
- << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
- ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
- << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
- ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
- << "Expected dex file to be at: " << GetDexSrc2();
-
- // GetMultiDexSrc2 should have the same primary dex checksum as
- // GetMultiDexSrc1, but a different secondary dex checksum.
- std::vector<std::unique_ptr<const DexFile>> multi1;
- ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
- GetMultiDexSrc1().c_str(), &error_msg, &multi1)) << error_msg;
- ASSERT_GT(multi1.size(), 1u);
-
- std::vector<std::unique_ptr<const DexFile>> multi2;
- ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
- GetMultiDexSrc2().c_str(), &error_msg, &multi2)) << error_msg;
- ASSERT_GT(multi2.size(), 1u);
-
- ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
- ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+ Dex2oatEnvironmentTest::SetUp();
}
- virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
- // options->push_back(std::make_pair("-verbose:oat", nullptr));
+ // Pre-Relocate the image to a known non-zero offset so we don't have to
+ // deal with the runtime randomly relocating the image by 0 and messing up
+ // the expected results of the tests.
+ bool PreRelocateImage(std::string* error_msg) {
+ std::string image;
+ if (!GetCachedImageFile(&image, error_msg)) {
+ return false;
+ }
- // Set up the image location.
- options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
- nullptr));
- // Make sure compilercallbacks are not set so that relocation will be
- // enabled.
- callbacks_.reset();
+ std::string patchoat = GetAndroidRoot();
+ patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat";
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+ argv.push_back("--input-image-location=" + GetImageLocation());
+ argv.push_back("--output-image-file=" + image);
+ argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
+ argv.push_back("--base-offset-delta=0x00008000");
+ return Exec(argv, error_msg);
}
virtual void PreRuntimeCreate() {
+ std::string error_msg;
+ ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
+
UnreserveImageSpace();
}
- virtual void PostRuntimeCreate() {
+ virtual void PostRuntimeCreate() OVERRIDE {
ReserveImageSpace();
}
- virtual void TearDown() {
- ClearDirectory(odex_dir_.c_str());
- ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
-
- ClearDirectory(odex_oat_dir_.c_str());
- ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
-
- ClearDirectory(scratch_dir_.c_str());
- ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
-
- CommonRuntimeTest::TearDown();
- }
-
- void Copy(std::string src, std::string dst) {
- std::ifstream src_stream(src, std::ios::binary);
- std::ofstream dst_stream(dst, std::ios::binary);
-
- dst_stream << src_stream.rdbuf();
- }
-
- // Returns the directory where the pre-compiled core.art can be found.
- // TODO: We should factor out this into common tests somewhere rather than
- // re-hardcoding it here (This was copied originally from the elf writer
- // test).
- std::string GetImageDirectory() {
- if (IsHost()) {
- const char* host_dir = getenv("ANDROID_HOST_OUT");
- CHECK(host_dir != nullptr);
- return std::string(host_dir) + "/framework";
- } else {
- return std::string("/data/art-test");
- }
- }
-
- std::string GetImageLocation() {
- return GetImageDirectory() + "/core.art";
- }
-
- std::string GetImageFile() {
- return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
- + "/core.art";
- }
-
- std::string GetDexSrc1() {
- return GetTestDexFileName("Main");
- }
-
- // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
- // file stripped.
- std::string GetStrippedDexSrc1() {
- return GetTestDexFileName("MainStripped");
- }
-
- std::string GetMultiDexSrc1() {
- return GetTestDexFileName("MultiDex");
- }
-
- // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
- // with the contents of the secondary dex file changed.
- std::string GetMultiDexSrc2() {
- return GetTestDexFileName("MultiDexModifiedSecondary");
- }
-
- std::string GetDexSrc2() {
- return GetTestDexFileName("Nested");
- }
-
- // Scratch directory, for dex and odex files (oat files will go in the
- // dalvik cache).
- std::string GetScratchDir() {
- return scratch_dir_;
- }
-
- // Odex directory is the subdirectory in the scratch directory where odex
- // files should be located.
- std::string GetOdexDir() {
- return odex_dir_;
- }
-
// Generate a non-PIC odex file for the purposes of test.
// The generated odex file will be un-relocated.
void GenerateOdexForTest(const std::string& dex_location,
const std::string& odex_location,
- CompilerFilter::Filter filter) {
- // To generate an un-relocated odex file, we first compile a relocated
- // version of the file, then manually call patchoat to make it look as if
- // it is unrelocated.
- std::string relocated_odex_location = odex_location + ".relocated";
+ CompilerFilter::Filter filter,
+ bool pic = false,
+ bool with_patch_info = true) {
+ // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+ // relocated image file.
+ std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
+ setenv("ANDROID_DATA", android_data_tmp.c_str(), 1);
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + relocated_odex_location);
+ args.push_back("--oat-file=" + odex_location);
args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+ args.push_back("--runtime-arg");
+ args.push_back("-Xnorelocate");
- // We need to use the quick compiler to generate non-PIC code, because
- // the optimizing compiler always generates PIC.
- args.push_back("--compiler-backend=Quick");
- args.push_back("--include-patch-information");
+ if (pic) {
+ args.push_back("--compile-pic");
+ }
+
+ if (with_patch_info) {
+ args.push_back("--include-patch-information");
+ }
std::string error_msg;
ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- // Use patchoat to unrelocate the relocated odex file.
- Runtime* runtime = Runtime::Current();
- std::vector<std::string> argv;
- argv.push_back(runtime->GetPatchoatExecutable());
- argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
- argv.push_back("--input-oat-file=" + relocated_odex_location);
- argv.push_back("--output-oat-file=" + odex_location);
- argv.push_back("--base-offset-delta=0x00008000");
- std::string command_line(Join(argv, ' '));
- ASSERT_TRUE(Exec(argv, &error_msg)) << error_msg;
+ setenv("ANDROID_DATA", android_data_.c_str(), 1);
// Verify the odex file was generated as expected and really is
// unrelocated.
@@ -229,13 +119,13 @@
dex_location.c_str(),
&error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- EXPECT_FALSE(odex_file->IsPic());
- EXPECT_TRUE(odex_file->HasPatchInfo());
+ EXPECT_EQ(pic, odex_file->IsPic());
+ EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo());
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
const std::vector<gc::space::ImageSpace*> image_spaces =
- runtime->GetHeap()->GetBootImageSpaces();
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr);
const ImageHeader& image_header = image_spaces[0]->GetImageHeader();
const OatHeader& oat_header = odex_file->GetOatHeader();
@@ -250,71 +140,15 @@
void GeneratePicOdexForTest(const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter) {
- // Temporarily redirect the dalvik cache so dex2oat doesn't find the
- // relocated image file.
- std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
- setenv("ANDROID_DATA", android_data_tmp.c_str(), 1);
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + odex_location);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--compile-pic");
- args.push_back("--runtime-arg");
- args.push_back("-Xnorelocate");
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
- setenv("ANDROID_DATA", android_data_.c_str(), 1);
-
- // Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
- odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- dex_location.c_str(),
- &error_msg));
- ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- EXPECT_TRUE(odex_file->IsPic());
- EXPECT_EQ(filter, odex_file->GetCompilerFilter());
+ GenerateOdexForTest(dex_location, odex_location, filter, true, false);
}
// Generate a non-PIC odex file without patch information for the purposes
// of test. The generated odex file will be un-relocated.
- // TODO: This won't work correctly if we depend on the boot image being
- // randomly relocated by a non-zero amount. We should have a better solution
- // for avoiding that flakiness and duplicating code to generate odex and oat
- // files for test.
void GenerateNoPatchOdexForTest(const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter) {
- // Temporarily redirect the dalvik cache so dex2oat doesn't find the
- // relocated image file.
- std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
- setenv("ANDROID_DATA", android_data_tmp.c_str(), 1);
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + odex_location);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--runtime-arg");
- args.push_back("-Xnorelocate");
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
- setenv("ANDROID_DATA", android_data_.c_str(), 1);
-
- // Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
- odex_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- dex_location.c_str(),
- &error_msg));
- ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- EXPECT_FALSE(odex_file->IsPic());
- EXPECT_FALSE(odex_file->HasPatchInfo());
- EXPECT_EQ(filter, odex_file->GetCompilerFilter());
+ GenerateOdexForTest(dex_location, odex_location, filter, false, false);
}
private:
@@ -326,11 +160,10 @@
MemMap::Init();
// Ensure a chunk of memory is reserved for the image space.
- uintptr_t reservation_start = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MIN_DELTA;
- uintptr_t reservation_end = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MAX_DELTA
- // Include the main space that has to come right after the
- // image in case of the GSS collector.
- + 384 * MB;
+ // The reservation_end includes room for the main space that has to come
+ // right after the image in case of the GSS collector.
+ uintptr_t reservation_start = ART_BASE_ADDRESS;
+ uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB;
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
@@ -365,9 +198,6 @@
image_reservation_.clear();
}
- std::string scratch_dir_;
- std::string odex_oat_dir_;
- std::string odex_dir_;
std::vector<std::unique_ptr<MemMap>> image_reservation_;
};
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index d7d2b5c..b7e6040 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -183,11 +183,6 @@
return dex_file_;
}
- void DeleteDexFile() {
- delete dex_file_;
- dex_file_ = nullptr;
- }
-
private:
static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) {
DCHECK(IsUint<16>(index));
@@ -206,36 +201,25 @@
static void AddDexFilesFromOat(const OatFile* oat_file,
bool already_loaded,
- /*out*/std::priority_queue<DexFileAndClassPair>* heap) {
+ /*out*/std::priority_queue<DexFileAndClassPair>* heap,
+ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
std::string error;
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error);
if (dex_file == nullptr) {
LOG(WARNING) << "Could not create dex file from oat file: " << error;
} else if (dex_file->NumClassDefs() > 0U) {
- heap->emplace(dex_file.release(), /*current_class_index*/0U, already_loaded);
+ heap->emplace(dex_file.get(), /*current_class_index*/0U, already_loaded);
+ opened_dex_files->push_back(std::move(dex_file));
}
}
}
static void AddNext(/*inout*/DexFileAndClassPair* original,
- /*inout*/std::priority_queue<DexFileAndClassPair>* heap,
- bool owning_dex_files) {
+ /*inout*/std::priority_queue<DexFileAndClassPair>* heap) {
if (original->DexFileHasMoreClasses()) {
original->Next();
heap->push(std::move(*original));
- } else if (owning_dex_files) {
- original->DeleteDexFile();
- }
-}
-
-static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap,
- bool owning_dex_files) {
- if (owning_dex_files) {
- while (!heap->empty()) {
- delete heap->top().GetDexFile();
- heap->pop();
- }
}
}
@@ -449,7 +433,6 @@
DCHECK(error_msg != nullptr);
std::priority_queue<DexFileAndClassPair> queue;
- bool owning_dex_files = false;
// Try to get dex files from the given class loader. If the class loader is null, or we do
// not support one of the class loaders in the chain, conservatively compare against all
@@ -479,6 +462,9 @@
// against the open oat files. Take the oat_file_manager_lock_ that protects oat_files_ accesses.
ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ // Vector that holds the newly opened dex files live, this is done to prevent leaks.
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+
if (!class_loader_ok) {
// Add dex files from already loaded oat files, but skip boot.
@@ -487,9 +473,6 @@
queue.pop();
}
- // Anything we load now is something we own and must be released later.
- owning_dex_files = true;
-
std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
// The same OatFile can be loaded multiple times at different addresses. In this case, we don't
// need to check both against each other since they would have resolved the same way at compile
@@ -502,7 +485,10 @@
boot_oat_files.end() && location != oat_file->GetLocation() &&
unique_locations.find(location) == unique_locations.end()) {
unique_locations.insert(location);
- AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue);
+ AddDexFilesFromOat(loaded_oat_file.get(),
+ /*already_loaded*/true,
+ &queue,
+ /*out*/&opened_dex_files);
}
}
}
@@ -511,12 +497,13 @@
const std::string
shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
if (AreSharedLibrariesOk(shared_libraries, queue)) {
- FreeDexFilesInHeap(&queue, owning_dex_files);
return false;
}
+ ScopedTrace st("Collision check");
+
// Add dex files from the oat file to check.
- AddDexFilesFromOat(oat_file, /*already_loaded*/false, &queue);
+ AddDexFilesFromOat(oat_file, /*already_loaded*/false, &queue, &opened_dex_files);
// Now drain the queue.
while (!queue.empty()) {
@@ -536,17 +523,16 @@
compare_pop.GetCachedDescriptor(),
compare_pop.GetDexFile()->GetLocation().c_str(),
top.GetDexFile()->GetLocation().c_str());
- FreeDexFilesInHeap(&queue, owning_dex_files);
return true;
}
queue.pop();
- AddNext(&top, &queue, owning_dex_files);
+ AddNext(&top, &queue);
} else {
// Something else. Done here.
break;
}
}
- AddNext(&compare_pop, &queue, owning_dex_files);
+ AddNext(&compare_pop, &queue);
}
return false;
@@ -710,7 +696,9 @@
if (dex_files.empty()) {
if (oat_file_assistant.HasOriginalDexFiles()) {
if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- if (!DexFile::Open(dex_location, dex_location, /*out*/ &error_msg, &dex_files)) {
+ static constexpr bool kVerifyChecksum = true;
+ if (!DexFile::Open(
+ dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) {
LOG(WARNING) << error_msg;
error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
+ " because: " + error_msg);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index eac5b43..595a47b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -176,8 +176,13 @@
.WithType<unsigned int>()
.IntoKey(M::JITInvokeTransitionWeight)
.Define("-Xjitsaveprofilinginfo")
- .WithValue(true)
- .IntoKey(M::JITSaveProfilingInfo)
+ .WithType<ProfileSaverOptions>()
+ .AppendValues()
+ .IntoKey(M::ProfileSaverOpts)
+ .Define("-Xps-_") // profile saver options -Xps-<key>:<value>
+ .WithType<ProfileSaverOptions>()
+ .AppendValues()
+ .IntoKey(M::ProfileSaverOpts) // NOTE: Appends into same key as -Xjitsaveprofilinginfo
.Define("-XX:HspaceCompactForOOMMinIntervalMs=_") // in ms
.WithType<MillisecondsToNanoseconds>() // store as ns
.IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
@@ -244,14 +249,6 @@
{"wallclock", TraceClockSource::kWall},
{"dualclock", TraceClockSource::kDual}})
.IntoKey(M::ProfileClock)
- .Define("-Xenable-profiler")
- .WithType<TestProfilerOptions>()
- .AppendValues()
- .IntoKey(M::ProfilerOpts) // NOTE: Appends into same key as -Xprofile-*
- .Define("-Xprofile-_") // -Xprofile-<key>:<value>
- .WithType<TestProfilerOptions>()
- .AppendValues()
- .IntoKey(M::ProfilerOpts) // NOTE: Appends into same key as -Xenable-profiler
.Define("-Xcompiler:_")
.WithType<std::string>()
.IntoKey(M::Compiler)
@@ -690,17 +687,13 @@
UsageMessage(stream, " -Xmethod-trace\n");
UsageMessage(stream, " -Xmethod-trace-file:filename");
UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n");
- UsageMessage(stream, " -Xenable-profiler\n");
- UsageMessage(stream, " -Xprofile-filename:filename\n");
- UsageMessage(stream, " -Xprofile-period:integervalue\n");
- UsageMessage(stream, " -Xprofile-duration:integervalue\n");
- UsageMessage(stream, " -Xprofile-interval:integervalue\n");
- UsageMessage(stream, " -Xprofile-backoff:doublevalue\n");
- UsageMessage(stream, " -Xprofile-start-immediately\n");
- UsageMessage(stream, " -Xprofile-top-k-threshold:doublevalue\n");
- UsageMessage(stream, " -Xprofile-top-k-change-threshold:doublevalue\n");
- UsageMessage(stream, " -Xprofile-type:{method,stack}\n");
- UsageMessage(stream, " -Xprofile-max-stack-depth:integervalue\n");
+ UsageMessage(stream, " -Xps-min-save-period-ms:integervalue\n");
+ UsageMessage(stream, " -Xps-save-resolved-classes-delay-ms:integervalue\n");
+ UsageMessage(stream, " -Xps-startup-method-samples:integervalue\n");
+ UsageMessage(stream, " -Xps-min-methods-to-save:integervalue\n");
+ UsageMessage(stream, " -Xps-min-classes-to-save:integervalue\n");
+ UsageMessage(stream, " -Xps-min-notification-before-wake:integervalue\n");
+ UsageMessage(stream, " -Xps-max-notification-before-wake:integervalue\n");
UsageMessage(stream, " -Xcompiler:filename\n");
UsageMessage(stream, " -Xcompiler-option dex2oat-option\n");
UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 5974fb6..1f5beb9 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -26,7 +26,7 @@
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "arch/instruction_set.h"
-#include "profiler_options.h"
+#include "jit/profile_saver_options.h"
#include "runtime_options.h"
namespace art {
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
deleted file mode 100644
index 6a77a9e..0000000
--- a/runtime/profiler.cc
+++ /dev/null
@@ -1,920 +0,0 @@
-/*
- * Copyright (C) 2011 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.
- */
-
-#include "profiler.h"
-
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-
-#include <fstream>
-
-#include "art_method-inl.h"
-#include "base/stl_util.h"
-#include "base/time_utils.h"
-#include "base/unix_file/fd_file.h"
-#include "class_linker.h"
-#include "common_throws.h"
-#include "dex_file-inl.h"
-#include "instrumentation.h"
-#include "mirror/class-inl.h"
-#include "mirror/dex_cache.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
-#include "os.h"
-#include "scoped_thread_state_change.h"
-#include "ScopedLocalRef.h"
-#include "thread.h"
-#include "thread_list.h"
-#include "utils.h"
-
-#include "entrypoints/quick/quick_entrypoints.h"
-
-namespace art {
-
-BackgroundMethodSamplingProfiler* BackgroundMethodSamplingProfiler::profiler_ = nullptr;
-pthread_t BackgroundMethodSamplingProfiler::profiler_pthread_ = 0U;
-volatile bool BackgroundMethodSamplingProfiler::shutting_down_ = false;
-
-// TODO: this profiler runs regardless of the state of the machine. Maybe we should use the
-// wakelock or something to modify the run characteristics. This can be done when we
-// have some performance data after it's been used for a while.
-
-// Walk through the method within depth of max_depth_ on the Java stack
-class BoundedStackVisitor : public StackVisitor {
- public:
- BoundedStackVisitor(std::vector<std::pair<ArtMethod*, uint32_t>>* stack,
- Thread* thread,
- uint32_t max_depth)
- SHARED_REQUIRES(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- stack_(stack),
- max_depth_(max_depth),
- depth_(0) {}
-
- bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
- ArtMethod* m = GetMethod();
- if (m->IsRuntimeMethod()) {
- return true;
- }
- uint32_t dex_pc_ = GetDexPc();
- stack_->push_back(std::make_pair(m, dex_pc_));
- ++depth_;
- if (depth_ < max_depth_) {
- return true;
- } else {
- return false;
- }
- }
-
- private:
- std::vector<std::pair<ArtMethod*, uint32_t>>* const stack_;
- const uint32_t max_depth_;
- uint32_t depth_;
-
- DISALLOW_COPY_AND_ASSIGN(BoundedStackVisitor);
-};
-
-// This is called from either a thread list traversal or from a checkpoint. Regardless
-// of which caller, the mutator lock must be held.
-static void GetSample(Thread* thread, void* arg) SHARED_REQUIRES(Locks::mutator_lock_) {
- BackgroundMethodSamplingProfiler* profiler =
- reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
- const ProfilerOptions profile_options = profiler->GetProfilerOptions();
- switch (profile_options.GetProfileType()) {
- case kProfilerMethod: {
- ArtMethod* method = thread->GetCurrentMethod(nullptr);
- if ((false) && method == nullptr) {
- LOG(INFO) << "No current method available";
- std::ostringstream os;
- thread->Dump(os);
- std::string data(os.str());
- LOG(INFO) << data;
- }
- profiler->RecordMethod(method);
- break;
- }
- case kProfilerBoundedStack: {
- std::vector<InstructionLocation> stack;
- uint32_t max_depth = profile_options.GetMaxStackDepth();
- BoundedStackVisitor bounded_stack_visitor(&stack, thread, max_depth);
- bounded_stack_visitor.WalkStack();
- profiler->RecordStack(stack);
- break;
- }
- default:
- LOG(INFO) << "This profile type is not implemented.";
- }
-}
-
-// A closure that is called by the thread checkpoint code.
-class SampleCheckpoint FINAL : public Closure {
- public:
- explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
- profiler_(profiler) {}
-
- void Run(Thread* thread) OVERRIDE {
- Thread* self = Thread::Current();
- if (thread == nullptr) {
- LOG(ERROR) << "Checkpoint with nullptr thread";
- return;
- }
-
- // Grab the mutator lock (shared access).
- ScopedObjectAccess soa(self);
-
- // Grab a sample.
- GetSample(thread, this->profiler_);
-
- // And finally tell the barrier that we're done.
- this->profiler_->GetBarrier().Pass(self);
- }
-
- private:
- BackgroundMethodSamplingProfiler* const profiler_;
-};
-
-bool BackgroundMethodSamplingProfiler::ShuttingDown(Thread* self) {
- MutexLock mu(self, *Locks::profiler_lock_);
- return shutting_down_;
-}
-
-void* BackgroundMethodSamplingProfiler::RunProfilerThread(void* arg) {
- Runtime* runtime = Runtime::Current();
- BackgroundMethodSamplingProfiler* profiler =
- reinterpret_cast<BackgroundMethodSamplingProfiler*>(arg);
-
- // Add a random delay for the first time run so that we don't hammer the CPU
- // with all profiles running at the same time.
- const int kRandomDelayMaxSecs = 30;
- const double kMaxBackoffSecs = 24*60*60; // Max backoff time.
-
- srand(MicroTime() * getpid());
- int startup_delay = rand() % kRandomDelayMaxSecs; // random delay for startup.
-
-
- CHECK(runtime->AttachCurrentThread("Profiler", true, runtime->GetSystemThreadGroup(),
- !runtime->IsAotCompiler()));
-
- Thread* self = Thread::Current();
-
- double backoff = 1.0;
- while (true) {
- if (ShuttingDown(self)) {
- break;
- }
-
- {
- // wait until we need to run another profile
- uint64_t delay_secs = profiler->options_.GetPeriodS() * backoff;
-
- // Add a startup delay to prevent all the profiles running at once.
- delay_secs += startup_delay;
-
- // Immediate startup for benchmarking?
- if (profiler->options_.GetStartImmediately() && startup_delay > 0) {
- delay_secs = 0;
- }
-
- startup_delay = 0;
-
- VLOG(profiler) << "Delaying profile start for " << delay_secs << " secs";
- MutexLock mu(self, profiler->wait_lock_);
- profiler->period_condition_.TimedWait(self, delay_secs * 1000, 0);
- // We were either signaled by Stop or timedout, in either case ignore the timed out result.
-
- // Expand the backoff by its coefficient, but don't go beyond the max.
- backoff = std::min(backoff * profiler->options_.GetBackoffCoefficient(), kMaxBackoffSecs);
- }
-
- if (ShuttingDown(self)) {
- break;
- }
-
-
- uint64_t start_us = MicroTime();
- uint64_t end_us = start_us + profiler->options_.GetDurationS() * UINT64_C(1000000);
- uint64_t now_us = start_us;
-
- VLOG(profiler) << "Starting profiling run now for "
- << PrettyDuration((end_us - start_us) * 1000);
-
- SampleCheckpoint check_point(profiler);
-
- size_t valid_samples = 0;
- while (now_us < end_us) {
- if (ShuttingDown(self)) {
- break;
- }
-
- usleep(profiler->options_.GetIntervalUs()); // Non-interruptible sleep.
-
- ThreadList* thread_list = runtime->GetThreadList();
-
- profiler->profiler_barrier_->Init(self, 0);
- size_t barrier_count = thread_list->RunCheckpointOnRunnableThreads(&check_point);
-
- // All threads are suspended, nothing to do.
- if (barrier_count == 0) {
- now_us = MicroTime();
- continue;
- }
-
- valid_samples += barrier_count;
-
- ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
-
- // Wait for the barrier to be crossed by all runnable threads. This wait
- // is done with a timeout so that we can detect problems with the checkpoint
- // running code. We should never see this.
- const uint32_t kWaitTimeoutMs = 10000;
-
- // Wait for all threads to pass the barrier.
- bool timed_out = profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
-
- // We should never get a timeout. If we do, it suggests a problem with the checkpoint
- // code. Crash the process in this case.
- CHECK(!timed_out);
-
- // Update the current time.
- now_us = MicroTime();
- }
-
- if (valid_samples > 0) {
- // After the profile has been taken, write it out.
- ScopedObjectAccess soa(self); // Acquire the mutator lock.
- uint32_t size = profiler->WriteProfile();
- VLOG(profiler) << "Profile size: " << size;
- }
- }
-
- LOG(INFO) << "Profiler shutdown";
- runtime->DetachCurrentThread();
- return nullptr;
-}
-
-// Write out the profile file if we are generating a profile.
-uint32_t BackgroundMethodSamplingProfiler::WriteProfile() {
- std::string full_name = output_filename_;
- VLOG(profiler) << "Saving profile to " << full_name;
-
- int fd = open(full_name.c_str(), O_RDWR);
- if (fd < 0) {
- // Open failed.
- LOG(ERROR) << "Failed to open profile file " << full_name;
- return 0;
- }
-
- // Lock the file for exclusive access. This will block if another process is using
- // the file.
- int err = flock(fd, LOCK_EX);
- if (err < 0) {
- LOG(ERROR) << "Failed to lock profile file " << full_name;
- return 0;
- }
-
- // Read the previous profile.
- profile_table_.ReadPrevious(fd, options_.GetProfileType());
-
- // Move back to the start of the file.
- lseek(fd, 0, SEEK_SET);
-
- // Format the profile output and write to the file.
- std::ostringstream os;
- uint32_t num_methods = DumpProfile(os);
- std::string data(os.str());
- const char *p = data.c_str();
- size_t length = data.length();
- size_t full_length = length;
- do {
- int n = ::write(fd, p, length);
- p += n;
- length -= n;
- } while (length > 0);
-
- // Truncate the file to the new length.
- if (ftruncate(fd, full_length) == -1) {
- LOG(ERROR) << "Failed to truncate profile file " << full_name;
- }
-
- // Now unlock the file, allowing another process in.
- err = flock(fd, LOCK_UN);
- if (err < 0) {
- LOG(ERROR) << "Failed to unlock profile file " << full_name;
- }
-
- // Done, close the file.
- ::close(fd);
-
- // Clean the profile for the next time.
- CleanProfile();
-
- return num_methods;
-}
-
-bool BackgroundMethodSamplingProfiler::Start(
- const std::string& output_filename, const ProfilerOptions& options) {
- if (!options.IsEnabled()) {
- return false;
- }
-
- CHECK(!output_filename.empty());
-
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, *Locks::profiler_lock_);
- // Don't start two profiler threads.
- if (profiler_ != nullptr) {
- return true;
- }
- }
-
- LOG(INFO) << "Starting profiler using output file: " << output_filename
- << " and options: " << options;
- {
- MutexLock mu(self, *Locks::profiler_lock_);
- profiler_ = new BackgroundMethodSamplingProfiler(output_filename, options);
-
- CHECK_PTHREAD_CALL(pthread_create, (&profiler_pthread_, nullptr, &RunProfilerThread,
- reinterpret_cast<void*>(profiler_)),
- "Profiler thread");
- }
- return true;
-}
-
-
-
-void BackgroundMethodSamplingProfiler::Stop() {
- BackgroundMethodSamplingProfiler* profiler = nullptr;
- pthread_t profiler_pthread = 0U;
- {
- MutexLock trace_mu(Thread::Current(), *Locks::profiler_lock_);
- CHECK(!shutting_down_);
- profiler = profiler_;
- shutting_down_ = true;
- profiler_pthread = profiler_pthread_;
- }
-
- // Now wake up the sampler thread if it sleeping.
- {
- MutexLock profile_mu(Thread::Current(), profiler->wait_lock_);
- profiler->period_condition_.Signal(Thread::Current());
- }
- // Wait for the sample thread to stop.
- CHECK_PTHREAD_CALL(pthread_join, (profiler_pthread, nullptr), "profiler thread shutdown");
-
- {
- MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
- profiler_ = nullptr;
- }
- delete profiler;
-}
-
-
-void BackgroundMethodSamplingProfiler::Shutdown() {
- Stop();
-}
-
-BackgroundMethodSamplingProfiler::BackgroundMethodSamplingProfiler(
- const std::string& output_filename, const ProfilerOptions& options)
- : output_filename_(output_filename),
- options_(options),
- wait_lock_("Profile wait lock"),
- period_condition_("Profile condition", wait_lock_),
- profile_table_(wait_lock_),
- profiler_barrier_(new Barrier(0)) {
- // Populate the filtered_methods set.
- // This is empty right now, but to add a method, do this:
- //
- // filtered_methods_.insert("void java.lang.Object.wait(long, int)");
-}
-
-// Filter out methods the profiler doesn't want to record.
-// We require mutator lock since some statistics will be updated here.
-bool BackgroundMethodSamplingProfiler::ProcessMethod(ArtMethod* method) {
- if (method == nullptr) {
- profile_table_.NullMethod();
- // Don't record a null method.
- return false;
- }
-
- mirror::Class* cls = method->GetDeclaringClass();
- if (cls != nullptr) {
- if (cls->GetClassLoader() == nullptr) {
- // Don't include things in the boot
- profile_table_.BootMethod();
- return false;
- }
- }
-
- bool is_filtered = false;
-
- if (strcmp(method->GetName(), "<clinit>") == 0) {
- // always filter out class init
- is_filtered = true;
- }
-
- // Filter out methods by name if there are any.
- if (!is_filtered && filtered_methods_.size() > 0) {
- std::string method_full_name = PrettyMethod(method);
-
- // Don't include specific filtered methods.
- is_filtered = filtered_methods_.count(method_full_name) != 0;
- }
- return !is_filtered;
-}
-
-// A method has been hit, record its invocation in the method map.
-// The mutator_lock must be held (shared) when this is called.
-void BackgroundMethodSamplingProfiler::RecordMethod(ArtMethod* method) {
- // Add to the profile table unless it is filtered out.
- if (ProcessMethod(method)) {
- profile_table_.Put(method);
- }
-}
-
-// Record the current bounded stack into sampling results.
-void BackgroundMethodSamplingProfiler::RecordStack(const std::vector<InstructionLocation>& stack) {
- if (stack.size() == 0) {
- return;
- }
- // Get the method on top of the stack. We use this method to perform filtering.
- ArtMethod* method = stack.front().first;
- if (ProcessMethod(method)) {
- profile_table_.PutStack(stack);
- }
-}
-
-// Clean out any recordings for the method traces.
-void BackgroundMethodSamplingProfiler::CleanProfile() {
- profile_table_.Clear();
-}
-
-uint32_t BackgroundMethodSamplingProfiler::DumpProfile(std::ostream& os) {
- return profile_table_.Write(os, options_.GetProfileType());
-}
-
-// Profile Table.
-// This holds a mapping of ArtMethod* to a count of how many times a sample
-// hit it at the top of the stack.
-ProfileSampleResults::ProfileSampleResults(Mutex& lock)
- : lock_(lock),
- num_samples_(0U),
- num_null_methods_(0U),
- num_boot_methods_(0U),
- previous_num_samples_(0U),
- previous_num_null_methods_(0U),
- previous_num_boot_methods_(0U) {
- for (int i = 0; i < kHashSize; i++) {
- table[i] = nullptr;
- }
- method_context_table = nullptr;
- stack_trie_root_ = nullptr;
-}
-
-ProfileSampleResults::~ProfileSampleResults() {
- Clear();
-}
-
-// Add a method to the profile table. If it's the first time the method
-// has been seen, add it with count=1, otherwise increment the count.
-void ProfileSampleResults::Put(ArtMethod* method) {
- MutexLock mu(Thread::Current(), lock_);
- uint32_t index = Hash(method);
- if (table[index] == nullptr) {
- table[index] = new Map();
- }
- Map::iterator i = table[index]->find(method);
- if (i == table[index]->end()) {
- (*table[index])[method] = 1;
- } else {
- i->second++;
- }
- num_samples_++;
-}
-
-// Add a bounded stack to the profile table. Only the count of the method on
-// top of the frame will be increased.
-void ProfileSampleResults::PutStack(const std::vector<InstructionLocation>& stack) {
- MutexLock mu(Thread::Current(), lock_);
- ScopedObjectAccess soa(Thread::Current());
- if (stack_trie_root_ == nullptr) {
- // The root of the stack trie is a dummy node so that we don't have to maintain
- // a collection of tries.
- stack_trie_root_ = new StackTrieNode();
- }
-
- StackTrieNode* current = stack_trie_root_;
- if (stack.size() == 0) {
- current->IncreaseCount();
- return;
- }
-
- for (std::vector<InstructionLocation>::const_reverse_iterator iter = stack.rbegin();
- iter != stack.rend(); ++iter) {
- InstructionLocation inst_loc = *iter;
- ArtMethod* method = inst_loc.first;
- if (method == nullptr) {
- // skip null method
- continue;
- }
- uint32_t dex_pc = inst_loc.second;
- uint32_t method_idx = method->GetDexMethodIndex();
- const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
- MethodReference method_ref(dex_file, method_idx);
- StackTrieNode* child = current->FindChild(method_ref, dex_pc);
- if (child != nullptr) {
- current = child;
- } else {
- uint32_t method_size = 0;
- const DexFile::CodeItem* codeitem = method->GetCodeItem();
- if (codeitem != nullptr) {
- method_size = codeitem->insns_size_in_code_units_;
- }
- StackTrieNode* new_node = new StackTrieNode(method_ref, dex_pc, method_size, current);
- current->AppendChild(new_node);
- current = new_node;
- }
- }
-
- if (current != stack_trie_root_ && current->GetCount() == 0) {
- // Insert into method_context table;
- if (method_context_table == nullptr) {
- method_context_table = new MethodContextMap();
- }
- MethodReference method = current->GetMethod();
- MethodContextMap::iterator i = method_context_table->find(method);
- if (i == method_context_table->end()) {
- TrieNodeSet* node_set = new TrieNodeSet();
- node_set->insert(current);
- (*method_context_table)[method] = node_set;
- } else {
- TrieNodeSet* node_set = i->second;
- node_set->insert(current);
- }
- }
- current->IncreaseCount();
- num_samples_++;
-}
-
-// Write the profile table to the output stream. Also merge with the previous profile.
-uint32_t ProfileSampleResults::Write(std::ostream& os, ProfileDataType type) {
- ScopedObjectAccess soa(Thread::Current());
- num_samples_ += previous_num_samples_;
- num_null_methods_ += previous_num_null_methods_;
- num_boot_methods_ += previous_num_boot_methods_;
-
- VLOG(profiler) << "Profile: "
- << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_;
- os << num_samples_ << "/" << num_null_methods_ << "/" << num_boot_methods_ << "\n";
- uint32_t num_methods = 0;
- if (type == kProfilerMethod) {
- for (int i = 0 ; i < kHashSize; i++) {
- Map *map = table[i];
- if (map != nullptr) {
- for (const auto &meth_iter : *map) {
- ArtMethod *method = meth_iter.first;
- std::string method_name = PrettyMethod(method);
-
- const DexFile::CodeItem* codeitem = method->GetCodeItem();
- uint32_t method_size = 0;
- if (codeitem != nullptr) {
- method_size = codeitem->insns_size_in_code_units_;
- }
- uint32_t count = meth_iter.second;
-
- // Merge this profile entry with one from a previous run (if present). Also
- // remove the previous entry.
- PreviousProfile::iterator pi = previous_.find(method_name);
- if (pi != previous_.end()) {
- count += pi->second.count_;
- previous_.erase(pi);
- }
- os << StringPrintf("%s/%u/%u\n", method_name.c_str(), count, method_size);
- ++num_methods;
- }
- }
- }
- } else if (type == kProfilerBoundedStack) {
- if (method_context_table != nullptr) {
- for (const auto &method_iter : *method_context_table) {
- MethodReference method = method_iter.first;
- TrieNodeSet* node_set = method_iter.second;
- std::string method_name = PrettyMethod(method.dex_method_index, *(method.dex_file));
- uint32_t method_size = 0;
- uint32_t total_count = 0;
- PreviousContextMap new_context_map;
- for (const auto &trie_node_i : *node_set) {
- StackTrieNode* node = trie_node_i;
- method_size = node->GetMethodSize();
- uint32_t count = node->GetCount();
- uint32_t dexpc = node->GetDexPC();
- total_count += count;
-
- StackTrieNode* current = node->GetParent();
- // We go backward on the trie to retrieve context and dex_pc until the dummy root.
- // The format of the context is "method_1@pc_1@method_2@pc_2@..."
- std::vector<std::string> context_vector;
- while (current != nullptr && current->GetParent() != nullptr) {
- context_vector.push_back(StringPrintf("%s@%u",
- PrettyMethod(current->GetMethod().dex_method_index, *(current->GetMethod().dex_file)).c_str(),
- current->GetDexPC()));
- current = current->GetParent();
- }
- std::string context_sig = Join(context_vector, '@');
- new_context_map[std::make_pair(dexpc, context_sig)] = count;
- }
-
- PreviousProfile::iterator pi = previous_.find(method_name);
- if (pi != previous_.end()) {
- total_count += pi->second.count_;
- PreviousContextMap* previous_context_map = pi->second.context_map_;
- if (previous_context_map != nullptr) {
- for (const auto &context_i : *previous_context_map) {
- uint32_t count = context_i.second;
- PreviousContextMap::iterator ci = new_context_map.find(context_i.first);
- if (ci == new_context_map.end()) {
- new_context_map[context_i.first] = count;
- } else {
- ci->second += count;
- }
- }
- }
- delete previous_context_map;
- previous_.erase(pi);
- }
- // We write out profile data with dex pc and context information in the following format:
- // "method/total_count/size/[pc_1:count_1:context_1#pc_2:count_2:context_2#...]".
- std::vector<std::string> context_count_vector;
- for (const auto &context_i : new_context_map) {
- context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first,
- context_i.second, context_i.first.second.c_str()));
- }
- os << StringPrintf("%s/%u/%u/[%s]\n", method_name.c_str(), total_count,
- method_size, Join(context_count_vector, '#').c_str());
- ++num_methods;
- }
- }
- }
-
- // Now we write out the remaining previous methods.
- for (const auto &pi : previous_) {
- if (type == kProfilerMethod) {
- os << StringPrintf("%s/%u/%u\n", pi.first.c_str(), pi.second.count_, pi.second.method_size_);
- } else if (type == kProfilerBoundedStack) {
- os << StringPrintf("%s/%u/%u/[", pi.first.c_str(), pi.second.count_, pi.second.method_size_);
- PreviousContextMap* previous_context_map = pi.second.context_map_;
- if (previous_context_map != nullptr) {
- std::vector<std::string> context_count_vector;
- for (const auto &context_i : *previous_context_map) {
- context_count_vector.push_back(StringPrintf("%u:%u:%s", context_i.first.first,
- context_i.second, context_i.first.second.c_str()));
- }
- os << Join(context_count_vector, '#');
- }
- os << "]\n";
- }
- ++num_methods;
- }
- return num_methods;
-}
-
-void ProfileSampleResults::Clear() {
- num_samples_ = 0;
- num_null_methods_ = 0;
- num_boot_methods_ = 0;
- for (int i = 0; i < kHashSize; i++) {
- delete table[i];
- table[i] = nullptr;
- }
- if (stack_trie_root_ != nullptr) {
- stack_trie_root_->DeleteChildren();
- delete stack_trie_root_;
- stack_trie_root_ = nullptr;
- if (method_context_table != nullptr) {
- delete method_context_table;
- method_context_table = nullptr;
- }
- }
- for (auto &pi : previous_) {
- if (pi.second.context_map_ != nullptr) {
- delete pi.second.context_map_;
- pi.second.context_map_ = nullptr;
- }
- }
- previous_.clear();
-}
-
-uint32_t ProfileSampleResults::Hash(ArtMethod* method) {
- return (PointerToLowMemUInt32(method) >> 3) % kHashSize;
-}
-
-// Read a single line into the given string. Returns true if everything OK, false
-// on EOF or error.
-static bool ReadProfileLine(int fd, std::string& line) {
- char buf[4];
- line.clear();
- while (true) {
- int n = read(fd, buf, 1); // TODO: could speed this up but is it worth it?
- if (n != 1) {
- return false;
- }
- if (buf[0] == '\n') {
- break;
- }
- line += buf[0];
- }
- return true;
-}
-
-void ProfileSampleResults::ReadPrevious(int fd, ProfileDataType type) {
- // Reset counters.
- previous_num_samples_ = previous_num_null_methods_ = previous_num_boot_methods_ = 0;
-
- std::string line;
-
- // The first line contains summary information.
- if (!ReadProfileLine(fd, line)) {
- return;
- }
- std::vector<std::string> summary_info;
- Split(line, '/', &summary_info);
- if (summary_info.size() != 3) {
- // Bad summary info. It should be count/nullcount/bootcount
- return;
- }
- previous_num_samples_ = strtoul(summary_info[0].c_str(), nullptr, 10);
- previous_num_null_methods_ = strtoul(summary_info[1].c_str(), nullptr, 10);
- previous_num_boot_methods_ = strtoul(summary_info[2].c_str(), nullptr, 10);
-
- // Now read each line until the end of file. Each line consists of 3 or 4 fields separated by /
- while (true) {
- if (!ReadProfileLine(fd, line)) {
- break;
- }
- std::vector<std::string> info;
- Split(line, '/', &info);
- if (info.size() != 3 && info.size() != 4) {
- // Malformed.
- break;
- }
- std::string methodname = info[0];
- uint32_t total_count = strtoul(info[1].c_str(), nullptr, 10);
- uint32_t size = strtoul(info[2].c_str(), nullptr, 10);
- PreviousContextMap* context_map = nullptr;
- if (type == kProfilerBoundedStack && info.size() == 4) {
- context_map = new PreviousContextMap();
- std::string context_counts_str = info[3].substr(1, info[3].size() - 2);
- std::vector<std::string> context_count_pairs;
- Split(context_counts_str, '#', &context_count_pairs);
- for (uint32_t i = 0; i < context_count_pairs.size(); ++i) {
- std::vector<std::string> context_count;
- Split(context_count_pairs[i], ':', &context_count);
- if (context_count.size() == 2) {
- // Handles the situtation when the profile file doesn't contain context information.
- uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10);
- uint32_t count = strtoul(context_count[1].c_str(), nullptr, 10);
- (*context_map)[std::make_pair(dexpc, "")] = count;
- } else {
- // Handles the situtation when the profile file contains context information.
- uint32_t dexpc = strtoul(context_count[0].c_str(), nullptr, 10);
- uint32_t count = strtoul(context_count[1].c_str(), nullptr, 10);
- std::string context = context_count[2];
- (*context_map)[std::make_pair(dexpc, context)] = count;
- }
- }
- }
- previous_[methodname] = PreviousValue(total_count, size, context_map);
- }
-}
-
-bool ProfileFile::LoadFile(const std::string& fileName) {
- LOG(VERBOSE) << "reading profile file " << fileName;
- struct stat st;
- int err = stat(fileName.c_str(), &st);
- if (err == -1) {
- LOG(VERBOSE) << "not found";
- return false;
- }
- if (st.st_size == 0) {
- return false; // Empty profiles are invalid.
- }
- std::ifstream in(fileName.c_str());
- if (!in) {
- LOG(VERBOSE) << "profile file " << fileName << " exists but can't be opened";
- LOG(VERBOSE) << "file owner: " << st.st_uid << ":" << st.st_gid;
- LOG(VERBOSE) << "me: " << getuid() << ":" << getgid();
- LOG(VERBOSE) << "file permissions: " << std::oct << st.st_mode;
- LOG(VERBOSE) << "errno: " << errno;
- return false;
- }
- // The first line contains summary information.
- std::string line;
- std::getline(in, line);
- if (in.eof()) {
- return false;
- }
- std::vector<std::string> summary_info;
- Split(line, '/', &summary_info);
- if (summary_info.size() != 3) {
- // Bad summary info. It should be total/null/boot.
- return false;
- }
- // This is the number of hits in all profiled methods (without null or boot methods)
- uint32_t total_count = strtoul(summary_info[0].c_str(), nullptr, 10);
-
- // Now read each line until the end of file. Each line consists of 3 fields separated by '/'.
- // Store the info in descending order given by the most used methods.
- typedef std::set<std::pair<int, std::vector<std::string>>> ProfileSet;
- ProfileSet countSet;
- while (!in.eof()) {
- std::getline(in, line);
- if (in.eof()) {
- break;
- }
- std::vector<std::string> info;
- Split(line, '/', &info);
- if (info.size() != 3 && info.size() != 4) {
- // Malformed.
- return false;
- }
- int count = atoi(info[1].c_str());
- countSet.insert(std::make_pair(-count, info));
- }
-
- uint32_t curTotalCount = 0;
- ProfileSet::iterator end = countSet.end();
- const ProfileData* prevData = nullptr;
- for (ProfileSet::iterator it = countSet.begin(); it != end ; it++) {
- const std::string& methodname = it->second[0];
- uint32_t count = -it->first;
- uint32_t size = strtoul(it->second[2].c_str(), nullptr, 10);
- double usedPercent = (count * 100.0) / total_count;
-
- curTotalCount += count;
- // Methods with the same count should be part of the same top K percentage bucket.
- double topKPercentage = (prevData != nullptr) && (prevData->GetCount() == count)
- ? prevData->GetTopKUsedPercentage()
- : 100 * static_cast<double>(curTotalCount) / static_cast<double>(total_count);
-
- // Add it to the profile map.
- ProfileData curData = ProfileData(methodname, count, size, usedPercent, topKPercentage);
- profile_map_[methodname] = curData;
- prevData = &curData;
- }
- return true;
-}
-
-bool ProfileFile::GetProfileData(ProfileFile::ProfileData* data, const std::string& method_name) {
- ProfileMap::iterator i = profile_map_.find(method_name);
- if (i == profile_map_.end()) {
- return false;
- }
- *data = i->second;
- return true;
-}
-
-bool ProfileFile::GetTopKSamples(std::set<std::string>& topKSamples, double topKPercentage) {
- ProfileMap::iterator end = profile_map_.end();
- for (ProfileMap::iterator it = profile_map_.begin(); it != end; it++) {
- if (it->second.GetTopKUsedPercentage() < topKPercentage) {
- topKSamples.insert(it->first);
- }
- }
- return true;
-}
-
-StackTrieNode* StackTrieNode::FindChild(MethodReference method, uint32_t dex_pc) {
- if (children_.size() == 0) {
- return nullptr;
- }
- // Create a dummy node for searching.
- StackTrieNode* node = new StackTrieNode(method, dex_pc, 0, nullptr);
- std::set<StackTrieNode*, StackTrieNodeComparator>::iterator i = children_.find(node);
- delete node;
- return (i == children_.end()) ? nullptr : *i;
-}
-
-void StackTrieNode::DeleteChildren() {
- for (auto &child : children_) {
- if (child != nullptr) {
- child->DeleteChildren();
- delete child;
- }
- }
-}
-
-} // namespace art
diff --git a/runtime/profiler.h b/runtime/profiler.h
deleted file mode 100644
index bd29f71..0000000
--- a/runtime/profiler.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2011 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_PROFILER_H_
-#define ART_RUNTIME_PROFILER_H_
-
-#include <memory>
-#include <ostream>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "barrier.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "globals.h"
-#include "instrumentation.h"
-#include "profiler_options.h"
-#include "os.h"
-#include "safe_map.h"
-#include "method_reference.h"
-
-namespace art {
-
-namespace mirror {
- class Class;
-} // namespace mirror
-class ArtMethod;
-class Thread;
-
-typedef std::pair<ArtMethod*, uint32_t> InstructionLocation;
-
-// This class stores the sampled bounded stacks in a trie structure. A path of the trie represents
-// a particular context with the method on top of the stack being a leaf or an internal node of the
-// trie rather than the root.
-class StackTrieNode {
- public:
- StackTrieNode(MethodReference method, uint32_t dex_pc, uint32_t method_size,
- StackTrieNode* parent) :
- parent_(parent), method_(method), dex_pc_(dex_pc),
- count_(0), method_size_(method_size) {
- }
- StackTrieNode() : parent_(nullptr), method_(nullptr, 0),
- dex_pc_(0), count_(0), method_size_(0) {
- }
- StackTrieNode* GetParent() { return parent_; }
- MethodReference GetMethod() { return method_; }
- uint32_t GetCount() { return count_; }
- uint32_t GetDexPC() { return dex_pc_; }
- uint32_t GetMethodSize() { return method_size_; }
- void AppendChild(StackTrieNode* child) { children_.insert(child); }
- StackTrieNode* FindChild(MethodReference method, uint32_t dex_pc);
- void DeleteChildren();
- void IncreaseCount() { ++count_; }
-
- private:
- // Comparator for stack trie node.
- struct StackTrieNodeComparator {
- bool operator()(StackTrieNode* node1, StackTrieNode* node2) const {
- MethodReference mr1 = node1->GetMethod();
- MethodReference mr2 = node2->GetMethod();
- if (mr1.dex_file == mr2.dex_file) {
- if (mr1.dex_method_index == mr2.dex_method_index) {
- return node1->GetDexPC() < node2->GetDexPC();
- } else {
- return mr1.dex_method_index < mr2.dex_method_index;
- }
- } else {
- return mr1.dex_file < mr2.dex_file;
- }
- }
- };
-
- std::set<StackTrieNode*, StackTrieNodeComparator> children_;
- StackTrieNode* parent_;
- MethodReference method_;
- uint32_t dex_pc_;
- uint32_t count_;
- uint32_t method_size_;
-};
-
-//
-// This class holds all the results for all runs of the profiler. It also
-// counts the number of null methods (where we can't determine the method) and
-// the number of methods in the boot path (where we have already compiled the method).
-//
-// This object is an internal profiler object and uses the same locking as the profiler
-// itself.
-class ProfileSampleResults {
- public:
- explicit ProfileSampleResults(Mutex& lock);
- ~ProfileSampleResults();
-
- void Put(ArtMethod* method) REQUIRES(!lock_);
- void PutStack(const std::vector<InstructionLocation>& stack_dump) REQUIRES(!lock_);
- uint32_t Write(std::ostream &os, ProfileDataType type);
- void ReadPrevious(int fd, ProfileDataType type);
- void Clear();
- uint32_t GetNumSamples() { return num_samples_; }
- void NullMethod() { ++num_null_methods_; }
- void BootMethod() { ++num_boot_methods_; }
-
- private:
- uint32_t Hash(ArtMethod* method);
- static constexpr int kHashSize = 17;
- Mutex& lock_; // Reference to the main profiler lock - we don't need two of them.
- uint32_t num_samples_; // Total number of samples taken.
- uint32_t num_null_methods_; // Number of samples where can don't know the method.
- uint32_t num_boot_methods_; // Number of samples in the boot path.
-
- typedef std::map<ArtMethod*, uint32_t> Map; // Map of method vs its count.
- Map *table[kHashSize];
-
- typedef std::set<StackTrieNode*> TrieNodeSet;
- // Map of method hit by profiler vs the set of stack trie nodes for this method.
- typedef std::map<MethodReference, TrieNodeSet*, MethodReferenceComparator> MethodContextMap;
- MethodContextMap *method_context_table;
- StackTrieNode* stack_trie_root_; // Root of the trie that stores sampled stack information.
-
- // Map from <pc, context> to counts.
- typedef std::map<std::pair<uint32_t, std::string>, uint32_t> PreviousContextMap;
- struct PreviousValue {
- PreviousValue() : count_(0), method_size_(0), context_map_(nullptr) {}
- PreviousValue(uint32_t count, uint32_t method_size, PreviousContextMap* context_map)
- : count_(count), method_size_(method_size), context_map_(context_map) {}
- uint32_t count_;
- uint32_t method_size_;
- PreviousContextMap* context_map_;
- };
-
- typedef std::map<std::string, PreviousValue> PreviousProfile;
- PreviousProfile previous_;
- uint32_t previous_num_samples_;
- uint32_t previous_num_null_methods_; // Number of samples where can don't know the method.
- uint32_t previous_num_boot_methods_; // Number of samples in the boot path.
-};
-
-//
-// The BackgroundMethodSamplingProfiler runs in a thread. Most of the time it is sleeping but
-// occasionally wakes up and counts the number of times a method is called. Each time
-// it ticks, it looks at the current method and records it in the ProfileSampleResults
-// table.
-//
-// The timing is controlled by a number of variables:
-// 1. Period: the time between sampling runs.
-// 2. Interval: the time between each sample in a run.
-// 3. Duration: the duration of a run.
-//
-// So the profiler thread is sleeping for the 'period' time. It wakes up and runs for the
-// 'duration'. The run consists of a series of samples, each of which is 'interval' microseconds
-// apart. At the end of a run, it writes the results table to a file and goes back to sleep.
-
-class BackgroundMethodSamplingProfiler {
- public:
- // Start a profile thread with the user-supplied arguments.
- // Returns true if the profile was started or if it was already running. Returns false otherwise.
- static bool Start(const std::string& output_filename, const ProfilerOptions& options)
- REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_,
- !Locks::profiler_lock_);
-
- // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Stop() REQUIRES(!Locks::profiler_lock_, !wait_lock_, !Locks::profiler_lock_)
- NO_THREAD_SAFETY_ANALYSIS;
- // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Shutdown() REQUIRES(!Locks::profiler_lock_) NO_THREAD_SAFETY_ANALYSIS;
-
- void RecordMethod(ArtMethod *method) SHARED_REQUIRES(Locks::mutator_lock_);
- void RecordStack(const std::vector<InstructionLocation>& stack)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool ProcessMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
- const ProfilerOptions& GetProfilerOptions() const { return options_; }
-
- Barrier& GetBarrier() {
- return *profiler_barrier_;
- }
-
- private:
- explicit BackgroundMethodSamplingProfiler(
- const std::string& output_filename, const ProfilerOptions& options);
-
- // The sampling interval in microseconds is passed as an argument.
- // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void* RunProfilerThread(void* arg) REQUIRES(!Locks::profiler_lock_)
- NO_THREAD_SAFETY_ANALYSIS;
-
- uint32_t WriteProfile() SHARED_REQUIRES(Locks::mutator_lock_);
-
- void CleanProfile();
- uint32_t DumpProfile(std::ostream& os) SHARED_REQUIRES(Locks::mutator_lock_);
- static bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
-
- static BackgroundMethodSamplingProfiler* profiler_ GUARDED_BY(Locks::profiler_lock_);
-
- // We need to shut the sample thread down at exit. Setting this to true will do that.
- static volatile bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
-
- // Sampling thread, non-zero when sampling.
- static pthread_t profiler_pthread_;
-
- // Some measure of the number of samples that are significant.
- static constexpr uint32_t kSignificantSamples = 10;
-
- // The name of the file where profile data will be written.
- std::string output_filename_;
- // The options used to start the profiler.
- const ProfilerOptions& options_;
-
-
- // Profile condition support.
- Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
-
- ProfileSampleResults profile_table_;
-
- std::unique_ptr<Barrier> profiler_barrier_;
-
- // Set of methods to be filtered out. This will probably be rare because
- // most of the methods we want to be filtered reside in the boot path and
- // are automatically filtered.
- typedef std::set<std::string> FilteredMethods;
- FilteredMethods filtered_methods_;
-
- DISALLOW_COPY_AND_ASSIGN(BackgroundMethodSamplingProfiler);
-};
-
-//
-// Contains profile data generated from previous runs of the program and stored
-// in a file. It is used to determine whether to compile a particular method or not.
-class ProfileFile {
- public:
- class ProfileData {
- public:
- ProfileData() : count_(0), method_size_(0), used_percent_(0), top_k_used_percentage_(0) {}
- ProfileData(const std::string& method_name, uint32_t count, uint32_t method_size,
- double used_percent, double top_k_used_percentage) :
- method_name_(method_name), count_(count), method_size_(method_size),
- used_percent_(used_percent), top_k_used_percentage_(top_k_used_percentage) {
- // TODO: currently method_size_ is unused
- UNUSED(method_size_);
- }
-
- double GetUsedPercent() const { return used_percent_; }
- uint32_t GetCount() const { return count_; }
- double GetTopKUsedPercentage() const { return top_k_used_percentage_; }
-
- private:
- std::string method_name_; // Method name.
- uint32_t count_; // Number of times it has been called.
- uint32_t method_size_; // Size of the method on dex instructions.
- double used_percent_; // Percentage of how many times this method was called.
- double top_k_used_percentage_; // The percentage of the group that comprise K% of the total
- // used methods this methods belongs to.
- };
-
- public:
- // Loads profile data from the given file. The new data are merged with any existing data.
- // Returns true if the file was loaded successfully and false otherwise.
- bool LoadFile(const std::string& filename);
-
- // Computes the group that comprise top_k_percentage of the total used methods.
- bool GetTopKSamples(std::set<std::string>& top_k_methods, double top_k_percentage);
-
- // If the given method has an entry in the profile table it updates the data
- // and returns true. Otherwise returns false and leaves the data unchanged.
- bool GetProfileData(ProfileData* data, const std::string& method_name);
-
- private:
- // Profile data is stored in a map, indexed by the full method name.
- typedef std::map<std::string, ProfileData> ProfileMap;
- ProfileMap profile_map_;
-};
-
-} // namespace art
-
-#endif // ART_RUNTIME_PROFILER_H_
diff --git a/runtime/profiler_options.h b/runtime/profiler_options.h
deleted file mode 100644
index 1db2f05..0000000
--- a/runtime/profiler_options.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2014 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_PROFILER_OPTIONS_H_
-#define ART_RUNTIME_PROFILER_OPTIONS_H_
-
-#include <string>
-#include <ostream>
-
-namespace art {
-
-enum ProfileDataType {
- kProfilerMethod, // Method only
- kProfilerBoundedStack, // Methods with Dex PC on top of the stack
-};
-std::ostream& operator<<(std::ostream& os, const ProfileDataType& rhs);
-
-class ProfilerOptions {
- public:
- static constexpr bool kDefaultEnabled = false;
- static constexpr uint32_t kDefaultPeriodS = 10;
- static constexpr uint32_t kDefaultDurationS = 20;
- static constexpr uint32_t kDefaultIntervalUs = 500;
- static constexpr double kDefaultBackoffCoefficient = 2.0;
- static constexpr bool kDefaultStartImmediately = false;
- static constexpr double kDefaultTopKThreshold = 90.0;
- static constexpr double kDefaultChangeInTopKThreshold = 10.0;
- static constexpr ProfileDataType kDefaultProfileData = kProfilerMethod;
- static constexpr uint32_t kDefaultMaxStackDepth = 3;
-
- ProfilerOptions() :
- enabled_(kDefaultEnabled),
- period_s_(kDefaultPeriodS),
- duration_s_(kDefaultDurationS),
- interval_us_(kDefaultIntervalUs),
- backoff_coefficient_(kDefaultBackoffCoefficient),
- start_immediately_(kDefaultStartImmediately),
- top_k_threshold_(kDefaultTopKThreshold),
- top_k_change_threshold_(kDefaultChangeInTopKThreshold),
- profile_type_(kDefaultProfileData),
- max_stack_depth_(kDefaultMaxStackDepth) {}
-
- ProfilerOptions(bool enabled,
- uint32_t period_s,
- uint32_t duration_s,
- uint32_t interval_us,
- double backoff_coefficient,
- bool start_immediately,
- double top_k_threshold,
- double top_k_change_threshold,
- ProfileDataType profile_type,
- uint32_t max_stack_depth):
- enabled_(enabled),
- period_s_(period_s),
- duration_s_(duration_s),
- interval_us_(interval_us),
- backoff_coefficient_(backoff_coefficient),
- start_immediately_(start_immediately),
- top_k_threshold_(top_k_threshold),
- top_k_change_threshold_(top_k_change_threshold),
- profile_type_(profile_type),
- max_stack_depth_(max_stack_depth) {}
-
- bool IsEnabled() const {
- return enabled_;
- }
-
- uint32_t GetPeriodS() const {
- return period_s_;
- }
-
- uint32_t GetDurationS() const {
- return duration_s_;
- }
-
- uint32_t GetIntervalUs() const {
- return interval_us_;
- }
-
- double GetBackoffCoefficient() const {
- return backoff_coefficient_;
- }
-
- bool GetStartImmediately() const {
- return start_immediately_;
- }
-
- double GetTopKThreshold() const {
- return top_k_threshold_;
- }
-
- double GetTopKChangeThreshold() const {
- return top_k_change_threshold_;
- }
-
- ProfileDataType GetProfileType() const {
- return profile_type_;
- }
-
- uint32_t GetMaxStackDepth() const {
- return max_stack_depth_;
- }
-
- private:
- friend std::ostream & operator<<(std::ostream &os, const ProfilerOptions& po) {
- os << "enabled=" << po.enabled_
- << ", period_s=" << po.period_s_
- << ", duration_s=" << po.duration_s_
- << ", interval_us=" << po.interval_us_
- << ", backoff_coefficient=" << po.backoff_coefficient_
- << ", start_immediately=" << po.start_immediately_
- << ", top_k_threshold=" << po.top_k_threshold_
- << ", top_k_change_threshold=" << po.top_k_change_threshold_
- << ", profile_type=" << po.profile_type_
- << ", max_stack_depth=" << po.max_stack_depth_;
- return os;
- }
-
- friend class ParsedOptions;
-
- // Whether or not the applications should be profiled.
- bool enabled_;
- // Generate profile every n seconds.
- uint32_t period_s_;
- // Run profile for n seconds.
- uint32_t duration_s_;
- // Microseconds between samples.
- uint32_t interval_us_;
- // Coefficient to exponential backoff.
- double backoff_coefficient_;
- // Whether the profile should start upon app startup or be delayed by some random offset.
- bool start_immediately_;
- // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
- double top_k_threshold_;
- // How much the top K% samples needs to change in order for the app to be recompiled.
- double top_k_change_threshold_;
- // The type of profile data dumped to the disk.
- ProfileDataType profile_type_;
- // The max depth of the stack collected by the profiler
- uint32_t max_stack_depth_;
-};
-
-} // namespace art
-
-
-#endif // ART_RUNTIME_PROFILER_OPTIONS_H_
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index b7bd99b..42e959c 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -37,11 +37,10 @@
class ReadBarrier {
public:
- // TODO: disable thse flags for production use.
// Enable the to-space invariant checks.
- static constexpr bool kEnableToSpaceInvariantChecks = true;
+ static constexpr bool kEnableToSpaceInvariantChecks = kIsDebugBuild;
// Enable the read barrier checks.
- static constexpr bool kEnableReadBarrierInvariantChecks = true;
+ static constexpr bool kEnableReadBarrierInvariantChecks = kIsDebugBuild;
// It's up to the implementation whether the given field gets updated whereas the return value
// must be an updated reference unless kAlwaysUpdateField is true.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index caf5545..21cd2aa 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -118,7 +118,6 @@
#include "oat_file_manager.h"
#include "os.h"
#include "parsed_options.h"
-#include "profiler.h"
#include "jit/profile_saver.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
@@ -628,17 +627,6 @@
VLOG(startup) << "Runtime::Start exiting";
finished_starting_ = true;
- if (profiler_options_.IsEnabled() && !profile_output_filename_.empty()) {
- // User has asked for a profile using -Xenable-profiler.
- // Create the profile file if it doesn't exist.
- int fd = open(profile_output_filename_.c_str(), O_RDWR|O_CREAT|O_EXCL, 0660);
- if (fd >= 0) {
- close(fd);
- } else if (errno != EEXIST) {
- LOG(WARNING) << "Failed to access the profile file. Profiler disabled.";
- }
- }
-
if (trace_config_.get() != nullptr && trace_config_->trace_file != "") {
ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
Trace::Start(trace_config_->trace_file.c_str(),
@@ -883,12 +871,13 @@
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i].c_str();
const char* dex_location = dex_locations[i].c_str();
+ static constexpr bool kVerifyChecksum = true;
std::string error_msg;
if (!OS::FileExists(dex_filename)) {
LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
continue;
}
- if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) {
+ if (!DexFile::Open(dex_filename, dex_location, kVerifyChecksum, &error_msg, dex_files)) {
LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
}
@@ -1196,26 +1185,6 @@
Trace::TraceOutputMode::kFile;
}
- {
- auto&& profiler_options = runtime_options.ReleaseOrDefault(Opt::ProfilerOpts);
- profile_output_filename_ = profiler_options.output_file_name_;
-
- // TODO: Don't do this, just change ProfilerOptions to include the output file name?
- ProfilerOptions other_options(
- profiler_options.enabled_,
- profiler_options.period_s_,
- profiler_options.duration_s_,
- profiler_options.interval_us_,
- profiler_options.backoff_coefficient_,
- profiler_options.start_immediately_,
- profiler_options.top_k_threshold_,
- profiler_options.top_k_change_threshold_,
- profiler_options.profile_type_,
- profiler_options.max_stack_depth_);
-
- profiler_options_ = other_options;
- }
-
// TODO: move this to just be an Trace::Start argument
Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
@@ -1758,7 +1727,6 @@
return;
}
- profile_output_filename_ = profile_output_filename;
jit_->StartProfileSaver(profile_output_filename,
code_paths,
foreign_dex_profile_path,
@@ -2009,9 +1977,4 @@
return (jit_ != nullptr) && jit_->UseJitCompilation();
}
-// Returns true if profile saving is enabled. GetJit() will be not null in this case.
-bool Runtime::SaveProfileInfo() const {
- return (jit_ != nullptr) && jit_->SaveProfilingInfo();
-}
-
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index b7f377d..afa8e48 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -36,7 +36,6 @@
#include "object_callbacks.h"
#include "offsets.h"
#include "process_state.h"
-#include "profiler_options.h"
#include "quick/quick_method_frame_info.h"
#include "runtime_stats.h"
#include "safe_map.h"
@@ -192,10 +191,6 @@
return image_location_;
}
- const ProfilerOptions& GetProfilerOptions() const {
- return profiler_options_;
- }
-
// Starts a runtime, which may cause threads to be started and code to run.
bool Start() UNLOCK_FUNCTION(Locks::mutator_lock_);
@@ -455,8 +450,6 @@
// Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
bool UseJitCompilation() const;
- // Returns true if profile saving is enabled. GetJit() will be not null in this case.
- bool SaveProfileInfo() const;
void PreZygoteFork();
bool InitZygote();
@@ -782,9 +775,6 @@
const bool is_running_on_memory_tool_;
- std::string profile_output_filename_;
- ProfilerOptions profiler_options_;
-
std::unique_ptr<TraceConfig> trace_config_;
instrumentation::Instrumentation instrumentation_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 635ff51..31206b5 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -75,7 +75,6 @@
RUNTIME_OPTIONS_KEY (unsigned int, JITInvokeTransitionWeight)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity)
-RUNTIME_OPTIONS_KEY (bool, JITSaveProfilingInfo, false)
RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
HSpaceCompactForOOMMinIntervalsMs,\
MsToNs(100 * 1000)) // 100s
@@ -105,7 +104,7 @@
RUNTIME_OPTIONS_KEY (unsigned int, MethodTraceFileSize, 10 * MB)
RUNTIME_OPTIONS_KEY (Unit, MethodTraceStreaming)
RUNTIME_OPTIONS_KEY (TraceClockSource, ProfileClock, kDefaultTraceClockSource) // -Xprofile:
-RUNTIME_OPTIONS_KEY (TestProfilerOptions, ProfilerOpts) // -Xenable-profiler, -Xprofile-*
+RUNTIME_OPTIONS_KEY (ProfileSaverOptions, ProfileSaverOpts) // -Xjitsaveprofilinginfo, -Xps-*
RUNTIME_OPTIONS_KEY (std::string, Compiler)
RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
CompilerOptions) // -Xcompiler-option ...
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
index ab69d4f..5fcb86e 100644
--- a/runtime/runtime_options.h
+++ b/runtime/runtime_options.h
@@ -29,8 +29,8 @@
#include "jit/jit_code_cache.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
-#include "profiler_options.h"
#include "arch/instruction_set.h"
+#include "jit/profile_saver_options.h"
#include "verifier/verify_mode.h"
#include <stdio.h>
#include <stdarg.h>
@@ -41,7 +41,6 @@
class DexFile;
struct XGcOption;
struct BackgroundGcOption;
-struct TestProfilerOptions;
#define DECLARE_KEY(Type, Name) static const Key<Type> Name
diff --git a/runtime/signal_set.h b/runtime/signal_set.h
index c272514..6f88852 100644
--- a/runtime/signal_set.h
+++ b/runtime/signal_set.h
@@ -38,8 +38,8 @@
}
void Block() {
- if (sigprocmask(SIG_BLOCK, &set_, nullptr) == -1) {
- PLOG(FATAL) << "sigprocmask failed";
+ if (pthread_sigmask(SIG_BLOCK, &set_, nullptr) != 0) {
+ PLOG(FATAL) << "pthread_sigmask failed";
}
}
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7c50f97..4647d67 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -19,6 +19,7 @@
#include "base/bit_vector.h"
#include "base/bit_utils.h"
+#include "dex_file.h"
#include "memory_region.h"
#include "leb128.h"
@@ -892,7 +893,11 @@
total_bit_size_ += MinimumBitsToStore(method_index_max);
dex_pc_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
- total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
+ // Note: We're not encoding the dex pc if there is none. That's the case
+ // for an intrinsified native method, such as String.charAt().
+ if (dex_pc_max != DexFile::kDexNoIndex) {
+ total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
+ }
invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
total_bit_size_ += MinimumBitsToStore(invoke_type_max);
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index d98f82a..3fd66a7 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -201,12 +201,22 @@
<< " state=" << old_state_and_flags.as_struct.state;
} else if ((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0) {
// Wait while our suspend count is non-zero.
- MutexLock mu(this, *Locks::thread_suspend_count_lock_);
+
+ // We pass null to the MutexLock as we may be in a situation where the
+ // runtime is shutting down. Guarding ourselves from that situation
+ // requires to take the shutdown lock, which is undesirable here.
+ Thread* thread_to_pass = nullptr;
+ if (kIsDebugBuild && !IsDaemon()) {
+ // We know we can make our debug locking checks on non-daemon threads,
+ // so re-enable them on debug builds.
+ thread_to_pass = this;
+ }
+ MutexLock mu(thread_to_pass, *Locks::thread_suspend_count_lock_);
old_state_and_flags.as_int = tls32_.state_and_flags.as_int;
DCHECK_EQ(old_state_and_flags.as_struct.state, old_state);
while ((old_state_and_flags.as_struct.flags & kSuspendRequest) != 0) {
// Re-check when Thread::resume_cond_ is notified.
- Thread::resume_cond_->Wait(this);
+ Thread::resume_cond_->Wait(thread_to_pass);
old_state_and_flags.as_int = tls32_.state_and_flags.as_int;
DCHECK_EQ(old_state_and_flags.as_struct.state, old_state);
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f1f4a12..b9ee442 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -924,10 +924,22 @@
Runtime* runtime = Runtime::Current();
bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler();
+
+ // Valgrind on arm doesn't give the right values here. Do not install the guard page, and
+ // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting
+ // stack_begin to 0.
+ const bool valgrind_on_arm =
+ (kRuntimeISA == kArm || kRuntimeISA == kArm64) &&
+ kMemoryToolIsValgrind &&
+ RUNNING_ON_MEMORY_TOOL != 0;
+ if (valgrind_on_arm) {
+ tlsPtr_.stack_begin = nullptr;
+ }
+
ResetDefaultStackEnd();
// Install the protected region if we are doing implicit overflow checks.
- if (implicit_stack_check) {
+ if (implicit_stack_check && !valgrind_on_arm) {
// The thread might have protected region at the bottom. We need
// to install our own region so we need to move the limits
// of the stack to make room for it.
@@ -1122,32 +1134,36 @@
}
void Thread::RunCheckpointFunction() {
- Closure *checkpoints[kMaxCheckpoints];
-
- // Grab the suspend_count lock and copy the current set of
- // checkpoints. Then clear the list and the flag. The RequestCheckpoint
- // function will also grab this lock so we prevent a race between setting
- // the kCheckpointRequest flag and clearing it.
- {
- MutexLock mu(this, *Locks::thread_suspend_count_lock_);
- for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
- checkpoints[i] = tlsPtr_.checkpoint_functions[i];
- tlsPtr_.checkpoint_functions[i] = nullptr;
+ bool done = false;
+ do {
+ // Grab the suspend_count lock and copy the checkpoints one by one. When the last checkpoint is
+ // copied, clear the list and the flag. The RequestCheckpoint function will also grab this lock
+ // to prevent a race between setting the kCheckpointRequest flag and clearing it.
+ Closure* checkpoint = nullptr;
+ {
+ MutexLock mu(this, *Locks::thread_suspend_count_lock_);
+ if (tlsPtr_.checkpoint_function != nullptr) {
+ checkpoint = tlsPtr_.checkpoint_function;
+ if (!checkpoint_overflow_.empty()) {
+ // Overflow list not empty, copy the first one out and continue.
+ tlsPtr_.checkpoint_function = checkpoint_overflow_.front();
+ checkpoint_overflow_.pop_front();
+ } else {
+ // No overflow checkpoints, this means that we are on the last pending checkpoint.
+ tlsPtr_.checkpoint_function = nullptr;
+ AtomicClearFlag(kCheckpointRequest);
+ done = true;
+ }
+ } else {
+ LOG(FATAL) << "Checkpoint flag set without pending checkpoint";
+ }
}
- AtomicClearFlag(kCheckpointRequest);
- }
- // Outside the lock, run all the checkpoint functions that
- // we collected.
- bool found_checkpoint = false;
- for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
- if (checkpoints[i] != nullptr) {
- ScopedTrace trace("Run checkpoint function");
- checkpoints[i]->Run(this);
- found_checkpoint = true;
- }
- }
- CHECK(found_checkpoint);
+ // Outside the lock, run the checkpoint functions that we collected.
+ ScopedTrace trace("Run checkpoint function");
+ DCHECK(checkpoint != nullptr);
+ checkpoint->Run(this);
+ } while (!done);
}
bool Thread::RequestCheckpoint(Closure* function) {
@@ -1157,20 +1173,6 @@
return false; // Fail, thread is suspended and so can't run a checkpoint.
}
- uint32_t available_checkpoint = kMaxCheckpoints;
- for (uint32_t i = 0 ; i < kMaxCheckpoints; ++i) {
- if (tlsPtr_.checkpoint_functions[i] == nullptr) {
- available_checkpoint = i;
- break;
- }
- }
- if (available_checkpoint == kMaxCheckpoints) {
- // No checkpoint functions available, we can't run a checkpoint
- return false;
- }
- tlsPtr_.checkpoint_functions[available_checkpoint] = function;
-
- // Checkpoint function installed now install flag bit.
// We must be runnable to request a checkpoint.
DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable);
union StateAndFlags new_state_and_flags;
@@ -1178,11 +1180,13 @@
new_state_and_flags.as_struct.flags |= kCheckpointRequest;
bool success = tls32_.state_and_flags.as_atomic_int.CompareExchangeStrongSequentiallyConsistent(
old_state_and_flags.as_int, new_state_and_flags.as_int);
- if (UNLIKELY(!success)) {
- // The thread changed state before the checkpoint was installed.
- CHECK_EQ(tlsPtr_.checkpoint_functions[available_checkpoint], function);
- tlsPtr_.checkpoint_functions[available_checkpoint] = nullptr;
- } else {
+ if (success) {
+ // Succeeded setting checkpoint flag, now insert the actual checkpoint.
+ if (tlsPtr_.checkpoint_function == nullptr) {
+ tlsPtr_.checkpoint_function = function;
+ } else {
+ checkpoint_overflow_.push_back(function);
+ }
CHECK_EQ(ReadFlag(kCheckpointRequest), true);
TriggerSuspend();
}
@@ -1624,9 +1628,7 @@
std::fill(tlsPtr_.rosalloc_runs,
tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread,
gc::allocator::RosAlloc::GetDedicatedFullRun());
- for (uint32_t i = 0; i < kMaxCheckpoints; ++i) {
- tlsPtr_.checkpoint_functions[i] = nullptr;
- }
+ tlsPtr_.checkpoint_function = nullptr;
for (uint32_t i = 0; i < kMaxSuspendBarriers; ++i) {
tlsPtr_.active_suspend_barriers[i] = nullptr;
}
@@ -1767,9 +1769,8 @@
}
CHECK_NE(GetState(), kRunnable);
CHECK_NE(ReadFlag(kCheckpointRequest), true);
- CHECK(tlsPtr_.checkpoint_functions[0] == nullptr);
- CHECK(tlsPtr_.checkpoint_functions[1] == nullptr);
- CHECK(tlsPtr_.checkpoint_functions[2] == nullptr);
+ CHECK(tlsPtr_.checkpoint_function == nullptr);
+ CHECK_EQ(checkpoint_overflow_.size(), 0u);
CHECK(tlsPtr_.flip_function == nullptr);
CHECK_EQ(tls32_.suspended_at_suspend_check, false);
diff --git a/runtime/thread.h b/runtime/thread.h
index 3c367ee..ab24625 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1060,6 +1060,8 @@
return tlsPtr_.mterp_alt_ibase;
}
+ // Notify that a signal is being handled. This is to protect us from doing recursive
+ // NPE handling after a SIGSEGV.
void NoteSignalBeingHandled() {
if (tls32_.handling_signal_) {
LOG(FATAL) << "Detected signal while processing a signal";
@@ -1220,9 +1222,6 @@
static void ThreadExitCallback(void* arg);
- // Maximum number of checkpoint functions.
- static constexpr uint32_t kMaxCheckpoints = 3;
-
// Maximum number of suspend barriers.
static constexpr uint32_t kMaxSuspendBarriers = 3;
@@ -1352,8 +1351,8 @@
instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0),
- last_no_thread_suspension_cause(nullptr), thread_local_objects(0),
- thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr),
+ last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
+ thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr),
thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr),
@@ -1452,9 +1451,9 @@
// If no_thread_suspension_ is > 0, what is causing that assertion.
const char* last_no_thread_suspension_cause;
- // Pending checkpoint function or null if non-pending. Installation guarding by
- // Locks::thread_suspend_count_lock_.
- Closure* checkpoint_functions[kMaxCheckpoints];
+ // Pending checkpoint function or null if non-pending. If this checkpoint is set and someone\
+ // requests another checkpoint, it goes to the checkpoint overflow list.
+ Closure* checkpoint_function GUARDED_BY(Locks::thread_suspend_count_lock_);
// Pending barriers that require passing or NULL if non-pending. Installation guarding by
// Locks::thread_suspend_count_lock_.
@@ -1468,12 +1467,12 @@
QuickEntryPoints quick_entrypoints;
// Thread-local allocation pointer.
- size_t thread_local_objects;
uint8_t* thread_local_start;
// thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for
// potentially better performance.
uint8_t* thread_local_pos;
uint8_t* thread_local_end;
+ size_t thread_local_objects;
// Mterp jump table bases.
void* mterp_current_ibase;
@@ -1517,6 +1516,9 @@
// Debug disable read barrier count, only is checked for debug builds and only in the runtime.
uint8_t debug_disallow_read_barrier_ = 0;
+ // Pending extra checkpoints if checkpoint_function_ is already used.
+ std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);
+
friend class Dbg; // For SetStateUnsafe.
friend class gc::collector::SemiSpace; // For getting stack traces.
friend class Runtime; // For CreatePeer.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index da21479..97bcb7d 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -234,7 +234,12 @@
os << "DALVIK THREADS (" << list_.size() << "):\n";
}
DumpCheckpoint checkpoint(&os, dump_native_stack);
- size_t threads_running_checkpoint = RunCheckpoint(&checkpoint);
+ size_t threads_running_checkpoint;
+ {
+ // Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time.
+ ScopedObjectAccess soa(Thread::Current());
+ threads_running_checkpoint = RunCheckpoint(&checkpoint);
+ }
if (threads_running_checkpoint != 0) {
checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
}
@@ -1141,9 +1146,10 @@
void ThreadList::SuspendAllDaemonThreadsForShutdown() {
ScopedTrace trace(__PRETTY_FUNCTION__);
Thread* self = Thread::Current();
- MutexLock mu(self, *Locks::thread_list_lock_);
size_t daemons_left = 0;
- { // Tell all the daemons it's time to suspend.
+ {
+ // Tell all the daemons it's time to suspend.
+ MutexLock mu(self, *Locks::thread_list_lock_);
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
for (const auto& thread : list_) {
// This is only run after all non-daemon threads have exited, so the remainder should all be
@@ -1172,13 +1178,16 @@
static constexpr size_t kSleepMicroseconds = 1000;
for (size_t i = 0; i < kTimeoutMicroseconds / kSleepMicroseconds; ++i) {
bool all_suspended = true;
- for (const auto& thread : list_) {
- if (thread != self && thread->GetState() == kRunnable) {
- if (!have_complained) {
- LOG(WARNING) << "daemon thread not yet suspended: " << *thread;
- have_complained = true;
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ for (const auto& thread : list_) {
+ if (thread != self && thread->GetState() == kRunnable) {
+ if (!have_complained) {
+ LOG(WARNING) << "daemon thread not yet suspended: " << *thread;
+ have_complained = true;
+ }
+ all_suspended = false;
}
- all_suspended = false;
}
}
if (all_suspended) {
@@ -1295,6 +1304,39 @@
}
}
+void ThreadList::VisitRootsForSuspendedThreads(RootVisitor* visitor) {
+ Thread* const self = Thread::Current();
+ std::vector<Thread*> threads_to_visit;
+
+ // Tell threads to suspend and copy them into list.
+ {
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ for (Thread* thread : list_) {
+ thread->ModifySuspendCount(self, +1, nullptr, false);
+ if (thread == self || thread->IsSuspended()) {
+ threads_to_visit.push_back(thread);
+ } else {
+ thread->ModifySuspendCount(self, -1, nullptr, false);
+ }
+ }
+ }
+
+ // Visit roots without holding thread_list_lock_ and thread_suspend_count_lock_ to prevent lock
+ // order violations.
+ for (Thread* thread : threads_to_visit) {
+ thread->VisitRoots(visitor);
+ }
+
+ // Restore suspend counts.
+ {
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ for (Thread* thread : threads_to_visit) {
+ thread->ModifySuspendCount(self, -1, nullptr, false);
+ }
+ }
+}
+
void ThreadList::VisitRoots(RootVisitor* visitor) const {
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
for (const auto& thread : list_) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index df81ad1..49f65e1 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -144,6 +144,10 @@
void VisitRoots(RootVisitor* visitor) const
SHARED_REQUIRES(Locks::mutator_lock_);
+ void VisitRootsForSuspendedThreads(RootVisitor* visitor)
+ REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Return a copy of the thread list.
std::list<Thread*> GetList() REQUIRES(Locks::thread_list_lock_) {
return list_;
diff --git a/test/004-ThreadStress/check b/test/004-ThreadStress/check
index ffbb8cf..77e4cdb 100755
--- a/test/004-ThreadStress/check
+++ b/test/004-ThreadStress/check
@@ -14,5 +14,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Only compare the last line.
-tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file
+# Do not compare numbers, so replace numbers with 'N'.
+sed '-es/[0-9][0-9]*/N/g' "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file
diff --git a/test/004-ThreadStress/expected.txt b/test/004-ThreadStress/expected.txt
index a26fb4f..772faf6 100644
--- a/test/004-ThreadStress/expected.txt
+++ b/test/004-ThreadStress/expected.txt
@@ -1 +1,11 @@
+JNI_OnLoad called
+Starting worker for N
+Starting worker for N
+Starting worker for N
+Starting worker for N
+Starting worker for N
+Finishing worker
+Finishing worker
+Finishing worker
+Finishing worker
Finishing worker
diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java
index b9a46de..5cae398 100644
--- a/test/004-ThreadStress/src/Main.java
+++ b/test/004-ThreadStress/src/Main.java
@@ -93,9 +93,7 @@
killTemp = osClass.getDeclaredMethod("kill", int.class, int.class);
} catch (Exception e) {
- if (!e.getClass().getName().equals("ErrnoException")) {
- e.printStackTrace(System.out);
- }
+ Main.printThrowable(e);
}
pid = pidTemp;
@@ -107,9 +105,10 @@
public boolean perform() {
try {
kill.invoke(null, pid, sigquit);
+ } catch (OutOfMemoryError e) {
} catch (Exception e) {
- if (!e.getClass().getName().equals("ErrnoException")) {
- e.printStackTrace(System.out);
+ if (!e.getClass().getName().equals(Main.errnoExceptionName)) {
+ Main.printThrowable(e);
}
}
return true;
@@ -154,7 +153,10 @@
private final static class StackTrace extends Operation {
@Override
public boolean perform() {
- Thread.currentThread().getStackTrace();
+ try {
+ Thread.currentThread().getStackTrace();
+ } catch (OutOfMemoryError e) {
+ }
return true;
}
}
@@ -264,6 +266,7 @@
}
public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
parseAndRun(args);
}
@@ -395,12 +398,21 @@
System.out.println(frequencyMap);
}
- runTest(numberOfThreads, numberOfDaemons, operationsPerThread, lock, frequencyMap);
+ try {
+ runTest(numberOfThreads, numberOfDaemons, operationsPerThread, lock, frequencyMap);
+ } catch (Throwable t) {
+ // In this case, the output should not contain all the required
+ // "Finishing worker" lines.
+ Main.printThrowable(t);
+ }
}
public static void runTest(final int numberOfThreads, final int numberOfDaemons,
final int operationsPerThread, final Object lock,
Map<Operation, Double> frequencyMap) throws Exception {
+ final Thread mainThread = Thread.currentThread();
+ final Barrier startBarrier = new Barrier(numberOfThreads + numberOfDaemons + 1);
+
// Each normal thread is going to do operationsPerThread
// operations. Each daemon thread will loop over all
// the operations and will not stop.
@@ -434,8 +446,9 @@
}
// Randomize the operation order
Collections.shuffle(Arrays.asList(operations));
- threadStresses[t] = t < numberOfThreads ? new Main(lock, t, operations) :
- new Daemon(lock, t, operations);
+ threadStresses[t] = (t < numberOfThreads)
+ ? new Main(lock, t, operations)
+ : new Daemon(lock, t, operations, mainThread, startBarrier);
}
// Enable to dump operation counts per thread to make sure its
@@ -470,32 +483,41 @@
runners[r] = new Thread("Runner thread " + r) {
final Main threadStress = ts;
public void run() {
- int id = threadStress.id;
- System.out.println("Starting worker for " + id);
- while (threadStress.nextOperation < operationsPerThread) {
- try {
- Thread thread = new Thread(ts, "Worker thread " + id);
- thread.start();
+ try {
+ int id = threadStress.id;
+ // No memory hungry task are running yet, so println() should succeed.
+ System.out.println("Starting worker for " + id);
+ // Wait until all runners and daemons reach the starting point.
+ startBarrier.await();
+ // Run the stress tasks.
+ while (threadStress.nextOperation < operationsPerThread) {
try {
+ Thread thread = new Thread(ts, "Worker thread " + id);
+ thread.start();
thread.join();
- } catch (InterruptedException e) {
- }
- System.out.println("Thread exited for " + id + " with "
- + (operationsPerThread - threadStress.nextOperation)
- + " operations remaining.");
- } catch (OutOfMemoryError e) {
- // Ignore OOME since we need to print "Finishing worker" for the test
- // to pass.
+ if (DEBUG) {
+ System.out.println(
+ "Thread exited for " + id + " with " +
+ (operationsPerThread - threadStress.nextOperation) +
+ " operations remaining.");
+ }
+ } catch (OutOfMemoryError e) {
+ // Ignore OOME since we need to print "Finishing worker"
+ // for the test to pass. This OOM can come from creating
+ // the Thread or from the DEBUG output.
+ // Note that the Thread creation may fail repeatedly,
+ // preventing the runner from making any progress,
+ // especially if the number of daemons is too high.
+ }
}
- }
- // Keep trying to print "Finishing worker" until it succeeds.
- while (true) {
- try {
- System.out.println("Finishing worker");
- break;
- } catch (OutOfMemoryError e) {
- }
+ // Print "Finishing worker" through JNI to avoid OOME.
+ Main.printString(Main.finishingWorkerMessage);
+ } catch (Throwable t) {
+ Main.printThrowable(t);
+ // Interrupt the main thread, so that it can orderly shut down
+ // instead of waiting indefinitely for some Barrier.
+ mainThread.interrupt();
}
}
};
@@ -528,6 +550,9 @@
for (int r = 0; r < runners.length; r++) {
runners[r].start();
}
+ // Wait for all threads to reach the starting point.
+ startBarrier.await();
+ // Wait for runners to finish.
for (int r = 0; r < runners.length; r++) {
runners[r].join();
}
@@ -570,8 +595,14 @@
}
private static class Daemon extends Main {
- private Daemon(Object lock, int id, Operation[] operations) {
+ private Daemon(Object lock,
+ int id,
+ Operation[] operations,
+ Thread mainThread,
+ Barrier startBarrier) {
super(lock, id, operations);
+ this.mainThread = mainThread;
+ this.startBarrier = startBarrier;
}
public void run() {
@@ -579,26 +610,74 @@
if (DEBUG) {
System.out.println("Starting ThreadStress Daemon " + id);
}
- int i = 0;
- while (true) {
- Operation operation = operations[i];
- if (DEBUG) {
- System.out.println("ThreadStress Daemon " + id
- + " operation " + i
- + " is " + operation);
+ startBarrier.await();
+ try {
+ int i = 0;
+ while (true) {
+ Operation operation = operations[i];
+ if (DEBUG) {
+ System.out.println("ThreadStress Daemon " + id
+ + " operation " + i
+ + " is " + operation);
+ }
+ operation.perform();
+ i = (i + 1) % operations.length;
}
- operation.perform();
- i = (i + 1) % operations.length;
+ } catch (OutOfMemoryError e) {
+ // Catch OutOfMemoryErrors since these can cause the test to fail it they print
+ // the stack trace after "Finishing worker". Note that operations should catch
+ // their own OOME, this guards only agains OOME in the DEBUG output.
}
- } catch (OutOfMemoryError e) {
- // Catch OutOfMemoryErrors since these can cause the test to fail it they print
- // the stack trace after "Finishing worker".
- } finally {
if (DEBUG) {
System.out.println("Finishing ThreadStress Daemon for " + id);
}
+ } catch (Throwable t) {
+ Main.printThrowable(t);
+ // Interrupt the main thread, so that it can orderly shut down
+ // instead of waiting indefinitely for some Barrier.
+ mainThread.interrupt();
}
}
+
+ final Thread mainThread;
+ final Barrier startBarrier;
}
+ // Note: java.util.concurrent.CyclicBarrier.await() allocates memory and may throw OOM.
+ // That is highly undesirable in this test, so we use our own simple barrier class.
+ // The only memory allocation that can happen here is the lock inflation which uses
+ // a native allocation. As such, it should succeed even if the Java heap is full.
+ // If the native allocation surprisingly fails, the program shall abort().
+ private static class Barrier {
+ public Barrier(int initialCount) {
+ count = initialCount;
+ }
+
+ public synchronized void await() throws InterruptedException {
+ --count;
+ if (count != 0) {
+ do {
+ wait();
+ } while (count != 0); // Check for spurious wakeup.
+ } else {
+ notifyAll();
+ }
+ }
+
+ private int count;
+ }
+
+ // Printing a String/Throwable through JNI requires only native memory and space
+ // in the local reference table, so it should succeed even if the Java heap is full.
+ private static native void printString(String s);
+ private static native void printThrowable(Throwable t);
+
+ static final String finishingWorkerMessage;
+ static final String errnoExceptionName;
+ static {
+ // We pre-allocate the strings in class initializer to avoid const-string
+ // instructions in code using these strings later as they may throw OOME.
+ finishingWorkerMessage = "Finishing worker\n";
+ errnoExceptionName = "ErrnoException";
+ }
}
diff --git a/test/004-ThreadStress/thread_stress.cc b/test/004-ThreadStress/thread_stress.cc
new file mode 100644
index 0000000..573c352
--- /dev/null
+++ b/test/004-ThreadStress/thread_stress.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <iostream>
+
+#include "jni.h"
+#include "mirror/string.h"
+#include "mirror/throwable.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_printString(JNIEnv*, jclass, jstring s) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::cout << soa.Decode<mirror::String*>(s)->ToModifiedUtf8();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_printThrowable(JNIEnv*, jclass, jthrowable t) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::cout << soa.Decode<mirror::Throwable*>(t)->Dump();
+}
+
+} // namespace art
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index b2f905e..9d4618a 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -39,16 +39,24 @@
}
}
- private static Unsafe getUnsafe() throws Exception {
+ private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
Class<?> unsafeClass = Unsafe.class;
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
System.loadLibrary(args[0]);
Unsafe unsafe = getUnsafe();
+
+ testArrayBaseOffset(unsafe);
+ testArrayIndexScale(unsafe);
+ testGetAndPutAndCAS(unsafe);
+ testGetAndPutVolatile(unsafe);
+ }
+
+ private static void testArrayBaseOffset(Unsafe unsafe) {
check(unsafe.arrayBaseOffset(boolean[].class), vmArrayBaseOffset(boolean[].class),
"Unsafe.arrayBaseOffset(boolean[])");
check(unsafe.arrayBaseOffset(byte[].class), vmArrayBaseOffset(byte[].class),
@@ -65,7 +73,9 @@
"Unsafe.arrayBaseOffset(long[])");
check(unsafe.arrayBaseOffset(Object[].class), vmArrayBaseOffset(Object[].class),
"Unsafe.arrayBaseOffset(Object[])");
+ }
+ private static void testArrayIndexScale(Unsafe unsafe) {
check(unsafe.arrayIndexScale(boolean[].class), vmArrayIndexScale(boolean[].class),
"Unsafe.arrayIndexScale(boolean[])");
check(unsafe.arrayIndexScale(byte[].class), vmArrayIndexScale(byte[].class),
@@ -82,7 +92,9 @@
"Unsafe.arrayIndexScale(long[])");
check(unsafe.arrayIndexScale(Object[].class), vmArrayIndexScale(Object[].class),
"Unsafe.arrayIndexScale(Object[])");
+ }
+ private static void testGetAndPutAndCAS(Unsafe unsafe) throws NoSuchFieldException {
TestClass t = new TestClass();
int intValue = 12345678;
@@ -185,12 +197,58 @@
}
}
+ private static void testGetAndPutVolatile(Unsafe unsafe) throws NoSuchFieldException {
+ TestVolatileClass tv = new TestVolatileClass();
+
+ int intValue = 12345678;
+ Field volatileIntField = TestVolatileClass.class.getDeclaredField("volatileIntVar");
+ long volatileIntOffset = unsafe.objectFieldOffset(volatileIntField);
+ check(unsafe.getIntVolatile(tv, volatileIntOffset),
+ 0,
+ "Unsafe.getIntVolatile(Object, long) - initial");
+ unsafe.putIntVolatile(tv, volatileIntOffset, intValue);
+ check(tv.volatileIntVar, intValue, "Unsafe.putIntVolatile(Object, long, int)");
+ check(unsafe.getIntVolatile(tv, volatileIntOffset),
+ intValue,
+ "Unsafe.getIntVolatile(Object, long)");
+
+ long longValue = 1234567887654321L;
+ Field volatileLongField = TestVolatileClass.class.getDeclaredField("volatileLongVar");
+ long volatileLongOffset = unsafe.objectFieldOffset(volatileLongField);
+ check(unsafe.getLongVolatile(tv, volatileLongOffset),
+ 0,
+ "Unsafe.getLongVolatile(Object, long) - initial");
+ unsafe.putLongVolatile(tv, volatileLongOffset, longValue);
+ check(tv.volatileLongVar, longValue, "Unsafe.putLongVolatile(Object, long, long)");
+ check(unsafe.getLongVolatile(tv, volatileLongOffset),
+ longValue,
+ "Unsafe.getLongVolatile(Object, long)");
+
+ Object objectValue = new Object();
+ Field volatileObjectField = TestVolatileClass.class.getDeclaredField("volatileObjectVar");
+ long volatileObjectOffset = unsafe.objectFieldOffset(volatileObjectField);
+ check(unsafe.getObjectVolatile(tv, volatileObjectOffset),
+ null,
+ "Unsafe.getObjectVolatile(Object, long) - initial");
+ unsafe.putObjectVolatile(tv, volatileObjectOffset, objectValue);
+ check(tv.volatileObjectVar, objectValue, "Unsafe.putObjectVolatile(Object, long, Object)");
+ check(unsafe.getObjectVolatile(tv, volatileObjectOffset),
+ objectValue,
+ "Unsafe.getObjectVolatile(Object, long)");
+ }
+
private static class TestClass {
public int intVar = 0;
public long longVar = 0;
public Object objectVar = null;
}
+ private static class TestVolatileClass {
+ public volatile int volatileIntVar = 0;
+ public volatile long volatileLongVar = 0;
+ public volatile Object volatileObjectVar = null;
+ }
+
private static native int vmArrayBaseOffset(Class clazz);
private static native int vmArrayIndexScale(Class clazz);
}
diff --git a/test/033-class-init-deadlock/expected.txt b/test/033-class-init-deadlock/expected.txt
index 182d0da..9e843a0 100644
--- a/test/033-class-init-deadlock/expected.txt
+++ b/test/033-class-init-deadlock/expected.txt
@@ -1,6 +1,4 @@
Deadlock test starting.
-A initializing...
-B initializing...
Deadlock test interrupting threads.
Deadlock test main thread bailing.
A initialized: false
diff --git a/test/033-class-init-deadlock/src/Main.java b/test/033-class-init-deadlock/src/Main.java
index 3233230..bd4d4ab 100644
--- a/test/033-class-init-deadlock/src/Main.java
+++ b/test/033-class-init-deadlock/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import java.util.concurrent.CyclicBarrier;
+
/**
* This causes most VMs to lock up.
*
@@ -23,6 +25,8 @@
public static boolean aInitialized = false;
public static boolean bInitialized = false;
+ public static CyclicBarrier barrier = new CyclicBarrier(3);
+
static public void main(String[] args) {
Thread thread1, thread2;
@@ -30,10 +34,10 @@
thread1 = new Thread() { public void run() { new A(); } };
thread2 = new Thread() { public void run() { new B(); } };
thread1.start();
- // Give thread1 a chance to start before starting thread2.
- try { Thread.sleep(1000); } catch (InterruptedException ie) { }
thread2.start();
+ // Not expecting any exceptions, so print them out if we get them.
+ try { barrier.await(); } catch (Exception e) { System.out.println(e); }
try { Thread.sleep(6000); } catch (InterruptedException ie) { }
System.out.println("Deadlock test interrupting threads.");
@@ -48,8 +52,8 @@
class A {
static {
- System.out.println("A initializing...");
- try { Thread.sleep(3000); } catch (InterruptedException ie) { }
+ // Not expecting any exceptions, so print them out if we get them.
+ try { Main.barrier.await(); } catch (Exception e) { System.out.println(e); }
new B();
System.out.println("A initialized");
Main.aInitialized = true;
@@ -58,8 +62,8 @@
class B {
static {
- System.out.println("B initializing...");
- try { Thread.sleep(3000); } catch (InterruptedException ie) { }
+ // Not expecting any exceptions, so print them out if we get them.
+ try { Main.barrier.await(); } catch (Exception e) { System.out.println(e); }
new A();
System.out.println("B initialized");
Main.bInitialized = true;
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index d878e69..dd89d64 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -33,7 +33,7 @@
14 (class java.lang.Short)
[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int)]
+[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
index c9110a9..b729301 100644
--- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
+++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc
@@ -27,8 +27,20 @@
namespace {
static volatile std::atomic<bool> vm_was_shutdown(false);
+static const int kThreadCount = 4;
+
+static std::atomic<int> barrier_count(kThreadCount + 1);
+
+static void JniThreadBarrierWait() {
+ barrier_count--;
+ while (barrier_count.load() != 0) {
+ usleep(1000);
+ }
+}
extern "C" JNIEXPORT void JNICALL Java_Main_waitAndCallIntoJniEnv(JNIEnv* env, jclass) {
+ // Wait for all threads to enter JNI together.
+ JniThreadBarrierWait();
// Wait until the runtime is shutdown.
while (!vm_was_shutdown.load()) {
usleep(1000);
@@ -40,6 +52,8 @@
// NO_RETURN does not work with extern "C" for target builds.
extern "C" JNIEXPORT void JNICALL Java_Main_destroyJavaVMAndExit(JNIEnv* env, jclass) {
+ // Wait for all threads to enter JNI together.
+ JniThreadBarrierWait();
// Fake up the managed stack so we can detach.
Thread* const self = Thread::Current();
self->SetTopOfStack(nullptr);
diff --git a/test/149-suspend-all-stress/check b/test/149-suspend-all-stress/check
new file mode 100755
index 0000000..d30b888
--- /dev/null
+++ b/test/149-suspend-all-stress/check
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 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.
+
+# Only compare the last line.
+tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file
diff --git a/test/149-suspend-all-stress/expected.txt b/test/149-suspend-all-stress/expected.txt
index f993efc..134d8d0 100644
--- a/test/149-suspend-all-stress/expected.txt
+++ b/test/149-suspend-all-stress/expected.txt
@@ -1,2 +1 @@
-JNI_OnLoad called
Finishing
diff --git a/test/149-suspend-all-stress/suspend_all.cc b/test/149-suspend-all-stress/suspend_all.cc
index c22ddad..dfd944a 100644
--- a/test/149-suspend-all-stress/suspend_all.cc
+++ b/test/149-suspend-all-stress/suspend_all.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/time_utils.h"
#include "jni.h"
#include "runtime.h"
#include "thread_list.h"
@@ -21,12 +22,42 @@
namespace art {
extern "C" JNIEXPORT void JNICALL Java_Main_suspendAndResume(JNIEnv*, jclass) {
- usleep(100 * 1000); // Leave some time for threads to get in here before we start suspending.
- for (size_t i = 0; i < 500; ++i) {
- Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__);
- usleep(500);
- Runtime::Current()->GetThreadList()->ResumeAll();
+ static constexpr size_t kInitialSleepUS = 100 * 1000; // 100ms.
+ usleep(kInitialSleepUS); // Leave some time for threads to get in here before we start suspending.
+ enum Operation {
+ kOPSuspendAll,
+ kOPDumpStack,
+ kOPSuspendAllDumpStack,
+ // Total number of operations.
+ kOPNumber,
+ };
+ const uint64_t start_time = NanoTime();
+ size_t iterations = 0;
+ // Run for a fixed period of 10 seconds.
+ while (NanoTime() - start_time < MsToNs(10 * 1000)) {
+ switch (static_cast<Operation>(iterations % kOPNumber)) {
+ case kOPSuspendAll: {
+ ScopedSuspendAll ssa(__FUNCTION__);
+ usleep(500);
+ break;
+ }
+ case kOPDumpStack: {
+ Runtime::Current()->GetThreadList()->Dump(LOG(INFO));
+ usleep(500);
+ break;
+ }
+ case kOPSuspendAllDumpStack: {
+ // Not yet supported.
+ // ScopedSuspendAll ssa(__FUNCTION__);
+ // Runtime::Current()->GetThreadList()->Dump(LOG(INFO));
+ break;
+ }
+ case kOPNumber:
+ break;
+ }
+ ++iterations;
}
+ LOG(INFO) << "Did " << iterations << " iterations";
}
} // namespace art
diff --git a/test/150-loadlibrary/expected.txt b/test/150-loadlibrary/expected.txt
new file mode 100644
index 0000000..41feacf
--- /dev/null
+++ b/test/150-loadlibrary/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Success.
diff --git a/test/150-loadlibrary/info.txt b/test/150-loadlibrary/info.txt
new file mode 100644
index 0000000..089d044
--- /dev/null
+++ b/test/150-loadlibrary/info.txt
@@ -0,0 +1 @@
+Check that passing the BootClassLoader to loadLibrary works.
diff --git a/test/150-loadlibrary/src/Main.java b/test/150-loadlibrary/src/Main.java
new file mode 100644
index 0000000..9086937
--- /dev/null
+++ b/test/150-loadlibrary/src/Main.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // Check whether we get the BootClassLoader (not null).
+ ClassLoader bootClassLoader = Object.class.getClassLoader();
+ if (bootClassLoader == null) {
+ throw new IllegalStateException("Expected non-null classloader for Object");
+ }
+
+ // Try to load libarttest(d) with the BootClassLoader. First construct the filename.
+ String libName = System.mapLibraryName(args[0]);
+ Method libPathsMethod = Runtime.class.getDeclaredMethod("getLibPaths");
+ libPathsMethod.setAccessible(true);
+ String[] libPaths = (String[])libPathsMethod.invoke(Runtime.getRuntime());
+ String fileName = null;
+ for (String p : libPaths) {
+ String candidate = p + libName;
+ if (new File(candidate).exists()) {
+ fileName = candidate;
+ break;
+ }
+ }
+ if (fileName == null) {
+ throw new IllegalStateException("Didn't find " + libName + " in " +
+ Arrays.toString(libPaths));
+ }
+
+ // Then call an internal function that accepts the classloader. Do not use load(), as it
+ // is deprecated and only there for backwards compatibility, and prints a warning to the
+ // log that we'd have to strip (it contains the pid).
+ Method m = Runtime.class.getDeclaredMethod("doLoad", String.class, ClassLoader.class);
+ m.setAccessible(true);
+ Object result = m.invoke(Runtime.getRuntime(), fileName, bootClassLoader);
+ if (result != null) {
+ throw new IllegalStateException(result.toString());
+ }
+
+ System.out.println("Success.");
+ }
+}
diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java
index 52d4259..f0bb6dd 100644
--- a/test/201-built-in-exception-detail-messages/src/Main.java
+++ b/test/201-built-in-exception-detail-messages/src/Main.java
@@ -461,7 +461,7 @@
"hello there".substring(9,14);
fail();
} catch (StringIndexOutOfBoundsException ex) {
- assertEquals("length=11; regionStart=9; regionLength=5", ex.getMessage());
+ assertEquals("length=11; index=14", ex.getMessage());
}
}
}
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 08b6cec..36f14d8 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -30,6 +30,11 @@
public void $noinline$f() {
throw new RuntimeException();
}
+
+ public int $inline$h(boolean cond) {
+ Super obj = (cond ? this : null);
+ return obj.hashCode();
+ }
}
class SubclassA extends Super {
@@ -620,6 +625,46 @@
o.mainField = 0;
}
+ /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (before)
+ /// CHECK-DAG: <<Arg:l\d+>> NewInstance
+ /// CHECK-DAG: InvokeVirtual [<<Arg>>,{{z\d+}}] method_name:Super.$inline$h
+
+ /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (after)
+ /// CHECK-DAG: <<Arg:l\d+>> NewInstance
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: <<Phi:l\d+>> Phi [<<Arg>>,<<Null>>] klass:SubclassA
+ /// CHECK-DAG: <<NCPhi:l\d+>> NullCheck [<<Phi>>]
+ /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:Super.hashCode
+
+ public void testThisArgumentMoreSpecific(boolean cond) {
+ // Inlining method from Super will build it with `this` typed as Super.
+ // Running RTP will sharpen it to SubclassA.
+ SubclassA obj = new SubclassA();
+ ((Super) obj).$inline$h(cond);
+ }
+
+ public static int $inline$hashCode(Super obj) {
+ return obj.hashCode();
+ }
+
+ /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (before)
+ /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Arg>>{{(,[ij]\d+)?}}] method_name:Main.$inline$hashCode
+
+ /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (after)
+ /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA
+ /// CHECK-DAG: <<NCArg:l\d+>> NullCheck [<<Arg>>] klass:SubclassA
+ /// CHECK-DAG: InvokeVirtual [<<NCArg>>] method_name:Super.hashCode
+
+ public void testExplicitArgumentMoreSpecific(SubclassA obj) {
+ // Inlining a method will build it with reference types from its signature,
+ // here the callee graph is built with Super as the type of its only argument.
+ // Running RTP after its ParameterValue instructions are replaced with actual
+ // arguments will type the inner graph more precisely.
+ $inline$hashCode(obj);
+ }
+
/// CHECK-START: void Main.testPhiHasOnlyNullInputs(boolean) inliner (before)
/// CHECK: <<Int:i\d+>> IntConstant 0
/// CHECK: <<Phi:l\d+>> Phi klass:Main exact:false
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 53c2e0b..ffce49d 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -18,6 +18,8 @@
public class Main {
+ static boolean doThrow = false;
+
public static void assertBooleanEquals(boolean expected, boolean result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
@@ -58,41 +60,43 @@
* Tiny programs exercising optimizations of arithmetic identities.
*/
- /// CHECK-START: long Main.Add0(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Add0(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
/// CHECK-DAG: <<Add:j\d+>> Add [<<Const0>>,<<Arg>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: long Main.Add0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Add0(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.Add0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Add0(long) instruction_simplifier (after)
/// CHECK-NOT: Add
- public static long Add0(long arg) {
+ public static long $noinline$Add0(long arg) {
+ if (doThrow) { throw new Error(); }
return 0 + arg;
}
- /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$AndAllOnes(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
/// CHECK-DAG: <<And:i\d+>> And [<<Arg>>,<<ConstF>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$AndAllOnes(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$AndAllOnes(int) instruction_simplifier (after)
/// CHECK-NOT: And
- public static int AndAllOnes(int arg) {
+ public static int $noinline$AndAllOnes(int arg) {
+ if (doThrow) { throw new Error(); }
return arg & -1;
}
- /// CHECK-START: int Main.UShr28And15(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$UShr28And15(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const28:i\d+>> IntConstant 28
/// CHECK-DAG: <<Const15:i\d+>> IntConstant 15
@@ -100,20 +104,21 @@
/// CHECK-DAG: <<And:i\d+>> And [<<UShr>>,<<Const15>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: int Main.UShr28And15(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$UShr28And15(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const28:i\d+>> IntConstant 28
/// CHECK-DAG: <<UShr:i\d+>> UShr [<<Arg>>,<<Const28>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: int Main.UShr28And15(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$UShr28And15(int) instruction_simplifier (after)
/// CHECK-NOT: And
- public static int UShr28And15(int arg) {
+ public static int $noinline$UShr28And15(int arg) {
+ if (doThrow) { throw new Error(); }
return (arg >>> 28) & 15;
}
- /// CHECK-START: long Main.UShr60And15(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$UShr60And15(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const60:i\d+>> IntConstant 60
/// CHECK-DAG: <<Const15:j\d+>> LongConstant 15
@@ -121,20 +126,21 @@
/// CHECK-DAG: <<And:j\d+>> And [<<UShr>>,<<Const15>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: long Main.UShr60And15(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$UShr60And15(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const60:i\d+>> IntConstant 60
/// CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const60>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: long Main.UShr60And15(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$UShr60And15(long) instruction_simplifier (after)
/// CHECK-NOT: And
- public static long UShr60And15(long arg) {
+ public static long $noinline$UShr60And15(long arg) {
+ if (doThrow) { throw new Error(); }
return (arg >>> 60) & 15;
}
- /// CHECK-START: int Main.UShr28And7(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$UShr28And7(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const28:i\d+>> IntConstant 28
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
@@ -142,7 +148,7 @@
/// CHECK-DAG: <<And:i\d+>> And [<<UShr>>,<<Const7>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: int Main.UShr28And7(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$UShr28And7(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const28:i\d+>> IntConstant 28
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
@@ -150,11 +156,12 @@
/// CHECK-DAG: <<And:i\d+>> And [<<UShr>>,<<Const7>>]
/// CHECK-DAG: Return [<<And>>]
- public static int UShr28And7(int arg) {
+ public static int $noinline$UShr28And7(int arg) {
+ if (doThrow) { throw new Error(); }
return (arg >>> 28) & 7;
}
- /// CHECK-START: long Main.UShr60And7(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$UShr60And7(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const60:i\d+>> IntConstant 60
/// CHECK-DAG: <<Const7:j\d+>> LongConstant 7
@@ -162,7 +169,7 @@
/// CHECK-DAG: <<And:j\d+>> And [<<UShr>>,<<Const7>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: long Main.UShr60And7(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$UShr60And7(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const60:i\d+>> IntConstant 60
/// CHECK-DAG: <<Const7:j\d+>> LongConstant 7
@@ -170,11 +177,12 @@
/// CHECK-DAG: <<And:j\d+>> And [<<UShr>>,<<Const7>>]
/// CHECK-DAG: Return [<<And>>]
- public static long UShr60And7(long arg) {
+ public static long $noinline$UShr60And7(long arg) {
+ if (doThrow) { throw new Error(); }
return (arg >>> 60) & 7;
}
- /// CHECK-START: int Main.Shr24And255(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$Shr24And255(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const24:i\d+>> IntConstant 24
/// CHECK-DAG: <<Const255:i\d+>> IntConstant 255
@@ -182,21 +190,22 @@
/// CHECK-DAG: <<And:i\d+>> And [<<Shr>>,<<Const255>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: int Main.Shr24And255(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Shr24And255(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const24:i\d+>> IntConstant 24
/// CHECK-DAG: <<UShr:i\d+>> UShr [<<Arg>>,<<Const24>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: int Main.Shr24And255(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Shr24And255(int) instruction_simplifier (after)
/// CHECK-NOT: Shr
/// CHECK-NOT: And
- public static int Shr24And255(int arg) {
+ public static int $noinline$Shr24And255(int arg) {
+ if (doThrow) { throw new Error(); }
return (arg >> 24) & 255;
}
- /// CHECK-START: long Main.Shr56And255(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Shr56And255(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const56:i\d+>> IntConstant 56
/// CHECK-DAG: <<Const255:j\d+>> LongConstant 255
@@ -204,21 +213,22 @@
/// CHECK-DAG: <<And:j\d+>> And [<<Shr>>,<<Const255>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: long Main.Shr56And255(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr56And255(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const56:i\d+>> IntConstant 56
/// CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const56>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: long Main.Shr56And255(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr56And255(long) instruction_simplifier (after)
/// CHECK-NOT: Shr
/// CHECK-NOT: And
- public static long Shr56And255(long arg) {
+ public static long $noinline$Shr56And255(long arg) {
+ if (doThrow) { throw new Error(); }
return (arg >> 56) & 255;
}
- /// CHECK-START: int Main.Shr24And127(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$Shr24And127(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const24:i\d+>> IntConstant 24
/// CHECK-DAG: <<Const127:i\d+>> IntConstant 127
@@ -226,7 +236,7 @@
/// CHECK-DAG: <<And:i\d+>> And [<<Shr>>,<<Const127>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: int Main.Shr24And127(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Shr24And127(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const24:i\d+>> IntConstant 24
/// CHECK-DAG: <<Const127:i\d+>> IntConstant 127
@@ -234,11 +244,12 @@
/// CHECK-DAG: <<And:i\d+>> And [<<Shr>>,<<Const127>>]
/// CHECK-DAG: Return [<<And>>]
- public static int Shr24And127(int arg) {
+ public static int $noinline$Shr24And127(int arg) {
+ if (doThrow) { throw new Error(); }
return (arg >> 24) & 127;
}
- /// CHECK-START: long Main.Shr56And127(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Shr56And127(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const56:i\d+>> IntConstant 56
/// CHECK-DAG: <<Const127:j\d+>> LongConstant 127
@@ -246,7 +257,7 @@
/// CHECK-DAG: <<And:j\d+>> And [<<Shr>>,<<Const127>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: long Main.Shr56And127(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr56And127(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const56:i\d+>> IntConstant 56
/// CHECK-DAG: <<Const127:j\d+>> LongConstant 127
@@ -254,267 +265,283 @@
/// CHECK-DAG: <<And:j\d+>> And [<<Shr>>,<<Const127>>]
/// CHECK-DAG: Return [<<And>>]
- public static long Shr56And127(long arg) {
+ public static long $noinline$Shr56And127(long arg) {
+ if (doThrow) { throw new Error(); }
return (arg >> 56) & 127;
}
- /// CHECK-START: long Main.Div1(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Div1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const1:j\d+>> LongConstant 1
/// CHECK-DAG: <<Div:j\d+>> Div [<<Arg>>,<<Const1>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: long Main.Div1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Div1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.Div1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Div1(long) instruction_simplifier (after)
/// CHECK-NOT: Div
- public static long Div1(long arg) {
+ public static long $noinline$Div1(long arg) {
+ if (doThrow) { throw new Error(); }
return arg / 1;
}
- /// CHECK-START: int Main.DivN1(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$DivN1(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1
/// CHECK-DAG: <<Div:i\d+>> Div [<<Arg>>,<<ConstN1>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: int Main.DivN1(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$DivN1(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>]
/// CHECK-DAG: Return [<<Neg>>]
- /// CHECK-START: int Main.DivN1(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$DivN1(int) instruction_simplifier (after)
/// CHECK-NOT: Div
- public static int DivN1(int arg) {
+ public static int $noinline$DivN1(int arg) {
+ if (doThrow) { throw new Error(); }
return arg / -1;
}
- /// CHECK-START: long Main.Mul1(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Mul1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const1:j\d+>> LongConstant 1
/// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const1>>,<<Arg>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Mul1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.Mul1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Mul1(long) instruction_simplifier (after)
/// CHECK-NOT: Mul
- public static long Mul1(long arg) {
+ public static long $noinline$Mul1(long arg) {
+ if (doThrow) { throw new Error(); }
return arg * 1;
}
- /// CHECK-START: int Main.MulN1(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$MulN1(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1
/// CHECK-DAG: <<Mul:i\d+>> Mul [<<Arg>>,<<ConstN1>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: int Main.MulN1(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$MulN1(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>]
/// CHECK-DAG: Return [<<Neg>>]
- /// CHECK-START: int Main.MulN1(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$MulN1(int) instruction_simplifier (after)
/// CHECK-NOT: Mul
- public static int MulN1(int arg) {
+ public static int $noinline$MulN1(int arg) {
+ if (doThrow) { throw new Error(); }
return arg * -1;
}
- /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$MulPowerOfTwo128(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const128:j\d+>> LongConstant 128
/// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const128>>,<<Arg>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$MulPowerOfTwo128(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
/// CHECK-DAG: <<Shl:j\d+>> Shl [<<Arg>>,<<Const7>>]
/// CHECK-DAG: Return [<<Shl>>]
- /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$MulPowerOfTwo128(long) instruction_simplifier (after)
/// CHECK-NOT: Mul
- public static long MulPowerOfTwo128(long arg) {
+ public static long $noinline$MulPowerOfTwo128(long arg) {
+ if (doThrow) { throw new Error(); }
return arg * 128;
}
- /// CHECK-START: int Main.Or0(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$Or0(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Or:i\d+>> Or [<<Arg>>,<<Const0>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: int Main.Or0(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Or0(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: int Main.Or0(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Or0(int) instruction_simplifier (after)
/// CHECK-NOT: Or
- public static int Or0(int arg) {
+ public static int $noinline$Or0(int arg) {
+ if (doThrow) { throw new Error(); }
return arg | 0;
}
- /// CHECK-START: long Main.OrSame(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$OrSame(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Or:j\d+>> Or [<<Arg>>,<<Arg>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$OrSame(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.OrSame(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$OrSame(long) instruction_simplifier (after)
/// CHECK-NOT: Or
- public static long OrSame(long arg) {
+ public static long $noinline$OrSame(long arg) {
+ if (doThrow) { throw new Error(); }
return arg | arg;
}
- /// CHECK-START: int Main.Shl0(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$Shl0(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Shl:i\d+>> Shl [<<Arg>>,<<Const0>>]
/// CHECK-DAG: Return [<<Shl>>]
- /// CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Shl0(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: int Main.Shl0(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Shl0(int) instruction_simplifier (after)
/// CHECK-NOT: Shl
- public static int Shl0(int arg) {
+ public static int $noinline$Shl0(int arg) {
+ if (doThrow) { throw new Error(); }
return arg << 0;
}
- /// CHECK-START: long Main.Shr0(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Shr0(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const0>>]
/// CHECK-DAG: Return [<<Shr>>]
- /// CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr0(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.Shr0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr0(long) instruction_simplifier (after)
/// CHECK-NOT: Shr
- public static long Shr0(long arg) {
+ public static long $noinline$Shr0(long arg) {
+ if (doThrow) { throw new Error(); }
return arg >> 0;
}
- /// CHECK-START: long Main.Shr64(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Shr64(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const64:i\d+>> IntConstant 64
/// CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const64>>]
/// CHECK-DAG: Return [<<Shr>>]
- /// CHECK-START: long Main.Shr64(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr64(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.Shr64(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Shr64(long) instruction_simplifier (after)
/// CHECK-NOT: Shr
- public static long Shr64(long arg) {
+ public static long $noinline$Shr64(long arg) {
+ if (doThrow) { throw new Error(); }
return arg >> 64;
}
- /// CHECK-START: long Main.Sub0(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$Sub0(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
/// CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg>>,<<Const0>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Sub0(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.Sub0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$Sub0(long) instruction_simplifier (after)
/// CHECK-NOT: Sub
- public static long Sub0(long arg) {
+ public static long $noinline$Sub0(long arg) {
+ if (doThrow) { throw new Error(); }
return arg - 0;
}
- /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$SubAliasNeg(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Const0>>,<<Arg>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$SubAliasNeg(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>]
/// CHECK-DAG: Return [<<Neg>>]
- /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$SubAliasNeg(int) instruction_simplifier (after)
/// CHECK-NOT: Sub
- public static int SubAliasNeg(int arg) {
+ public static int $noinline$SubAliasNeg(int arg) {
+ if (doThrow) { throw new Error(); }
return 0 - arg;
}
- /// CHECK-START: long Main.UShr0(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$UShr0(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const0>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$UShr0(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.UShr0(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$UShr0(long) instruction_simplifier (after)
/// CHECK-NOT: UShr
- public static long UShr0(long arg) {
+ public static long $noinline$UShr0(long arg) {
+ if (doThrow) { throw new Error(); }
return arg >>> 0;
}
- /// CHECK-START: int Main.Xor0(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$Xor0(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<Const0>>]
/// CHECK-DAG: Return [<<Xor>>]
- /// CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Xor0(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: int Main.Xor0(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$Xor0(int) instruction_simplifier (after)
/// CHECK-NOT: Xor
- public static int Xor0(int arg) {
+ public static int $noinline$Xor0(int arg) {
+ if (doThrow) { throw new Error(); }
return arg ^ 0;
}
- /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<ConstF:i\d+>> IntConstant -1
/// CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<ConstF>>]
/// CHECK-DAG: Return [<<Xor>>]
- /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
/// CHECK-DAG: Return [<<Not>>]
- /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after)
/// CHECK-NOT: Xor
- public static int XorAllOnes(int arg) {
+ public static int $noinline$XorAllOnes(int arg) {
+ if (doThrow) { throw new Error(); }
return arg ^ -1;
}
@@ -525,7 +552,7 @@
* `InstructionSimplifierVisitor::TryMoveNegOnInputsAfterBinop`.
*/
- /// CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$AddNegs1(int, int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
@@ -533,7 +560,7 @@
/// CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$AddNegs1(int, int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-NOT: Neg
@@ -541,7 +568,8 @@
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>]
/// CHECK-DAG: Return [<<Neg>>]
- public static int AddNegs1(int arg1, int arg2) {
+ public static int $noinline$AddNegs1(int arg1, int arg2) {
+ if (doThrow) { throw new Error(); }
return -arg1 + -arg2;
}
@@ -556,7 +584,7 @@
* increasing the register pressure by creating or extending live ranges.
*/
- /// CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$AddNegs2(int, int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
@@ -566,7 +594,7 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$AddNegs2(int, int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
@@ -577,7 +605,7 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: int Main.AddNegs2(int, int) GVN (after)
+ /// CHECK-START: int Main.$noinline$AddNegs2(int, int) GVN (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>]
@@ -586,7 +614,8 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Add>>,<<Add>>]
/// CHECK-DAG: Return [<<Or>>]
- public static int AddNegs2(int arg1, int arg2) {
+ public static int $noinline$AddNegs2(int arg1, int arg2) {
+ if (doThrow) { throw new Error(); }
int temp1 = -arg1;
int temp2 = -arg2;
return (temp1 + temp2) | (temp1 + temp2);
@@ -600,7 +629,7 @@
* the loop.
*/
- /// CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$AddNegs3(long, long) instruction_simplifier (before)
// -------------- Arguments and initial negation operations.
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
@@ -612,7 +641,7 @@
/// CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>]
/// CHECK: Goto
- /// CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$AddNegs3(long, long) instruction_simplifier (after)
// -------------- Arguments and initial negation operations.
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
@@ -625,7 +654,8 @@
/// CHECK-NOT: Neg
/// CHECK: Goto
- public static long AddNegs3(long arg1, long arg2) {
+ public static long $noinline$AddNegs3(long arg1, long arg2) {
+ if (doThrow) { throw new Error(); }
long res = 0;
long n_arg1 = -arg1;
long n_arg2 = -arg2;
@@ -641,24 +671,25 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitAdd`.
*/
- /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$AddNeg1(long, long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
/// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>]
/// CHECK-DAG: <<Add:j\d+>> Add [<<Neg>>,<<Arg2>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$AddNeg1(long, long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
/// CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg2>>,<<Arg1>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$AddNeg1(long, long) instruction_simplifier (after)
/// CHECK-NOT: Neg
/// CHECK-NOT: Add
- public static long AddNeg1(long arg1, long arg2) {
+ public static long $noinline$AddNeg1(long arg1, long arg2) {
+ if (doThrow) { throw new Error(); }
return -arg1 + arg2;
}
@@ -671,7 +702,7 @@
* increasing the register pressure by creating or extending live ranges.
*/
- /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$AddNeg2(long, long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
/// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>]
@@ -680,7 +711,7 @@
/// CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>]
/// CHECK-DAG: Return [<<Res>>]
- /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$AddNeg2(long, long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
/// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>]
@@ -689,10 +720,11 @@
/// CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>]
/// CHECK-DAG: Return [<<Res>>]
- /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$AddNeg2(long, long) instruction_simplifier (after)
/// CHECK-NOT: Sub
- public static long AddNeg2(long arg1, long arg2) {
+ public static long $noinline$AddNeg2(long arg1, long arg2) {
+ if (doThrow) { throw new Error(); }
long temp = -arg2;
return (arg1 + temp) | (arg1 + temp);
}
@@ -702,20 +734,21 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNeg`.
*/
- /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$NegNeg1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg>>]
/// CHECK-DAG: <<Neg2:j\d+>> Neg [<<Neg1>>]
/// CHECK-DAG: Return [<<Neg2>>]
- /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$NegNeg1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$NegNeg1(long) instruction_simplifier (after)
/// CHECK-NOT: Neg
- public static long NegNeg1(long arg) {
+ public static long $noinline$NegNeg1(long arg) {
+ if (doThrow) { throw new Error(); }
return -(-arg);
}
@@ -726,29 +759,30 @@
* and in `InstructionSimplifierVisitor::VisitAdd`.
*/
- /// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$NegNeg2(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg>>]
/// CHECK-DAG: <<Neg2:i\d+>> Neg [<<Neg1>>]
/// CHECK-DAG: <<Add:i\d+>> Add [<<Neg2>>,<<Neg1>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NegNeg2(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg>>,<<Arg>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NegNeg2(int) instruction_simplifier (after)
/// CHECK-NOT: Neg
/// CHECK-NOT: Add
- /// CHECK-START: int Main.NegNeg2(int) constant_folding_after_inlining (after)
+ /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding_after_inlining (after)
/// CHECK: <<Const0:i\d+>> IntConstant 0
/// CHECK-NOT: Neg
/// CHECK-NOT: Add
/// CHECK: Return [<<Const0>>]
- public static int NegNeg2(int arg) {
+ public static int $noinline$NegNeg2(int arg) {
+ if (doThrow) { throw new Error(); }
int temp = -arg;
return temp + -temp;
}
@@ -760,22 +794,23 @@
* and in `InstructionSimplifierVisitor::VisitSub`.
*/
- /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$NegNeg3(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
/// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg>>]
/// CHECK-DAG: <<Sub:j\d+>> Sub [<<Const0>>,<<Neg>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$NegNeg3(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$NegNeg3(long) instruction_simplifier (after)
/// CHECK-NOT: Neg
/// CHECK-NOT: Sub
- public static long NegNeg3(long arg) {
+ public static long $noinline$NegNeg3(long arg) {
+ if (doThrow) { throw new Error(); }
return 0 - -arg;
}
@@ -785,23 +820,24 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNeg`.
*/
- /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$NegSub1(int, int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>]
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Sub>>]
/// CHECK-DAG: Return [<<Neg>>]
- /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NegSub1(int, int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg2>>,<<Arg1>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NegSub1(int, int) instruction_simplifier (after)
/// CHECK-NOT: Neg
- public static int NegSub1(int arg1, int arg2) {
+ public static int $noinline$NegSub1(int arg1, int arg2) {
+ if (doThrow) { throw new Error(); }
return -(arg1 - arg2);
}
@@ -815,7 +851,7 @@
* increasing the register pressure by creating or extending live ranges.
*/
- /// CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$NegSub2(int, int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>]
@@ -824,7 +860,7 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NegSub2(int, int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>]
@@ -833,7 +869,8 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>]
/// CHECK-DAG: Return [<<Or>>]
- public static int NegSub2(int arg1, int arg2) {
+ public static int $noinline$NegSub2(int arg1, int arg2) {
+ if (doThrow) { throw new Error(); }
int temp = arg1 - arg2;
return -temp | -temp;
}
@@ -843,41 +880,43 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`.
*/
- /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Not1:j\d+>> Not [<<Arg>>]
/// CHECK-DAG: <<Not2:j\d+>> Not [<<Not1>>]
/// CHECK-DAG: Return [<<Not2>>]
- /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after)
/// CHECK-NOT: Not
- public static long NotNot1(long arg) {
+ public static long $noinline$NotNot1(long arg) {
+ if (doThrow) { throw new Error(); }
return ~~arg;
}
- /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Not1:i\d+>> Not [<<Arg>>]
/// CHECK-DAG: <<Not2:i\d+>> Not [<<Not1>>]
/// CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
/// CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Not>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after)
/// CHECK: Not
/// CHECK-NOT: Not
- public static int NotNot2(int arg) {
+ public static int $noinline$NotNot2(int arg) {
+ if (doThrow) { throw new Error(); }
int temp = ~arg;
return temp + ~temp;
}
@@ -887,24 +926,25 @@
* The transformation tested is implemented in `InstructionSimplifierVisitor::VisitSub`.
*/
- /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$SubNeg1(int, int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>]
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Neg>>,<<Arg2>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$SubNeg1(int, int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Add:i\d+>> Add [<<Arg1>>,<<Arg2>>]
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>]
/// CHECK-DAG: Return [<<Neg>>]
- /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$SubNeg1(int, int) instruction_simplifier (after)
/// CHECK-NOT: Sub
- public static int SubNeg1(int arg1, int arg2) {
+ public static int $noinline$SubNeg1(int arg1, int arg2) {
+ if (doThrow) { throw new Error(); }
return -arg1 - arg2;
}
@@ -918,7 +958,7 @@
* increasing the register pressure by creating or extending live ranges.
*/
- /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$SubNeg2(int, int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>]
@@ -927,7 +967,7 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$SubNeg2(int, int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg1:i\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
/// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>]
@@ -936,10 +976,11 @@
/// CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$SubNeg2(int, int) instruction_simplifier (after)
/// CHECK-NOT: Add
- public static int SubNeg2(int arg1, int arg2) {
+ public static int $noinline$SubNeg2(int arg1, int arg2) {
+ if (doThrow) { throw new Error(); }
int temp = -arg1;
return (temp - arg2) | (temp - arg2);
}
@@ -951,7 +992,7 @@
* the loop.
*/
- /// CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$SubNeg3(long, long) instruction_simplifier (before)
// -------------- Arguments and initial negation operation.
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
@@ -962,7 +1003,7 @@
/// CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>]
/// CHECK: Goto
- /// CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$SubNeg3(long, long) instruction_simplifier (after)
// -------------- Arguments and initial negation operation.
/// CHECK-DAG: <<Arg1:j\d+>> ParameterValue
/// CHECK-DAG: <<Arg2:j\d+>> ParameterValue
@@ -974,7 +1015,8 @@
/// CHECK-NOT: Neg
/// CHECK: Goto
- public static long SubNeg3(long arg1, long arg2) {
+ public static long $noinline$SubNeg3(long arg1, long arg2) {
+ if (doThrow) { throw new Error(); }
long res = 0;
long temp = -arg1;
for (long i = 0; i < 1; i++) {
@@ -983,7 +1025,7 @@
return res;
}
- /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
+ /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -993,15 +1035,16 @@
/// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
/// CHECK-DAG: Return [<<NotCond>>]
- /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<True:i\d+>> IntConstant 1
/// CHECK-DAG: Return [<<True>>]
- public static boolean EqualBoolVsIntConst(boolean arg) {
+ public static boolean $noinline$EqualBoolVsIntConst(boolean arg) {
+ if (doThrow) { throw new Error(); }
return (arg ? 0 : 1) != 2;
}
- /// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
+ /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -1011,11 +1054,12 @@
/// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
/// CHECK-DAG: Return [<<NotCond>>]
- /// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<False:i\d+>> IntConstant 0
/// CHECK-DAG: Return [<<False>>]
- public static boolean NotEqualBoolVsIntConst(boolean arg) {
+ public static boolean $noinline$NotEqualBoolVsIntConst(boolean arg) {
+ if (doThrow) { throw new Error(); }
return (arg ? 0 : 1) == 2;
}
@@ -1025,7 +1069,7 @@
* remove the second.
*/
- /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (before)
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -1033,7 +1077,7 @@
/// CHECK-DAG: <<NotNotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<NotArg>>]
/// CHECK-DAG: Return [<<NotNotArg>>]
- /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
@@ -1041,81 +1085,86 @@
return !arg;
}
- public static boolean NotNotBool(boolean arg) {
+ public static boolean $noinline$NotNotBool(boolean arg) {
+ if (doThrow) { throw new Error(); }
return !(NegateValue(arg));
}
- /// CHECK-START: float Main.Div2(float) instruction_simplifier (before)
+ /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<Const2>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: float Main.Div2(float) instruction_simplifier (after)
+ /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<ConstP5:f\d+>> FloatConstant 0.5
/// CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstP5>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: float Main.Div2(float) instruction_simplifier (after)
+ /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (after)
/// CHECK-NOT: Div
- public static float Div2(float arg) {
+ public static float $noinline$Div2(float arg) {
+ if (doThrow) { throw new Error(); }
return arg / 2.0f;
}
- /// CHECK-START: double Main.Div2(double) instruction_simplifier (before)
+ /// CHECK-START: double Main.$noinline$Div2(double) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:d\d+>> ParameterValue
/// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2
/// CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<Const2>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: double Main.Div2(double) instruction_simplifier (after)
+ /// CHECK-START: double Main.$noinline$Div2(double) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:d\d+>> ParameterValue
/// CHECK-DAG: <<ConstP5:d\d+>> DoubleConstant 0.5
/// CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstP5>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: double Main.Div2(double) instruction_simplifier (after)
+ /// CHECK-START: double Main.$noinline$Div2(double) instruction_simplifier (after)
/// CHECK-NOT: Div
- public static double Div2(double arg) {
+ public static double $noinline$Div2(double arg) {
+ if (doThrow) { throw new Error(); }
return arg / 2.0;
}
- /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (before)
+ /// CHECK-START: float Main.$noinline$DivMP25(float) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<ConstMP25:f\d+>> FloatConstant -0.25
/// CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<ConstMP25>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after)
+ /// CHECK-START: float Main.$noinline$DivMP25(float) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<ConstM4:f\d+>> FloatConstant -4
/// CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstM4>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after)
+ /// CHECK-START: float Main.$noinline$DivMP25(float) instruction_simplifier (after)
/// CHECK-NOT: Div
- public static float DivMP25(float arg) {
+ public static float $noinline$DivMP25(float arg) {
+ if (doThrow) { throw new Error(); }
return arg / -0.25f;
}
- /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (before)
+ /// CHECK-START: double Main.$noinline$DivMP25(double) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:d\d+>> ParameterValue
/// CHECK-DAG: <<ConstMP25:d\d+>> DoubleConstant -0.25
/// CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<ConstMP25>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after)
+ /// CHECK-START: double Main.$noinline$DivMP25(double) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:d\d+>> ParameterValue
/// CHECK-DAG: <<ConstM4:d\d+>> DoubleConstant -4
/// CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstM4>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after)
+ /// CHECK-START: double Main.$noinline$DivMP25(double) instruction_simplifier (after)
/// CHECK-NOT: Div
- public static double DivMP25(double arg) {
+ public static double $noinline$DivMP25(double arg) {
+ if (doThrow) { throw new Error(); }
return arg / -0.25f;
}
@@ -1123,18 +1172,19 @@
* Test strength reduction of factors of the form (2^n + 1).
*/
- /// CHECK-START: int Main.mulPow2Plus1(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$mulPow2Plus1(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const9:i\d+>> IntConstant 9
/// CHECK: Mul [<<Arg>>,<<Const9>>]
- /// CHECK-START: int Main.mulPow2Plus1(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$mulPow2Plus1(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK: <<Shift:i\d+>> Shl [<<Arg>>,<<Const3>>]
/// CHECK-NEXT: Add [<<Arg>>,<<Shift>>]
- public static int mulPow2Plus1(int arg) {
+ public static int $noinline$mulPow2Plus1(int arg) {
+ if (doThrow) { throw new Error(); }
return arg * 9;
}
@@ -1142,62 +1192,69 @@
* Test strength reduction of factors of the form (2^n - 1).
*/
- /// CHECK-START: long Main.mulPow2Minus1(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$mulPow2Minus1(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const31:j\d+>> LongConstant 31
/// CHECK: Mul [<<Const31>>,<<Arg>>]
- /// CHECK-START: long Main.mulPow2Minus1(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$mulPow2Minus1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
/// CHECK: <<Shift:j\d+>> Shl [<<Arg>>,<<Const5>>]
/// CHECK-NEXT: Sub [<<Shift>>,<<Arg>>]
- public static long mulPow2Minus1(long arg) {
+ public static long $noinline$mulPow2Minus1(long arg) {
+ if (doThrow) { throw new Error(); }
return arg * 31;
}
- /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_after_bce (before)
+ /// CHECK-START: int Main.$noinline$booleanFieldNotEqualOne() instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
/// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Field>>,<<Const1>>]
/// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
/// CHECK-DAG: Return [<<Select>>]
- /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_after_bce (after)
+ /// CHECK-START: int Main.$noinline$booleanFieldNotEqualOne() instruction_simplifier_after_bce (after)
+ /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
/// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>]
/// CHECK-DAG: Return [<<Select>>]
- public static int booleanFieldNotEqualOne() {
+ public static int $noinline$booleanFieldNotEqualOne() {
+ if (doThrow) { throw new Error(); }
return (booleanField == $inline$true()) ? 13 : 54;
}
- /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_after_bce (before)
+ /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
/// CHECK-DAG: <<NE:z\d+>> Equal [<<Field>>,<<Const0>>]
/// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
/// CHECK-DAG: Return [<<Select>>]
- /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_after_bce (after)
+ /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier_after_bce (after)
+ /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
/// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>]
/// CHECK-DAG: Return [<<Select>>]
- public static int booleanFieldEqualZero() {
+ public static int $noinline$booleanFieldEqualZero() {
+ if (doThrow) { throw new Error(); }
return (booleanField != $inline$false()) ? 13 : 54;
}
- /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before)
+ /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -1210,7 +1267,7 @@
/// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
/// CHECK-DAG: Return [<<Result>>]
- /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (after)
+ /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
@@ -1221,11 +1278,12 @@
// Note that we match `LE` from Select because there are two identical
// LessThanOrEqual instructions.
- public static int intConditionNotEqualOne(int i) {
+ public static int $noinline$intConditionNotEqualOne(int i) {
+ if (doThrow) { throw new Error(); }
return ((i > 42) == $inline$true()) ? 13 : 54;
}
- /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before)
+ /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -1238,7 +1296,7 @@
/// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
/// CHECK-DAG: Return [<<Result>>]
- /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (after)
+ /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
@@ -1249,16 +1307,17 @@
// Note that we match `LE` from Select because there are two identical
// LessThanOrEqual instructions.
- public static int intConditionEqualZero(int i) {
+ public static int $noinline$intConditionEqualZero(int i) {
+ if (doThrow) { throw new Error(); }
return ((i > 42) != $inline$false()) ? 13 : 54;
}
// Test that conditions on float/double are not flipped.
- /// CHECK-START: int Main.floatConditionNotEqualOne(float) builder (after)
+ /// CHECK-START: int Main.$noinline$floatConditionNotEqualOne(float) builder (after)
/// CHECK: LessThanOrEqual
- /// CHECK-START: int Main.floatConditionNotEqualOne(float) instruction_simplifier_before_codegen (after)
+ /// CHECK-START: int Main.$noinline$floatConditionNotEqualOne(float) instruction_simplifier_before_codegen (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
@@ -1267,14 +1326,15 @@
/// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>]
/// CHECK-DAG: Return [<<Select>>]
- public static int floatConditionNotEqualOne(float f) {
+ public static int $noinline$floatConditionNotEqualOne(float f) {
+ if (doThrow) { throw new Error(); }
return ((f > 42.0f) == true) ? 13 : 54;
}
- /// CHECK-START: int Main.doubleConditionEqualZero(double) builder (after)
+ /// CHECK-START: int Main.$noinline$doubleConditionEqualZero(double) builder (after)
/// CHECK: LessThanOrEqual
- /// CHECK-START: int Main.doubleConditionEqualZero(double) instruction_simplifier_before_codegen (after)
+ /// CHECK-START: int Main.$noinline$doubleConditionEqualZero(double) instruction_simplifier_before_codegen (after)
/// CHECK-DAG: <<Arg:d\d+>> ParameterValue
/// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
@@ -1283,42 +1343,45 @@
/// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>]
/// CHECK-DAG: Return [<<Select>>]
- public static int doubleConditionEqualZero(double d) {
+ public static int $noinline$doubleConditionEqualZero(double d) {
+ if (doThrow) { throw new Error(); }
return ((d > 42.0) != false) ? 13 : 54;
}
- /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$intToDoubleToInt(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
/// CHECK-DAG: Return [<<Int>>]
- /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$intToDoubleToInt(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$intToDoubleToInt(int) instruction_simplifier (after)
/// CHECK-NOT: TypeConversion
- public static int intToDoubleToInt(int value) {
+ public static int $noinline$intToDoubleToInt(int value) {
+ if (doThrow) { throw new Error(); }
// Lossless conversion followed by a conversion back.
return (int) (double) value;
}
- /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (before)
+ /// CHECK-START: java.lang.String Main.$noinline$intToDoubleToIntPrint(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
- /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.$noinline$intToDoubleToIntPrint(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: {{d\d+}} TypeConversion [<<Arg>>]
- /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.$noinline$intToDoubleToIntPrint(int) instruction_simplifier (after)
/// CHECK-DAG: TypeConversion
/// CHECK-NOT: TypeConversion
- public static String intToDoubleToIntPrint(int value) {
+ public static String $noinline$intToDoubleToIntPrint(int value) {
+ if (doThrow) { throw new Error(); }
// Lossless conversion followed by a conversion back
// with another use of the intermediate result.
double d = (double) value;
@@ -1326,55 +1389,58 @@
return "d=" + d + ", i=" + i;
}
- /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$byteToDoubleToInt(byte) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:b\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
/// CHECK-DAG: Return [<<Int>>]
- /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$byteToDoubleToInt(byte) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:b\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$byteToDoubleToInt(byte) instruction_simplifier (after)
/// CHECK-NOT: TypeConversion
- public static int byteToDoubleToInt(byte value) {
+ public static int $noinline$byteToDoubleToInt(byte value) {
+ if (doThrow) { throw new Error(); }
// Lossless conversion followed by another conversion, use implicit conversion.
return (int) (double) value;
}
- /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$floatToDoubleToInt(float) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
/// CHECK-DAG: Return [<<Int>>]
- /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$floatToDoubleToInt(float) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: Return [<<Int>>]
- /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$floatToDoubleToInt(float) instruction_simplifier (after)
/// CHECK-DAG: TypeConversion
/// CHECK-NOT: TypeConversion
- public static int floatToDoubleToInt(float value) {
+ public static int $noinline$floatToDoubleToInt(float value) {
+ if (doThrow) { throw new Error(); }
// Lossless conversion followed by another conversion.
return (int) (double) value;
}
- /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (before)
+ /// CHECK-START: java.lang.String Main.$noinline$floatToDoubleToIntPrint(float) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
- /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.$noinline$floatToDoubleToIntPrint(float) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>]
- public static String floatToDoubleToIntPrint(float value) {
+ public static String $noinline$floatToDoubleToIntPrint(float value) {
+ if (doThrow) { throw new Error(); }
// Lossless conversion followed by another conversion with
// an extra use of the intermediate result.
double d = (double) value;
@@ -1382,176 +1448,186 @@
return "d=" + d + ", i=" + i;
}
- /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (before)
+ /// CHECK-START: short Main.$noinline$byteToDoubleToShort(byte) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:b\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Short>>]
- /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$byteToDoubleToShort(byte) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:b\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$byteToDoubleToShort(byte) instruction_simplifier (after)
/// CHECK-NOT: TypeConversion
- public static short byteToDoubleToShort(byte value) {
+ public static short $noinline$byteToDoubleToShort(byte value) {
+ if (doThrow) { throw new Error(); }
// Originally, this is byte->double->int->short. The first conversion is lossless,
// so we merge this with the second one to byte->int which we omit as it's an implicit
// conversion. Then we eliminate the resulting byte->short as an implicit conversion.
return (short) (double) value;
}
- /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (before)
+ /// CHECK-START: short Main.$noinline$charToDoubleToShort(char) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:c\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Short>>]
- /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$charToDoubleToShort(char) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:c\d+>> ParameterValue
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: Return [<<Short>>]
- /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$charToDoubleToShort(char) instruction_simplifier (after)
/// CHECK-DAG: TypeConversion
/// CHECK-NOT: TypeConversion
- public static short charToDoubleToShort(char value) {
+ public static short $noinline$charToDoubleToShort(char value) {
+ if (doThrow) { throw new Error(); }
// Originally, this is char->double->int->short. The first conversion is lossless,
// so we merge this with the second one to char->int which we omit as it's an implicit
// conversion. Then we are left with the resulting char->short conversion.
return (short) (double) value;
}
- /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (before)
+ /// CHECK-START: short Main.$noinline$floatToIntToShort(float) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Short>>]
- /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$floatToIntToShort(float) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:f\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Short>>]
- public static short floatToIntToShort(float value) {
+ public static short $noinline$floatToIntToShort(float value) {
+ if (doThrow) { throw new Error(); }
// Lossy FP to integral conversion followed by another conversion: no simplification.
return (short) value;
}
- /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$intToFloatToInt(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>]
/// CHECK-DAG: Return [<<Int>>]
- /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$intToFloatToInt(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>]
/// CHECK-DAG: Return [<<Int>>]
- public static int intToFloatToInt(int value) {
+ public static int $noinline$intToFloatToInt(int value) {
+ if (doThrow) { throw new Error(); }
// Lossy integral to FP conversion followed another conversion: no simplification.
return (int) (float) value;
}
- /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (before)
+ /// CHECK-START: double Main.$noinline$longToIntToDouble(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Double>>]
- /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (after)
+ /// CHECK-START: double Main.$noinline$longToIntToDouble(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Double>>]
- public static double longToIntToDouble(long value) {
+ public static double $noinline$longToIntToDouble(long value) {
+ if (doThrow) { throw new Error(); }
// Lossy long-to-int conversion followed an integral to FP conversion: no simplification.
return (double) (int) value;
}
- /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (before)
+ /// CHECK-START: long Main.$noinline$longToIntToLong(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Long>>]
- /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (after)
+ /// CHECK-START: long Main.$noinline$longToIntToLong(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Long>>]
- public static long longToIntToLong(long value) {
+ public static long $noinline$longToIntToLong(long value) {
+ if (doThrow) { throw new Error(); }
// Lossy long-to-int conversion followed an int-to-long conversion: no simplification.
return (long) (int) value;
}
- /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (before)
+ /// CHECK-START: short Main.$noinline$shortToCharToShort(short) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Char>>]
/// CHECK-DAG: Return [<<Short>>]
- /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$shortToCharToShort(short) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- public static short shortToCharToShort(short value) {
+ public static short $noinline$shortToCharToShort(short value) {
+ if (doThrow) { throw new Error(); }
// Integral conversion followed by non-widening integral conversion to original type.
return (short) (char) value;
}
- /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$shortToLongToInt(short) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Long>>]
/// CHECK-DAG: Return [<<Int>>]
- /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$shortToLongToInt(short) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
- public static int shortToLongToInt(short value) {
+ public static int $noinline$shortToLongToInt(short value) {
+ if (doThrow) { throw new Error(); }
// Integral conversion followed by non-widening integral conversion, use implicit conversion.
return (int) (long) value;
}
- /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (before)
+ /// CHECK-START: byte Main.$noinline$shortToCharToByte(short) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Char>>]
/// CHECK-DAG: Return [<<Byte>>]
- /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (after)
+ /// CHECK-START: byte Main.$noinline$shortToCharToByte(short) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: Return [<<Byte>>]
- public static byte shortToCharToByte(short value) {
+ public static byte $noinline$shortToCharToByte(short value) {
+ if (doThrow) { throw new Error(); }
// Integral conversion followed by non-widening integral conversion losing bits
// from the original type. Simplify to use only one conversion.
return (byte) (char) value;
}
- /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (before)
+ /// CHECK-START: java.lang.String Main.$noinline$shortToCharToBytePrint(short) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>]
- /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (after)
+ /// CHECK-START: java.lang.String Main.$noinline$shortToCharToBytePrint(short) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>]
- public static String shortToCharToBytePrint(short value) {
+ public static String $noinline$shortToCharToBytePrint(short value) {
+ if (doThrow) { throw new Error(); }
// Integral conversion followed by non-widening integral conversion losing bits
// from the original type with an extra use of the intermediate result.
char c = (char) value;
@@ -1559,7 +1635,7 @@
return "c=" + ((int) c) + ", b=" + ((int) b); // implicit conversions.
}
- /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (before)
+ /// CHECK-START: byte Main.$noinline$longAnd0xffToByte(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Mask:j\d+>> LongConstant 255
/// CHECK-DAG: <<And:j\d+>> And [<<Mask>>,<<Arg>>]
@@ -1567,58 +1643,61 @@
/// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Int>>]
/// CHECK-DAG: Return [<<Byte>>]
- /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (after)
+ /// CHECK-START: byte Main.$noinline$longAnd0xffToByte(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: Return [<<Byte>>]
- /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (after)
+ /// CHECK-START: byte Main.$noinline$longAnd0xffToByte(long) instruction_simplifier (after)
/// CHECK-NOT: And
- public static byte longAnd0xffToByte(long value) {
+ public static byte $noinline$longAnd0xffToByte(long value) {
+ if (doThrow) { throw new Error(); }
return (byte) (value & 0xff);
}
- /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (before)
+ /// CHECK-START: char Main.$noinline$intAnd0x1ffffToChar(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Mask:i\d+>> IntConstant 131071
/// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
/// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<And>>]
/// CHECK-DAG: Return [<<Char>>]
- /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (after)
+ /// CHECK-START: char Main.$noinline$intAnd0x1ffffToChar(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: Return [<<Char>>]
- /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (after)
+ /// CHECK-START: char Main.$noinline$intAnd0x1ffffToChar(int) instruction_simplifier (after)
/// CHECK-NOT: And
- public static char intAnd0x1ffffToChar(int value) {
+ public static char $noinline$intAnd0x1ffffToChar(int value) {
+ if (doThrow) { throw new Error(); }
// Keeping all significant bits and one more.
return (char) (value & 0x1ffff);
}
- /// CHECK-START: short Main.intAnd0x17fffToShort(int) instruction_simplifier (before)
+ /// CHECK-START: short Main.$noinline$intAnd0x17fffToShort(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Mask:i\d+>> IntConstant 98303
/// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<And>>]
/// CHECK-DAG: Return [<<Short>>]
- /// CHECK-START: short Main.intAnd0x17fffToShort(int) instruction_simplifier (after)
+ /// CHECK-START: short Main.$noinline$intAnd0x17fffToShort(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Mask:i\d+>> IntConstant 98303
/// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
/// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<And>>]
/// CHECK-DAG: Return [<<Short>>]
- public static short intAnd0x17fffToShort(int value) {
+ public static short $noinline$intAnd0x17fffToShort(int value) {
+ if (doThrow) { throw new Error(); }
// No simplification: clearing a significant bit.
return (short) (value & 0x17fff);
}
- /// CHECK-START: double Main.shortAnd0xffffToShortToDouble(short) instruction_simplifier (before)
+ /// CHECK-START: double Main.$noinline$shortAnd0xffffToShortToDouble(short) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Mask:i\d+>> IntConstant 65535
/// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
@@ -1626,45 +1705,49 @@
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Same>>]
/// CHECK-DAG: Return [<<Double>>]
- /// CHECK-START: double Main.shortAnd0xffffToShortToDouble(short) instruction_simplifier (after)
+ /// CHECK-START: double Main.$noinline$shortAnd0xffffToShortToDouble(short) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:s\d+>> ParameterValue
/// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
/// CHECK-DAG: Return [<<Double>>]
- public static double shortAnd0xffffToShortToDouble(short value) {
+ public static double $noinline$shortAnd0xffffToShortToDouble(short value) {
+ if (doThrow) { throw new Error(); }
short same = (short) (value & 0xffff);
return (double) same;
}
- /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$intReverseCondition(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
/// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Const42>>,<<Arg>>]
- /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$intReverseCondition(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
/// CHECK-DAG: <<GE:z\d+>> GreaterThanOrEqual [<<Arg>>,<<Const42>>]
- public static int intReverseCondition(int i) {
+ public static int $noinline$intReverseCondition(int i) {
+ if (doThrow) { throw new Error(); }
return (42 > i) ? 13 : 54;
}
- /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.$noinline$intReverseConditionNaN(int) instruction_simplifier (before)
/// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42
/// CHECK-DAG: <<Result:d\d+>> InvokeStaticOrDirect
/// CHECK-DAG: <<CMP:i\d+>> Compare [<<Const42>>,<<Result>>]
- /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.$noinline$intReverseConditionNaN(int) instruction_simplifier (after)
/// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42
/// CHECK-DAG: <<Result:d\d+>> InvokeStaticOrDirect
/// CHECK-DAG: <<EQ:z\d+>> Equal [<<Result>>,<<Const42>>]
- public static int intReverseConditionNaN(int i) {
+ public static int $noinline$intReverseConditionNaN(int i) {
+ if (doThrow) { throw new Error(); }
return (42 != Math.sqrt(i)) ? 13 : 54;
}
- public static int runSmaliTest(String name, boolean input) {
+ public static int $noinline$runSmaliTest(String name, boolean input) {
+ if (doThrow) { throw new Error(); }
try {
Class<?> c = Class.forName("SmaliTests");
Method m = c.getMethod(name, new Class[] { boolean.class });
@@ -1674,155 +1757,270 @@
}
}
+ /// CHECK-START: int Main.$noinline$intUnnecessaryShiftMasking(int, int) instruction_simplifier (before)
+ /// CHECK: <<Value:i\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const31:i\d+>> IntConstant 31
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const31>>]
+ /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Value>>,<<And>>]
+ /// CHECK-DAG: Return [<<Shl>>]
+
+ /// CHECK-START: int Main.$noinline$intUnnecessaryShiftMasking(int, int) instruction_simplifier (after)
+ /// CHECK: <<Value:i\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Value>>,<<Shift>>]
+ /// CHECK-DAG: Return [<<Shl>>]
+
+ public static int $noinline$intUnnecessaryShiftMasking(int value, int shift) {
+ if (doThrow) { throw new Error(); }
+ return value << (shift & 31);
+ }
+
+ /// CHECK-START: long Main.$noinline$longUnnecessaryShiftMasking(long, int) instruction_simplifier (before)
+ /// CHECK: <<Value:j\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const63:i\d+>> IntConstant 63
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const63>>]
+ /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Value>>,<<And>>]
+ /// CHECK-DAG: Return [<<Shr>>]
+
+ /// CHECK-START: long Main.$noinline$longUnnecessaryShiftMasking(long, int) instruction_simplifier (after)
+ /// CHECK: <<Value:j\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Value>>,<<Shift>>]
+ /// CHECK-DAG: Return [<<Shr>>]
+
+ public static long $noinline$longUnnecessaryShiftMasking(long value, int shift) {
+ if (doThrow) { throw new Error(); }
+ return value >> (shift & 63);
+ }
+
+ /// CHECK-START: int Main.$noinline$intUnnecessaryWiderShiftMasking(int, int) instruction_simplifier (before)
+ /// CHECK: <<Value:i\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const255:i\d+>> IntConstant 255
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const255>>]
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Value>>,<<And>>]
+ /// CHECK-DAG: Return [<<UShr>>]
+
+ /// CHECK-START: int Main.$noinline$intUnnecessaryWiderShiftMasking(int, int) instruction_simplifier (after)
+ /// CHECK: <<Value:i\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Value>>,<<Shift>>]
+ /// CHECK-DAG: Return [<<UShr>>]
+
+ public static int $noinline$intUnnecessaryWiderShiftMasking(int value, int shift) {
+ if (doThrow) { throw new Error(); }
+ return value >>> (shift & 0xff);
+ }
+
+ /// CHECK-START: long Main.$noinline$longSmallerShiftMasking(long, int) instruction_simplifier (before)
+ /// CHECK: <<Value:j\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const3>>]
+ /// CHECK-DAG: <<Shl:j\d+>> Shl [<<Value>>,<<And>>]
+ /// CHECK-DAG: Return [<<Shl>>]
+
+ /// CHECK-START: long Main.$noinline$longSmallerShiftMasking(long, int) instruction_simplifier (after)
+ /// CHECK: <<Value:j\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const3>>]
+ /// CHECK-DAG: <<Shl:j\d+>> Shl [<<Value>>,<<And>>]
+ /// CHECK-DAG: Return [<<Shl>>]
+
+ public static long $noinline$longSmallerShiftMasking(long value, int shift) {
+ if (doThrow) { throw new Error(); }
+ return value << (shift & 3);
+ }
+
+ /// CHECK-START: int Main.$noinline$otherUseOfUnnecessaryShiftMasking(int, int) instruction_simplifier (before)
+ /// CHECK: <<Value:i\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const31:i\d+>> IntConstant 31
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const31>>]
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Value>>,<<And>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Shr>>,<<And>>]
+ /// CHECK-DAG: Return [<<Add>>]
+
+ /// CHECK-START: int Main.$noinline$otherUseOfUnnecessaryShiftMasking(int, int) instruction_simplifier (after)
+ /// CHECK: <<Value:i\d+>> ParameterValue
+ /// CHECK: <<Shift:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const31:i\d+>> IntConstant 31
+ /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const31>>]
+ /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Value>>,<<Shift>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Shr>>,<<And>>]
+ /// CHECK-DAG: Return [<<Add>>]
+
+ public static int $noinline$otherUseOfUnnecessaryShiftMasking(int value, int shift) {
+ if (doThrow) { throw new Error(); }
+ int temp = shift & 31;
+ return (value >> temp) + temp;
+ }
+
public static void main(String[] args) {
int arg = 123456;
- assertLongEquals(Add0(arg), arg);
- assertIntEquals(AndAllOnes(arg), arg);
- assertLongEquals(Div1(arg), arg);
- assertIntEquals(DivN1(arg), -arg);
- assertLongEquals(Mul1(arg), arg);
- assertIntEquals(MulN1(arg), -arg);
- assertLongEquals(MulPowerOfTwo128(arg), (128 * arg));
- assertIntEquals(Or0(arg), arg);
- assertLongEquals(OrSame(arg), arg);
- assertIntEquals(Shl0(arg), arg);
- assertLongEquals(Shr0(arg), arg);
- assertLongEquals(Shr64(arg), arg);
- assertLongEquals(Sub0(arg), arg);
- assertIntEquals(SubAliasNeg(arg), -arg);
- assertLongEquals(UShr0(arg), arg);
- assertIntEquals(Xor0(arg), arg);
- assertIntEquals(XorAllOnes(arg), ~arg);
- assertIntEquals(AddNegs1(arg, arg + 1), -(arg + arg + 1));
- assertIntEquals(AddNegs2(arg, arg + 1), -(arg + arg + 1));
- assertLongEquals(AddNegs3(arg, arg + 1), -(2 * arg + 1));
- assertLongEquals(AddNeg1(arg, arg + 1), 1);
- assertLongEquals(AddNeg2(arg, arg + 1), -1);
- assertLongEquals(NegNeg1(arg), arg);
- assertIntEquals(NegNeg2(arg), 0);
- assertLongEquals(NegNeg3(arg), arg);
- assertIntEquals(NegSub1(arg, arg + 1), 1);
- assertIntEquals(NegSub2(arg, arg + 1), 1);
- assertLongEquals(NotNot1(arg), arg);
- assertIntEquals(NotNot2(arg), -1);
- assertIntEquals(SubNeg1(arg, arg + 1), -(arg + arg + 1));
- assertIntEquals(SubNeg2(arg, arg + 1), -(arg + arg + 1));
- assertLongEquals(SubNeg3(arg, arg + 1), -(2 * arg + 1));
- assertBooleanEquals(EqualBoolVsIntConst(true), true);
- assertBooleanEquals(EqualBoolVsIntConst(true), true);
- assertBooleanEquals(NotEqualBoolVsIntConst(false), false);
- assertBooleanEquals(NotEqualBoolVsIntConst(false), false);
- assertBooleanEquals(NotNotBool(true), true);
- assertBooleanEquals(NotNotBool(false), false);
- assertFloatEquals(Div2(100.0f), 50.0f);
- assertDoubleEquals(Div2(150.0), 75.0);
- assertFloatEquals(DivMP25(100.0f), -400.0f);
- assertDoubleEquals(DivMP25(150.0), -600.0);
- assertIntEquals(UShr28And15(0xc1234567), 0xc);
- assertLongEquals(UShr60And15(0xc123456787654321L), 0xcL);
- assertIntEquals(UShr28And7(0xc1234567), 0x4);
- assertLongEquals(UShr60And7(0xc123456787654321L), 0x4L);
- assertIntEquals(Shr24And255(0xc1234567), 0xc1);
- assertLongEquals(Shr56And255(0xc123456787654321L), 0xc1L);
- assertIntEquals(Shr24And127(0xc1234567), 0x41);
- assertLongEquals(Shr56And127(0xc123456787654321L), 0x41L);
- assertIntEquals(0, mulPow2Plus1(0));
- assertIntEquals(9, mulPow2Plus1(1));
- assertIntEquals(18, mulPow2Plus1(2));
- assertIntEquals(900, mulPow2Plus1(100));
- assertIntEquals(111105, mulPow2Plus1(12345));
- assertLongEquals(0, mulPow2Minus1(0));
- assertLongEquals(31, mulPow2Minus1(1));
- assertLongEquals(62, mulPow2Minus1(2));
- assertLongEquals(3100, mulPow2Minus1(100));
- assertLongEquals(382695, mulPow2Minus1(12345));
+ assertLongEquals(arg, $noinline$Add0(arg));
+ assertIntEquals(arg, $noinline$AndAllOnes(arg));
+ assertLongEquals(arg, $noinline$Div1(arg));
+ assertIntEquals(-arg, $noinline$DivN1(arg));
+ assertLongEquals(arg, $noinline$Mul1(arg));
+ assertIntEquals(-arg, $noinline$MulN1(arg));
+ assertLongEquals((128 * arg), $noinline$MulPowerOfTwo128(arg));
+ assertIntEquals(arg, $noinline$Or0(arg));
+ assertLongEquals(arg, $noinline$OrSame(arg));
+ assertIntEquals(arg, $noinline$Shl0(arg));
+ assertLongEquals(arg, $noinline$Shr0(arg));
+ assertLongEquals(arg, $noinline$Shr64(arg));
+ assertLongEquals(arg, $noinline$Sub0(arg));
+ assertIntEquals(-arg, $noinline$SubAliasNeg(arg));
+ assertLongEquals(arg, $noinline$UShr0(arg));
+ assertIntEquals(arg, $noinline$Xor0(arg));
+ assertIntEquals(~arg, $noinline$XorAllOnes(arg));
+ assertIntEquals(-(arg + arg + 1), $noinline$AddNegs1(arg, arg + 1));
+ assertIntEquals(-(arg + arg + 1), $noinline$AddNegs2(arg, arg + 1));
+ assertLongEquals(-(2 * arg + 1), $noinline$AddNegs3(arg, arg + 1));
+ assertLongEquals(1, $noinline$AddNeg1(arg, arg + 1));
+ assertLongEquals(-1, $noinline$AddNeg2(arg, arg + 1));
+ assertLongEquals(arg, $noinline$NegNeg1(arg));
+ assertIntEquals(0, $noinline$NegNeg2(arg));
+ assertLongEquals(arg, $noinline$NegNeg3(arg));
+ assertIntEquals(1, $noinline$NegSub1(arg, arg + 1));
+ assertIntEquals(1, $noinline$NegSub2(arg, arg + 1));
+ assertLongEquals(arg, $noinline$NotNot1(arg));
+ assertIntEquals(-1, $noinline$NotNot2(arg));
+ assertIntEquals(-(arg + arg + 1), $noinline$SubNeg1(arg, arg + 1));
+ assertIntEquals(-(arg + arg + 1), $noinline$SubNeg2(arg, arg + 1));
+ assertLongEquals(-(2 * arg + 1), $noinline$SubNeg3(arg, arg + 1));
+ assertBooleanEquals(true, $noinline$EqualBoolVsIntConst(true));
+ assertBooleanEquals(true, $noinline$EqualBoolVsIntConst(true));
+ assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false));
+ assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false));
+ assertBooleanEquals(true, $noinline$NotNotBool(true));
+ assertBooleanEquals(false, $noinline$NotNotBool(false));
+ assertFloatEquals(50.0f, $noinline$Div2(100.0f));
+ assertDoubleEquals(75.0, $noinline$Div2(150.0));
+ assertFloatEquals(-400.0f, $noinline$DivMP25(100.0f));
+ assertDoubleEquals(-600.0, $noinline$DivMP25(150.0));
+ assertIntEquals(0xc, $noinline$UShr28And15(0xc1234567));
+ assertLongEquals(0xcL, $noinline$UShr60And15(0xc123456787654321L));
+ assertIntEquals(0x4, $noinline$UShr28And7(0xc1234567));
+ assertLongEquals(0x4L, $noinline$UShr60And7(0xc123456787654321L));
+ assertIntEquals(0xc1, $noinline$Shr24And255(0xc1234567));
+ assertLongEquals(0xc1L, $noinline$Shr56And255(0xc123456787654321L));
+ assertIntEquals(0x41, $noinline$Shr24And127(0xc1234567));
+ assertLongEquals(0x41L, $noinline$Shr56And127(0xc123456787654321L));
+ assertIntEquals(0, $noinline$mulPow2Plus1(0));
+ assertIntEquals(9, $noinline$mulPow2Plus1(1));
+ assertIntEquals(18, $noinline$mulPow2Plus1(2));
+ assertIntEquals(900, $noinline$mulPow2Plus1(100));
+ assertIntEquals(111105, $noinline$mulPow2Plus1(12345));
+ assertLongEquals(0, $noinline$mulPow2Minus1(0));
+ assertLongEquals(31, $noinline$mulPow2Minus1(1));
+ assertLongEquals(62, $noinline$mulPow2Minus1(2));
+ assertLongEquals(3100, $noinline$mulPow2Minus1(100));
+ assertLongEquals(382695, $noinline$mulPow2Minus1(12345));
booleanField = false;
- assertIntEquals(booleanFieldNotEqualOne(), 54);
- assertIntEquals(booleanFieldEqualZero(), 54);
+ assertIntEquals($noinline$booleanFieldNotEqualOne(), 54);
+ assertIntEquals($noinline$booleanFieldEqualZero(), 54);
booleanField = true;
- assertIntEquals(booleanFieldNotEqualOne(), 13);
- assertIntEquals(booleanFieldEqualZero(), 13);
- assertIntEquals(intConditionNotEqualOne(6), 54);
- assertIntEquals(intConditionNotEqualOne(43), 13);
- assertIntEquals(intConditionEqualZero(6), 54);
- assertIntEquals(intConditionEqualZero(43), 13);
- assertIntEquals(floatConditionNotEqualOne(6.0f), 54);
- assertIntEquals(floatConditionNotEqualOne(43.0f), 13);
- assertIntEquals(doubleConditionEqualZero(6.0), 54);
- assertIntEquals(doubleConditionEqualZero(43.0), 13);
+ assertIntEquals(13, $noinline$booleanFieldNotEqualOne());
+ assertIntEquals(13, $noinline$booleanFieldEqualZero());
+ assertIntEquals(54, $noinline$intConditionNotEqualOne(6));
+ assertIntEquals(13, $noinline$intConditionNotEqualOne(43));
+ assertIntEquals(54, $noinline$intConditionEqualZero(6));
+ assertIntEquals(13, $noinline$intConditionEqualZero(43));
+ assertIntEquals(54, $noinline$floatConditionNotEqualOne(6.0f));
+ assertIntEquals(13, $noinline$floatConditionNotEqualOne(43.0f));
+ assertIntEquals(54, $noinline$doubleConditionEqualZero(6.0));
+ assertIntEquals(13, $noinline$doubleConditionEqualZero(43.0));
- assertIntEquals(1234567, intToDoubleToInt(1234567));
- assertIntEquals(Integer.MIN_VALUE, intToDoubleToInt(Integer.MIN_VALUE));
- assertIntEquals(Integer.MAX_VALUE, intToDoubleToInt(Integer.MAX_VALUE));
- assertStringEquals("d=7654321.0, i=7654321", intToDoubleToIntPrint(7654321));
- assertIntEquals(12, byteToDoubleToInt((byte) 12));
- assertIntEquals(Byte.MIN_VALUE, byteToDoubleToInt(Byte.MIN_VALUE));
- assertIntEquals(Byte.MAX_VALUE, byteToDoubleToInt(Byte.MAX_VALUE));
- assertIntEquals(11, floatToDoubleToInt(11.3f));
- assertStringEquals("d=12.25, i=12", floatToDoubleToIntPrint(12.25f));
- assertIntEquals(123, byteToDoubleToShort((byte) 123));
- assertIntEquals(Byte.MIN_VALUE, byteToDoubleToShort(Byte.MIN_VALUE));
- assertIntEquals(Byte.MAX_VALUE, byteToDoubleToShort(Byte.MAX_VALUE));
- assertIntEquals(1234, charToDoubleToShort((char) 1234));
- assertIntEquals(Character.MIN_VALUE, charToDoubleToShort(Character.MIN_VALUE));
- assertIntEquals(/* sign-extended */ -1, charToDoubleToShort(Character.MAX_VALUE));
- assertIntEquals(12345, floatToIntToShort(12345.75f));
- assertIntEquals(Short.MAX_VALUE, floatToIntToShort((float)(Short.MIN_VALUE - 1)));
- assertIntEquals(Short.MIN_VALUE, floatToIntToShort((float)(Short.MAX_VALUE + 1)));
- assertIntEquals(-54321, intToFloatToInt(-54321));
- assertDoubleEquals((double) 0x12345678, longToIntToDouble(0x1234567812345678L));
- assertDoubleEquals(0.0, longToIntToDouble(Long.MIN_VALUE));
- assertDoubleEquals(-1.0, longToIntToDouble(Long.MAX_VALUE));
- assertLongEquals(0x0000000012345678L, longToIntToLong(0x1234567812345678L));
- assertLongEquals(0xffffffff87654321L, longToIntToLong(0x1234567887654321L));
- assertLongEquals(0L, longToIntToLong(Long.MIN_VALUE));
- assertLongEquals(-1L, longToIntToLong(Long.MAX_VALUE));
- assertIntEquals((short) -5678, shortToCharToShort((short) -5678));
- assertIntEquals(Short.MIN_VALUE, shortToCharToShort(Short.MIN_VALUE));
- assertIntEquals(Short.MAX_VALUE, shortToCharToShort(Short.MAX_VALUE));
- assertIntEquals(5678, shortToLongToInt((short) 5678));
- assertIntEquals(Short.MIN_VALUE, shortToLongToInt(Short.MIN_VALUE));
- assertIntEquals(Short.MAX_VALUE, shortToLongToInt(Short.MAX_VALUE));
- assertIntEquals(0x34, shortToCharToByte((short) 0x1234));
- assertIntEquals(-0x10, shortToCharToByte((short) 0x12f0));
- assertIntEquals(0, shortToCharToByte(Short.MIN_VALUE));
- assertIntEquals(-1, shortToCharToByte(Short.MAX_VALUE));
- assertStringEquals("c=1025, b=1", shortToCharToBytePrint((short) 1025));
- assertStringEquals("c=1023, b=-1", shortToCharToBytePrint((short) 1023));
- assertStringEquals("c=65535, b=-1", shortToCharToBytePrint((short) -1));
+ assertIntEquals(1234567, $noinline$intToDoubleToInt(1234567));
+ assertIntEquals(Integer.MIN_VALUE, $noinline$intToDoubleToInt(Integer.MIN_VALUE));
+ assertIntEquals(Integer.MAX_VALUE, $noinline$intToDoubleToInt(Integer.MAX_VALUE));
+ assertStringEquals("d=7654321.0, i=7654321", $noinline$intToDoubleToIntPrint(7654321));
+ assertIntEquals(12, $noinline$byteToDoubleToInt((byte) 12));
+ assertIntEquals(Byte.MIN_VALUE, $noinline$byteToDoubleToInt(Byte.MIN_VALUE));
+ assertIntEquals(Byte.MAX_VALUE, $noinline$byteToDoubleToInt(Byte.MAX_VALUE));
+ assertIntEquals(11, $noinline$floatToDoubleToInt(11.3f));
+ assertStringEquals("d=12.25, i=12", $noinline$floatToDoubleToIntPrint(12.25f));
+ assertIntEquals(123, $noinline$byteToDoubleToShort((byte) 123));
+ assertIntEquals(Byte.MIN_VALUE, $noinline$byteToDoubleToShort(Byte.MIN_VALUE));
+ assertIntEquals(Byte.MAX_VALUE, $noinline$byteToDoubleToShort(Byte.MAX_VALUE));
+ assertIntEquals(1234, $noinline$charToDoubleToShort((char) 1234));
+ assertIntEquals(Character.MIN_VALUE, $noinline$charToDoubleToShort(Character.MIN_VALUE));
+ assertIntEquals(/* sign-extended */ -1, $noinline$charToDoubleToShort(Character.MAX_VALUE));
+ assertIntEquals(12345, $noinline$floatToIntToShort(12345.75f));
+ assertIntEquals(Short.MAX_VALUE, $noinline$floatToIntToShort((float)(Short.MIN_VALUE - 1)));
+ assertIntEquals(Short.MIN_VALUE, $noinline$floatToIntToShort((float)(Short.MAX_VALUE + 1)));
+ assertIntEquals(-54321, $noinline$intToFloatToInt(-54321));
+ assertDoubleEquals((double) 0x12345678, $noinline$longToIntToDouble(0x1234567812345678L));
+ assertDoubleEquals(0.0, $noinline$longToIntToDouble(Long.MIN_VALUE));
+ assertDoubleEquals(-1.0, $noinline$longToIntToDouble(Long.MAX_VALUE));
+ assertLongEquals(0x0000000012345678L, $noinline$longToIntToLong(0x1234567812345678L));
+ assertLongEquals(0xffffffff87654321L, $noinline$longToIntToLong(0x1234567887654321L));
+ assertLongEquals(0L, $noinline$longToIntToLong(Long.MIN_VALUE));
+ assertLongEquals(-1L, $noinline$longToIntToLong(Long.MAX_VALUE));
+ assertIntEquals((short) -5678, $noinline$shortToCharToShort((short) -5678));
+ assertIntEquals(Short.MIN_VALUE, $noinline$shortToCharToShort(Short.MIN_VALUE));
+ assertIntEquals(Short.MAX_VALUE, $noinline$shortToCharToShort(Short.MAX_VALUE));
+ assertIntEquals(5678, $noinline$shortToLongToInt((short) 5678));
+ assertIntEquals(Short.MIN_VALUE, $noinline$shortToLongToInt(Short.MIN_VALUE));
+ assertIntEquals(Short.MAX_VALUE, $noinline$shortToLongToInt(Short.MAX_VALUE));
+ assertIntEquals(0x34, $noinline$shortToCharToByte((short) 0x1234));
+ assertIntEquals(-0x10, $noinline$shortToCharToByte((short) 0x12f0));
+ assertIntEquals(0, $noinline$shortToCharToByte(Short.MIN_VALUE));
+ assertIntEquals(-1, $noinline$shortToCharToByte(Short.MAX_VALUE));
+ assertStringEquals("c=1025, b=1", $noinline$shortToCharToBytePrint((short) 1025));
+ assertStringEquals("c=1023, b=-1", $noinline$shortToCharToBytePrint((short) 1023));
+ assertStringEquals("c=65535, b=-1", $noinline$shortToCharToBytePrint((short) -1));
- assertIntEquals(0x21, longAnd0xffToByte(0x1234432112344321L));
- assertIntEquals(0, longAnd0xffToByte(Long.MIN_VALUE));
- assertIntEquals(-1, longAnd0xffToByte(Long.MAX_VALUE));
- assertIntEquals(0x1234, intAnd0x1ffffToChar(0x43211234));
- assertIntEquals(0, intAnd0x1ffffToChar(Integer.MIN_VALUE));
- assertIntEquals(Character.MAX_VALUE, intAnd0x1ffffToChar(Integer.MAX_VALUE));
- assertIntEquals(0x4321, intAnd0x17fffToShort(0x87654321));
- assertIntEquals(0x0888, intAnd0x17fffToShort(0x88888888));
- assertIntEquals(0, intAnd0x17fffToShort(Integer.MIN_VALUE));
- assertIntEquals(Short.MAX_VALUE, intAnd0x17fffToShort(Integer.MAX_VALUE));
+ assertIntEquals(0x21, $noinline$longAnd0xffToByte(0x1234432112344321L));
+ assertIntEquals(0, $noinline$longAnd0xffToByte(Long.MIN_VALUE));
+ assertIntEquals(-1, $noinline$longAnd0xffToByte(Long.MAX_VALUE));
+ assertIntEquals(0x1234, $noinline$intAnd0x1ffffToChar(0x43211234));
+ assertIntEquals(0, $noinline$intAnd0x1ffffToChar(Integer.MIN_VALUE));
+ assertIntEquals(Character.MAX_VALUE, $noinline$intAnd0x1ffffToChar(Integer.MAX_VALUE));
+ assertIntEquals(0x4321, $noinline$intAnd0x17fffToShort(0x87654321));
+ assertIntEquals(0x0888, $noinline$intAnd0x17fffToShort(0x88888888));
+ assertIntEquals(0, $noinline$intAnd0x17fffToShort(Integer.MIN_VALUE));
+ assertIntEquals(Short.MAX_VALUE, $noinline$intAnd0x17fffToShort(Integer.MAX_VALUE));
- assertDoubleEquals(0.0, shortAnd0xffffToShortToDouble((short) 0));
- assertDoubleEquals(1.0, shortAnd0xffffToShortToDouble((short) 1));
- assertDoubleEquals(-2.0, shortAnd0xffffToShortToDouble((short) -2));
- assertDoubleEquals(12345.0, shortAnd0xffffToShortToDouble((short) 12345));
- assertDoubleEquals((double)Short.MAX_VALUE, shortAnd0xffffToShortToDouble(Short.MAX_VALUE));
- assertDoubleEquals((double)Short.MIN_VALUE, shortAnd0xffffToShortToDouble(Short.MIN_VALUE));
+ assertDoubleEquals(0.0, $noinline$shortAnd0xffffToShortToDouble((short) 0));
+ assertDoubleEquals(1.0, $noinline$shortAnd0xffffToShortToDouble((short) 1));
+ assertDoubleEquals(-2.0, $noinline$shortAnd0xffffToShortToDouble((short) -2));
+ assertDoubleEquals(12345.0, $noinline$shortAnd0xffffToShortToDouble((short) 12345));
+ assertDoubleEquals((double)Short.MAX_VALUE,
+ $noinline$shortAnd0xffffToShortToDouble(Short.MAX_VALUE));
+ assertDoubleEquals((double)Short.MIN_VALUE,
+ $noinline$shortAnd0xffffToShortToDouble(Short.MIN_VALUE));
- assertIntEquals(intReverseCondition(41), 13);
- assertIntEquals(intReverseConditionNaN(-5), 13);
+ assertIntEquals(13, $noinline$intReverseCondition(41));
+ assertIntEquals(13, $noinline$intReverseConditionNaN(-5));
for (String condition : new String[] { "Equal", "NotEqual" }) {
for (String constant : new String[] { "True", "False" }) {
for (String side : new String[] { "Rhs", "Lhs" }) {
String name = condition + constant + side;
- assertIntEquals(runSmaliTest(name, true), 5);
- assertIntEquals(runSmaliTest(name, false), 3);
+ assertIntEquals(5, $noinline$runSmaliTest(name, true));
+ assertIntEquals(3, $noinline$runSmaliTest(name, false));
}
}
}
+
+ assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3));
+ assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3 + 32));
+ assertLongEquals(0xffffffffffffeaf3L, $noinline$longUnnecessaryShiftMasking(0xabcdef0123456789L, 50));
+ assertLongEquals(0xffffffffffffeaf3L, $noinline$longUnnecessaryShiftMasking(0xabcdef0123456789L, 50 + 64));
+ assertIntEquals(0x2af37b, $noinline$intUnnecessaryWiderShiftMasking(0xabcdef01, 10));
+ assertIntEquals(0x2af37b, $noinline$intUnnecessaryWiderShiftMasking(0xabcdef01, 10 + 128));
+ assertLongEquals(0xaf37bc048d159e24L, $noinline$longSmallerShiftMasking(0xabcdef0123456789L, 2));
+ assertLongEquals(0xaf37bc048d159e24L, $noinline$longSmallerShiftMasking(0xabcdef0123456789L, 2 + 256));
+ assertIntEquals(0xfffd5e7c, $noinline$otherUseOfUnnecessaryShiftMasking(0xabcdef01, 13));
+ assertIntEquals(0xfffd5e7c, $noinline$otherUseOfUnnecessaryShiftMasking(0xabcdef01, 13 + 512));
}
private static boolean $inline$true() { return true; }
diff --git a/test/478-checker-clinit-check-pruning/expected.txt b/test/478-checker-clinit-check-pruning/expected.txt
index 7de097f..6f73b65 100644
--- a/test/478-checker-clinit-check-pruning/expected.txt
+++ b/test/478-checker-clinit-check-pruning/expected.txt
@@ -10,3 +10,4 @@
Main$ClassWithClinit10's static initializer
Main$ClassWithClinit11's static initializer
Main$ClassWithClinit12's static initializer
+Main$ClassWithClinit13's static initializer
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 7993513..6fc12f1 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -16,6 +16,8 @@
public class Main {
+ static boolean doThrow = false;
+
/*
* Ensure an inlined static invoke explicitly triggers the
* initialization check of the called method's declaring class, and
@@ -310,12 +312,12 @@
/// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before)
/// CHECK-NOT: ClinitCheck
- static void constClassAndInvokeStatic(Iterable it) {
+ static void constClassAndInvokeStatic(Iterable<?> it) {
$opt$inline$ignoreClass(ClassWithClinit7.class);
ClassWithClinit7.someStaticMethod(it);
}
- static void $opt$inline$ignoreClass(Class c) {
+ static void $opt$inline$ignoreClass(Class<?> c) {
}
static class ClassWithClinit7 {
@@ -324,7 +326,7 @@
}
// Note: not inlined from constClassAndInvokeStatic() but fully inlined from main().
- static void someStaticMethod(Iterable it) {
+ static void someStaticMethod(Iterable<?> it) {
// We're not inlining invoke-interface at the moment.
it.iterator();
}
@@ -341,7 +343,7 @@
/// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before)
/// CHECK-NOT: ClinitCheck
- static void sgetAndInvokeStatic(Iterable it) {
+ static void sgetAndInvokeStatic(Iterable<?> it) {
$opt$inline$ignoreInt(ClassWithClinit8.value);
ClassWithClinit8.someStaticMethod(it);
}
@@ -356,7 +358,7 @@
}
// Note: not inlined from sgetAndInvokeStatic() but fully inlined from main().
- static void someStaticMethod(Iterable it) {
+ static void someStaticMethod(Iterable<?> it) {
// We're not inlining invoke-interface at the moment.
it.iterator();
}
@@ -372,7 +374,7 @@
/// CHECK: ClinitCheck
/// CHECK: InvokeStaticOrDirect clinit_check:none
- static void constClassSgetAndInvokeStatic(Iterable it) {
+ static void constClassSgetAndInvokeStatic(Iterable<?> it) {
$opt$inline$ignoreClass(ClassWithClinit9.class);
$opt$inline$ignoreInt(ClassWithClinit9.value);
ClassWithClinit9.someStaticMethod(it);
@@ -385,7 +387,7 @@
}
// Note: not inlined from constClassSgetAndInvokeStatic() but fully inlined from main().
- static void someStaticMethod(Iterable it) {
+ static void someStaticMethod(Iterable<?> it) {
// We're not inlining invoke-interface at the moment.
it.iterator();
}
@@ -403,12 +405,12 @@
/// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before)
/// CHECK-NOT: ClinitCheck
- static void inlinedInvokeStaticViaNonStatic(Iterable it) {
+ static void inlinedInvokeStaticViaNonStatic(Iterable<?> it) {
inlinedInvokeStaticViaNonStaticHelper(null);
inlinedInvokeStaticViaNonStaticHelper(it);
}
- static void inlinedInvokeStaticViaNonStaticHelper(Iterable it) {
+ static void inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it) {
ClassWithClinit10.inlinedForNull(it);
}
@@ -418,7 +420,7 @@
System.out.println("Main$ClassWithClinit10's static initializer");
}
- static void inlinedForNull(Iterable it) {
+ static void inlinedForNull(Iterable<?> it) {
if (it != null) {
// We're not inlining invoke-interface at the moment.
it.iterator();
@@ -443,7 +445,7 @@
/// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before)
/// CHECK-NOT: ClinitCheck
- static void inlinedInvokeStaticViaStatic(Iterable it) {
+ static void inlinedInvokeStaticViaStatic(Iterable<?> it) {
ClassWithClinit11.callInlinedForNull(it);
}
@@ -453,11 +455,11 @@
System.out.println("Main$ClassWithClinit11's static initializer");
}
- static void callInlinedForNull(Iterable it) {
+ static void callInlinedForNull(Iterable<?> it) {
inlinedForNull(it);
}
- static void inlinedForNull(Iterable it) {
+ static void inlinedForNull(Iterable<?> it) {
// We're not inlining invoke-interface at the moment.
it.iterator();
}
@@ -475,7 +477,7 @@
/// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before)
/// CHECK-NOT: ClinitCheck
- static void inlinedInvokeStaticViaStaticTwice(Iterable it) {
+ static void inlinedInvokeStaticViaStaticTwice(Iterable<?> it) {
ClassWithClinit12.callInlinedForNull(null);
ClassWithClinit12.callInlinedForNull(it);
}
@@ -486,11 +488,11 @@
System.out.println("Main$ClassWithClinit12's static initializer");
}
- static void callInlinedForNull(Iterable it) {
+ static void callInlinedForNull(Iterable<?> it) {
inlinedForNull(it);
}
- static void inlinedForNull(Iterable it) {
+ static void inlinedForNull(Iterable<?> it) {
if (it != null) {
// We're not inlining invoke-interface at the moment.
it.iterator();
@@ -498,6 +500,28 @@
}
}
+ static class ClassWithClinit13 {
+ static {
+ System.out.println("Main$ClassWithClinit13's static initializer");
+ }
+
+ public static void $inline$forwardToGetIterator(Iterable<?> it) {
+ $noinline$getIterator(it);
+ }
+
+ public static void $noinline$getIterator(Iterable<?> it) {
+ // We're not inlining invoke-interface at the moment.
+ it.iterator();
+ }
+ }
+
+ // TODO: Write checker statements.
+ static Object $noinline$testInliningAndNewInstance(Iterable<?> it) {
+ if (doThrow) { throw new Error(); }
+ ClassWithClinit13.$inline$forwardToGetIterator(it);
+ return new ClassWithClinit13();
+ }
+
// TODO: Add a test for the case of a static method whose declaring
// class type index is not available (i.e. when `storage_index`
// equals `DexFile::kDexNoIndex` in
@@ -517,5 +541,6 @@
inlinedInvokeStaticViaNonStatic(it);
inlinedInvokeStaticViaStatic(it);
inlinedInvokeStaticViaStaticTwice(it);
+ $noinline$testInliningAndNewInstance(it);
}
}
diff --git a/test/530-checker-loops1/src/Main.java b/test/530-checker-loops1/src/Main.java
index 948a7b7..dde4d62 100644
--- a/test/530-checker-loops1/src/Main.java
+++ b/test/530-checker-loops1/src/Main.java
@@ -562,7 +562,7 @@
//
/// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ /// CHECK-NOT: Deoptimize
private static void linearTriangularOnTwoArrayLengths(int n) {
int[] a = new int[n];
for (int i = 0; i < a.length; i++) {
@@ -604,7 +604,7 @@
//
/// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ /// CHECK-NOT: Deoptimize
private static void linearTriangularOnParameter(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++) {
@@ -619,56 +619,56 @@
}
}
- /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (before)
+ /// CHECK-START: void Main.linearTriangularStrictLower(int) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
//
- /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (after)
+ /// CHECK-START: void Main.linearTriangularStrictLower(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- // TODO: also CHECK-NOT: Deoptimize, see b/27151190
- private static void linearTriangularVariationsInnerStrict(int n) {
+ /// CHECK-NOT: Deoptimize
+ private static void linearTriangularStrictLower(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
a[j] += 1;
}
- for (int j = i - 1; j > -1; j--) {
+ for (int j = i - 1; j >= 0; j--) {
a[j] += 1;
}
for (int j = i; j < n; j++) {
a[j] += 1;
}
- for (int j = n - 1; j > i - 1; j--) {
+ for (int j = n - 1; j >= i; j--) {
a[j] += 1;
}
}
verifyTriangular(a);
}
- /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (before)
+ /// CHECK-START: void Main.linearTriangularStrictUpper(int) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
//
- /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (after)
+ /// CHECK-START: void Main.linearTriangularStrictUpper(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- // TODO: also CHECK-NOT: Deoptimize, see b/27151190
- private static void linearTriangularVariationsInnerNonStrict(int n) {
+ /// CHECK-NOT: Deoptimize
+ private static void linearTriangularStrictUpper(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++) {
- for (int j = 0; j <= i - 1; j++) {
+ for (int j = 0; j <= i; j++) {
a[j] += 1;
}
- for (int j = i - 1; j >= 0; j--) {
+ for (int j = i; j >= 0; j--) {
a[j] += 1;
}
- for (int j = i; j <= n - 1; j++) {
+ for (int j = i + 1; j < n; j++) {
a[j] += 1;
}
- for (int j = n - 1; j >= i; j--) {
+ for (int j = n - 1; j >= i + 1; j--) {
a[j] += 1;
}
}
@@ -802,8 +802,8 @@
linearTriangularOnTwoArrayLengths(10);
linearTriangularOnOneArrayLength(10);
linearTriangularOnParameter(10);
- linearTriangularVariationsInnerStrict(10);
- linearTriangularVariationsInnerNonStrict(10);
+ linearTriangularStrictLower(10);
+ linearTriangularStrictUpper(10);
{
int[] t = linearTriangularOOB();
for (int i = 0; i < 200; i++) {
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index b12fbd6..7acf008 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -31,7 +31,7 @@
//
/// CHECK-START: void Main.bubble(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
- // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ /// CHECK-NOT: Deoptimize
private static void bubble(int[] a) {
for (int i = a.length; --i >= 0;) {
for (int j = 0; j < i; j++) {
@@ -301,6 +301,53 @@
} while (-1 <= i);
}
+ /// CHECK-START: void Main.justRightTriangular1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.justRightTriangular1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static void justRightTriangular1() {
+ int[] a = { 1 } ;
+ for (int i = Integer.MIN_VALUE + 5; i <= Integer.MIN_VALUE + 10; i++) {
+ for (int j = Integer.MIN_VALUE + 4; j < i - 5; j++) {
+ sResult += a[j - (Integer.MIN_VALUE + 4)];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.justRightTriangular2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.justRightTriangular2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static void justRightTriangular2() {
+ int[] a = { 1 } ;
+ for (int i = Integer.MIN_VALUE + 5; i <= 10; i++) {
+ for (int j = 4; j < i - 5; j++) {
+ sResult += a[j - 4];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.justOOBTriangular() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.justOOBTriangular() BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.justOOBTriangular() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void justOOBTriangular() {
+ int[] a = { 1 } ;
+ for (int i = Integer.MIN_VALUE + 4; i <= 10; i++) {
+ for (int j = 4; j < i - 5; j++) {
+ sResult += a[j - 4];
+ }
+ }
+ }
+
/// CHECK-START: void Main.hiddenOOB1(int) BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -315,7 +362,6 @@
// Dangerous loop where careless static range analysis would yield strict upper bound
// on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound
// becomes really positive due to arithmetic wrap-around, causing OOB.
- // Dynamic BCE is feasible though, since it checks the range.
for (int j = 4; j < i - 5; j++) {
sResult += a[j - 4];
}
@@ -336,13 +382,32 @@
// Dangerous loop where careless static range analysis would yield strict lower bound
// on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound
// becomes really negative due to arithmetic wrap-around, causing OOB.
- // Dynamic BCE is feasible though, since it checks the range.
for (int j = 6; j > i + 5; j--) {
sResult += a[j - 6];
}
}
}
+ /// CHECK-START: void Main.hiddenOOB3(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenOOB3(int) BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenOOB3(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenOOB3(int hi) {
+ int[] a = { 11 } ;
+ for (int i = -1; i <= hi; i++) {
+ // Dangerous loop where careless static range analysis would yield strict lower bound
+ // on index j of 0. For large i, the initial value of j becomes really negative due
+ // to arithmetic wrap-around, causing OOB.
+ for (int j = i + 1; j < 1; j++) {
+ sResult += a[j];
+ }
+ }
+ }
+
/// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -376,7 +441,6 @@
for (int i = -1; i <= 0; i++) {
// Dangerous loop similar as above where the loop is now finite, but the
// loop still goes out of bounds for i = -1 due to the large upper bound.
- // Dynamic BCE is feasible though, since it checks the range.
for (int j = -4; j < 2147483646 * i - 3; j++) {
sResult += a[j + 4];
}
@@ -432,6 +496,25 @@
}
}
+ /// CHECK-START: int Main.doNotHoist(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.doNotHoist(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static int doNotHoist(int[] a) {
+ int n = a.length;
+ int x = 0;
+ // BCE applies, but hoisting would crash the loop.
+ for (int i = -10000; i < 10000; i++) {
+ for (int j = 0; j <= 1; j++) {
+ if (0 <= i && i < n)
+ x += a[i];
+ }
+ }
+ return x;
+ }
+
+
/// CHECK-START: int[] Main.add() BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -687,7 +770,7 @@
/// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
// Order matters:
/// CHECK: Deoptimize loop:<<Loop:B\d+>>
- // CHECK-NOT: Goto loop:<<Loop>>
+ /// CHECK-NOT: Goto loop:<<Loop>>
/// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
/// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
/// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
@@ -839,6 +922,8 @@
expectEquals(55, justRightDown1());
expectEquals(55, justRightDown2());
expectEquals(55, justRightDown3());
+
+ // Large bounds OOB.
sResult = 0;
try {
justOOBUp();
@@ -890,6 +975,23 @@
}
expectEquals(1055, sResult);
+ // Triangular.
+ sResult = 0;
+ justRightTriangular1();
+ expectEquals(1, sResult);
+ if (HEAVY) {
+ sResult = 0;
+ justRightTriangular2();
+ expectEquals(1, sResult);
+ }
+ sResult = 0;
+ try {
+ justOOBTriangular();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1001, sResult);
+
// Hidden OOB.
sResult = 0;
try {
@@ -912,6 +1014,15 @@
sResult += 1000;
}
expectEquals(1, sResult);
+ sResult = 0;
+ try {
+ hiddenOOB3(-1); // no OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(11, sResult);
+
+ // Expensive hidden OOB test.
if (HEAVY) {
sResult = 0;
try {
@@ -920,7 +1031,16 @@
sResult += 1000;
}
expectEquals(1002, sResult);
+ sResult = 0;
+ try {
+ hiddenOOB3(2147483647); // OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1011, sResult);
}
+
+ // More hidden OOB.
sResult = 0;
try {
hiddenInfiniteOOB();
@@ -966,6 +1086,9 @@
expectEquals(i < 128 ? i : 0, a200[i]);
}
+ // No hoisting after BCE.
+ expectEquals(110, doNotHoist(x));
+
// Addition.
{
int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 };
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 24ed2fe..26475ae 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -30,6 +30,18 @@
}
}
+ public static void assertCharEquals(char expected, char result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertStringContains(String searchTerm, String result) {
+ if (result == null || !result.contains(searchTerm)) {
+ throw new Error("Search term: " + searchTerm + ", not found in: " + result);
+ }
+ }
+
public static void main(String[] args) {
stringEqualsSame();
stringArgumentNotNull("Foo");
@@ -41,6 +53,57 @@
assertBooleanEquals(true, $opt$noinline$isStringEmpty(""));
assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc"));
assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789"));
+
+ assertCharEquals('a', $opt$noinline$stringCharAt("a", 0));
+ assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0));
+ assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1));
+ assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2));
+ assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7));
+
+ try {
+ $opt$noinline$stringCharAt("abc", -1);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
+ }
+ try {
+ $opt$noinline$stringCharAt("abc", 3);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
+ }
+ try {
+ $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
+ }
+
+ assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7));
+ assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10));
+
+ assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc"));
+ assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3));
+ try {
+ $opt$noinline$stringSumLeadingChars("abcdef", 7);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringSumLeadingChars",
+ sioob.getStackTrace()[1].toString());
+ }
+ assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef"));
+ try {
+ $opt$noinline$stringSum4LeadingChars("abc");
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringSum4LeadingChars",
+ sioob.getStackTrace()[1].toString());
+ }
}
/// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (before)
@@ -81,6 +144,144 @@
return s.isEmpty();
}
+ /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<String:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Pos:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+ /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+ /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ static public char $opt$noinline$stringCharAt(String s, int pos) {
+ if (doThrow) { throw new Error(); }
+ return s.charAt(pos);
+ }
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<String:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Pos:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+ /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+ /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ static public char $opt$noinline$stringCharAtCatch(String s, int pos) {
+ if (doThrow) { throw new Error(); }
+ try {
+ return s.charAt(pos);
+ } catch (StringIndexOutOfBoundsException ignored) {
+ return '\0';
+ }
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringLength
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringLength
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-NOT: ArrayLength is_string_length:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+
+ static public int $opt$noinline$stringSumChars(String s) {
+ if (doThrow) { throw new Error(); }
+ int sum = 0;
+ int len = s.length();
+ for (int i = 0; i < len; ++i) {
+ sum += s.charAt(i);
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
+ /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]]
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
+ /// CHECK-NOT: BoundsCheck is_string_char_at:true
+
+ static public int $opt$noinline$stringSumLeadingChars(String s, int n) {
+ if (doThrow) { throw new Error(); }
+ int sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += s.charAt(i);
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
+ /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]]
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
+ /// CHECK-NOT: BoundsCheck is_string_char_at:true
+
+ static public int $opt$noinline$stringSum4LeadingChars(String s) {
+ if (doThrow) { throw new Error(); }
+ int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3);
+ return sum;
+ }
+
/// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
/// CHECK: InvokeStaticOrDirect
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 3d985bf..09a77ed 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -28,6 +28,12 @@
}
}
+ public static void assertClassEquals(Class<?> expected, Class<?> result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
public static boolean doThrow = false;
private static int $noinline$foo(int x) {
@@ -251,6 +257,66 @@
return "non-boot-image-string";
}
+ /// CHECK-START: java.lang.Class Main.$noinline$getStringClass() sharpening (before)
+ /// CHECK: LoadClass load_kind:DexCacheViaMethod class_name:java.lang.String
+
+ /// CHECK-START-X86: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
+ /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
+ /// CHECK-START-ARM: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
+ /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
+ public static Class<?> $noinline$getStringClass() {
+ // Prevent inlining to avoid the string comparison being optimized away.
+ if (doThrow) { throw new Error(); }
+ // String class is known to be in the boot image.
+ return String.class;
+ }
+
+ /// CHECK-START: java.lang.Class Main.$noinline$getOtherClass() sharpening (before)
+ /// CHECK: LoadClass load_kind:DexCacheViaMethod class_name:Other
+
+ /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+ /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other
+
+ /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (after)
+ /// CHECK-DAG: X86ComputeBaseMethodAddress
+ /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other
+
+ /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+ /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other
+
+ /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+ /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other
+
+ /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_arm (after)
+ /// CHECK-DAG: ArmDexCacheArraysBase
+ /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other
+
+ /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+ /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other
+
+ public static Class<?> $noinline$getOtherClass() {
+ // Prevent inlining to avoid the string comparison being optimized away.
+ if (doThrow) { throw new Error(); }
+ // Other class is not in the boot image.
+ return Other.class;
+ }
+
public static void main(String[] args) {
assertIntEquals(1, testSimple(1));
assertIntEquals(1, testDiamond(false, 1));
@@ -262,5 +328,10 @@
assertIntEquals(-6, testLoopWithDiamond(new int[]{ 3, 4 }, true, 1));
assertStringEquals("", $noinline$getBootImageString());
assertStringEquals("non-boot-image-string", $noinline$getNonBootImageString());
+ assertClassEquals(String.class, $noinline$getStringClass());
+ assertClassEquals(Other.class, $noinline$getOtherClass());
}
}
+
+class Other {
+}
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
index 7b2c6cb..c0d93dd 100644
--- a/test/566-polymorphic-inlining/polymorphic_inline.cc
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -17,6 +17,7 @@
#include "art_method.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
+#include "jit/profiling_info.h"
#include "oat_quick_method_header.h"
#include "scoped_thread_state_change.h"
#include "stack_map.h"
@@ -37,8 +38,10 @@
if (code_cache->ContainsPc(header->GetCode())) {
break;
} else {
- // sleep one second to give time to the JIT compiler.
- sleep(1);
+ // Sleep to yield to the compiler thread.
+ usleep(1000);
+ // Will either ensure it's compiled or do the compilation itself.
+ jit->CompileMethod(method, soa.Self(), /* osr */ false);
}
}
@@ -47,7 +50,25 @@
CHECK(info.HasInlineInfo(encoding));
}
-extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline(JNIEnv*, jclass cls) {
+static void allocate_profiling_info(jclass cls, const char* method_name) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+ ArtMethod* method = klass->FindDeclaredDirectMethodByName(method_name, sizeof(void*));
+ ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfilingInfo566(JNIEnv*, jclass cls) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ return;
+ }
+
+ allocate_profiling_info(cls, "testInvokeVirtual");
+ allocate_profiling_info(cls, "testInvokeInterface");
+ allocate_profiling_info(cls, "$noinline$testInlineToSameTarget");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline566(JNIEnv*, jclass cls) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit == nullptr) {
return;
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index a59ce5b..d39e6ed 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -39,6 +39,9 @@
itfs[1] = mains[1] = new Subclass();
itfs[2] = mains[2] = new OtherSubclass();
+ // Create the profiling info eagerly to make sure they are filled.
+ ensureProfilingInfo566();
+
// Make testInvokeVirtual and testInvokeInterface hot to get them jitted.
// We pass Main and Subclass to get polymorphic inlining based on calling
// the same method.
@@ -51,7 +54,7 @@
$noinline$testInlineToSameTarget(mains[1]);
}
- ensureJittedAndPolymorphicInline();
+ ensureJittedAndPolymorphicInline566();
// At this point, the JIT should have compiled both methods, and inline
// sameInvokeVirtual and sameInvokeInterface.
@@ -71,12 +74,12 @@
}
public Class sameInvokeVirtual() {
- field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo.
return Main.class;
}
public Class sameInvokeInterface() {
- field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo.
return Itf.class;
}
@@ -95,7 +98,8 @@
public Object field = new Object();
- public static native void ensureJittedAndPolymorphicInline();
+ public static native void ensureJittedAndPolymorphicInline566();
+ public static native void ensureProfilingInfo566();
public void increment() {
field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java
index 59741d6..e0a76ca 100644
--- a/test/570-checker-select/src/Main.java
+++ b/test/570-checker-select/src/Main.java
@@ -16,6 +16,8 @@
public class Main {
+ static boolean doThrow = false;
+
/// CHECK-START: int Main.BoolCond_IntVarVar(boolean, int, int) register (after)
/// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}]
@@ -35,6 +37,10 @@
/// CHECK: cmovnz/ne
public static int BoolCond_IntVarVar(boolean cond, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? x : y;
}
@@ -57,6 +63,10 @@
/// CHECK: cmovnz/ne
public static int BoolCond_IntVarCst(boolean cond, int x) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? x : 1;
}
@@ -79,6 +89,10 @@
/// CHECK: cmovnz/ne
public static int BoolCond_IntCstVar(boolean cond, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? 1 : y;
}
@@ -102,6 +116,10 @@
/// CHECK-NEXT: cmovnz/ne
public static long BoolCond_LongVarVar(boolean cond, long x, long y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? x : y;
}
@@ -125,6 +143,10 @@
/// CHECK-NEXT: cmovnz/ne
public static long BoolCond_LongVarCst(boolean cond, long x) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? x : 1L;
}
@@ -148,6 +170,10 @@
/// CHECK-NEXT: cmovnz/ne
public static long BoolCond_LongCstVar(boolean cond, long y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? 1L : y;
}
@@ -160,6 +186,10 @@
/// CHECK-NEXT: fcsel ne
public static float BoolCond_FloatVarVar(boolean cond, float x, float y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? x : y;
}
@@ -172,6 +202,10 @@
/// CHECK-NEXT: fcsel ne
public static float BoolCond_FloatVarCst(boolean cond, float x) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? x : 1.0f;
}
@@ -184,6 +218,10 @@
/// CHECK-NEXT: fcsel ne
public static float BoolCond_FloatCstVar(boolean cond, float y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return cond ? 1.0f : y;
}
@@ -207,6 +245,10 @@
/// CHECK: cmovle/ng
public static int IntNonmatCond_IntVarVar(int a, int b, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return a > b ? x : y;
}
@@ -233,6 +275,10 @@
/// CHECK: cmovle/ng
public static int IntMatCond_IntVarVar(int a, int b, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
int result = (a > b ? x : y);
return result + (a > b ? 0 : 1);
}
@@ -258,6 +304,10 @@
/// CHECK-NEXT: cmovle/ng
public static long IntNonmatCond_LongVarVar(int a, int b, long x, long y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return a > b ? x : y;
}
@@ -291,6 +341,10 @@
/// CHECK-NEXT: cmovnz/ne
public static long IntMatCond_LongVarVar(int a, int b, long x, long y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
long result = (a > b ? x : y);
return result + (a > b ? 0L : 1L);
}
@@ -310,6 +364,10 @@
/// CHECK: cmovle/ngq
public static long LongNonmatCond_LongVarVar(long a, long b, long x, long y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return a > b ? x : y;
}
@@ -334,6 +392,10 @@
/// CHECK: cmovnz/neq
public static long LongMatCond_LongVarVar(long a, long b, long x, long y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
long result = (a > b ? x : y);
return result + (a > b ? 0L : 1L);
}
@@ -349,6 +411,10 @@
/// CHECK-NEXT: csel le
public static int FloatLtNonmatCond_IntVarVar(float a, float b, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return a > b ? x : y;
}
@@ -363,6 +429,10 @@
/// CHECK-NEXT: csel hs
public static int FloatGtNonmatCond_IntVarVar(float a, float b, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return a < b ? x : y;
}
@@ -377,6 +447,10 @@
/// CHECK-NEXT: fcsel hs
public static float FloatGtNonmatCond_FloatVarVar(float a, float b, float x, float y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
return a < b ? x : y;
}
@@ -393,6 +467,10 @@
/// CHECK-NEXT: csel le
public static int FloatLtMatCond_IntVarVar(float a, float b, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
int result = (a > b ? x : y);
return result + (a > b ? 0 : 1);
}
@@ -410,6 +488,10 @@
/// CHECK-NEXT: csel hs
public static int FloatGtMatCond_IntVarVar(float a, float b, int x, int y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
int result = (a < b ? x : y);
return result + (a < b ? 0 : 1);
}
@@ -427,10 +509,70 @@
/// CHECK-NEXT: fcsel hs
public static float FloatGtMatCond_FloatVarVar(float a, float b, float x, float y) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
float result = (a < b ? x : y);
return result + (a < b ? 0 : 1);
}
+ /// CHECK-START: int Main.BoolCond_0_m1(boolean) register (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+
+ /// CHECK-START-ARM64: int Main.BoolCond_0_m1(boolean) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK-NEXT: cmp {{w\d+}}, #0x0 (0)
+ /// CHECK-NEXT: csetm {{w\d+}}, eq
+
+ /// CHECK-START-X86_64: int Main.BoolCond_0_m1(boolean) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
+ /// CHECK-START-X86: int Main.BoolCond_0_m1(boolean) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
+ public static int BoolCond_0_m1(boolean cond) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
+ return cond ? 0 : -1;
+ }
+
+ /// CHECK-START: int Main.BoolCond_m1_0(boolean) register (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+
+ /// CHECK-START-ARM64: int Main.BoolCond_m1_0(boolean) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK-NEXT: cmp {{w\d+}}, #0x0 (0)
+ /// CHECK-NEXT: csetm {{w\d+}}, ne
+
+ /// CHECK-START-X86_64: int Main.BoolCond_m1_0(boolean) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
+ /// CHECK-START-X86: int Main.BoolCond_m1_0(boolean) disassembly (after)
+ /// CHECK: <<Cond:z\d+>> ParameterValue
+ /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK: cmovnz/ne
+
+ public static int BoolCond_m1_0(boolean cond) {
+ if (doThrow) {
+ // Try defeating inlining.
+ throw new Error();
+ }
+ return cond ? -1 : 0;
+ }
+
public static void assertEqual(int expected, int actual) {
if (expected != actual) {
throw new Error("Assertion failed: " + expected + " != " + actual);
@@ -499,5 +641,10 @@
assertEqual(8, FloatGtMatCond_FloatVarVar(3, 2, 5, 7));
assertEqual(8, FloatGtMatCond_FloatVarVar(Float.NaN, 2, 5, 7));
assertEqual(8, FloatGtMatCond_FloatVarVar(2, Float.NaN, 5, 7));
+
+ assertEqual(0, BoolCond_0_m1(true));
+ assertEqual(-1, BoolCond_0_m1(false));
+ assertEqual(-1, BoolCond_m1_0(true));
+ assertEqual(0, BoolCond_m1_0(false));
}
}
diff --git a/test/600-verifier-fails/expected.txt b/test/600-verifier-fails/expected.txt
index 8399969..eaa0c93 100644
--- a/test/600-verifier-fails/expected.txt
+++ b/test/600-verifier-fails/expected.txt
@@ -2,3 +2,4 @@
passed B
passed C
passed D
+passed E
diff --git a/test/600-verifier-fails/src/Main.java b/test/600-verifier-fails/src/Main.java
index 64c3d5c..fa25d58 100644
--- a/test/600-verifier-fails/src/Main.java
+++ b/test/600-verifier-fails/src/Main.java
@@ -38,7 +38,6 @@
test("B");
test("C");
test("D");
- // TODO: enable again
- // test("E");
+ test("E");
}
}
diff --git a/test/606-erroneous-class/expected.txt b/test/606-erroneous-class/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/606-erroneous-class/expected.txt
diff --git a/test/606-erroneous-class/info.txt b/test/606-erroneous-class/info.txt
new file mode 100644
index 0000000..42cbb7a
--- /dev/null
+++ b/test/606-erroneous-class/info.txt
@@ -0,0 +1,3 @@
+Regression test for a DCHECK in the DexCache which prevented erroneous classes
+from being stored into it. This was bogus because the status of a class can be
+changed by another thread.
\ No newline at end of file
diff --git a/test/606-erroneous-class/smali-multidex/ClassA.smali b/test/606-erroneous-class/smali-multidex/ClassA.smali
new file mode 100644
index 0000000..f87fcb2
--- /dev/null
+++ b/test/606-erroneous-class/smali-multidex/ClassA.smali
@@ -0,0 +1,27 @@
+# Copyright (C) 2016 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.
+
+.class public final LClassA;
+.super Ljava/lang/Object;
+
+.method public static foo()V
+ .registers 1
+ # Obtain the ErrClass type from Dex cache of the first Dex file. Note that
+ # because the first Dex file has already been verified, we know the class
+ # is erroneous at this point.
+ sget-object v0, LClassB;->g:LErrClass;
+ # Use the object in a way that will try to store the ErrClass type in
+ # the Dex cache of the second Dex file.
+ invoke-virtual {v0}, LErrClass;->foo()V
+.end method
diff --git a/test/606-erroneous-class/smali/ClassB.smali b/test/606-erroneous-class/smali/ClassB.smali
new file mode 100644
index 0000000..80754c8
--- /dev/null
+++ b/test/606-erroneous-class/smali/ClassB.smali
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 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.
+
+.class public LClassB;
+.super Ljava/lang/Object;
+
+.field public static g:LErrClass;
diff --git a/test/606-erroneous-class/smali/ErrClass.smali b/test/606-erroneous-class/smali/ErrClass.smali
new file mode 100644
index 0000000..740f1e1
--- /dev/null
+++ b/test/606-erroneous-class/smali/ErrClass.smali
@@ -0,0 +1,26 @@
+# Copyright (C) 2016 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.
+
+.class public final LErrClass;
+.super Ljava/lang/Object;
+
+.field public g:Ljava/lang/Object;
+
+.method public foo()V
+ .registers 6
+ # Use a new instance before initializing it => hard verifier error.
+ new-instance v0, LSomeClass;
+ iput-object v0, p0, LErrClass;->g:Ljava/lang/Object;
+ return-void
+.end method
diff --git a/test/606-erroneous-class/src/Main.java b/test/606-erroneous-class/src/Main.java
new file mode 100644
index 0000000..7dbe567
--- /dev/null
+++ b/test/606-erroneous-class/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ // Nothing to run.
+ }
+}
diff --git a/test/607-daemon-stress/expected.txt b/test/607-daemon-stress/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/607-daemon-stress/expected.txt
diff --git a/test/607-daemon-stress/info.txt b/test/607-daemon-stress/info.txt
new file mode 100644
index 0000000..1047b76
--- /dev/null
+++ b/test/607-daemon-stress/info.txt
@@ -0,0 +1,3 @@
+Stress test for daemon threads stuck in a method that requires the thread list lock.
+(for example Thread.isInterrupted). The shutdown thread used to block those daemons
+from making progress.
diff --git a/test/607-daemon-stress/src/Main.java b/test/607-daemon-stress/src/Main.java
new file mode 100644
index 0000000..56ef410
--- /dev/null
+++ b/test/607-daemon-stress/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main extends Thread {
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 5; i++) {
+ Main m = new Main();
+ m.setDaemon(true);
+ m.start();
+ }
+ // Sleep a while to give some time for the threads to start.
+ Thread.sleep(1000);
+ }
+
+ public void run() {
+ while (!isInterrupted());
+ }
+}
diff --git a/test/608-checker-unresolved-lse/expected.txt b/test/608-checker-unresolved-lse/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/608-checker-unresolved-lse/expected.txt
diff --git a/test/608-checker-unresolved-lse/info.txt b/test/608-checker-unresolved-lse/info.txt
new file mode 100644
index 0000000..466d5f4
--- /dev/null
+++ b/test/608-checker-unresolved-lse/info.txt
@@ -0,0 +1,3 @@
+Regression test for the load store elimination optimization,
+which used to wrongly remove field stores in the presence of
+unresolved accesses.
diff --git a/test/608-checker-unresolved-lse/run b/test/608-checker-unresolved-lse/run
new file mode 100644
index 0000000..226891f
--- /dev/null
+++ b/test/608-checker-unresolved-lse/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 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.
+
+# Use secondary switch to add secondary dex file to class path.
+exec ${RUN} "${@}" --secondary
diff --git a/test/608-checker-unresolved-lse/src-dex2oat-unresolved/MissingSuperClass.java b/test/608-checker-unresolved-lse/src-dex2oat-unresolved/MissingSuperClass.java
new file mode 100644
index 0000000..b11b9be
--- /dev/null
+++ b/test/608-checker-unresolved-lse/src-dex2oat-unresolved/MissingSuperClass.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class MissingSuperClass {
+}
diff --git a/test/608-checker-unresolved-lse/src/Main.java b/test/608-checker-unresolved-lse/src/Main.java
new file mode 100644
index 0000000..c6f8854
--- /dev/null
+++ b/test/608-checker-unresolved-lse/src/Main.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// We make Main extend an unresolved super class. This will lead to an
+// unresolved access to Foo.field, as we won't know if Main can access
+// a package private field.
+public class Main extends MissingSuperClass {
+
+ public static void main(String[] args) {
+ instanceFieldTest();
+ staticFieldTest();
+ instanceFieldTest2();
+ }
+
+ /// CHECK-START: void Main.instanceFieldTest() inliner (before)
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: void Main.instanceFieldTest() inliner (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: UnresolvedInstanceFieldGet
+
+ // Load store elimination used to remove the InstanceFieldSet, thinking
+ // that the UnresolvedInstanceFieldGet was not related. However inlining
+ // can put you in a situation where the UnresolvedInstanceFieldGet resolves
+ // to the same field as the one in InstanceFieldSet. So the InstanceFieldSet
+ // must be preserved.
+
+ /// CHECK-START: void Main.instanceFieldTest() load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: UnresolvedInstanceFieldGet
+ public static void instanceFieldTest() {
+ Foo f = new Foo();
+ if (f.iField != 42) {
+ throw new Error("Expected 42, got " + f.iField);
+ }
+ }
+
+ /// CHECK-START: void Main.instanceFieldTest2() inliner (before)
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldGet
+
+ /// CHECK-START: void Main.instanceFieldTest2() inliner (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: UnresolvedInstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ // Load store elimination will eliminate the first InstanceFieldGet because
+ // it simply follows an InstanceFieldSet. It must however not eliminate the second
+ // InstanceFieldGet, as the UnresolvedInstanceFieldSet might resolve to the same
+ // field.
+
+ /// CHECK-START: void Main.instanceFieldTest2() load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldGet
+ /// CHECK: UnresolvedInstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ public static void instanceFieldTest2() {
+ Foo f = new Foo();
+ int a = f.$inline$GetInstanceField();
+ f.iField = 43;
+ a = f.$inline$GetInstanceField();
+ if (a != 43) {
+ throw new Error("Expected 43, got " + a);
+ }
+ }
+
+ /// CHECK-START: void Main.staticFieldTest() inliner (before)
+ /// CHECK-NOT: StaticFieldSet
+
+ /// CHECK-START: void Main.staticFieldTest() inliner (after)
+ /// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldSet
+ /// CHECK: UnresolvedStaticFieldGet
+
+ /// CHECK-START: void Main.staticFieldTest() load_store_elimination (after)
+ /// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldSet
+ /// CHECK: UnresolvedStaticFieldGet
+ public static void staticFieldTest() {
+ // Ensure Foo is initialized.
+ Foo f = new Foo();
+ f.$inline$StaticSet42();
+ f.$inline$StaticSet43();
+ if (Foo.sField != 43) {
+ throw new Error("Expected 43, got " + Foo.sField);
+ }
+ }
+}
+
+class Foo {
+ // field needs to be package-private to make the access in Main.main
+ // unresolved.
+ int iField;
+ static int sField;
+
+ public void $inline$StaticSet42() {
+ sField = 42;
+ }
+
+ public void $inline$StaticSet43() {
+ sField = 43;
+ }
+
+ public int $inline$GetInstanceField() {
+ return iField;
+ }
+
+ // Constructor needs to be public to get it resolved in Main.main
+ // and therefore inlined.
+ public Foo() {
+ iField = 42;
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index feee7c2..01790ae 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -25,6 +25,7 @@
004-SignalTest/signaltest.cc \
004-ReferenceMap/stack_walk_refmap_jni.cc \
004-StackWalk/stack_walk_jni.cc \
+ 004-ThreadStress/thread_stress.cc \
004-UnsafeTest/unsafe_test.cc \
044-proxy/native_proxy.cc \
051-thread/thread_test.cc \
@@ -87,7 +88,11 @@
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk
ifeq ($$(art_target_or_host),target)
$(call set-target-local-clang-vars)
- $(call set-target-local-cflags-vars,debug)
+ ifeq ($$(suffix),d)
+ $(call set-target-local-cflags-vars,debug)
+ else
+ $(call set-target-local-cflags-vars,ndebug)
+ endif
LOCAL_SHARED_LIBRARIES += libdl
LOCAL_MULTILIB := both
LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32)
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ee651b5..dd6b6f3 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -209,9 +209,10 @@
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
-# Disable 137-cfi (b/27391690).
+# Disable 149-suspend-all-stress, its output is flaky (b/28988206).
# Disable 577-profile-foreign-dex (b/27454772).
TEST_ART_BROKEN_ALL_TARGET_TESTS := \
+ 149-suspend-all-stress \
577-profile-foreign-dex \
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -293,7 +294,8 @@
147-stripped-dex-fallback \
554-jit-profile-file \
529-checker-unresolved \
- 555-checker-regression-x86const
+ 555-checker-regression-x86const \
+ 608-checker-unresolved-lse
ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-prebuild, \
diff --git a/test/dexdump/all.dex b/test/dexdump/all.dex
new file mode 100644
index 0000000..caf678d
--- /dev/null
+++ b/test/dexdump/all.dex
Binary files differ
diff --git a/test/dexdump/all.lst b/test/dexdump/all.lst
new file mode 100644
index 0000000..17ab9ca
--- /dev/null
+++ b/test/dexdump/all.lst
@@ -0,0 +1,21 @@
+#all.dex
+0x0000043c 8 A <init> ()V (none) -1
+0x00000454 58 A arrays ()V (none) -1
+0x000004a0 130 A binary_ops ()V (none) -1
+0x00000534 66 A binary_ops_2addr ()V (none) -1
+0x00000588 34 A binary_ops_lit16 ()V (none) -1
+0x000005bc 46 A binary_ops_lit8 ()V (none) -1
+0x000005fc 22 A compares ()V (none) -1
+0x00000624 50 A conditionals ()V (none) -1
+0x00000668 56 A constants ()V (none) -1
+0x000006b0 108 A misc ()V (none) -1
+0x0000072c 46 A moves ()V (none) -1
+0x0000076c 32 A packed_switch ()V (none) -1
+0x0000079c 2 A return32 ()I (none) -1
+0x000007b0 2 A return64 ()I (none) -1
+0x000007c4 2 A return_object ()Ljava/lang/Object; (none) -1
+0x000007d8 44 A sparse_switch ()V (none) -1
+0x00000814 58 A static_fields ()V (none) -1
+0x00000860 44 A unary_ops ()V (none) -1
+0x0000089c 58 A instance_fields ()V (none) -1
+0x000008e8 30 A invokes ()V (none) -1
diff --git a/test/dexdump/all.txt b/test/dexdump/all.txt
new file mode 100644
index 0000000..af4fb4c
--- /dev/null
+++ b/test/dexdump/all.txt
@@ -0,0 +1,622 @@
+Processing 'all.dex'...
+Opened 'all.dex', DEX version '035'
+DEX file header:
+magic : 'dex\n035\0'
+checksum : d5134208
+signature : 7af6...100f
+file_size : 2572
+header_size : 112
+link_size : 0
+link_off : 0 (0x000000)
+string_ids_size : 46
+string_ids_off : 112 (0x000070)
+type_ids_size : 10
+type_ids_off : 296 (0x000128)
+proto_ids_size : 3
+proto_ids_off : 336 (0x000150)
+field_ids_size : 14
+field_ids_off : 372 (0x000174)
+method_ids_size : 21
+method_ids_off : 484 (0x0001e4)
+class_defs_size : 1
+class_defs_off : 652 (0x00028c)
+data_size : 1888
+data_off : 684 (0x0002ac)
+
+Class #0 header:
+class_idx : 4
+access_flags : 1 (0x0001)
+superclass_idx : 5
+interfaces_off : 0 (0x000000)
+source_file_idx : -1
+annotations_off : 0 (0x000000)
+class_data_off : 2310 (0x000906)
+static_fields_size : 7
+instance_fields_size: 7
+direct_methods_size : 18
+virtual_methods_size: 2
+
+Class #0 -
+ Class descriptor : 'LA;'
+ Access flags : 0x0001 (PUBLIC)
+ Superclass : 'Ljava/lang/Object;'
+ Interfaces -
+ Static fields -
+ #0 : (in LA;)
+ name : 'sB'
+ type : 'B'
+ access : 0x000a (PRIVATE STATIC)
+ #1 : (in LA;)
+ name : 'sC'
+ type : 'C'
+ access : 0x000a (PRIVATE STATIC)
+ #2 : (in LA;)
+ name : 'sI'
+ type : 'I'
+ access : 0x000a (PRIVATE STATIC)
+ #3 : (in LA;)
+ name : 'sJ'
+ type : 'J'
+ access : 0x000a (PRIVATE STATIC)
+ #4 : (in LA;)
+ name : 'sO'
+ type : 'LA;'
+ access : 0x000a (PRIVATE STATIC)
+ #5 : (in LA;)
+ name : 'sS'
+ type : 'S'
+ access : 0x000a (PRIVATE STATIC)
+ #6 : (in LA;)
+ name : 'sZ'
+ type : 'Z'
+ access : 0x000a (PRIVATE STATIC)
+ Instance fields -
+ #0 : (in LA;)
+ name : 'mB'
+ type : 'B'
+ access : 0x0002 (PRIVATE)
+ #1 : (in LA;)
+ name : 'mC'
+ type : 'C'
+ access : 0x0002 (PRIVATE)
+ #2 : (in LA;)
+ name : 'mI'
+ type : 'I'
+ access : 0x0002 (PRIVATE)
+ #3 : (in LA;)
+ name : 'mJ'
+ type : 'J'
+ access : 0x0002 (PRIVATE)
+ #4 : (in LA;)
+ name : 'mO'
+ type : 'LA;'
+ access : 0x0002 (PRIVATE)
+ #5 : (in LA;)
+ name : 'mS'
+ type : 'S'
+ access : 0x0002 (PRIVATE)
+ #6 : (in LA;)
+ name : 'mZ'
+ type : 'Z'
+ access : 0x0002 (PRIVATE)
+ Direct methods -
+ #0 : (in LA;)
+ name : '<init>'
+ type : '()V'
+ access : 0x10001 (PUBLIC CONSTRUCTOR)
+ code -
+ registers : 1
+ ins : 1
+ outs : 1
+ insns size : 4 16-bit code units
+00042c: |[00042c] A.<init>:()V
+00043c: 7010 1400 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0014
+000442: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #1 : (in LA;)
+ name : 'arrays'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 3
+ ins : 0
+ outs : 0
+ insns size : 29 16-bit code units
+000444: |[000444] A.arrays:()V
+000454: 4400 0102 |0000: aget v0, v1, v2
+000458: 4500 0102 |0002: aget-wide v0, v1, v2
+00045c: 4600 0102 |0004: aget-object v0, v1, v2
+000460: 4700 0102 |0006: aget-boolean v0, v1, v2
+000464: 4800 0102 |0008: aget-byte v0, v1, v2
+000468: 4900 0102 |000a: aget-char v0, v1, v2
+00046c: 4a00 0102 |000c: aget-short v0, v1, v2
+000470: 4b00 0102 |000e: aput v0, v1, v2
+000474: 4c00 0102 |0010: aput-wide v0, v1, v2
+000478: 4d00 0102 |0012: aput-object v0, v1, v2
+00047c: 4e00 0102 |0014: aput-boolean v0, v1, v2
+000480: 4f00 0102 |0016: aput-byte v0, v1, v2
+000484: 5000 0102 |0018: aput-char v0, v1, v2
+000488: 5100 0102 |001a: aput-short v0, v1, v2
+00048c: 0e00 |001c: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #2 : (in LA;)
+ name : 'binary_ops'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 3
+ ins : 0
+ outs : 0
+ insns size : 65 16-bit code units
+000490: |[000490] A.binary_ops:()V
+0004a0: 9000 0102 |0000: add-int v0, v1, v2
+0004a4: 9100 0102 |0002: sub-int v0, v1, v2
+0004a8: 9200 0102 |0004: mul-int v0, v1, v2
+0004ac: 9300 0102 |0006: div-int v0, v1, v2
+0004b0: 9400 0102 |0008: rem-int v0, v1, v2
+0004b4: 9500 0102 |000a: and-int v0, v1, v2
+0004b8: 9600 0102 |000c: or-int v0, v1, v2
+0004bc: 9700 0102 |000e: xor-int v0, v1, v2
+0004c0: 9800 0102 |0010: shl-int v0, v1, v2
+0004c4: 9900 0102 |0012: shr-int v0, v1, v2
+0004c8: 9a00 0102 |0014: ushr-int v0, v1, v2
+0004cc: 9b00 0102 |0016: add-long v0, v1, v2
+0004d0: 9c00 0102 |0018: sub-long v0, v1, v2
+0004d4: 9d00 0102 |001a: mul-long v0, v1, v2
+0004d8: 9e00 0102 |001c: div-long v0, v1, v2
+0004dc: 9f00 0102 |001e: rem-long v0, v1, v2
+0004e0: a000 0102 |0020: and-long v0, v1, v2
+0004e4: a100 0102 |0022: or-long v0, v1, v2
+0004e8: a200 0102 |0024: xor-long v0, v1, v2
+0004ec: a300 0102 |0026: shl-long v0, v1, v2
+0004f0: a400 0102 |0028: shr-long v0, v1, v2
+0004f4: a500 0102 |002a: ushr-long v0, v1, v2
+0004f8: a600 0102 |002c: add-float v0, v1, v2
+0004fc: a700 0102 |002e: sub-float v0, v1, v2
+000500: a800 0102 |0030: mul-float v0, v1, v2
+000504: a900 0102 |0032: div-float v0, v1, v2
+000508: aa00 0102 |0034: rem-float v0, v1, v2
+00050c: ab00 0102 |0036: add-double v0, v1, v2
+000510: ac00 0102 |0038: sub-double v0, v1, v2
+000514: ad00 0102 |003a: mul-double v0, v1, v2
+000518: ae00 0102 |003c: div-double v0, v1, v2
+00051c: af00 0102 |003e: rem-double v0, v1, v2
+000520: 0e00 |0040: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #3 : (in LA;)
+ name : 'binary_ops_2addr'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 33 16-bit code units
+000524: |[000524] A.binary_ops_2addr:()V
+000534: b010 |0000: add-int/2addr v0, v1
+000536: b110 |0001: sub-int/2addr v0, v1
+000538: b210 |0002: mul-int/2addr v0, v1
+00053a: b310 |0003: div-int/2addr v0, v1
+00053c: b410 |0004: rem-int/2addr v0, v1
+00053e: b510 |0005: and-int/2addr v0, v1
+000540: b610 |0006: or-int/2addr v0, v1
+000542: b710 |0007: xor-int/2addr v0, v1
+000544: b810 |0008: shl-int/2addr v0, v1
+000546: b910 |0009: shr-int/2addr v0, v1
+000548: ba10 |000a: ushr-int/2addr v0, v1
+00054a: bb10 |000b: add-long/2addr v0, v1
+00054c: bc10 |000c: sub-long/2addr v0, v1
+00054e: bd10 |000d: mul-long/2addr v0, v1
+000550: be10 |000e: div-long/2addr v0, v1
+000552: bf10 |000f: rem-long/2addr v0, v1
+000554: c010 |0010: and-long/2addr v0, v1
+000556: c110 |0011: or-long/2addr v0, v1
+000558: c210 |0012: xor-long/2addr v0, v1
+00055a: c310 |0013: shl-long/2addr v0, v1
+00055c: c410 |0014: shr-long/2addr v0, v1
+00055e: c510 |0015: ushr-long/2addr v0, v1
+000560: c610 |0016: add-float/2addr v0, v1
+000562: c710 |0017: sub-float/2addr v0, v1
+000564: c810 |0018: mul-float/2addr v0, v1
+000566: c910 |0019: div-float/2addr v0, v1
+000568: ca10 |001a: rem-float/2addr v0, v1
+00056a: cb10 |001b: add-double/2addr v0, v1
+00056c: cc10 |001c: sub-double/2addr v0, v1
+00056e: cd10 |001d: mul-double/2addr v0, v1
+000570: ce10 |001e: div-double/2addr v0, v1
+000572: cf10 |001f: rem-double/2addr v0, v1
+000574: 0e00 |0020: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #4 : (in LA;)
+ name : 'binary_ops_lit16'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 17 16-bit code units
+000578: |[000578] A.binary_ops_lit16:()V
+000588: d010 3412 |0000: add-int/lit16 v0, v1, #int 4660 // #1234
+00058c: d110 3412 |0002: rsub-int v0, v1, #int 4660 // #1234
+000590: d210 3412 |0004: mul-int/lit16 v0, v1, #int 4660 // #1234
+000594: d310 3412 |0006: div-int/lit16 v0, v1, #int 4660 // #1234
+000598: d410 3412 |0008: rem-int/lit16 v0, v1, #int 4660 // #1234
+00059c: d510 3412 |000a: and-int/lit16 v0, v1, #int 4660 // #1234
+0005a0: d610 3412 |000c: or-int/lit16 v0, v1, #int 4660 // #1234
+0005a4: d710 3412 |000e: xor-int/lit16 v0, v1, #int 4660 // #1234
+0005a8: 0e00 |0010: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #5 : (in LA;)
+ name : 'binary_ops_lit8'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 23 16-bit code units
+0005ac: |[0005ac] A.binary_ops_lit8:()V
+0005bc: d800 0112 |0000: add-int/lit8 v0, v1, #int 18 // #12
+0005c0: d900 0112 |0002: rsub-int/lit8 v0, v1, #int 18 // #12
+0005c4: da00 0112 |0004: mul-int/lit8 v0, v1, #int 18 // #12
+0005c8: db00 0112 |0006: div-int/lit8 v0, v1, #int 18 // #12
+0005cc: dc00 0112 |0008: rem-int/lit8 v0, v1, #int 18 // #12
+0005d0: dd00 0112 |000a: and-int/lit8 v0, v1, #int 18 // #12
+0005d4: de00 0112 |000c: or-int/lit8 v0, v1, #int 18 // #12
+0005d8: df00 0112 |000e: xor-int/lit8 v0, v1, #int 18 // #12
+0005dc: e000 0112 |0010: shl-int/lit8 v0, v1, #int 18 // #12
+0005e0: e100 0112 |0012: shr-int/lit8 v0, v1, #int 18 // #12
+0005e4: e200 0112 |0014: ushr-int/lit8 v0, v1, #int 18 // #12
+0005e8: 0e00 |0016: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #6 : (in LA;)
+ name : 'compares'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 3
+ ins : 0
+ outs : 0
+ insns size : 11 16-bit code units
+0005ec: |[0005ec] A.compares:()V
+0005fc: 2d00 0102 |0000: cmpl-float v0, v1, v2
+000600: 2e00 0102 |0002: cmpg-float v0, v1, v2
+000604: 2f00 0102 |0004: cmpl-double v0, v1, v2
+000608: 3000 0102 |0006: cmpg-double v0, v1, v2
+00060c: 3100 0102 |0008: cmp-long v0, v1, v2
+000610: 0e00 |000a: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #7 : (in LA;)
+ name : 'conditionals'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 25 16-bit code units
+000614: |[000614] A.conditionals:()V
+000624: 3210 1800 |0000: if-eq v0, v1, 0018 // +0018
+000628: 3310 1600 |0002: if-ne v0, v1, 0018 // +0016
+00062c: 3410 1400 |0004: if-lt v0, v1, 0018 // +0014
+000630: 3510 1200 |0006: if-ge v0, v1, 0018 // +0012
+000634: 3610 1000 |0008: if-gt v0, v1, 0018 // +0010
+000638: 3710 0e00 |000a: if-le v0, v1, 0018 // +000e
+00063c: 3800 0c00 |000c: if-eqz v0, 0018 // +000c
+000640: 3900 0a00 |000e: if-nez v0, 0018 // +000a
+000644: 3a00 0800 |0010: if-ltz v0, 0018 // +0008
+000648: 3b00 0600 |0012: if-gez v0, 0018 // +0006
+00064c: 3c00 0400 |0014: if-gtz v0, 0018 // +0004
+000650: 3d00 0200 |0016: if-lez v0, 0018 // +0002
+000654: 0e00 |0018: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #8 : (in LA;)
+ name : 'constants'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 0
+ insns size : 28 16-bit code units
+000658: |[000658] A.constants:()V
+000668: 1210 |0000: const/4 v0, #int 1 // #1
+00066a: 1300 3412 |0001: const/16 v0, #int 4660 // #1234
+00066e: 1400 7856 3412 |0003: const v0, #float 5.69046e-28 // #12345678
+000674: 1500 3412 |0006: const/high16 v0, #int 305397760 // #1234
+000678: 1600 3412 |0008: const-wide/16 v0, #int 4660 // #1234
+00067c: 1700 7856 3412 |000a: const-wide/32 v0, #float 5.69046e-28 // #12345678
+000682: 1800 efcd ab90 7856 3412 |000d: const-wide v0, #double 5.62635e-221 // #1234567890abcdef
+00068c: 1900 3412 |0012: const-wide/high16 v0, #long 1311673391471656960 // #1234
+000690: 1a00 2c00 |0014: const-string v0, "string" // string@002c
+000694: 1b00 2c00 0000 |0016: const-string/jumbo v0, "string" // string@0000002c
+00069a: 1c00 0500 |0019: const-class v0, Ljava/lang/Object; // type@0005
+00069e: 0e00 |001b: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #9 : (in LA;)
+ name : 'misc'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 5
+ ins : 0
+ outs : 0
+ insns size : 54 16-bit code units
+0006a0: |[0006a0] A.misc:()V
+0006b0: 0000 |0000: nop // spacer
+0006b2: 1d00 |0001: monitor-enter v0
+0006b4: 1e00 |0002: monitor-exit v0
+0006b6: 1f00 0500 |0003: check-cast v0, Ljava/lang/Object; // type@0005
+0006ba: 2010 0500 |0005: instance-of v0, v1, Ljava/lang/Object; // type@0005
+0006be: 2110 |0007: array-length v0, v1
+0006c0: 2200 0500 |0008: new-instance v0, Ljava/lang/Object; // type@0005
+0006c4: 2310 0500 |000a: new-array v0, v1, Ljava/lang/Object; // type@0005
+0006c8: 2454 0900 1032 |000c: filled-new-array {v0, v1, v2, v3, v4}, [Ljava/lang/Object; // type@0009
+0006ce: 2505 0900 0000 |000f: filled-new-array/range {v0, v1, v2, v3, v4}, [Ljava/lang/Object; // type@0009
+0006d4: 2600 0c00 0000 |0012: fill-array-data v0, 0000001e // +0000000c
+0006da: 2700 |0015: throw v0
+0006dc: 2806 |0016: goto 001c // +0006
+0006de: 2900 0500 |0017: goto/16 001c // +0005
+0006e2: 2a00 0300 0000 |0019: goto/32 #00000003
+0006e8: 0e00 |001c: return-void
+0006ea: 0000 |001d: nop // spacer
+0006ec: 0003 0400 0a00 0000 0100 0000 0200 ... |001e: array-data (24 units)
+ catches : (none)
+ positions :
+ locals :
+
+ #10 : (in LA;)
+ name : 'moves'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 23 16-bit code units
+00071c: |[00071c] A.moves:()V
+00072c: 0110 |0000: move v0, v1
+00072e: 0200 0100 |0001: move/from16 v0, v1
+000732: 0300 0000 0100 |0003: move/16 v0, v1
+000738: 0410 |0006: move-wide v0, v1
+00073a: 0500 0100 |0007: move-wide/from16 v0, v1
+00073e: 0600 0000 0100 |0009: move-wide/16 v0, v1
+000744: 0710 |000c: move-object v0, v1
+000746: 0800 0100 |000d: move-object/from16 v0, v1
+00074a: 0900 0000 0100 |000f: move-object/16 v0, v1
+000750: 0a00 |0012: move-result v0
+000752: 0b00 |0013: move-result-wide v0
+000754: 0c00 |0014: move-result-object v0
+000756: 0d00 |0015: move-exception v0
+000758: 0e00 |0016: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #11 : (in LA;)
+ name : 'packed_switch'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 0
+ insns size : 16 16-bit code units
+00075c: |[00075c] A.packed_switch:()V
+00076c: 2b00 0800 0000 |0000: packed-switch v0, 00000008 // +00000008
+000772: 0e00 |0003: return-void
+000774: 28ff |0004: goto 0003 // -0001
+000776: 28fe |0005: goto 0003 // -0002
+000778: 28fd |0006: goto 0003 // -0003
+00077a: 0000 |0007: nop // spacer
+00077c: 0001 0200 feff ff7f 0500 0000 0600 ... |0008: packed-switch-data (8 units)
+ catches : (none)
+ positions :
+ locals :
+
+ #12 : (in LA;)
+ name : 'return32'
+ type : '()I'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 0
+ insns size : 1 16-bit code units
+00078c: |[00078c] A.return32:()I
+00079c: 0f00 |0000: return v0
+ catches : (none)
+ positions :
+ locals :
+
+ #13 : (in LA;)
+ name : 'return64'
+ type : '()I'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 1 16-bit code units
+0007a0: |[0007a0] A.return64:()I
+0007b0: 1000 |0000: return-wide v0
+ catches : (none)
+ positions :
+ locals :
+
+ #14 : (in LA;)
+ name : 'return_object'
+ type : '()Ljava/lang/Object;'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 0
+ insns size : 1 16-bit code units
+0007b4: |[0007b4] A.return_object:()Ljava/lang/Object;
+0007c4: 1100 |0000: return-object v0
+ catches : (none)
+ positions :
+ locals :
+
+ #15 : (in LA;)
+ name : 'sparse_switch'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 22 16-bit code units
+0007c8: |[0007c8] A.sparse_switch:()V
+0007d8: 2c00 0400 0000 |0000: sparse-switch v0, 00000004 // +00000004
+0007de: 0e00 |0003: return-void
+0007e0: 0002 0400 1111 0000 2222 0000 3333 ... |0004: sparse-switch-data (18 units)
+ catches : (none)
+ positions :
+ locals :
+
+ #16 : (in LA;)
+ name : 'static_fields'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 0
+ insns size : 29 16-bit code units
+000804: |[000804] A.static_fields:()V
+000814: 6000 0900 |0000: sget v0, LA;.sI:I // field@0009
+000818: 6100 0a00 |0002: sget-wide v0, LA;.sJ:J // field@000a
+00081c: 6200 0b00 |0004: sget-object v0, LA;.sO:LA; // field@000b
+000820: 6300 0d00 |0006: sget-boolean v0, LA;.sZ:Z // field@000d
+000824: 6400 0700 |0008: sget-byte v0, LA;.sB:B // field@0007
+000828: 6500 0800 |000a: sget-char v0, LA;.sC:C // field@0008
+00082c: 6600 0c00 |000c: sget-short v0, LA;.sS:S // field@000c
+000830: 6700 0900 |000e: sput v0, LA;.sI:I // field@0009
+000834: 6800 0a00 |0010: sput-wide v0, LA;.sJ:J // field@000a
+000838: 6900 0b00 |0012: sput-object v0, LA;.sO:LA; // field@000b
+00083c: 6a00 0d00 |0014: sput-boolean v0, LA;.sZ:Z // field@000d
+000840: 6b00 0700 |0016: sput-byte v0, LA;.sB:B // field@0007
+000844: 6c00 0800 |0018: sput-char v0, LA;.sC:C // field@0008
+000848: 6d00 0500 |001a: sput-short v0, LA;.mS:S // field@0005
+00084c: 0e00 |001c: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #17 : (in LA;)
+ name : 'unary_ops'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 0
+ insns size : 22 16-bit code units
+000850: |[000850] A.unary_ops:()V
+000860: 7b10 |0000: neg-int v0, v1
+000862: 7c10 |0001: not-int v0, v1
+000864: 7d10 |0002: neg-long v0, v1
+000866: 7e10 |0003: not-long v0, v1
+000868: 7f10 |0004: neg-float v0, v1
+00086a: 8010 |0005: neg-double v0, v1
+00086c: 8110 |0006: int-to-long v0, v1
+00086e: 8210 |0007: int-to-float v0, v1
+000870: 8310 |0008: int-to-double v0, v1
+000872: 8410 |0009: long-to-int v0, v1
+000874: 8510 |000a: long-to-float v0, v1
+000876: 8610 |000b: long-to-double v0, v1
+000878: 8710 |000c: float-to-int v0, v1
+00087a: 8810 |000d: float-to-long v0, v1
+00087c: 8910 |000e: float-to-double v0, v1
+00087e: 8a10 |000f: double-to-int v0, v1
+000880: 8b10 |0010: double-to-long v0, v1
+000882: 8c10 |0011: double-to-float v0, v1
+000884: 8d10 |0012: int-to-byte v0, v1
+000886: 8e10 |0013: int-to-char v0, v1
+000888: 8f10 |0014: int-to-short v0, v1
+00088a: 0e00 |0015: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ Virtual methods -
+ #0 : (in LA;)
+ name : 'instance_fields'
+ type : '()V'
+ access : 0x0001 (PUBLIC)
+ code -
+ registers : 2
+ ins : 1
+ outs : 0
+ insns size : 29 16-bit code units
+00088c: |[00088c] A.instance_fields:()V
+00089c: 5210 0900 |0000: iget v0, v1, LA;.sI:I // field@0009
+0008a0: 5310 0a00 |0002: iget-wide v0, v1, LA;.sJ:J // field@000a
+0008a4: 5410 0b00 |0004: iget-object v0, v1, LA;.sO:LA; // field@000b
+0008a8: 5510 0d00 |0006: iget-boolean v0, v1, LA;.sZ:Z // field@000d
+0008ac: 5610 0700 |0008: iget-byte v0, v1, LA;.sB:B // field@0007
+0008b0: 5710 0800 |000a: iget-char v0, v1, LA;.sC:C // field@0008
+0008b4: 5810 0c00 |000c: iget-short v0, v1, LA;.sS:S // field@000c
+0008b8: 5910 0900 |000e: iput v0, v1, LA;.sI:I // field@0009
+0008bc: 5a10 0a00 |0010: iput-wide v0, v1, LA;.sJ:J // field@000a
+0008c0: 5b10 0b00 |0012: iput-object v0, v1, LA;.sO:LA; // field@000b
+0008c4: 5c10 0d00 |0014: iput-boolean v0, v1, LA;.sZ:Z // field@000d
+0008c8: 5d10 0700 |0016: iput-byte v0, v1, LA;.sB:B // field@0007
+0008cc: 5e10 0800 |0018: iput-char v0, v1, LA;.sC:C // field@0008
+0008d0: 5f10 0c00 |001a: iput-short v0, v1, LA;.sS:S // field@000c
+0008d4: 0e00 |001c: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #1 : (in LA;)
+ name : 'invokes'
+ type : '()V'
+ access : 0x0001 (PUBLIC)
+ code -
+ registers : 5
+ ins : 1
+ outs : 1
+ insns size : 15 16-bit code units
+0008d8: |[0008d8] A.invokes:()V
+0008e8: 6e54 0a00 1032 |0000: invoke-virtual {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a
+0008ee: 6f54 0a00 1032 |0003: invoke-super {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a
+0008f4: 7054 0a00 1032 |0006: invoke-direct {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a
+0008fa: 7154 0a00 1032 |0009: invoke-static {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a
+000900: 7254 0a00 1032 |000c: invoke-interface {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a
+ catches : (none)
+ positions :
+ locals :
+
+ source_file_idx : -1 (unknown)
+
diff --git a/test/dexdump/all.xml b/test/dexdump/all.xml
new file mode 100644
index 0000000..b623ecb
--- /dev/null
+++ b/test/dexdump/all.xml
@@ -0,0 +1,211 @@
+<api>
+<package name=""
+>
+<class name="A"
+ extends="java.lang.Object"
+ interface="false"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<constructor name="A"
+ type="A"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+<method name="arrays"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="binary_ops"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="binary_ops_2addr"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="binary_ops_lit16"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="binary_ops_lit8"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="compares"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="conditionals"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="constants"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="misc"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="moves"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="packed_switch"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="return32"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="return64"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="return_object"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="sparse_switch"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="static_fields"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="unary_ops"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="instance_fields"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="invokes"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+</class>
+</package>
+</api>
diff --git a/test/dexdump/bytecodes.txt b/test/dexdump/bytecodes.txt
old mode 100755
new mode 100644
index 4c8b79b..e1a381e
--- a/test/dexdump/bytecodes.txt
+++ b/test/dexdump/bytecodes.txt
@@ -12,8 +12,8 @@
string_ids_off : 112 (0x000070)
type_ids_size : 42
type_ids_off : 724 (0x0002d4)
-proto_ids_size : 12
-proto_ids_off : 892 (0x00037c)
+proto_ids_size : 12
+proto_ids_off : 892 (0x00037c)
field_ids_size : 40
field_ids_off : 1036 (0x00040c)
method_ids_size : 28
@@ -36,6 +36,11 @@
direct_methods_size : 0
virtual_methods_size: 1
+Class #0 annotations:
+Annotations on class
+ VISIBILITY_RUNTIME Ljava/lang/annotation/Retention; value=CLASS
+ VISIBILITY_RUNTIME Ljava/lang/annotation/Target; value={ TYPE FIELD METHOD PARAMETER CONSTRUCTOR LOCAL_VARIABLE }
+
Class #0 -
Class descriptor : 'Landroid/annotation/SuppressLint;'
Access flags : 0x2601 (PUBLIC INTERFACE ABSTRACT ANNOTATION)
@@ -67,6 +72,11 @@
direct_methods_size : 0
virtual_methods_size: 1
+Class #1 annotations:
+Annotations on class
+ VISIBILITY_RUNTIME Ljava/lang/annotation/Retention; value=CLASS
+ VISIBILITY_RUNTIME Ljava/lang/annotation/Target; value={ TYPE METHOD CONSTRUCTOR }
+
Class #1 -
Class descriptor : 'Landroid/annotation/TargetApi;'
Access flags : 0x2601 (PUBLIC INTERFACE ABSTRACT ANNOTATION)
@@ -144,6 +154,11 @@
direct_methods_size : 1
virtual_methods_size: 0
+Class #3 annotations:
+Annotations on class
+ VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=Lcom/google/android/test/R;
+ VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=25 name="attr"
+
Class #3 -
Class descriptor : 'Lcom/google/android/test/R$attr;'
Access flags : 0x0011 (PUBLIC FINAL)
@@ -186,6 +201,11 @@
direct_methods_size : 1
virtual_methods_size: 0
+Class #4 annotations:
+Annotations on class
+ VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=Lcom/google/android/test/R;
+ VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=25 name="drawable"
+
Class #4 -
Class descriptor : 'Lcom/google/android/test/R$drawable;'
Access flags : 0x0011 (PUBLIC FINAL)
@@ -233,6 +253,10 @@
direct_methods_size : 1
virtual_methods_size: 0
+Class #5 annotations:
+Annotations on class
+ VISIBILITY_SYSTEM Ldalvik/annotation/MemberClasses; value={ Lcom/google/android/test/R$attr; Lcom/google/android/test/R$drawable; }
+
Class #5 -
Class descriptor : 'Lcom/google/android/test/R;'
Access flags : 0x0011 (PUBLIC FINAL)
@@ -275,6 +299,10 @@
direct_methods_size : 13
virtual_methods_size: 2
+Class #6 annotations:
+Annotations on method #13 'doit'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Exception; }
+
Class #6 -
Class descriptor : 'Lcom/google/android/test/Test;'
Access flags : 0x0001 (PUBLIC)
@@ -418,17 +446,17 @@
000a02: 6a00 1800 |0001: sput-boolean v0, Lcom/google/android/test/Test;.sBool:Z // field@0018
000a06: 1300 1f00 |0003: const/16 v0, #int 31 // #1f
000a0a: 6b00 1700 |0005: sput-byte v0, Lcom/google/android/test/Test;.sB:B // field@0017
-000a0e: 1400 ffff 0000 |0007: const v0, #float 0.000000 // #0000ffff
+000a0e: 1400 ffff 0000 |0007: const v0, #float 9.18341e-41 // #0000ffff
000a14: 6c00 1900 |000a: sput-char v0, Lcom/google/android/test/Test;.sC:C // field@0019
000a18: 1300 3412 |000c: const/16 v0, #int 4660 // #1234
000a1c: 6d00 1f00 |000e: sput-short v0, Lcom/google/android/test/Test;.sS:S // field@001f
-000a20: 1400 7856 3412 |0010: const v0, #float 0.000000 // #12345678
+000a20: 1400 7856 3412 |0010: const v0, #float 5.69046e-28 // #12345678
000a26: 6700 1c00 |0013: sput v0, Lcom/google/android/test/Test;.sI:I // field@001c
-000a2a: 1800 ffff cdab 7956 3412 |0015: const-wide v0, #double 0.000000 // #12345679abcdffff
+000a2a: 1800 ffff cdab 7956 3412 |0015: const-wide v0, #double 5.62635e-221 // #12345679abcdffff
000a34: 6800 1d00 |001a: sput-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d
-000a38: 1400 00e4 4046 |001c: const v0, #float 12345.000000 // #4640e400
+000a38: 1400 00e4 4046 |001c: const v0, #float 12345 // #4640e400
000a3e: 6700 1b00 |001f: sput v0, Lcom/google/android/test/Test;.sF:F // field@001b
-000a42: 1800 0000 0000 801c c840 |0021: const-wide v0, #double 12345.000000 // #40c81c8000000000
+000a42: 1800 0000 0000 801c c840 |0021: const-wide v0, #double 12345 // #40c81c8000000000
000a4c: 6800 1a00 |0026: sput-wide v0, Lcom/google/android/test/Test;.sD:D // field@001a
000a50: 1200 |0028: const/4 v0, #int 0 // #0
000a52: 6900 1e00 |0029: sput-object v0, Lcom/google/android/test/Test;.sO:Ljava/lang/Object; // field@001e
@@ -471,17 +499,17 @@
000ab4: 5c81 0d00 |0008: iput-boolean v1, v8, Lcom/google/android/test/Test;.mBool:Z // field@000d
000ab8: 1301 1f00 |000a: const/16 v1, #int 31 // #1f
000abc: 5d81 0c00 |000c: iput-byte v1, v8, Lcom/google/android/test/Test;.mB:B // field@000c
-000ac0: 1401 ffff 0000 |000e: const v1, #float 0.000000 // #0000ffff
+000ac0: 1401 ffff 0000 |000e: const v1, #float 9.18341e-41 // #0000ffff
000ac6: 5e81 0e00 |0011: iput-char v1, v8, Lcom/google/android/test/Test;.mC:C // field@000e
000aca: 1301 3412 |0013: const/16 v1, #int 4660 // #1234
000ace: 5f81 1500 |0015: iput-short v1, v8, Lcom/google/android/test/Test;.mS:S // field@0015
-000ad2: 1401 7856 3412 |0017: const v1, #float 0.000000 // #12345678
+000ad2: 1401 7856 3412 |0017: const v1, #float 5.69046e-28 // #12345678
000ad8: 5981 1100 |001a: iput v1, v8, Lcom/google/android/test/Test;.mI:I // field@0011
-000adc: 1802 ffff cdab 7956 3412 |001c: const-wide v2, #double 0.000000 // #12345679abcdffff
+000adc: 1802 ffff cdab 7956 3412 |001c: const-wide v2, #double 5.62635e-221 // #12345679abcdffff
000ae6: 5a82 1200 |0021: iput-wide v2, v8, Lcom/google/android/test/Test;.mL:J // field@0012
-000aea: 1401 00e4 4046 |0023: const v1, #float 12345.000000 // #4640e400
+000aea: 1401 00e4 4046 |0023: const v1, #float 12345 // #4640e400
000af0: 5981 1000 |0026: iput v1, v8, Lcom/google/android/test/Test;.mF:F // field@0010
-000af4: 1802 0000 0000 801c c840 |0028: const-wide v2, #double 12345.000000 // #40c81c8000000000
+000af4: 1802 0000 0000 801c c840 |0028: const-wide v2, #double 12345 // #40c81c8000000000
000afe: 5a82 0f00 |002d: iput-wide v2, v8, Lcom/google/android/test/Test;.mD:D // field@000f
000b02: 1201 |002f: const/4 v1, #int 0 // #0
000b04: 5b81 1300 |0030: iput-object v1, v8, Lcom/google/android/test/Test;.mO:Ljava/lang/Object; // field@0013
@@ -626,7 +654,7 @@
000cc6: 8d00 |0011: int-to-byte v0, v0
000cc8: 5db0 0c00 |0012: iput-byte v0, v11, Lcom/google/android/test/Test;.mB:B // field@000c
000ccc: 57b0 0e00 |0014: iget-char v0, v11, Lcom/google/android/test/Test;.mC:C // field@000e
-000cd0: 1401 ffff 0000 |0016: const v1, #float 0.000000 // #0000ffff
+000cd0: 1401 ffff 0000 |0016: const v1, #float 9.18341e-41 // #0000ffff
000cd6: b010 |0019: add-int/2addr v0, v1
000cd8: 8e00 |001a: int-to-char v0, v0
000cda: 5eb0 0e00 |001b: iput-char v0, v11, Lcom/google/android/test/Test;.mC:C // field@000e
@@ -635,7 +663,7 @@
000ce6: 8f00 |0021: int-to-short v0, v0
000ce8: 5fb0 1500 |0022: iput-short v0, v11, Lcom/google/android/test/Test;.mS:S // field@0015
000cec: 52b0 1100 |0024: iget v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011
-000cf0: 1401 7856 3412 |0026: const v1, #float 0.000000 // #12345678
+000cf0: 1401 7856 3412 |0026: const v1, #float 5.69046e-28 // #12345678
000cf6: b010 |0029: add-int/2addr v0, v1
000cf8: 59b0 1100 |002a: iput v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011
000cfc: 52b0 1100 |002c: iget v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011
@@ -643,7 +671,7 @@
000d04: b010 |0030: add-int/2addr v0, v1
000d06: 59b0 1100 |0031: iput v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011
000d0a: 53b0 1200 |0033: iget-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012
-000d0e: 1802 ffff cdab 7956 3412 |0035: const-wide v2, #double 0.000000 // #12345679abcdffff
+000d0e: 1802 ffff cdab 7956 3412 |0035: const-wide v2, #double 5.62635e-221 // #12345679abcdffff
000d18: bb20 |003a: add-long/2addr v0, v2
000d1a: 5ab0 1200 |003b: iput-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012
000d1e: 53b0 1200 |003d: iget-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012
@@ -651,7 +679,7 @@
000d26: bb20 |0041: add-long/2addr v0, v2
000d28: 5ab0 1200 |0042: iput-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012
000d2c: 52b0 1000 |0044: iget v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010
-000d30: 1401 00e4 4046 |0046: const v1, #float 12345.000000 // #4640e400
+000d30: 1401 00e4 4046 |0046: const v1, #float 12345 // #4640e400
000d36: 52b2 1000 |0049: iget v2, v11, Lcom/google/android/test/Test;.mF:F // field@0010
000d3a: 1503 803f |004b: const/high16 v3, #int 1065353216 // #3f80
000d3e: c732 |004d: sub-float/2addr v2, v3
@@ -664,7 +692,7 @@
000d50: c610 |0056: add-float/2addr v0, v1
000d52: 59b0 1000 |0057: iput v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010
000d56: 53b0 0f00 |0059: iget-wide v0, v11, Lcom/google/android/test/Test;.mD:D // field@000f
-000d5a: 1802 0000 0000 801c c840 |005b: const-wide v2, #double 12345.000000 // #40c81c8000000000
+000d5a: 1802 0000 0000 801c c840 |005b: const-wide v2, #double 12345 // #40c81c8000000000
000d64: 53b4 0f00 |0060: iget-wide v4, v11, Lcom/google/android/test/Test;.mD:D // field@000f
000d68: 1906 f03f |0062: const-wide/high16 v6, #long 4607182418800017408 // #3ff0
000d6c: cc64 |0064: sub-double/2addr v4, v6
@@ -681,7 +709,7 @@
000d8a: 2d00 0001 |0073: cmpl-float v0, v0, v1
000d8e: 3800 2900 |0075: if-eqz v0, 009e // +0029
000d92: 52b0 1000 |0077: iget v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010
-000d96: 1401 9a99 993e |0079: const v1, #float 0.300000 // #3e99999a
+000d96: 1401 9a99 993e |0079: const v1, #float 0.3 // #3e99999a
000d9c: 2d00 0001 |007c: cmpl-float v0, v0, v1
000da0: 3900 2000 |007e: if-nez v0, 009e // +0020
000da4: 52b0 1000 |0080: iget v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010
@@ -707,7 +735,7 @@
000df2: 2f00 0002 |00a7: cmpl-double v0, v0, v2
000df6: 3800 2b00 |00a9: if-eqz v0, 00d4 // +002b
000dfa: 53b0 0f00 |00ab: iget-wide v0, v11, Lcom/google/android/test/Test;.mD:D // field@000f
-000dfe: 1802 3333 3333 3333 d33f |00ad: const-wide v2, #double 0.300000 // #3fd3333333333333
+000dfe: 1802 3333 3333 3333 d33f |00ad: const-wide v2, #double 0.3 // #3fd3333333333333
000e08: 2f00 0002 |00b2: cmpl-double v0, v0, v2
000e0c: 3900 2000 |00b4: if-nez v0, 00d4 // +0020
000e10: 53b0 0f00 |00b6: iget-wide v0, v11, Lcom/google/android/test/Test;.mD:D // field@000f
@@ -790,7 +818,7 @@
000eb8: 8d00 |000c: int-to-byte v0, v0
000eba: 6b00 1700 |000d: sput-byte v0, Lcom/google/android/test/Test;.sB:B // field@0017
000ebe: 6500 1900 |000f: sget-char v0, Lcom/google/android/test/Test;.sC:C // field@0019
-000ec2: 1401 ffff 0000 |0011: const v1, #float 0.000000 // #0000ffff
+000ec2: 1401 ffff 0000 |0011: const v1, #float 9.18341e-41 // #0000ffff
000ec8: b010 |0014: add-int/2addr v0, v1
000eca: 8e00 |0015: int-to-char v0, v0
000ecc: 6c00 1900 |0016: sput-char v0, Lcom/google/android/test/Test;.sC:C // field@0019
@@ -799,7 +827,7 @@
000ed8: 8f00 |001c: int-to-short v0, v0
000eda: 6d00 1f00 |001d: sput-short v0, Lcom/google/android/test/Test;.sS:S // field@001f
000ede: 6000 1c00 |001f: sget v0, Lcom/google/android/test/Test;.sI:I // field@001c
-000ee2: 1401 7856 3412 |0021: const v1, #float 0.000000 // #12345678
+000ee2: 1401 7856 3412 |0021: const v1, #float 5.69046e-28 // #12345678
000ee8: b010 |0024: add-int/2addr v0, v1
000eea: 6700 1c00 |0025: sput v0, Lcom/google/android/test/Test;.sI:I // field@001c
000eee: 6000 1c00 |0027: sget v0, Lcom/google/android/test/Test;.sI:I // field@001c
@@ -807,7 +835,7 @@
000ef6: b010 |002b: add-int/2addr v0, v1
000ef8: 6700 1c00 |002c: sput v0, Lcom/google/android/test/Test;.sI:I // field@001c
000efc: 6100 1d00 |002e: sget-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d
-000f00: 1802 ffff cdab 7956 3412 |0030: const-wide v2, #double 0.000000 // #12345679abcdffff
+000f00: 1802 ffff cdab 7956 3412 |0030: const-wide v2, #double 5.62635e-221 // #12345679abcdffff
000f0a: bb20 |0035: add-long/2addr v0, v2
000f0c: 6800 1d00 |0036: sput-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d
000f10: 6100 1d00 |0038: sget-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d
@@ -815,7 +843,7 @@
000f18: bb20 |003c: add-long/2addr v0, v2
000f1a: 6800 1d00 |003d: sput-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d
000f1e: 6000 1b00 |003f: sget v0, Lcom/google/android/test/Test;.sF:F // field@001b
-000f22: 1401 00e4 4046 |0041: const v1, #float 12345.000000 // #4640e400
+000f22: 1401 00e4 4046 |0041: const v1, #float 12345 // #4640e400
000f28: 6002 1b00 |0044: sget v2, Lcom/google/android/test/Test;.sF:F // field@001b
000f2c: 7f22 |0046: neg-float v2, v2
000f2e: 1503 803f |0047: const/high16 v3, #int 1065353216 // #3f80
@@ -830,7 +858,7 @@
000f48: c610 |0054: add-float/2addr v0, v1
000f4a: 6700 1b00 |0055: sput v0, Lcom/google/android/test/Test;.sF:F // field@001b
000f4e: 6100 1a00 |0057: sget-wide v0, Lcom/google/android/test/Test;.sD:D // field@001a
-000f52: 1802 0000 0000 801c c840 |0059: const-wide v2, #double 12345.000000 // #40c81c8000000000
+000f52: 1802 0000 0000 801c c840 |0059: const-wide v2, #double 12345 // #40c81c8000000000
000f5c: 6104 1a00 |005e: sget-wide v4, Lcom/google/android/test/Test;.sD:D // field@001a
000f60: 8044 |0060: neg-double v4, v4
000f62: 1906 f03f |0061: const-wide/high16 v6, #long 4607182418800017408 // #3ff0
diff --git a/test/dexdump/checkers.txt b/test/dexdump/checkers.txt
old mode 100755
new mode 100644
index 5c8336f..aee6e64
--- a/test/dexdump/checkers.txt
+++ b/test/dexdump/checkers.txt
@@ -12,8 +12,8 @@
string_ids_off : 112 (0x000070)
type_ids_size : 58
type_ids_off : 1404 (0x00057c)
-proto_ids_size : 88
-proto_ids_off : 1636 (0x000664)
+proto_ids_size : 88
+proto_ids_off : 1636 (0x000664)
field_ids_size : 108
field_ids_off : 2692 (0x000a84)
method_ids_size : 177
@@ -836,7 +836,7 @@
001c8a: 1300 5829 |0147: const/16 v0, #int 10584 // #2958
001c8e: 2300 3600 |0149: new-array v0, v0, [B // type@0036
001c92: 6900 6800 |014b: sput-object v0, Lcom/google/android/checkers/g;.p:[B // field@0068
-001c96: 1400 00c1 0300 |014d: const v0, #float 0.000000 // #0003c100
+001c96: 1400 00c1 0300 |014d: const v0, #float 3.44742e-40 // #0003c100
001c9c: 2300 3600 |0150: new-array v0, v0, [B // type@0036
001ca0: 6900 6900 |0152: sput-object v0, Lcom/google/android/checkers/g;.q:[B // field@0069
001ca4: 6e10 1100 0a00 |0154: invoke-virtual {v10}, Landroid/content/Context;.getResources:()Landroid/content/res/Resources; // method@0011
@@ -2044,7 +2044,7 @@
002bd0: 5433 3b00 |004c: iget-object v3, v3, Lcom/google/android/checkers/a;.b:[I // field@003b
002bd4: 4403 0309 |004e: aget v3, v3, v9
002bd8: 5983 2a00 |0050: iput v3, v8, Lcom/google/android/checkers/CheckersView;.x:I // field@002a
-002bdc: 1403 6666 663f |0052: const v3, #float 0.900000 // #3f666666
+002bdc: 1403 6666 663f |0052: const v3, #float 0.9 // #3f666666
002be2: 5983 1e00 |0055: iput v3, v8, Lcom/google/android/checkers/CheckersView;.l:F // field@001e
002be6: 3800 4500 |0057: if-eqz v0, 009c // +0045
002bea: 5483 2200 |0059: iget-object v3, v8, Lcom/google/android/checkers/CheckersView;.p:Lcom/google/android/checkers/a; // field@0022
@@ -2943,7 +2943,7 @@
0036f2: 0800 1c00 |0197: move-object/from16 v0, v28
0036f6: 5202 1e00 |0199: iget v2, v0, Lcom/google/android/checkers/CheckersView;.l:F // field@001e
0036fa: 8922 |019b: float-to-double v2, v2
-0036fc: 1804 9a99 9999 9999 a93f |019c: const-wide v4, #double 0.050000 // #3fa999999999999a
+0036fc: 1804 9a99 9999 9999 a93f |019c: const-wide v4, #double 0.05 // #3fa999999999999a
003706: cc42 |01a1: sub-double/2addr v2, v4
003708: 8c22 |01a2: double-to-float v2, v2
00370a: 0800 1c00 |01a3: move-object/from16 v0, v28
@@ -3568,7 +3568,7 @@
003f38: 28e9 |001e: goto 0007 // -0017
003f3a: 1300 3075 |001f: const/16 v0, #int 30000 // #7530
003f3e: 28e6 |0021: goto 0007 // -001a
-003f40: 1400 60ea 0000 |0022: const v0, #float 0.000000 // #0000ea60
+003f40: 1400 60ea 0000 |0022: const v0, #float 8.40779e-41 // #0000ea60
003f46: 28e2 |0025: goto 0007 // -001e
003f48: 0d00 |0026: move-exception v0
003f4a: 1e02 |0027: monitor-exit v2
@@ -3811,7 +3811,7 @@
004024: 1302 0040 |0046: const/16 v2, #int 16384 // #4000
004028: 4b02 0001 |0048: aput v2, v0, v1
00402c: 1301 1300 |004a: const/16 v1, #int 19 // #13
-004030: 1402 0080 0000 |004c: const v2, #float 0.000000 // #00008000
+004030: 1402 0080 0000 |004c: const v2, #float 4.59177e-41 // #00008000
004036: 4b02 0001 |004f: aput v2, v0, v1
00403a: 1501 0100 |0051: const/high16 v1, #int 65536 // #1
00403e: 4b01 0006 |0053: aput v1, v0, v6
@@ -3931,7 +3931,7 @@
0041f6: 1302 0040 |012f: const/16 v2, #int 16384 // #4000
0041fa: 4b02 0001 |0131: aput v2, v0, v1
0041fe: 1301 1200 |0133: const/16 v1, #int 18 // #12
-004202: 1402 0080 0000 |0135: const v2, #float 0.000000 // #00008000
+004202: 1402 0080 0000 |0135: const v2, #float 4.59177e-41 // #00008000
004208: 4b02 0001 |0138: aput v2, v0, v1
00420c: 1301 1400 |013a: const/16 v1, #int 20 // #14
004210: 1502 0100 |013c: const/high16 v2, #int 65536 // #1
@@ -3996,7 +3996,7 @@
0042fa: 1301 0040 |01b1: const/16 v1, #int 16384 // #4000
0042fe: 4b01 0006 |01b3: aput v1, v0, v6
004302: 1301 1600 |01b5: const/16 v1, #int 22 // #16
-004306: 1402 0080 0000 |01b7: const v2, #float 0.000000 // #00008000
+004306: 1402 0080 0000 |01b7: const v2, #float 4.59177e-41 // #00008000
00430c: 4b02 0001 |01ba: aput v2, v0, v1
004310: 1301 1800 |01bc: const/16 v1, #int 24 // #18
004314: 1502 0200 |01be: const/high16 v2, #int 131072 // #2
@@ -4045,7 +4045,7 @@
0043b4: 1301 0040 |020e: const/16 v1, #int 16384 // #4000
0043b8: 4b01 0004 |0210: aput v1, v0, v4
0043bc: 1301 0b00 |0212: const/16 v1, #int 11 // #b
-0043c0: 1402 0080 0000 |0214: const v2, #float 0.000000 // #00008000
+0043c0: 1402 0080 0000 |0214: const v2, #float 4.59177e-41 // #00008000
0043c6: 4b02 0001 |0217: aput v2, v0, v1
0043ca: 1301 0d00 |0219: const/16 v1, #int 13 // #d
0043ce: 1502 0100 |021b: const/high16 v2, #int 65536 // #1
@@ -4167,7 +4167,7 @@
004588: 1301 0900 |02f8: const/16 v1, #int 9 // #9
00458c: 1302 0040 |02fa: const/16 v2, #int 16384 // #4000
004590: 4b02 0001 |02fc: aput v2, v0, v1
-004594: 1401 0080 0000 |02fe: const v1, #float 0.000000 // #00008000
+004594: 1401 0080 0000 |02fe: const v1, #float 4.59177e-41 // #00008000
00459a: 4b01 0004 |0301: aput v1, v0, v4
00459e: 1301 0c00 |0303: const/16 v1, #int 12 // #c
0045a2: 1502 0100 |0305: const/high16 v2, #int 65536 // #1
@@ -4226,7 +4226,7 @@
00466e: 1302 0040 |036b: const/16 v2, #int 16384 // #4000
004672: 4b02 0001 |036d: aput v2, v0, v1
004676: 1261 |036f: const/4 v1, #int 6 // #6
-004678: 1402 0080 0000 |0370: const v2, #float 0.000000 // #00008000
+004678: 1402 0080 0000 |0370: const v2, #float 4.59177e-41 // #00008000
00467e: 4b02 0001 |0373: aput v2, v0, v1
004682: 1301 0800 |0375: const/16 v1, #int 8 // #8
004686: 1502 0200 |0377: const/high16 v2, #int 131072 // #2
@@ -4496,7 +4496,7 @@
004c16: 3803 3400 |0047: if-eqz v3, 007b // +0034
004c1a: 0800 1800 |0049: move-object/from16 v0, v24
004c1e: 5203 5100 |004b: iget v3, v0, Lcom/google/android/checkers/a;.x:I // field@0051
-004c22: 1404 ffff 0f00 |004d: const v4, #float 0.000000 // #000fffff
+004c22: 1404 ffff 0f00 |004d: const v4, #float 1.46937e-39 // #000fffff
004c28: b534 |0050: and-int/2addr v4, v3
004c2a: 0800 1800 |0051: move-object/from16 v0, v24
004c2e: 5405 5200 |0053: iget-object v5, v0, Lcom/google/android/checkers/a;.y:[I // field@0052
@@ -4516,7 +4516,7 @@
004c66: 5405 5300 |006f: iget-object v5, v0, Lcom/google/android/checkers/a;.z:[S // field@0053
004c6a: 4a04 0504 |0071: aget-short v4, v5, v4
004c6e: 2c03 8104 0000 |0073: sparse-switch v3, 000004f4 // +00000481
-004c74: 1403 3f42 0f00 |0076: const v3, #float 0.000000 // #000f423f
+004c74: 1403 3f42 0f00 |0076: const v3, #float 1.4013e-39 // #000f423f
004c7a: 3334 a1ff |0079: if-ne v4, v3, 001a // -005f
004c7e: 0800 1800 |007b: move-object/from16 v0, v24
004c82: 0201 1b00 |007d: move/from16 v1, v27
@@ -4897,7 +4897,7 @@
0051da: 28c4 |0329: goto 02ed // -003c
0051dc: 0200 1900 |032a: move/from16 v0, v25
0051e0: 3704 4afd |032c: if-le v4, v0, 0076 // -02b6
-0051e4: 1404 3f42 0f00 |032e: const v4, #float 0.000000 // #000f423f
+0051e4: 1404 3f42 0f00 |032e: const v4, #float 1.4013e-39 // #000f423f
0051ea: 2900 45fd |0331: goto/16 0076 // -02bb
0051ee: 0200 1a00 |0333: move/from16 v0, v26
0051f2: 3404 f9ff |0335: if-lt v4, v0, 032e // -0007
@@ -5020,7 +5020,7 @@
0053a2: 3545 bd00 |040d: if-ge v5, v4, 04ca // +00bd
0053a6: 0800 1800 |040f: move-object/from16 v0, v24
0053aa: 5204 3e00 |0411: iget v4, v0, Lcom/google/android/checkers/a;.e:I // field@003e
-0053ae: 1405 1100 0088 |0413: const v5, #float -0.000000 // #88000011
+0053ae: 1405 1100 0088 |0413: const v5, #float -3.85187e-34 // #88000011
0053b4: b554 |0416: and-int/2addr v4, v5
0053b6: 3804 0900 |0417: if-eqz v4, 0420 // +0009
0053ba: 7110 9e00 0400 |0419: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
@@ -5052,11 +5052,11 @@
005418: 1507 00a0 |0448: const/high16 v7, #int -1610612736 // #a000
00541c: 3376 0400 |044a: if-ne v6, v7, 044e // +0004
005420: d803 03f4 |044c: add-int/lit8 v3, v3, #int -12 // #f4
-005424: 1406 0066 6600 |044e: const v6, #float 0.000000 // #00666600
+005424: 1406 0066 6600 |044e: const v6, #float 9.40381e-39 // #00666600
00542a: b564 |0451: and-int/2addr v4, v6
00542c: 7110 9e00 0400 |0452: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
005432: 0a04 |0455: move-result v4
-005434: 1406 0066 6600 |0456: const v6, #float 0.000000 // #00666600
+005434: 1406 0066 6600 |0456: const v6, #float 9.40381e-39 // #00666600
00543a: b565 |0459: and-int/2addr v5, v6
00543c: 7110 9e00 0500 |045a: invoke-static {v5}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
005442: 0a05 |045d: move-result v5
@@ -5064,13 +5064,13 @@
005446: b043 |045f: add-int/2addr v3, v4
005448: 0800 1800 |0460: move-object/from16 v0, v24
00544c: 5204 3d00 |0462: iget v4, v0, Lcom/google/android/checkers/a;.d:I // field@003d
-005450: 1405 1818 1818 |0464: const v5, #float 0.000000 // #18181818
+005450: 1405 1818 1818 |0464: const v5, #float 1.96577e-24 // #18181818
005456: b554 |0467: and-int/2addr v4, v5
005458: 7110 9e00 0400 |0468: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
00545e: 0a04 |046b: move-result v4
005460: 0800 1800 |046c: move-object/from16 v0, v24
005464: 5205 3f00 |046e: iget v5, v0, Lcom/google/android/checkers/a;.f:I // field@003f
-005468: 1406 1818 1818 |0470: const v6, #float 0.000000 // #18181818
+005468: 1406 1818 1818 |0470: const v6, #float 1.96577e-24 // #18181818
00546e: b565 |0473: and-int/2addr v5, v6
005470: 7110 9e00 0500 |0474: invoke-static {v5}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
005476: 0a05 |0477: move-result v5
@@ -5078,7 +5078,7 @@
00547a: b143 |0479: sub-int/2addr v3, v4
00547c: 0800 1800 |047a: move-object/from16 v0, v24
005480: 5204 3e00 |047c: iget v4, v0, Lcom/google/android/checkers/a;.e:I // field@003e
-005484: 1405 0800 0010 |047e: const v5, #float 0.000000 // #10000008
+005484: 1405 0800 0010 |047e: const v5, #float 2.52436e-29 // #10000008
00548a: b554 |0481: and-int/2addr v4, v5
00548c: 3804 0900 |0482: if-eqz v4, 048b // +0009
005490: 7110 9e00 0400 |0484: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
@@ -5087,7 +5087,7 @@
00549c: b143 |048a: sub-int/2addr v3, v4
00549e: 0800 1800 |048b: move-object/from16 v0, v24
0054a2: 5204 4000 |048d: iget v4, v0, Lcom/google/android/checkers/a;.g:I // field@0040
-0054a6: 1405 0800 0010 |048f: const v5, #float 0.000000 // #10000008
+0054a6: 1405 0800 0010 |048f: const v5, #float 2.52436e-29 // #10000008
0054ac: b554 |0492: and-int/2addr v4, v5
0054ae: 3804 4c00 |0493: if-eqz v4, 04df // +004c
0054b2: 7110 9e00 0400 |0495: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
@@ -5124,7 +5124,7 @@
00551c: 3745 56ff |04ca: if-le v5, v4, 0420 // -00aa
005520: 0800 1800 |04cc: move-object/from16 v0, v24
005524: 5204 4000 |04ce: iget v4, v0, Lcom/google/android/checkers/a;.g:I // field@0040
-005528: 1405 1100 0088 |04d0: const v5, #float -0.000000 // #88000011
+005528: 1405 1100 0088 |04d0: const v5, #float -3.85187e-34 // #88000011
00552e: b554 |04d3: and-int/2addr v4, v5
005530: 3804 4cff |04d4: if-eqz v4, 0420 // -00b4
005534: 7110 9e00 0400 |04d6: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e
@@ -5407,7 +5407,7 @@
005868: 0126 |0010: move v6, v2
00586a: 0135 |0011: move v5, v3
00586c: 5240 5100 |0012: iget v0, v4, Lcom/google/android/checkers/a;.x:I // field@0051
-005870: 1401 ffff 0f00 |0014: const v1, #float 0.000000 // #000fffff
+005870: 1401 ffff 0f00 |0014: const v1, #float 1.46937e-39 // #000fffff
005876: b501 |0017: and-int/2addr v1, v0
005878: 5442 5200 |0018: iget-object v2, v4, Lcom/google/android/checkers/a;.y:[I // field@0052
00587c: 4b00 0201 |001a: aput v0, v2, v1
@@ -5568,19 +5568,19 @@
005a54: e203 1404 |0016: ushr-int/lit8 v3, v20, #int 4 // #04
005a58: b543 |0018: and-int/2addr v3, v4
005a5a: 3803 1200 |0019: if-eqz v3, 002b // +0012
-005a5e: 1401 e0e0 e0e0 |001b: const v1, #float -129633581999069331456.000000 // #e0e0e0e0
+005a5e: 1401 e0e0 e0e0 |001b: const v1, #float -1.29634e+20 // #e0e0e0e0
005a64: b531 |001e: and-int/2addr v1, v3
005a66: e201 0105 |001f: ushr-int/lit8 v1, v1, #int 5 // #05
-005a6a: 1405 0007 0707 |0021: const v5, #float 0.000000 // #07070700
+005a6a: 1405 0007 0707 |0021: const v5, #float 1.01583e-34 // #07070700
005a70: b553 |0024: and-int/2addr v3, v5
005a72: e203 0303 |0025: ushr-int/lit8 v3, v3, #int 3 // #03
005a76: b631 |0027: or-int/2addr v1, v3
005a78: b521 |0028: and-int/2addr v1, v2
005a7a: de01 0100 |0029: or-int/lit8 v1, v1, #int 0 // #00
-005a7e: 1403 e0e0 e0e0 |002b: const v3, #float -129633581999069331456.000000 // #e0e0e0e0
+005a7e: 1403 e0e0 e0e0 |002b: const v3, #float -1.29634e+20 // #e0e0e0e0
005a84: 9503 0314 |002e: and-int v3, v3, v20
005a88: e203 0305 |0030: ushr-int/lit8 v3, v3, #int 5 // #05
-005a8c: 1405 0007 0707 |0032: const v5, #float 0.000000 // #07070700
+005a8c: 1405 0007 0707 |0032: const v5, #float 1.01583e-34 // #07070700
005a92: 9505 0514 |0035: and-int v5, v5, v20
005a96: e205 0503 |0037: ushr-int/lit8 v5, v5, #int 3 // #03
005a9a: b653 |0039: or-int/2addr v3, v5
@@ -5597,19 +5597,19 @@
005abc: 3802 1500 |004a: if-eqz v2, 005f // +0015
005ac0: 0800 1100 |004c: move-object/from16 v0, v17
005ac4: 5203 3e00 |004e: iget v3, v0, Lcom/google/android/checkers/a;.e:I // field@003e
-005ac8: 1405 0707 0707 |0050: const v5, #float 0.000000 // #07070707
+005ac8: 1405 0707 0707 |0050: const v5, #float 1.01583e-34 // #07070707
005ace: b525 |0053: and-int/2addr v5, v2
005ad0: e005 0505 |0054: shl-int/lit8 v5, v5, #int 5 // #05
-005ad4: 1406 e0e0 e000 |0056: const v6, #float 0.000000 // #00e0e0e0
+005ad4: 1406 e0e0 e000 |0056: const v6, #float 2.06518e-38 // #00e0e0e0
005ada: b562 |0059: and-int/2addr v2, v6
005adc: e002 0203 |005a: shl-int/lit8 v2, v2, #int 3 // #03
005ae0: b652 |005c: or-int/2addr v2, v5
005ae2: b532 |005d: and-int/2addr v2, v3
005ae4: b621 |005e: or-int/2addr v1, v2
-005ae6: 1402 0707 0707 |005f: const v2, #float 0.000000 // #07070707
+005ae6: 1402 0707 0707 |005f: const v2, #float 1.01583e-34 // #07070707
005aec: 9502 0214 |0062: and-int v2, v2, v20
005af0: e002 0205 |0064: shl-int/lit8 v2, v2, #int 5 // #05
-005af4: 1403 e0e0 e000 |0066: const v3, #float 0.000000 // #00e0e0e0
+005af4: 1403 e0e0 e000 |0066: const v3, #float 2.06518e-38 // #00e0e0e0
005afa: 9503 0314 |0069: and-int v3, v3, v20
005afe: e003 0303 |006b: shl-int/lit8 v3, v3, #int 3 // #03
005b02: b632 |006d: or-int/2addr v2, v3
@@ -5802,19 +5802,19 @@
005dd4: 3803 1600 |01d6: if-eqz v3, 01ec // +0016
005dd8: 0800 1100 |01d8: move-object/from16 v0, v17
005ddc: 5201 4000 |01da: iget v1, v0, Lcom/google/android/checkers/a;.g:I // field@0040
-005de0: 1405 e0e0 e0e0 |01dc: const v5, #float -129633581999069331456.000000 // #e0e0e0e0
+005de0: 1405 e0e0 e0e0 |01dc: const v5, #float -1.29634e+20 // #e0e0e0e0
005de6: b535 |01df: and-int/2addr v5, v3
005de8: e205 0505 |01e0: ushr-int/lit8 v5, v5, #int 5 // #05
-005dec: 1406 0007 0707 |01e2: const v6, #float 0.000000 // #07070700
+005dec: 1406 0007 0707 |01e2: const v6, #float 1.01583e-34 // #07070700
005df2: b563 |01e5: and-int/2addr v3, v6
005df4: e203 0303 |01e6: ushr-int/lit8 v3, v3, #int 3 // #03
005df8: b653 |01e8: or-int/2addr v3, v5
005dfa: b531 |01e9: and-int/2addr v1, v3
005dfc: de01 0100 |01ea: or-int/lit8 v1, v1, #int 0 // #00
-005e00: 1403 e0e0 e0e0 |01ec: const v3, #float -129633581999069331456.000000 // #e0e0e0e0
+005e00: 1403 e0e0 e0e0 |01ec: const v3, #float -1.29634e+20 // #e0e0e0e0
005e06: 9503 0314 |01ef: and-int v3, v3, v20
005e0a: e203 0305 |01f1: ushr-int/lit8 v3, v3, #int 5 // #05
-005e0e: 1405 0007 0707 |01f3: const v5, #float 0.000000 // #07070700
+005e0e: 1405 0007 0707 |01f3: const v5, #float 1.01583e-34 // #07070700
005e14: 9505 0514 |01f6: and-int v5, v5, v20
005e18: e205 0503 |01f8: ushr-int/lit8 v5, v5, #int 3 // #03
005e1c: b653 |01fa: or-int/2addr v3, v5
@@ -5828,19 +5828,19 @@
005e34: e003 1404 |0206: shl-int/lit8 v3, v20, #int 4 // #04
005e38: b543 |0208: and-int/2addr v3, v4
005e3a: 3803 1100 |0209: if-eqz v3, 021a // +0011
-005e3e: 1405 0707 0707 |020b: const v5, #float 0.000000 // #07070707
+005e3e: 1405 0707 0707 |020b: const v5, #float 1.01583e-34 // #07070707
005e44: b535 |020e: and-int/2addr v5, v3
005e46: e005 0505 |020f: shl-int/lit8 v5, v5, #int 5 // #05
-005e4a: 1406 e0e0 e000 |0211: const v6, #float 0.000000 // #00e0e0e0
+005e4a: 1406 e0e0 e000 |0211: const v6, #float 2.06518e-38 // #00e0e0e0
005e50: b563 |0214: and-int/2addr v3, v6
005e52: e003 0303 |0215: shl-int/lit8 v3, v3, #int 3 // #03
005e56: b653 |0217: or-int/2addr v3, v5
005e58: b523 |0218: and-int/2addr v3, v2
005e5a: b631 |0219: or-int/2addr v1, v3
-005e5c: 1403 0707 0707 |021a: const v3, #float 0.000000 // #07070707
+005e5c: 1403 0707 0707 |021a: const v3, #float 1.01583e-34 // #07070707
005e62: 9503 0314 |021d: and-int v3, v3, v20
005e66: e003 0305 |021f: shl-int/lit8 v3, v3, #int 5 // #05
-005e6a: 1405 e0e0 e000 |0221: const v5, #float 0.000000 // #00e0e0e0
+005e6a: 1405 e0e0 e000 |0221: const v5, #float 2.06518e-38 // #00e0e0e0
005e70: 9505 0514 |0224: and-int v5, v5, v20
005e74: e005 0503 |0226: shl-int/lit8 v5, v5, #int 3 // #03
005e78: b653 |0228: or-int/2addr v3, v5
@@ -6423,9 +6423,9 @@
outs : 6
insns size : 461 16-bit code units
006604: |[006604] com.google.android.checkers.a.b:(IZI)Z
-006614: 1404 e0e0 e000 |0000: const v4, #float 0.000000 // #00e0e0e0
+006614: 1404 e0e0 e000 |0000: const v4, #float 2.06518e-38 // #00e0e0e0
00661a: 1216 |0003: const/4 v6, #int 1 // #1
-00661c: 1403 e0e0 e0e0 |0004: const v3, #float -129633581999069331456.000000 // #e0e0e0e0
+00661c: 1403 e0e0 e0e0 |0004: const v3, #float -1.29634e+20 // #e0e0e0e0
006622: 130a 0008 |0007: const/16 v10, #int 2048 // #800
006626: 1309 0002 |0009: const/16 v9, #int 512 // #200
00662a: 380d e400 |000b: if-eqz v13, 00ef // +00e4
@@ -6436,7 +6436,7 @@
00663e: 9502 0e03 |0015: and-int v2, v14, v3
006642: e202 0205 |0017: ushr-int/lit8 v2, v2, #int 5 // #05
006646: b621 |0019: or-int/2addr v1, v2
-006648: 1402 0007 0707 |001a: const v2, #float 0.000000 // #07070700
+006648: 1402 0007 0707 |001a: const v2, #float 1.01583e-34 // #07070700
00664e: b5e2 |001d: and-int/2addr v2, v14
006650: e202 0203 |001e: ushr-int/lit8 v2, v2, #int 3 // #03
006654: b621 |0020: or-int/2addr v1, v2
@@ -6453,14 +6453,14 @@
006676: 9502 0e03 |0031: and-int v2, v14, v3
00667a: e202 0205 |0033: ushr-int/lit8 v2, v2, #int 5 // #05
00667e: b621 |0035: or-int/2addr v1, v2
-006680: 1402 0007 0707 |0036: const v2, #float 0.000000 // #07070700
+006680: 1402 0007 0707 |0036: const v2, #float 1.01583e-34 // #07070700
006686: b5e2 |0039: and-int/2addr v2, v14
006688: e202 0203 |003a: ushr-int/lit8 v2, v2, #int 3 // #03
00668c: b621 |003c: or-int/2addr v1, v2
00668e: b510 |003d: and-int/2addr v0, v1
006690: 52b1 3e00 |003e: iget v1, v11, Lcom/google/android/checkers/a;.e:I // field@003e
006694: e002 0e04 |0040: shl-int/lit8 v2, v14, #int 4 // #04
-006698: 1403 0707 0707 |0042: const v3, #float 0.000000 // #07070707
+006698: 1403 0707 0707 |0042: const v3, #float 1.01583e-34 // #07070707
00669e: b5e3 |0045: and-int/2addr v3, v14
0066a0: e003 0305 |0046: shl-int/lit8 v3, v3, #int 5 // #05
0066a4: b632 |0048: or-int/2addr v2, v3
@@ -6563,7 +6563,7 @@
0067f6: 3900 5400 |00f1: if-nez v0, 0145 // +0054
0067fa: 52b0 3f00 |00f3: iget v0, v11, Lcom/google/android/checkers/a;.f:I // field@003f
0067fe: e001 0e04 |00f5: shl-int/lit8 v1, v14, #int 4 // #04
-006802: 1402 0707 0707 |00f7: const v2, #float 0.000000 // #07070707
+006802: 1402 0707 0707 |00f7: const v2, #float 1.01583e-34 // #07070707
006808: b5e2 |00fa: and-int/2addr v2, v14
00680a: e002 0205 |00fb: shl-int/lit8 v2, v2, #int 5 // #05
00680e: b621 |00fd: or-int/2addr v1, v2
@@ -6611,7 +6611,7 @@
0068a2: 52b1 3f00 |0147: iget v1, v11, Lcom/google/android/checkers/a;.f:I // field@003f
0068a6: b610 |0149: or-int/2addr v0, v1
0068a8: e001 0e04 |014a: shl-int/lit8 v1, v14, #int 4 // #04
-0068ac: 1402 0707 0707 |014c: const v2, #float 0.000000 // #07070707
+0068ac: 1402 0707 0707 |014c: const v2, #float 1.01583e-34 // #07070707
0068b2: b5e2 |014f: and-int/2addr v2, v14
0068b4: e002 0205 |0150: shl-int/lit8 v2, v2, #int 5 // #05
0068b8: b621 |0152: or-int/2addr v1, v2
@@ -6624,7 +6624,7 @@
0068ce: b5e3 |015d: and-int/2addr v3, v14
0068d0: e203 0305 |015e: ushr-int/lit8 v3, v3, #int 5 // #05
0068d4: b632 |0160: or-int/2addr v2, v3
-0068d6: 1403 0007 0707 |0161: const v3, #float 0.000000 // #07070700
+0068d6: 1403 0007 0707 |0161: const v3, #float 1.01583e-34 // #07070700
0068dc: b5e3 |0164: and-int/2addr v3, v14
0068de: e203 0303 |0165: ushr-int/lit8 v3, v3, #int 3 // #03
0068e2: b632 |0167: or-int/2addr v2, v3
diff --git a/test/dexdump/run-all-tests b/test/dexdump/run-all-tests
index 11ab55a..c9976cd 100755
--- a/test/dexdump/run-all-tests
+++ b/test/dexdump/run-all-tests
@@ -39,7 +39,7 @@
# Set up dexdump binary and flags to test.
DEXD="${ANDROID_HOST_OUT}/bin/dexdump2"
-DEXDFLAGS1="-dfh"
+DEXDFLAGS1="-adfh"
DEXDFLAGS2="-e -l xml"
# Set up dexlist binary and flags to test.
diff --git a/test/dexdump/staticfields.txt b/test/dexdump/staticfields.txt
index 022605f..f6d8f19 100644
--- a/test/dexdump/staticfields.txt
+++ b/test/dexdump/staticfields.txt
@@ -12,8 +12,8 @@
string_ids_off : 112 (0x000070)
type_ids_size : 12
type_ids_off : 224 (0x0000e0)
-proto_ids_size : 1
-proto_ids_off : 272 (0x000110)
+proto_ids_size : 1
+proto_ids_off : 272 (0x000110)
field_ids_size : 12
field_ids_off : 284 (0x00011c)
method_ids_size : 2
@@ -71,12 +71,12 @@
name : 'test05_public_static_final_float_46_47'
type : 'F'
access : 0x0019 (PUBLIC STATIC FINAL)
- value : 46.470001
+ value : 46.47
#6 : (in LStaticFields;)
name : 'test06_public_static_final_double_48_49'
type : 'D'
access : 0x0019 (PUBLIC STATIC FINAL)
- value : 48.490000
+ value : 48.49
#7 : (in LStaticFields;)
name : 'test07_public_static_final_string'
type : 'Ljava/lang/String;'
diff --git a/test/dexdump/staticfields.xml b/test/dexdump/staticfields.xml
index c906f0a..9082f0e 100644
--- a/test/dexdump/staticfields.xml
+++ b/test/dexdump/staticfields.xml
@@ -66,7 +66,7 @@
static="true"
final="true"
visibility="public"
- value="46.470001"
+ value="46.47"
>
</field>
<field name="test06_public_static_final_double_48_49"
@@ -76,7 +76,7 @@
static="true"
final="true"
visibility="public"
- value="48.490000"
+ value="48.49"
>
</field>
<field name="test07_public_static_final_string"
@@ -86,7 +86,7 @@
static="true"
final="true"
visibility="public"
- value="abc \><"'&	
"
+ value="abc \><"'&	
"
>
</field>
<field name="test08_public_static_final_object_null"
diff --git a/test/dexdump/values.dex b/test/dexdump/values.dex
new file mode 100644
index 0000000..84602d0
--- /dev/null
+++ b/test/dexdump/values.dex
Binary files differ
diff --git a/test/dexdump/values.lst b/test/dexdump/values.lst
new file mode 100644
index 0000000..0dbe3a9
--- /dev/null
+++ b/test/dexdump/values.lst
@@ -0,0 +1,3 @@
+#values.dex
+0x000003bc 8 Test <clinit> ()V Test.java 66
+0x000003d4 8 Test <init> ()V Test.java 1
diff --git a/test/dexdump/values.txt b/test/dexdump/values.txt
new file mode 100644
index 0000000..7f831b1
--- /dev/null
+++ b/test/dexdump/values.txt
@@ -0,0 +1,355 @@
+Processing 'values.dex'...
+Opened 'values.dex', DEX version '035'
+DEX file header:
+magic : 'dex\n035\0'
+checksum : 7605eec0
+signature : c197...a065
+file_size : 1864
+header_size : 112
+link_size : 0
+link_off : 0 (0x000000)
+string_ids_size : 70
+string_ids_off : 112 (0x000070)
+type_ids_size : 12
+type_ids_off : 392 (0x000188)
+proto_ids_size : 1
+proto_ids_off : 440 (0x0001b8)
+field_ids_size : 54
+field_ids_off : 452 (0x0001c4)
+method_ids_size : 3
+method_ids_off : 884 (0x000374)
+class_defs_size : 1
+class_defs_off : 908 (0x00038c)
+data_size : 924
+data_off : 940 (0x0003ac)
+
+Class #0 header:
+class_idx : 6
+access_flags : 1 (0x0001)
+superclass_idx : 7
+interfaces_off : 0 (0x000000)
+source_file_idx : 13
+annotations_off : 0 (0x000000)
+class_data_off : 1578 (0x00062a)
+static_fields_size : 54
+instance_fields_size: 0
+direct_methods_size : 2
+virtual_methods_size: 0
+
+Class #0 -
+ Class descriptor : 'LTest;'
+ Access flags : 0x0001 (PUBLIC)
+ Superclass : 'Ljava/lang/Object;'
+ Interfaces -
+ Static fields -
+ #0 : (in LTest;)
+ name : 'mB0'
+ type : 'B'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #1 : (in LTest;)
+ name : 'mB1'
+ type : 'B'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 127
+ #2 : (in LTest;)
+ name : 'mB2'
+ type : 'B'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -128
+ #3 : (in LTest;)
+ name : 'mB3'
+ type : 'B'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -1
+ #4 : (in LTest;)
+ name : 'mC0'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #5 : (in LTest;)
+ name : 'mC1'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 32767
+ #6 : (in LTest;)
+ name : 'mC2'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 32768
+ #7 : (in LTest;)
+ name : 'mC3'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 255
+ #8 : (in LTest;)
+ name : 'mC4'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 65520
+ #9 : (in LTest;)
+ name : 'mC5'
+ type : 'C'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 65535
+ #10 : (in LTest;)
+ name : 'mD0'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -inf
+ #11 : (in LTest;)
+ name : 'mD1'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 4.94066e-324
+ #12 : (in LTest;)
+ name : 'mD2'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -0
+ #13 : (in LTest;)
+ name : 'mD3'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #14 : (in LTest;)
+ name : 'mD4'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 1.79769e+308
+ #15 : (in LTest;)
+ name : 'mD5'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : inf
+ #16 : (in LTest;)
+ name : 'mD6'
+ type : 'D'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : nan
+ #17 : (in LTest;)
+ name : 'mF0'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -inf
+ #18 : (in LTest;)
+ name : 'mF1'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 1.4013e-45
+ #19 : (in LTest;)
+ name : 'mF2'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -0
+ #20 : (in LTest;)
+ name : 'mF3'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #21 : (in LTest;)
+ name : 'mF4'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 3.40282e+38
+ #22 : (in LTest;)
+ name : 'mF5'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : inf
+ #23 : (in LTest;)
+ name : 'mF6'
+ type : 'F'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : nan
+ #24 : (in LTest;)
+ name : 'mI0'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #25 : (in LTest;)
+ name : 'mI1'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 2147483647
+ #26 : (in LTest;)
+ name : 'mI2'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -2147483648
+ #27 : (in LTest;)
+ name : 'mI3'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 255
+ #28 : (in LTest;)
+ name : 'mI4'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -16
+ #29 : (in LTest;)
+ name : 'mI5'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -4096
+ #30 : (in LTest;)
+ name : 'mI6'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -1048576
+ #31 : (in LTest;)
+ name : 'mI7'
+ type : 'I'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -1
+ #32 : (in LTest;)
+ name : 'mJ0'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #33 : (in LTest;)
+ name : 'mJ1'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 9223372036854775807
+ #34 : (in LTest;)
+ name : 'mJ2'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -9223372036854775808
+ #35 : (in LTest;)
+ name : 'mJ3'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 255
+ #36 : (in LTest;)
+ name : 'mJ4'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -16
+ #37 : (in LTest;)
+ name : 'mJ5'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -4096
+ #38 : (in LTest;)
+ name : 'mJ6'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -1048576
+ #39 : (in LTest;)
+ name : 'mJ7'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -268435456
+ #40 : (in LTest;)
+ name : 'mJ8'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -68719476736
+ #41 : (in LTest;)
+ name : 'mJ9'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -17592186044416
+ #42 : (in LTest;)
+ name : 'mJa'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -4503599627370496
+ #43 : (in LTest;)
+ name : 'mJb'
+ type : 'J'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -1
+ #44 : (in LTest;)
+ name : 'mObject'
+ type : 'Ljava/lang/Object;'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : null
+ #45 : (in LTest;)
+ name : 'mS0'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 0
+ #46 : (in LTest;)
+ name : 'mS1'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 32767
+ #47 : (in LTest;)
+ name : 'mS2'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -32768
+ #48 : (in LTest;)
+ name : 'mS3'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : 255
+ #49 : (in LTest;)
+ name : 'mS4'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -16
+ #50 : (in LTest;)
+ name : 'mS5'
+ type : 'S'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : -1
+ #51 : (in LTest;)
+ name : 'mString'
+ type : 'Ljava/lang/String;'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : "<&\"JOHO\"&>\n"
+ #52 : (in LTest;)
+ name : 'mZ0'
+ type : 'Z'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : false
+ #53 : (in LTest;)
+ name : 'mZ1'
+ type : 'Z'
+ access : 0x0019 (PUBLIC STATIC FINAL)
+ value : true
+ Instance fields -
+ Direct methods -
+ #0 : (in LTest;)
+ name : '<clinit>'
+ type : '()V'
+ access : 0x10008 (STATIC CONSTRUCTOR)
+ code -
+ registers : 1
+ ins : 0
+ outs : 0
+ insns size : 4 16-bit code units
+0003ac: |[0003ac] Test.<clinit>:()V
+0003bc: 1200 |0000: const/4 v0, #int 0 // #0
+0003be: 6900 2c00 |0001: sput-object v0, LTest;.mObject:Ljava/lang/Object; // field@002c
+0003c2: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=66
+ locals :
+
+ #1 : (in LTest;)
+ name : '<init>'
+ type : '()V'
+ access : 0x10001 (PUBLIC CONSTRUCTOR)
+ code -
+ registers : 1
+ ins : 1
+ outs : 1
+ insns size : 4 16-bit code units
+0003c4: |[0003c4] Test.<init>:()V
+0003d4: 7010 0200 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0002
+0003da: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=1
+ locals :
+ 0x0000 - 0x0004 reg=0 this LTest;
+
+ Virtual methods -
+ source_file_idx : 13 (Test.java)
+
diff --git a/test/dexdump/values.xml b/test/dexdump/values.xml
new file mode 100644
index 0000000..d6ba48d
--- /dev/null
+++ b/test/dexdump/values.xml
@@ -0,0 +1,561 @@
+<api>
+<package name=""
+>
+<class name="Test"
+ extends="java.lang.Object"
+ interface="false"
+ abstract="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+<field name="mB0"
+ type="byte"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mB1"
+ type="byte"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="127"
+>
+</field>
+<field name="mB2"
+ type="byte"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-128"
+>
+</field>
+<field name="mB3"
+ type="byte"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-1"
+>
+</field>
+<field name="mC0"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mC1"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="32767"
+>
+</field>
+<field name="mC2"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="32768"
+>
+</field>
+<field name="mC3"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="255"
+>
+</field>
+<field name="mC4"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="65520"
+>
+</field>
+<field name="mC5"
+ type="char"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="65535"
+>
+</field>
+<field name="mD0"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-inf"
+>
+</field>
+<field name="mD1"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="4.94066e-324"
+>
+</field>
+<field name="mD2"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-0"
+>
+</field>
+<field name="mD3"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mD4"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="1.79769e+308"
+>
+</field>
+<field name="mD5"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="inf"
+>
+</field>
+<field name="mD6"
+ type="double"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="nan"
+>
+</field>
+<field name="mF0"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-inf"
+>
+</field>
+<field name="mF1"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="1.4013e-45"
+>
+</field>
+<field name="mF2"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-0"
+>
+</field>
+<field name="mF3"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mF4"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="3.40282e+38"
+>
+</field>
+<field name="mF5"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="inf"
+>
+</field>
+<field name="mF6"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="nan"
+>
+</field>
+<field name="mI0"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mI1"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="2147483647"
+>
+</field>
+<field name="mI2"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-2147483648"
+>
+</field>
+<field name="mI3"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="255"
+>
+</field>
+<field name="mI4"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-16"
+>
+</field>
+<field name="mI5"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-4096"
+>
+</field>
+<field name="mI6"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-1048576"
+>
+</field>
+<field name="mI7"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-1"
+>
+</field>
+<field name="mJ0"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mJ1"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="9223372036854775807"
+>
+</field>
+<field name="mJ2"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-9223372036854775808"
+>
+</field>
+<field name="mJ3"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="255"
+>
+</field>
+<field name="mJ4"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-16"
+>
+</field>
+<field name="mJ5"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-4096"
+>
+</field>
+<field name="mJ6"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-1048576"
+>
+</field>
+<field name="mJ7"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-268435456"
+>
+</field>
+<field name="mJ8"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-68719476736"
+>
+</field>
+<field name="mJ9"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-17592186044416"
+>
+</field>
+<field name="mJa"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-4503599627370496"
+>
+</field>
+<field name="mJb"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-1"
+>
+</field>
+<field name="mObject"
+ type="java.lang.Object"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="null"
+>
+</field>
+<field name="mS0"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="0"
+>
+</field>
+<field name="mS1"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="32767"
+>
+</field>
+<field name="mS2"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-32768"
+>
+</field>
+<field name="mS3"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="255"
+>
+</field>
+<field name="mS4"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-16"
+>
+</field>
+<field name="mS5"
+ type="short"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="-1"
+>
+</field>
+<field name="mString"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="<&"JOHO"&>
"
+>
+</field>
+<field name="mZ0"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="false"
+>
+</field>
+<field name="mZ1"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ visibility="public"
+ value="true"
+>
+</field>
+<constructor name="Test"
+ type="Test"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</constructor>
+</class>
+</package>
+</api>
diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt
new file mode 100644
index 0000000..7ae6d53
--- /dev/null
+++ b/test/valgrind-target-suppressions.txt
@@ -0,0 +1,52 @@
+# Valgrind does not recognize the ashmen ioctl() calls on ARM64, so it assumes that a size
+# parameter is a pointer.
+{
+ ashmem ioctl
+ Memcheck:Param
+ ioctl(generic)
+ ...
+ fun:ioctl
+ fun:ashmem_create_region
+}
+
+# It seems that on ARM64 Valgrind considers the canary value used by the Clang stack protector to
+# be an uninitialized value.
+{
+ jemalloc chunk_alloc_cache
+ Memcheck:Cond
+ fun:je_chunk_alloc_cache
+}
+
+# The VectorImpl class does not hold a pointer to the allocated SharedBuffer structure, but to the
+# beginning of the data, which is effectively an interior pointer. Valgrind has limitations when
+# dealing with interior pointers.
+{
+ VectorImpl
+ Memcheck:Leak
+ match-leak-kinds:possible
+ fun:malloc
+ # The wildcards make this rule work both for 32-bit and 64-bit environments.
+ fun:_ZN7android12SharedBuffer5allocE?
+ fun:_ZN7android10VectorImpl5_growE??
+}
+
+# Clang/LLVM uses memcpy for *x = *y, even though x == y (which is undefined behavior). Ignore.
+# b/29279679, https://llvm.org/bugs/show_bug.cgi?id=11763
+{
+ MemCpySelfAssign
+ Memcheck:Overlap
+ fun:memcpy
+ fun:je_tsd_set
+ fun:je_tsd_fetch
+ fun:je_malloc_tsd_boot0
+}
+
+# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing
+# ANDROID_DATA. Ignore all setenv leaks.
+{
+ SetenvAndroidDataReinit
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:setenv
+}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 304c2a9..d88a4a0 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -51,7 +51,7 @@
make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
elif [[ $mode == "target" ]]; then
make_command="make $j_arg $showcommands build-art-target-tests $common_targets"
- make_command+=" libjavacrypto libjavacoretests linker toybox toolbox sh"
+ make_command+=" libjavacrypto libjavacoretests libnetd_client linker toybox toolbox sh"
make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
fi