am 5be30072: ART: Remove wrong DCHECK
* commit '5be30072c5a750617dc3f9380776d074f26d9f32':
ART: Remove wrong DCHECK
diff --git a/Android.mk b/Android.mk
index 15e8308..1edd543 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,15 +24,16 @@
include $(art_path)/build/Android.common_path.mk
-# following the example of build's dont_bother for clean targets
-ifneq (,$(filter clean-oat,$(MAKECMDGOALS)))
-art_dont_bother := true
+# Following the example of build's dont_bother for clean targets.
+art_dont_bother := false
+ifneq (,$(filter clean-oat%,$(MAKECMDGOALS)))
+ art_dont_bother := true
endif
-ifneq (,$(filter clean-oat-host,$(MAKECMDGOALS)))
-art_dont_bother := true
-endif
-ifneq (,$(filter clean-oat-target,$(MAKECMDGOALS)))
-art_dont_bother := true
+
+# Don't bother with tests unless there is a test-art* or build-art* target.
+art_test_bother := false
+ifneq (,$(filter %tests test-art% build-art%,$(MAKECMDGOALS)))
+ art_test_bother := true
endif
.PHONY: clean-oat
@@ -77,7 +78,8 @@
.PHONY: clean-oat-target
clean-oat-target:
- adb remount
+ adb root
+ adb wait-for-device remount
adb shell rm -rf $(ART_TARGET_NATIVETEST_DIR)
adb shell rm -rf $(ART_TARGET_TEST_DIR)
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*/*
@@ -123,10 +125,15 @@
ifdef TARGET_2ND_ARCH
ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
endif
+ifdef HOST_2ND_ARCH
+ART_HOST_DEPENDENCIES += $(2ND_HOST_OUT_SHARED_LIBRARIES)/libjavacore.so
+endif
########################################################################
# test rules
+ifeq ($(art_test_bother),true)
+
# All the dependencies that must be built ahead of sync-ing them onto the target device.
TEST_ART_TARGET_SYNC_DEPS :=
@@ -137,7 +144,8 @@
# Sync test files to the target, depends upon all things that must be pushed to the target.
.PHONY: test-art-target-sync
test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
- adb remount
+ adb root
+ adb wait-for-device remount
adb sync
# Undefine variable now its served its purpose.
@@ -229,6 +237,11 @@
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
+# Valgrind. Currently only 32b gtests.
+.PHONY: valgrind-test-art-host
+valgrind-test-art-host: valgrind-test-art-host-gtest32
+ $(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
########################################################################
# target test rules
@@ -290,6 +303,8 @@
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
+endif # art_test_bother
+
########################################################################
# oat-target and oat-target-sync rules
@@ -340,7 +355,8 @@
.PHONY: oat-target-sync
oat-target-sync: oat-target
- adb remount
+ adb root
+ adb wait-for-device remount
adb sync
########################################################################
@@ -349,51 +365,39 @@
build-art: build-art-host build-art-target
.PHONY: build-art-host
-build-art-host: $(ART_HOST_EXECUTABLES) $(ART_HOST_GTEST_EXECUTABLES) $(HOST_CORE_IMG_OUT) $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION)
+build-art-host: $(HOST_OUT_EXECUTABLES)/art $(ART_HOST_DEPENDENCIES) $(HOST_CORE_IMG_OUT) $(2ND_HOST_CORE_IMG_OUT)
.PHONY: build-art-target
-build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_GTEST_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so
-
-########################################################################
-# "m art-host" for just building the files needed to run the art script
-.PHONY: art-host
-ifeq ($(HOST_PREFER_32_BIT),true)
-art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/bin/dalvikvm
-else
-art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm64 $(HOST_OUT)/bin/dalvikvm32 $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT)/bin/patchoat $(HOST_CORE_IMG_OUT) $(HOST_OUT)/lib/libjavacore.so $(HOST_OUT)/lib64/libjavacore.so $(HOST_OUT)/bin/dalvikvm
-endif
-
-.PHONY: art-host-debug
-art-host-debug: art-host $(HOST_OUT)/lib/libartd.so $(HOST_OUT)/bin/dex2oatd $(HOST_OUT)/bin/patchoatd
+build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
########################################################################
# targets to switch back and forth from libdvm to libart
.PHONY: use-art
use-art:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell setprop persist.sys.dalvik.vm.lib.2 libart.so
adb shell start
.PHONY: use-artd
use-artd:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell setprop persist.sys.dalvik.vm.lib.2 libartd.so
adb shell start
.PHONY: use-dalvik
use-dalvik:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell setprop persist.sys.dalvik.vm.lib.2 libdvm.so
adb shell start
.PHONY: use-art-full
use-art-full:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
adb shell setprop dalvik.vm.dex2oat-filter ""
adb shell setprop dalvik.vm.image-dex2oat-filter ""
@@ -402,8 +406,8 @@
.PHONY: use-artd-full
use-artd-full:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
adb shell setprop dalvik.vm.dex2oat-filter ""
adb shell setprop dalvik.vm.image-dex2oat-filter ""
@@ -412,8 +416,8 @@
.PHONY: use-art-smart
use-art-smart:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
adb shell setprop dalvik.vm.image-dex2oat-filter ""
@@ -422,8 +426,8 @@
.PHONY: use-art-interpret-only
use-art-interpret-only:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
@@ -432,8 +436,8 @@
.PHONY: use-artd-interpret-only
use-artd-interpret-only:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
adb shell setprop dalvik.vm.dex2oat-filter "interpret-only"
adb shell setprop dalvik.vm.image-dex2oat-filter "interpret-only"
@@ -442,8 +446,8 @@
.PHONY: use-art-verify-none
use-art-verify-none:
- adb root && sleep 3
- adb shell stop
+ adb root
+ adb wait-for-device shell stop
adb shell rm -rf $(ART_TARGET_DALVIK_CACHE_DIR)/*
adb shell setprop dalvik.vm.dex2oat-filter "verify-none"
adb shell setprop dalvik.vm.image-dex2oat-filter "verify-none"
@@ -453,3 +457,7 @@
########################################################################
endif # !art_dont_bother
+
+# Clear locally used variables.
+art_dont_bother :=
+art_test_bother :=
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 0dcefea..386128e 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -158,6 +158,7 @@
-Wno-unused-parameter \
-Wstrict-aliasing \
-fstrict-aliasing \
+ -Wunreachable-code \
-fvisibility=protected
ART_TARGET_CLANG_CFLAGS :=
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 7e38157..ee72706 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -26,18 +26,6 @@
# List of known broken tests that we won't attempt to execute. The test name must be the full
# rule name such as test-art-host-oat-optimizing-HelloWorld64.
ART_TEST_KNOWN_BROKEN := \
- test-art-host-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
- test-art-host-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
- test-art-host-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
- test-art-host-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
- test-art-host-run-test-gcverify-optimizing-no-prebuild-004-SignalTest32 \
- test-art-host-run-test-gcverify-optimizing-prebuild-004-SignalTest32 \
- test-art-host-run-test-gcverify-optimizing-norelocate-004-SignalTest32 \
- test-art-host-run-test-gcverify-optimizing-relocate-004-SignalTest32 \
- test-art-host-run-test-optimizing-no-prebuild-004-SignalTest32 \
- test-art-host-run-test-optimizing-prebuild-004-SignalTest32 \
- test-art-host-run-test-optimizing-norelocate-004-SignalTest32 \
- test-art-host-run-test-optimizing-relocate-004-SignalTest32 \
test-art-target-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
test-art-target-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
test-art-target-run-test-gcstress-default-prebuild-004-SignalTest32 \
@@ -45,7 +33,16 @@
test-art-target-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
test-art-target-run-test-gcstress-default-relocate-004-SignalTest32 \
test-art-target-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
- test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32
+ test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32 \
+ test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC32 \
+ test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC32 \
+ test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC32 \
+ test-art-host-run-test-gcstress-default-prebuild-114-ParallelGC64 \
+ test-art-host-run-test-gcstress-interpreter-prebuild-114-ParallelGC64 \
+ test-art-host-run-test-gcstress-optimizing-prebuild-114-ParallelGC64
+
+# Failing valgrind tests.
+# Note: *all* 64b tests involving the runtime do not work currently. b/15170219.
# List of known failing tests that when executed won't cause test execution to not finish.
# The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64.
@@ -55,7 +52,13 @@
ART_TEST_KEEP_GOING ?= true
# Do you want all tests, even those that are time consuming?
-ART_TEST_FULL ?= true
+ART_TEST_FULL ?= false
+
+# Do you want default compiler tests run?
+ART_TEST_DEFAULT_COMPILER ?= true
+
+# Do you want interpreter tests run?
+ART_TEST_INTERPRETER ?= $(ART_TEST_FULL)
# Do you want optimizing compiler tests run?
ART_TEST_OPTIMIZING ?= $(ART_TEST_FULL)
@@ -69,17 +72,23 @@
# Do you want tests with the GC stress mode enabled run?
ART_TEST_GC_STRESS ?= $(ART_TEST_FULL)
-# Do you want run-tests with relocation enabled?
-ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
+# Do you want tests with the JNI forcecopy mode enabled run?
+ART_TEST_JNI_FORCECOPY ?= $(ART_TEST_FULL)
-# Do you want run-tests with relocation disabled?
+# Do you want run-tests with relocation disabled run?
ART_TEST_RUN_TEST_NO_RELOCATE ?= $(ART_TEST_FULL)
-# Do you want run-tests with prebuild disabled?
+# Do you want run-tests with no prebuilding enabled run?
ART_TEST_RUN_TEST_NO_PREBUILD ?= $(ART_TEST_FULL)
-# Do you want run-tests with prebuild enabled?
-ART_TEST_RUN_TEST_PREBUILD ?= true
+# Do you want run-tests without a pregenerated core.art?
+ART_TEST_RUN_TEST_NO_IMAGE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation enabled but patchoat failing?
+ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT ?= $(ART_TEST_FULL)
+
+# Do you want run-tests without a dex2oat?
+ART_TEST_RUN_TEST_NO_DEX2OAT ?= $(ART_TEST_FULL)
# Do you want failed tests to have their artifacts cleaned up?
ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a7d852b..af43a3c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -48,7 +48,7 @@
# Dex file dependencies for each gtest.
ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MyClass Nested Statics StaticsFromCode
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature
+ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Nested
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
@@ -61,6 +61,9 @@
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_OAT_OUT) $(2ND_HOST_CORE_OAT_OUT)
ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_OAT_OUT) $(2ND_TARGET_CORE_OAT_OUT)
+ART_GTEST_jni_internal_test_TARGET_DEPS := $(TARGET_CORE_DEX_FILES)
+ART_GTEST_proxy_test_TARGET_DEPS := $(TARGET_CORE_DEX_FILES)
+ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_OAT_OUT) $(2ND_HOST_CORE_OAT_OUT)
# The path for which all the source files are relative, not actually the current directory.
LOCAL_PATH := art
@@ -137,18 +140,24 @@
compiler/jni/jni_compiler_test.cc \
compiler/oat_test.cc \
compiler/optimizing/codegen_test.cc \
+ compiler/optimizing/dead_code_elimination_test.cc \
+ compiler/optimizing/constant_propagation_test.cc \
compiler/optimizing/dominator_test.cc \
compiler/optimizing/find_loops_test.cc \
+ compiler/optimizing/graph_checker_test.cc \
compiler/optimizing/graph_test.cc \
+ compiler/optimizing/gvn_test.cc \
compiler/optimizing/linearize_test.cc \
compiler/optimizing/liveness_test.cc \
compiler/optimizing/live_interval_test.cc \
compiler/optimizing/live_ranges_test.cc \
+ compiler/optimizing/nodes_test.cc \
compiler/optimizing/parallel_move_test.cc \
compiler/optimizing/pretty_printer_test.cc \
compiler/optimizing/register_allocator_test.cc \
compiler/optimizing/ssa_test.cc \
compiler/optimizing/stack_map_test.cc \
+ compiler/optimizing/suspend_check_test.cc \
compiler/output_stream_test.cc \
compiler/utils/arena_allocator_test.cc \
compiler/utils/dedupe_set_test.cc \
@@ -440,7 +449,14 @@
endif
rule_name := $(3)test-art-$(1)-gtest$(4)
- dependencies := $$(ART_TEST_$(2)_GTEST$(4)_RULES)
+ 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)
+ endif
.PHONY: $$(rule_name)
$$(rule_name): $$(dependencies)
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 10936a4..1c462eb 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -25,8 +25,10 @@
# Use dex2oat debug version for better error reporting
# $(1): 2ND_ or undefined, 2ND_ for 32-bit host builds.
+# NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
+# run-test --no-image
define create-core-oat-host-rules
-$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY)
+$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_LOCATIONS) $$(DEX2OATD_DEPENDENCY)
@echo "host dex2oat: $$@ ($$?)"
@mkdir -p $$(dir $$@)
$$(hide) $$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 69f9387..8b5e6d5 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -48,6 +48,7 @@
dex/quick/mips/target_mips.cc \
dex/quick/mips/utility_mips.cc \
dex/quick/mir_to_lir.cc \
+ dex/quick/quick_compiler.cc \
dex/quick/ralloc_util.cc \
dex/quick/resource_mask.cc \
dex/quick/x86/assemble_x86.cc \
@@ -62,6 +63,7 @@
dex/mir_method_info.cc \
dex/mir_optimization.cc \
dex/bb_optimizations.cc \
+ dex/compiler_ir.cc \
dex/post_opt_passes.cc \
dex/pass_driver_me_opts.cc \
dex/pass_driver_me_post_opt.cc \
@@ -82,12 +84,18 @@
jni/quick/x86_64/calling_convention_x86_64.cc \
jni/quick/calling_convention.cc \
jni/quick/jni_compiler.cc \
+ llvm/llvm_compiler.cc \
optimizing/builder.cc \
optimizing/code_generator.cc \
optimizing/code_generator_arm.cc \
optimizing/code_generator_x86.cc \
optimizing/code_generator_x86_64.cc \
+ optimizing/constant_propagation.cc \
+ optimizing/dead_code_elimination.cc \
+ optimizing/graph_checker.cc \
optimizing/graph_visualizer.cc \
+ optimizing/gvn.cc \
+ optimizing/instruction_simplifier.cc \
optimizing/locations.cc \
optimizing/nodes.cc \
optimizing/optimizing_compiler.cc \
@@ -107,6 +115,7 @@
utils/arm64/assembler_arm64.cc \
utils/arm64/managed_register_arm64.cc \
utils/assembler.cc \
+ utils/dwarf_cfi.cc \
utils/mips/assembler_mips.cc \
utils/mips/managed_register_mips.cc \
utils/x86/assembler_x86.cc \
@@ -115,10 +124,8 @@
utils/x86_64/managed_register_x86_64.cc \
utils/scoped_arena_allocator.cc \
buffered_output_stream.cc \
- compilers.cc \
compiler.cc \
elf_fixup.cc \
- elf_patcher.cc \
elf_stripper.cc \
elf_writer.cc \
elf_writer_quick.cc \
@@ -282,10 +289,10 @@
endef
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
$(eval $(call build-libart-compiler,host,ndebug))
endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
$(eval $(call build-libart-compiler,host,debug))
endif
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 1e6c038..fbaed9f 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -217,11 +217,8 @@
// No code? You must mean to go into the interpreter.
// Or the generic JNI...
if (!method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
- const void* method_code = GetPortableToInterpreterBridge();
-#else
- const void* method_code = GetQuickToInterpreterBridge();
-#endif
+ const void* method_code = kUsePortableCompiler ? GetPortableToInterpreterBridge()
+ : GetQuickToInterpreterBridge();
OatFile::OatMethod oat_method = CreateOatMethod(method_code, nullptr);
oat_method.LinkMethod(method);
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -234,7 +231,6 @@
}
}
// Create bridges to transition between different kinds of compiled bridge.
-#if defined(ART_USE_PORTABLE_COMPILER)
if (method->GetEntryPointFromPortableCompiledCode() == nullptr) {
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
} else {
@@ -242,9 +238,6 @@
method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
method->SetIsPortableCompiled();
}
-#else
- CHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
-#endif
}
void CommonCompilerTest::MakeExecutable(const void* code_start, size_t code_length) {
@@ -387,9 +380,9 @@
CompileMethod(method);
}
-void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
- const char* method_name, const char* signature)
-SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader,
+ const char* class_name, const char* method_name,
+ const char* signature) {
std::string class_descriptor(DotToDescriptor(class_name));
Thread* self = Thread::Current();
mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index f098a34..698bf3b 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -148,16 +148,40 @@
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
+ SrcMap* src_mapping_table,
const std::vector<uint8_t>& mapping_table,
const std::vector<uint8_t>& vmap_table,
const std::vector<uint8_t>& native_gc_map,
- const std::vector<uint8_t>* cfi_info)
+ const std::vector<uint8_t>* cfi_info,
+ const ArrayRef<LinkerPatch>& patches)
: CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
- mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
- vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
- gc_map_(driver->DeduplicateGCMap(native_gc_map)),
- cfi_info_(driver->DeduplicateCFIInfo(cfi_info)) {
+ src_mapping_table_(driver->DeduplicateSrcMappingTable(src_mapping_table->Arrange())),
+ mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+ vmap_table_(driver->DeduplicateVMapTable(vmap_table)),
+ gc_map_(driver->DeduplicateGCMap(native_gc_map)),
+ cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
+ patches_(patches.begin(), patches.end()) {
+}
+
+CompiledMethod::CompiledMethod(CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const std::vector<uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ const std::vector<uint8_t>& mapping_table,
+ const std::vector<uint8_t>& stack_map)
+ : CompiledCode(driver, instruction_set, quick_code),
+ frame_size_in_bytes_(frame_size_in_bytes),
+ core_spill_mask_(core_spill_mask),
+ fp_spill_mask_(fp_spill_mask),
+ src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+ mapping_table_(driver->DeduplicateMappingTable(mapping_table)),
+ vmap_table_(driver->DeduplicateVMapTable(stack_map)),
+ gc_map_(nullptr),
+ cfi_info_(nullptr),
+ patches_() {
}
CompiledMethod::CompiledMethod(CompilerDriver* driver,
@@ -165,14 +189,17 @@
const std::vector<uint8_t>& code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
- const uint32_t fp_spill_mask)
+ const uint32_t fp_spill_mask,
+ const std::vector<uint8_t>* cfi_info)
: CompiledCode(driver, instruction_set, code),
frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask),
+ src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
- cfi_info_(nullptr) {
+ cfi_info_(driver->DeduplicateCFIInfo(cfi_info)),
+ patches_() {
}
// Constructs a CompiledMethod for the Portable compiler.
@@ -181,19 +208,26 @@
const std::string& symbol)
: CompiledCode(driver, instruction_set, code, symbol),
frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
- fp_spill_mask_(0), gc_map_(driver->DeduplicateGCMap(gc_map)) {
- mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
- vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
+ fp_spill_mask_(0),
+ src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+ mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
+ vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
+ gc_map_(driver->DeduplicateGCMap(gc_map)),
+ cfi_info_(nullptr),
+ patches_() {
}
CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set,
const std::string& code, const std::string& symbol)
: CompiledCode(driver, instruction_set, code, symbol),
frame_size_in_bytes_(kStackAlignment), core_spill_mask_(0),
- fp_spill_mask_(0) {
- mapping_table_ = driver->DeduplicateMappingTable(std::vector<uint8_t>());
- vmap_table_ = driver->DeduplicateVMapTable(std::vector<uint8_t>());
- gc_map_ = driver->DeduplicateGCMap(std::vector<uint8_t>());
+ fp_spill_mask_(0),
+ src_mapping_table_(driver->DeduplicateSrcMappingTable(SrcMap())),
+ mapping_table_(driver->DeduplicateMappingTable(std::vector<uint8_t>())),
+ vmap_table_(driver->DeduplicateVMapTable(std::vector<uint8_t>())),
+ gc_map_(driver->DeduplicateGCMap(std::vector<uint8_t>())),
+ cfi_info_(nullptr),
+ patches_() {
}
} // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index b8cd851..cdae8d2 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -22,7 +22,9 @@
#include <vector>
#include "instruction_set.h"
+#include "method_reference.h"
#include "utils.h"
+#include "utils/array_ref.h"
namespace llvm {
class Function;
@@ -100,9 +102,189 @@
std::vector<uint32_t> oatdata_offsets_to_compiled_code_offset_;
};
-class CompiledMethod : public CompiledCode {
+class SrcMapElem {
public:
- // Constructs a CompiledMethod for the non-LLVM compilers.
+ uint32_t from_;
+ int32_t to_;
+
+ explicit operator int64_t() const {
+ return (static_cast<int64_t>(to_) << 32) | from_;
+ }
+
+ bool operator<(const SrcMapElem& sme) const {
+ return int64_t(*this) < int64_t(sme);
+ }
+
+ bool operator==(const SrcMapElem& sme) const {
+ return int64_t(*this) == int64_t(sme);
+ }
+
+ explicit operator uint8_t() const {
+ return static_cast<uint8_t>(from_ + to_);
+ }
+};
+
+class SrcMap FINAL : public std::vector<SrcMapElem> {
+ public:
+ void SortByFrom() {
+ std::sort(begin(), end(), [] (const SrcMapElem& lhs, const SrcMapElem& rhs) -> bool {
+ return lhs.from_ < rhs.from_;
+ });
+ }
+
+ const_iterator FindByTo(int32_t to) const {
+ return std::lower_bound(begin(), end(), SrcMapElem({0, to}));
+ }
+
+ SrcMap& Arrange() {
+ if (!empty()) {
+ std::sort(begin(), end());
+ resize(std::unique(begin(), end()) - begin());
+ shrink_to_fit();
+ }
+ return *this;
+ }
+
+ void DeltaFormat(const SrcMapElem& start, uint32_t highest_pc) {
+ // Convert from abs values to deltas.
+ if (!empty()) {
+ SortByFrom();
+
+ // TODO: one PC can be mapped to several Java src lines.
+ // do we want such a one-to-many correspondence?
+
+ // get rid of the highest values
+ size_t i = size() - 1;
+ for (; i > 0 ; i--) {
+ if ((*this)[i].from_ < highest_pc) {
+ break;
+ }
+ }
+ this->resize(i + 1);
+
+ for (size_t i = size(); --i >= 1; ) {
+ (*this)[i].from_ -= (*this)[i-1].from_;
+ (*this)[i].to_ -= (*this)[i-1].to_;
+ }
+ DCHECK((*this)[0].from_ >= start.from_);
+ (*this)[0].from_ -= start.from_;
+ (*this)[0].to_ -= start.to_;
+ }
+ }
+};
+
+enum LinkerPatchType {
+ kLinkerPatchMethod,
+ kLinkerPatchCall,
+ kLinkerPatchCallRelative, // NOTE: Actual patching is instruction_set-dependent.
+ kLinkerPatchType,
+};
+
+class LinkerPatch {
+ public:
+ static LinkerPatch MethodPatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t target_method_idx) {
+ return LinkerPatch(literal_offset, kLinkerPatchMethod,
+ target_method_idx, target_dex_file);
+ }
+
+ static LinkerPatch CodePatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t target_method_idx) {
+ return LinkerPatch(literal_offset, kLinkerPatchCall,
+ target_method_idx, target_dex_file);
+ }
+
+ static LinkerPatch RelativeCodePatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t target_method_idx) {
+ return LinkerPatch(literal_offset, kLinkerPatchCallRelative,
+ target_method_idx, target_dex_file);
+ }
+
+ static LinkerPatch TypePatch(size_t literal_offset,
+ const DexFile* target_dex_file,
+ uint32_t target_type_idx) {
+ return LinkerPatch(literal_offset, kLinkerPatchType, target_type_idx, target_dex_file);
+ }
+
+ LinkerPatch(const LinkerPatch& other) = default;
+ LinkerPatch& operator=(const LinkerPatch& other) = default;
+
+ size_t LiteralOffset() const {
+ return literal_offset_;
+ }
+
+ LinkerPatchType Type() const {
+ return patch_type_;
+ }
+
+ MethodReference TargetMethod() const {
+ DCHECK(patch_type_ == kLinkerPatchMethod ||
+ patch_type_ == kLinkerPatchCall || patch_type_ == kLinkerPatchCallRelative);
+ return MethodReference(target_dex_file_, target_idx_);
+ }
+
+ const DexFile* TargetTypeDexFile() const {
+ DCHECK(patch_type_ == kLinkerPatchType);
+ return target_dex_file_;
+ }
+
+ uint32_t TargetTypeIndex() const {
+ DCHECK(patch_type_ == kLinkerPatchType);
+ return target_idx_;
+ }
+
+ private:
+ LinkerPatch(size_t literal_offset, LinkerPatchType patch_type,
+ uint32_t target_idx, const DexFile* target_dex_file)
+ : literal_offset_(literal_offset),
+ patch_type_(patch_type),
+ target_idx_(target_idx),
+ target_dex_file_(target_dex_file) {
+ }
+
+ size_t literal_offset_;
+ LinkerPatchType patch_type_;
+ uint32_t target_idx_; // Method index (Call/Method patches) or type index (Type patches).
+ const DexFile* target_dex_file_;
+
+ friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
+ friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
+};
+
+inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
+ return lhs.literal_offset_ == rhs.literal_offset_ &&
+ lhs.patch_type_ == rhs.patch_type_ &&
+ lhs.target_idx_ == rhs.target_idx_ &&
+ lhs.target_dex_file_ == rhs.target_dex_file_;
+}
+
+inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) {
+ return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_
+ : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_
+ : (lhs.target_idx_ != rhs.target_idx_) ? lhs.target_idx_ < rhs.target_idx_
+ : lhs.target_dex_file_ < rhs.target_dex_file_;
+}
+
+class CompiledMethod FINAL : public CompiledCode {
+ public:
+ // Constructs a CompiledMethod for Quick.
+ CompiledMethod(CompilerDriver* driver,
+ InstructionSet instruction_set,
+ const std::vector<uint8_t>& quick_code,
+ const size_t frame_size_in_bytes,
+ const uint32_t core_spill_mask,
+ const uint32_t fp_spill_mask,
+ SrcMap* src_mapping_table,
+ const std::vector<uint8_t>& mapping_table,
+ const std::vector<uint8_t>& vmap_table,
+ const std::vector<uint8_t>& native_gc_map,
+ const std::vector<uint8_t>* cfi_info,
+ const ArrayRef<LinkerPatch>& patches = ArrayRef<LinkerPatch>());
+
+ // Constructs a CompiledMethod for Optimizing.
CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
const std::vector<uint8_t>& quick_code,
@@ -110,9 +292,7 @@
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
const std::vector<uint8_t>& mapping_table,
- const std::vector<uint8_t>& vmap_table,
- const std::vector<uint8_t>& native_gc_map,
- const std::vector<uint8_t>* cfi_info);
+ const std::vector<uint8_t>& vmap_table);
// Constructs a CompiledMethod for the QuickJniCompiler.
CompiledMethod(CompilerDriver* driver,
@@ -120,7 +300,8 @@
const std::vector<uint8_t>& quick_code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
- const uint32_t fp_spill_mask);
+ const uint32_t fp_spill_mask,
+ const std::vector<uint8_t>* cfi_info);
// Constructs a CompiledMethod for the Portable compiler.
CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const std::string& code,
@@ -144,6 +325,11 @@
return fp_spill_mask_;
}
+ const SrcMap& GetSrcMappingTable() const {
+ DCHECK(src_mapping_table_ != nullptr);
+ return *src_mapping_table_;
+ }
+
const std::vector<uint8_t>& GetMappingTable() const {
DCHECK(mapping_table_ != nullptr);
return *mapping_table_;
@@ -154,15 +340,18 @@
return *vmap_table_;
}
- const std::vector<uint8_t>& GetGcMap() const {
- DCHECK(gc_map_ != nullptr);
- return *gc_map_;
+ std::vector<uint8_t> const* GetGcMap() const {
+ return gc_map_;
}
const std::vector<uint8_t>* GetCFIInfo() const {
return cfi_info_;
}
+ const std::vector<LinkerPatch>& GetPatches() const {
+ return patches_;
+ }
+
private:
// For quick code, the size of the activation used by the code.
const size_t frame_size_in_bytes_;
@@ -170,6 +359,8 @@
const uint32_t core_spill_mask_;
// For quick code, a bit mask describing spilled FPR callee-save registers.
const uint32_t fp_spill_mask_;
+ // For quick code, a set of pairs (PC, Line) mapping from native PC offset to Java line
+ SrcMap* src_mapping_table_;
// For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
// native PC offset. Size prefixed.
std::vector<uint8_t>* mapping_table_;
@@ -180,6 +371,8 @@
std::vector<uint8_t>* gc_map_;
// For quick code, a FDE entry for the debug_frame section.
std::vector<uint8_t>* cfi_info_;
+ // For quick code, linker patches needed by the method.
+ std::vector<LinkerPatch> patches_;
};
} // namespace art
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index a832c31..fbfd8e6 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -15,14 +15,12 @@
*/
#include "compiler.h"
-#include "compilers.h"
-#include "driver/compiler_driver.h"
-#include "mirror/art_method-inl.h"
-#ifdef ART_USE_PORTABLE_COMPILER
-#include "dex/portable/mir_to_gbc.h"
-#include "elf_writer_mclinker.h"
-#endif
+#include "base/logging.h"
+#include "dex/quick/quick_compiler.h"
+#include "driver/compiler_driver.h"
+#include "llvm/llvm_compiler.h"
+#include "optimizing/optimizing_compiler.h"
namespace art {
@@ -60,137 +58,21 @@
return nullptr;
}
-
-#ifdef ART_USE_PORTABLE_COMPILER
-
-extern "C" void ArtInitCompilerContext(art::CompilerDriver* driver);
-
-extern "C" void ArtUnInitCompilerContext(art::CompilerDriver* driver);
-
-extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver* driver,
- const art::DexFile::CodeItem* code_item,
- uint32_t access_flags,
- art::InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const art::DexFile& dex_file);
-
-extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver* driver,
- uint32_t access_flags, uint32_t method_idx,
- const art::DexFile& dex_file);
-
-extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver* driver,
- std::string const& filename);
-
-
-class LLVMCompiler FINAL : public Compiler {
- public:
- explicit LLVMCompiler(CompilerDriver* driver) : Compiler(driver, 1000) {}
-
- void Init() const OVERRIDE {
- ArtInitCompilerContext(GetCompilerDriver());
- }
-
- void UnInit() const OVERRIDE {
- ArtUnInitCompilerContext(GetCompilerDriver());
- }
-
- CompiledMethod* Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const OVERRIDE {
- CompiledMethod* method = TryCompileWithSeaIR(code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- if (method != nullptr) {
- return method;
- }
-
- return ArtCompileMethod(GetCompilerDriver(),
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- }
-
- CompiledMethod* JniCompile(uint32_t access_flags,
- uint32_t method_idx,
- const DexFile& dex_file) const OVERRIDE {
- return ArtLLVMJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
- }
-
- uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const {
- return reinterpret_cast<uintptr_t>(method->GetEntryPointFromPortableCompiledCode());
- }
-
- bool WriteElf(art::File* file,
- OatWriter* oat_writer,
- const std::vector<const art::DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host, const CompilerDriver& driver) const
- OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return art::ElfWriterMclinker::Create(
- file, oat_writer, dex_files, android_root, is_host, driver);
- }
-
- Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
- return PortableCodeGenerator(
- cu, cu->mir_graph.get(), &cu->arena,
- reinterpret_cast<art::llvm::LlvmCompilationUnit*>(compilation_unit));
- }
-
- void InitCompilationUnit(CompilationUnit& cu) const {
- // Fused long branches not currently useful in bitcode.
- cu.disable_opt |=
- (1 << kBranchFusing) |
- (1 << kSuppressExceptionEdges);
- }
-
- bool IsPortable() const OVERRIDE {
- return true;
- }
-
- void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
- typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&);
-
- SetBitcodeFileNameFn set_bitcode_file_name =
- reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName);
-
- set_bitcode_file_name(driver, filename);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LLVMCompiler);
-};
-#endif
-
Compiler* Compiler::Create(CompilerDriver* driver, Compiler::Kind kind) {
switch (kind) {
case kQuick:
- return new QuickCompiler(driver);
- break;
+ return CreateQuickCompiler(driver);
+
case kOptimizing:
- return new OptimizingCompiler(driver);
- break;
+ return CreateOptimizingCompiler(driver);
+
case kPortable:
-#ifdef ART_USE_PORTABLE_COMPILER
- return new LLVMCompiler(driver);
-#else
- LOG(FATAL) << "Portable compiler not compiled";
-#endif
- break;
+ {
+ Compiler* compiler = CreateLLVMCompiler(driver);
+ CHECK(compiler != nullptr) << "Portable compiler not compiled";
+ return compiler;
+ }
+
default:
LOG(FATAL) << "UNREACHABLE";
}
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 4caebf3..05fa858 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -26,13 +26,19 @@
struct CompilationUnit;
class CompilerDriver;
class CompiledMethod;
-class MIRGraph;
class OatWriter;
namespace mirror {
class ArtMethod;
}
+// Base class for compiler-specific thread-local storage for compiler worker threads
+class CompilerTls {
+ public:
+ CompilerTls() {}
+ ~CompilerTls() {}
+};
+
class Compiler {
public:
enum Kind {
@@ -47,6 +53,9 @@
virtual void UnInit() const = 0;
+ virtual bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu)
+ const = 0;
+
virtual CompiledMethod* Compile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
@@ -109,6 +118,10 @@
return nullptr;
}
+ virtual CompilerTls* CreateNewCompilerTls() {
+ return nullptr;
+ }
+
protected:
explicit Compiler(CompilerDriver* driver, uint64_t warning) :
driver_(driver), maximum_compilation_time_before_warning_(warning) {
diff --git a/compiler/compilers.cc b/compiler/compilers.cc
deleted file mode 100644
index 250924a..0000000
--- a/compiler/compilers.cc
+++ /dev/null
@@ -1,157 +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.
- */
-
-#include "compilers.h"
-
-#include "dex/mir_graph.h"
-#include "dex/quick/mir_to_lir.h"
-#include "elf_writer_quick.h"
-#include "mirror/art_method-inl.h"
-
-namespace art {
-
-extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver* driver);
-extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver* driver);
-extern "C" art::CompiledMethod* ArtQuickCompileMethod(art::CompilerDriver* driver,
- const art::DexFile::CodeItem* code_item,
- uint32_t access_flags,
- art::InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const art::DexFile& dex_file);
-
-extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver* driver,
- uint32_t access_flags, uint32_t method_idx,
- const art::DexFile& dex_file);
-
-// Hack for CFI CIE initialization
-extern std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64);
-
-void QuickCompiler::Init() const {
- ArtInitQuickCompilerContext(GetCompilerDriver());
-}
-
-void QuickCompiler::UnInit() const {
- ArtUnInitQuickCompilerContext(GetCompilerDriver());
-}
-
-CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const {
- CompiledMethod* method = TryCompileWithSeaIR(code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
- if (method != nullptr) {
- return method;
- }
-
- return ArtQuickCompileMethod(GetCompilerDriver(),
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file);
-}
-
-CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
- uint32_t method_idx,
- const DexFile& dex_file) const {
- return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
-}
-
-uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
- return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
-}
-
-bool QuickCompiler::WriteElf(art::File* file,
- OatWriter* oat_writer,
- const std::vector<const art::DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host) const {
- return art::ElfWriterQuick::Create(file, oat_writer, dex_files, android_root, is_host,
- *GetCompilerDriver());
-}
-
-Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
- Mir2Lir* mir_to_lir = nullptr;
- switch (cu->instruction_set) {
- case kThumb2:
- mir_to_lir = ArmCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
- break;
- case kArm64:
- mir_to_lir = Arm64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
- break;
- case kMips:
- mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
- break;
- case kX86:
- // Fall-through.
- case kX86_64:
- mir_to_lir = X86CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
- break;
- default:
- LOG(FATAL) << "Unexpected instruction set: " << cu->instruction_set;
- }
-
- /* The number of compiler temporaries depends on backend so set it up now if possible */
- if (mir_to_lir) {
- size_t max_temps = mir_to_lir->GetMaxPossibleCompilerTemps();
- bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps);
- CHECK(set_max);
- }
- return mir_to_lir;
-}
-
-std::vector<uint8_t>* QuickCompiler::GetCallFrameInformationInitialization(
- const CompilerDriver& driver) const {
- if (driver.GetInstructionSet() == kX86) {
- return X86CFIInitialization(false);
- }
- if (driver.GetInstructionSet() == kX86_64) {
- return X86CFIInitialization(true);
- }
- return nullptr;
-}
-
-CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const {
- CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
- method_idx, class_loader, dex_file);
- if (method != nullptr) {
- return method;
- }
-
- return QuickCompiler::Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
- class_loader, dex_file);
-}
-
-} // namespace art
diff --git a/compiler/compilers.h b/compiler/compilers.h
deleted file mode 100644
index 2c231e1..0000000
--- a/compiler/compilers.h
+++ /dev/null
@@ -1,102 +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_COMPILER_COMPILERS_H_
-#define ART_COMPILER_COMPILERS_H_
-
-#include "compiler.h"
-
-namespace art {
-
-class QuickCompiler : public Compiler {
- public:
- explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
-
- void Init() const OVERRIDE;
-
- void UnInit() const OVERRIDE;
-
- CompiledMethod* Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const OVERRIDE;
-
- CompiledMethod* JniCompile(uint32_t access_flags,
- uint32_t method_idx,
- const DexFile& dex_file) const OVERRIDE;
-
- uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- bool WriteElf(art::File* file,
- OatWriter* oat_writer,
- const std::vector<const art::DexFile*>& dex_files,
- const std::string& android_root,
- bool is_host) const
- OVERRIDE
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
-
- void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE {}
-
- /*
- * @brief Generate and return Dwarf CFI initialization, if supported by the
- * backend.
- * @param driver CompilerDriver for this compile.
- * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF
- * information.
- * @note This is used for backtrace information in generated code.
- */
- std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver) const
- OVERRIDE;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
-};
-
-class OptimizingCompiler FINAL : public QuickCompiler {
- public:
- explicit OptimizingCompiler(CompilerDriver* driver);
-
- CompiledMethod* Compile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const OVERRIDE;
-
- CompiledMethod* TryCompile(const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const DexFile& dex_file) const;
-
- private:
- std::unique_ptr<std::ostream> visualizer_output_;
-
- DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_COMPILERS_H_
diff --git a/compiler/dex/backend.h b/compiler/dex/backend.h
index 1f24849..cab3427 100644
--- a/compiler/dex/backend.h
+++ b/compiler/dex/backend.h
@@ -38,14 +38,15 @@
/*
* Return the number of reservable vector registers supported
- * @param fp_used ‘true’ if floating point computations will be
- * executed while vector registers are reserved.
+ * @param long_or_fp ‘true’ if floating point computations will be
+ * executed or the operations will be long type while vector
+ * registers are reserved.
* @return the number of vector registers that are available
* @note The backend should ensure that sufficient vector registers
* are held back to generate scalar code without exhausting vector
* registers, if scalar code also uses the vector registers.
*/
- virtual int NumReservableVectorRegisters(bool fp_used) { return 0; }
+ virtual int NumReservableVectorRegisters(bool long_or_fp) { return 0; }
protected:
explicit Backend(ArenaAllocator* arena) : arena_(arena) {}
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index 920cde2..6a610ab 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -23,9 +23,9 @@
/*
* Code Layout pass implementation start.
*/
-bool CodeLayout::Worker(const PassDataHolder* data) const {
+bool CodeLayout::Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
CompilationUnit* c_unit = pass_me_data_holder->c_unit;
DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
@@ -38,9 +38,9 @@
/*
* BasicBlock Combine pass implementation start.
*/
-bool BBCombine::Worker(const PassDataHolder* data) const {
+bool BBCombine::Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
CompilationUnit* c_unit = pass_me_data_holder->c_unit;
DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index 7395324..b2c348b 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_
#define ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_
+#include "base/casts.h"
#include "compiler_internals.h"
#include "pass_me.h"
@@ -33,16 +34,16 @@
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->DoCacheFieldLoweringInfo();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->DoCacheFieldLoweringInfo();
}
bool Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return cUnit->mir_graph->HasFieldAccess();
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return c_unit->mir_graph->HasFieldAccess();
}
};
@@ -57,16 +58,16 @@
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->DoCacheMethodLoweringInfo();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->DoCacheMethodLoweringInfo();
}
bool Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return cUnit->mir_graph->HasInvokes();
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return c_unit->mir_graph->HasInvokes();
}
};
@@ -83,35 +84,35 @@
bool Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return cUnit->mir_graph->InlineSpecialMethodsGate();
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return c_unit->mir_graph->InlineSpecialMethodsGate();
}
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->InlineSpecialMethodsStart();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->InlineSpecialMethodsStart();
}
- bool Worker(const PassDataHolder* data) const {
+ bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
- CompilationUnit* cUnit = pass_me_data_holder->c_unit;
- DCHECK(cUnit != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
DCHECK(bb != nullptr);
- cUnit->mir_graph->InlineSpecialMethods(bb);
+ c_unit->mir_graph->InlineSpecialMethods(bb);
// No need of repeating, so just return false.
return false;
}
void End(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->InlineSpecialMethodsEnd();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->InlineSpecialMethodsEnd();
}
};
@@ -126,12 +127,13 @@
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->VerifyDataflow();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->VerifyDataflow();
+ c_unit->mir_graph->ClearAllVisitedFlags();
}
- bool Worker(const PassDataHolder* data) const;
+ bool Worker(PassDataHolder* data) const;
};
/**
@@ -146,26 +148,26 @@
void Start(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->EliminateNullChecksAndInferTypesStart();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->EliminateNullChecksAndInferTypesStart();
}
- bool Worker(const PassDataHolder* data) const {
+ bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
- CompilationUnit* cUnit = pass_me_data_holder->c_unit;
- DCHECK(cUnit != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
DCHECK(bb != nullptr);
- return cUnit->mir_graph->EliminateNullChecksAndInferTypes(bb);
+ return c_unit->mir_graph->EliminateNullChecksAndInferTypes(bb);
}
void End(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->EliminateNullChecksAndInferTypesEnd();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->EliminateNullChecksAndInferTypesEnd();
}
};
@@ -177,26 +179,26 @@
bool Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return cUnit->mir_graph->EliminateClassInitChecksGate();
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return c_unit->mir_graph->EliminateClassInitChecksGate();
}
- bool Worker(const PassDataHolder* data) const {
+ bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
- CompilationUnit* cUnit = pass_me_data_holder->c_unit;
- DCHECK(cUnit != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
DCHECK(bb != nullptr);
- return cUnit->mir_graph->EliminateClassInitChecks(bb);
+ return c_unit->mir_graph->EliminateClassInitChecks(bb);
}
void End(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->EliminateClassInitChecksEnd();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->EliminateClassInitChecksEnd();
}
};
@@ -212,26 +214,26 @@
bool Gate(const PassDataHolder* data) const OVERRIDE {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return cUnit->mir_graph->ApplyGlobalValueNumberingGate();
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return c_unit->mir_graph->ApplyGlobalValueNumberingGate();
}
- bool Worker(const PassDataHolder* data) const OVERRIDE {
+ bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
- CompilationUnit* cUnit = pass_me_data_holder->c_unit;
- DCHECK(cUnit != nullptr);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
DCHECK(bb != nullptr);
- return cUnit->mir_graph->ApplyGlobalValueNumbering(bb);
+ return c_unit->mir_graph->ApplyGlobalValueNumbering(bb);
}
void End(PassDataHolder* data) const OVERRIDE {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- cUnit->mir_graph->ApplyGlobalValueNumberingEnd();
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ c_unit->mir_graph->ApplyGlobalValueNumberingEnd();
}
};
@@ -246,12 +248,12 @@
bool Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return ((cUnit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return ((c_unit->disable_opt & (1 << kSuppressExceptionEdges)) != 0);
}
- bool Worker(const PassDataHolder* data) const;
+ bool Worker(PassDataHolder* data) const;
};
/**
@@ -265,9 +267,9 @@
bool Gate(const PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
- DCHECK(cUnit != nullptr);
- return ((cUnit->disable_opt & (1 << kBBOpt)) == 0);
+ CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ DCHECK(c_unit != nullptr);
+ return ((c_unit->disable_opt & (1 << kBBOpt)) == 0);
}
void Start(PassDataHolder* data) const;
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index dcc67c3..e4003bf 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -107,14 +107,52 @@
enum ExtendedMIROpcode {
kMirOpFirst = kNumPackedOpcodes,
kMirOpPhi = kMirOpFirst,
+
+ // @brief Copy from one VR to another.
+ // @details
+ // vA: destination VR
+ // vB: source VR
kMirOpCopy,
+
+ // @brief Used to do float comparison with less-than bias.
+ // @details Unlike cmpl-float, this does not store result of comparison in VR.
+ // vA: left-hand side VR for comparison.
+ // vB: right-hand side VR for comparison.
kMirOpFusedCmplFloat,
+
+ // @brief Used to do float comparison with greater-than bias.
+ // @details Unlike cmpg-float, this does not store result of comparison in VR.
+ // vA: left-hand side VR for comparison.
+ // vB: right-hand side VR for comparison.
kMirOpFusedCmpgFloat,
+
+ // @brief Used to do double comparison with less-than bias.
+ // @details Unlike cmpl-double, this does not store result of comparison in VR.
+ // vA: left-hand side wide VR for comparison.
+ // vB: right-hand side wide VR for comparison.
kMirOpFusedCmplDouble,
+
+ // @brief Used to do double comparison with greater-than bias.
+ // @details Unlike cmpl-double, this does not store result of comparison in VR.
+ // vA: left-hand side wide VR for comparison.
+ // vB: right-hand side wide VR for comparison.
kMirOpFusedCmpgDouble,
+
+ // @brief Used to do comparison of 64-bit long integers.
+ // @details Unlike cmp-long, this does not store result of comparison in VR.
+ // vA: left-hand side wide VR for comparison.
+ // vB: right-hand side wide VR for comparison.
kMirOpFusedCmpLong,
+
+ // @brief This represents no-op.
kMirOpNop,
+
+ // @brief Do a null check on the object register.
+ // @details The backends may implement this implicitly or explicitly. This MIR is guaranteed
+ // to have the correct offset as an exception thrower.
+ // vA: object register
kMirOpNullCheck,
+
kMirOpRangeCheck,
kMirOpDivZeroCheck,
kMirOpCheck,
@@ -218,16 +256,47 @@
// vC: TypeSize
kMirOpPackedSet,
- // @brief Reserve N vector registers (named 0..N-1)
- // vA: Number of registers
+ // @brief Reserve a range of vector registers.
+ // vA: Start vector register to reserve.
+ // vB: Inclusive end vector register to reserve.
// @note: The backend may choose to map vector numbers used in vector opcodes.
// Reserved registers are removed from the list of backend temporary pool.
kMirOpReserveVectorRegisters,
- // @brief Free Reserved vector registers
+ // @brief Free a range of reserved vector registers
+ // vA: Start vector register to unreserve.
+ // vB: Inclusive end vector register to unreserve.
// @note: All currently reserved vector registers are returned to the temporary pool.
kMirOpReturnVectorRegisters,
+ // @brief Create a memory barrier.
+ // vA: a constant defined by enum MemBarrierKind.
+ kMirOpMemBarrier,
+
+ // @brief Used to fill a vector register with array values.
+ // @details Just as with normal arrays, access on null object register must ensure NullPointerException
+ // and invalid index must ensure ArrayIndexOutOfBoundsException. Exception behavior must be the same
+ // as the aget it replaced and must happen at same index. Therefore, it is generally recommended that
+ // before using this MIR, it is proven that exception is guaranteed to not be thrown and marked with
+ // MIR_IGNORE_NULL_CHECK and MIR_IGNORE_RANGE_CHECK.
+ // vA: destination vector register
+ // vB: array register
+ // vC: index register
+ // arg[0]: TypeSize (most other vector opcodes have this in vC)
+ kMirOpPackedArrayGet,
+
+ // @brief Used to store a vector register into array.
+ // @details Just as with normal arrays, access on null object register must ensure NullPointerException
+ // and invalid index must ensure ArrayIndexOutOfBoundsException. Exception behavior must be the same
+ // as the aget it replaced and must happen at same index. Therefore, it is generally recommended that
+ // before using this MIR, it is proven that exception is guaranteed to not be thrown and marked with
+ // MIR_IGNORE_NULL_CHECK and MIR_IGNORE_RANGE_CHECK.
+ // vA: source vector register
+ // vB: array register
+ // vC: index register
+ // arg[0]: TypeSize (most other vector opcodes have this in vC)
+ kMirOpPackedArrayPut,
+
kMirOpLast,
};
@@ -243,6 +312,7 @@
kMIRIgnoreSuspendCheck,
kMIRDup,
kMIRMark, // Temporary node mark.
+ kMIRStoreNonTemporal,
kMIRLastMIRFlag,
};
@@ -447,12 +517,15 @@
* -# Use LoadAny barrier ~= (LoadLoad | LoadStore) ~= acquire barrierafter each volatile load.
* -# Use StoreStore barrier after all stores but before return from any constructor whose
* class has final fields.
+ * -# Use NTStoreStore to order non-temporal stores with respect to all later
+ * store-to-memory instructions. Only generated together with non-temporal stores.
*/
enum MemBarrierKind {
kAnyStore,
kLoadAny,
kStoreStore,
- kAnyAny
+ kAnyAny,
+ kNTStoreStore,
};
std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind);
@@ -528,6 +601,7 @@
kFixupLoad, // Mostly for immediates.
kFixupVLoad, // FP load which *may* be pc-relative.
kFixupCBxZ, // Cbz, Cbnz.
+ kFixupTBxZ, // Tbz, Tbnz.
kFixupPushPop, // Not really pc relative, but changes size based on args.
kFixupCondBranch, // Conditional branch
kFixupT1Branch, // Thumb1 Unconditional branch
diff --git a/compiler/dex/compiler_internals.h b/compiler/dex/compiler_internals.h
index 9dd0272..2019f0b 100644
--- a/compiler/dex/compiler_internals.h
+++ b/compiler/dex/compiler_internals.h
@@ -23,14 +23,9 @@
#include <stdio.h>
#include "base/logging.h"
-#include "class_linker.h"
-#include "driver/compiler_driver.h"
-#include "quick/mir_to_lir.h"
#include "mir_graph.h"
#include "compiler_ir.h"
-#include "frontend.h"
-#include "monitor.h"
-#include "thread.h"
+#include "frontend.h" // Debug flags.
#include "utils.h"
#endif // ART_COMPILER_DEX_COMPILER_INTERNALS_H_
diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc
new file mode 100644
index 0000000..909c995
--- /dev/null
+++ b/compiler/dex/compiler_ir.cc
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include "compiler_ir.h"
+
+#include "backend.h"
+#include "frontend.h"
+#include "mir_graph.h"
+
+namespace art {
+
+CompilationUnit::CompilationUnit(ArenaPool* pool)
+ : compiler_driver(nullptr),
+ class_linker(nullptr),
+ dex_file(nullptr),
+ class_loader(nullptr),
+ class_def_idx(0),
+ method_idx(0),
+ access_flags(0),
+ invoke_type(kDirect),
+ shorty(nullptr),
+ disable_opt(0),
+ enable_debug(0),
+ verbose(false),
+ compiler(nullptr),
+ instruction_set(kNone),
+ target64(false),
+ compiler_flip_match(false),
+ arena(pool),
+ arena_stack(pool),
+ mir_graph(nullptr),
+ cg(nullptr),
+ timings("QuickCompiler", true, false),
+ print_pass(false) {
+}
+
+CompilationUnit::~CompilationUnit() {
+ overridden_pass_options.clear();
+}
+
+void CompilationUnit::StartTimingSplit(const char* label) {
+ if (compiler_driver->GetDumpPasses()) {
+ timings.StartTiming(label);
+ }
+}
+
+void CompilationUnit::NewTimingSplit(const char* label) {
+ if (compiler_driver->GetDumpPasses()) {
+ timings.EndTiming();
+ timings.StartTiming(label);
+ }
+}
+
+void CompilationUnit::EndTiming() {
+ if (compiler_driver->GetDumpPasses()) {
+ timings.EndTiming();
+ if (enable_debug & (1 << kDebugTimings)) {
+ LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file);
+ LOG(INFO) << Dumpable<TimingLogger>(timings);
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 66fb608..37e3a7a 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -17,25 +17,28 @@
#ifndef ART_COMPILER_DEX_COMPILER_IR_H_
#define ART_COMPILER_DEX_COMPILER_IR_H_
+#include <string>
#include <vector>
#include "compiler_enums.h"
-#include "dex/quick/mir_to_lir.h"
-#include "dex_instruction.h"
#include "driver/compiler_driver.h"
-#include "driver/dex_compilation_unit.h"
-#include "safe_map.h"
#include "utils/scoped_arena_allocator.h"
#include "base/timing_logger.h"
#include "utils/arena_allocator.h"
namespace art {
-struct ArenaMemBlock;
class Backend;
-struct Memstats;
+class ClassLinker;
class MIRGraph;
-class Mir2Lir;
+
+/*
+ * TODO: refactoring pass to move these (and other) typedefs towards usage style of runtime to
+ * add type safety (see runtime/offsets.h).
+ */
+typedef uint32_t DexOffset; // Dex offset in code units.
+typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff.
+typedef uint32_t CodeOffset; // Native code offset in bytes.
struct CompilationUnit {
explicit CompilationUnit(ArenaPool* pool);
@@ -55,7 +58,6 @@
jobject class_loader; // compiling method's class loader.
uint16_t class_def_idx; // compiling method's defining class definition index.
uint32_t method_idx; // compiling method's index into method_ids of DexFile.
- const DexFile::CodeItem* code_item; // compiling method's DexFile code_item.
uint32_t access_flags; // compiling method's access flags.
InvokeType invoke_type; // compiling method's invocation type.
const char* shorty; // compiling method's shorty.
@@ -69,12 +71,6 @@
InstructionSetFeatures GetInstructionSetFeatures() {
return compiler_driver->GetInstructionSetFeatures();
}
- // TODO: much of this info available elsewhere. Go to the original source?
- uint16_t num_dalvik_registers; // method->registers_size.
- const uint16_t* insns;
- uint16_t num_ins;
- uint16_t num_outs;
- uint16_t num_regs; // Unlike num_dalvik_registers, does not include ins.
// If non-empty, apply optimizer/debug flags only to matching methods.
std::string compiler_method_match;
@@ -89,6 +85,15 @@
std::unique_ptr<Backend> cg; // Target-specific codegen.
TimingLogger timings;
bool print_pass; // Do we want to print a pass or not?
+
+ /**
+ * @brief Holds pass options for current pass being applied to compilation unit.
+ * @details This is updated for every pass to contain the overridden pass options
+ * that were specified by user. The pass itself will check this to see if the
+ * default settings have been changed. The key is simply the option string without
+ * the pass name.
+ */
+ SafeMap<const std::string, int> overridden_pass_options;
};
} // namespace art
diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h
index d1abf7f..89f2b5c 100644
--- a/compiler/dex/dataflow_iterator-inl.h
+++ b/compiler/dex/dataflow_iterator-inl.h
@@ -28,7 +28,7 @@
// Are we not yet at the end?
if (idx_ < end_idx_) {
// Get the next index.
- BasicBlockId bb_id = block_id_list_->Get(idx_);
+ BasicBlockId bb_id = (*block_id_list_)[idx_];
res = mir_graph_->GetBasicBlock(bb_id);
idx_++;
}
@@ -51,7 +51,7 @@
// Are we not yet at the end?
if (idx_ < end_idx_) {
// Get the BasicBlockId.
- BasicBlockId bb_id = block_id_list_->Get(idx_);
+ BasicBlockId bb_id = (*block_id_list_)[idx_];
res = mir_graph_->GetBasicBlock(bb_id);
idx_++;
}
@@ -66,7 +66,7 @@
// Are we not yet at the end?
if (idx_ >= 0) {
// Get the BasicBlockId.
- BasicBlockId bb_id = block_id_list_->Get(idx_);
+ BasicBlockId bb_id = (*block_id_list_)[idx_];
res = mir_graph_->GetBasicBlock(bb_id);
idx_--;
}
@@ -89,7 +89,7 @@
// Are we not yet done?
if (idx_ >= 0) {
// Get the BasicBlockId.
- BasicBlockId bb_id = block_id_list_->Get(idx_);
+ BasicBlockId bb_id = (*block_id_list_)[idx_];
res = mir_graph_->GetBasicBlock(bb_id);
idx_--;
}
@@ -97,34 +97,28 @@
return res;
}
-// AllNodes uses the existing GrowableArray iterator, and should be considered unordered.
+// AllNodes uses the existing block list, and should be considered unordered.
inline BasicBlock* AllNodesIterator::Next(bool had_change) {
- BasicBlock* res = NULL;
-
- // Suppose we want to keep looking.
- bool keep_looking = true;
-
- // Find the next BasicBlock.
- while (keep_looking == true) {
- // Get next BasicBlock.
- res = all_nodes_iterator_.Next();
-
- // Are we done or is the BasicBlock not hidden?
- if ((res == NULL) || (res->hidden == false)) {
- keep_looking = false;
- }
- }
-
// Update changed: if had_changed is true, we remember it for the whole iteration.
changed_ |= had_change;
+ BasicBlock* res = nullptr;
+ while (idx_ != end_idx_) {
+ BasicBlock* bb = mir_graph_->GetBlockList()[idx_++];
+ DCHECK(bb != nullptr);
+ if (!bb->hidden) {
+ res = bb;
+ break;
+ }
+ }
+
return res;
}
inline BasicBlock* LoopRepeatingTopologicalSortIterator::Next(bool had_change) {
if (idx_ != 0) {
// Mark last processed block visited.
- BasicBlock* bb = mir_graph_->GetBasicBlock(block_id_list_->Get(idx_ - 1));
+ BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx_ - 1]);
bb->visited = true;
if (had_change) {
// If we had a change we need to revisit the children.
@@ -138,16 +132,17 @@
while (true) {
// Pop loops we have left and check if we need to recalculate one of them.
// NOTE: We need to do this even if idx_ == end_idx_.
- while (loop_head_stack_->Size() != 0u &&
- loop_ends_->Get(loop_head_stack_->Peek().first) == idx_) {
- auto top = loop_head_stack_->Peek();
+ while (loop_head_stack_->size() != 0u &&
+ (*loop_ends_)[loop_head_stack_->back().first] == idx_) {
+ auto top = loop_head_stack_->back();
uint16_t loop_head_idx = top.first;
bool recalculated = top.second;
- loop_head_stack_->Pop();
- BasicBlock* loop_head = mir_graph_->GetBasicBlock(block_id_list_->Get(loop_head_idx));
+ loop_head_stack_->pop_back();
+ BasicBlock* loop_head = mir_graph_->GetBasicBlock((*block_id_list_)[loop_head_idx]);
DCHECK(loop_head != nullptr);
if (!recalculated || !loop_head->visited) {
- loop_head_stack_->Insert(std::make_pair(loop_head_idx, true)); // Recalculating this loop.
+ // Recalculating this loop.
+ loop_head_stack_->push_back(std::make_pair(loop_head_idx, true));
idx_ = loop_head_idx + 1;
return loop_head;
}
@@ -160,11 +155,11 @@
// Get next block and return it if unvisited.
BasicBlockId idx = idx_;
idx_ += 1;
- BasicBlock* bb = mir_graph_->GetBasicBlock(block_id_list_->Get(idx));
+ BasicBlock* bb = mir_graph_->GetBasicBlock((*block_id_list_)[idx]);
DCHECK(bb != nullptr);
if (!bb->visited) {
- if (loop_ends_->Get(idx) != 0u) {
- loop_head_stack_->Insert(std::make_pair(idx, false)); // Not recalculating.
+ if ((*loop_ends_)[idx] != 0u) {
+ loop_head_stack_->push_back(std::make_pair(idx, false)); // Not recalculating.
}
return bb;
}
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index 06d6832..188f1d9 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -104,7 +104,7 @@
MIRGraph* const mir_graph_; /**< @brief the MIRGraph */
const int32_t start_idx_; /**< @brief the start index for the iteration */
const int32_t end_idx_; /**< @brief the last index for the iteration */
- GrowableArray<BasicBlockId>* block_id_list_; /**< @brief the list of BasicBlocks we want to iterate on */
+ const ArenaVector<BasicBlockId>* block_id_list_; /**< @brief the list of BasicBlocks we want to iterate on */
int32_t idx_; /**< @brief Current index for the iterator */
int32_t repeats_; /**< @brief Number of repeats over the iteration */
bool changed_; /**< @brief Has something changed during the current iteration? */
@@ -124,7 +124,7 @@
: DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
// Extra setup for the PreOrderDfsIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDfsOrder();
+ block_id_list_ = &mir_graph->GetDfsOrder();
}
/**
@@ -155,7 +155,7 @@
: DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
// Extra setup for the RepeatingPreOrderDfsIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDfsOrder();
+ block_id_list_ = &mir_graph->GetDfsOrder();
}
/**
@@ -186,7 +186,7 @@
: DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
// Extra setup for the RepeatingPostOrderDfsIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDfsPostOrder();
+ block_id_list_ = &mir_graph->GetDfsPostOrder();
}
/**
@@ -216,7 +216,7 @@
: DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
// Extra setup for the ReversePostOrderDfsIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDfsPostOrder();
+ block_id_list_ = &mir_graph->GetDfsPostOrder();
}
/**
@@ -247,7 +247,7 @@
: DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
// Extra setup for the RepeatingReversePostOrderDfsIterator
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDfsPostOrder();
+ block_id_list_ = &mir_graph->GetDfsPostOrder();
}
/**
@@ -277,7 +277,7 @@
: DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
// Extra setup for thePostOrderDOMIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDomPostOrder();
+ block_id_list_ = &mir_graph->GetDomPostOrder();
}
/**
@@ -304,15 +304,14 @@
* @param mir_graph The MIRGraph considered.
*/
explicit AllNodesIterator(MIRGraph* mir_graph)
- : DataflowIterator(mir_graph, 0, 0),
- all_nodes_iterator_(mir_graph->GetBlockList()) {
+ : DataflowIterator(mir_graph, 0, mir_graph->GetBlockList().size()) {
}
/**
* @brief Resetting the iterator.
*/
void Reset() {
- all_nodes_iterator_.Reset();
+ idx_ = 0;
}
/**
@@ -321,9 +320,6 @@
* @return the next BasicBlock following the iteration order, 0 if finished.
*/
virtual BasicBlock* Next(bool had_change = false) ALWAYS_INLINE;
-
- private:
- GrowableArray<BasicBlock*>::Iterator all_nodes_iterator_; /**< @brief The list of all the nodes */
};
/**
@@ -337,10 +333,10 @@
* @param mir_graph The MIRGraph considered.
*/
explicit TopologicalSortIterator(MIRGraph* mir_graph)
- : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
+ : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()) {
// Extra setup for TopologicalSortIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetTopologicalSortOrder();
+ block_id_list_ = &mir_graph->GetTopologicalSortOrder();
}
/**
@@ -369,10 +365,10 @@
* @param mir_graph The MIRGraph considered.
*/
explicit RepeatingTopologicalSortIterator(MIRGraph* mir_graph)
- : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()) {
+ : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()) {
// Extra setup for RepeatingTopologicalSortIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetTopologicalSortOrder();
+ block_id_list_ = &mir_graph->GetTopologicalSortOrder();
}
/**
@@ -408,19 +404,19 @@
* @param mir_graph The MIRGraph considered.
*/
explicit LoopRepeatingTopologicalSortIterator(MIRGraph* mir_graph)
- : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()),
- loop_ends_(mir_graph->GetTopologicalSortOrderLoopEnds()),
+ : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder().size()),
+ loop_ends_(&mir_graph->GetTopologicalSortOrderLoopEnds()),
loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) {
// Extra setup for RepeatingTopologicalSortIterator.
idx_ = start_idx_;
- block_id_list_ = mir_graph->GetTopologicalSortOrder();
+ block_id_list_ = &mir_graph->GetTopologicalSortOrder();
// Clear visited flags and check that the loop head stack is empty.
mir_graph->ClearAllVisitedFlags();
- DCHECK_EQ(loop_head_stack_->Size(), 0u);
+ DCHECK_EQ(loop_head_stack_->size(), 0u);
}
~LoopRepeatingTopologicalSortIterator() {
- DCHECK_EQ(loop_head_stack_->Size(), 0u);
+ DCHECK_EQ(loop_head_stack_->size(), 0u);
}
/**
@@ -431,8 +427,8 @@
virtual BasicBlock* Next(bool had_change = false) OVERRIDE;
private:
- const GrowableArray<BasicBlockId>* const loop_ends_;
- GrowableArray<std::pair<uint16_t, bool>>* const loop_head_stack_;
+ const ArenaVector<BasicBlockId>* const loop_ends_;
+ ArenaVector<std::pair<uint16_t, bool>>* const loop_head_stack_;
};
} // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index b9f9437..f9a05c2 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -121,15 +121,25 @@
break;
case Instruction::IPUT:
- case Instruction::IPUT_BOOLEAN:
- case Instruction::IPUT_BYTE:
- case Instruction::IPUT_CHAR:
- case Instruction::IPUT_SHORT:
- // These opcodes have the same implementation in interpreter so group
- // them under IPUT_QUICK.
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
break;
+ case Instruction::IPUT_BOOLEAN:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN_QUICK, true);
+ break;
+
+ case Instruction::IPUT_BYTE:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE_QUICK, true);
+ break;
+
+ case Instruction::IPUT_CHAR:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR_QUICK, true);
+ break;
+
+ case Instruction::IPUT_SHORT:
+ CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT_QUICK, true);
+ break;
+
case Instruction::IPUT_WIDE:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
break;
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 45fc19e..07f3033 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -14,14 +14,15 @@
* limitations under the License.
*/
+#include "frontend.h"
+
#include <cstdint>
+#include "backend.h"
#include "compiler.h"
#include "compiler_internals.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
-#include "dataflow_iterator-inl.h"
-#include "leb128.h"
#include "mirror/object.h"
#include "pass_driver_me_opts.h"
#include "runtime.h"
@@ -32,22 +33,14 @@
namespace art {
-extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver* driver) {
- CHECK(driver->GetCompilerContext() == nullptr);
-}
-
-extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver* driver) {
- CHECK(driver->GetCompilerContext() == nullptr);
-}
-
/* Default optimizer/debug setting for the compiler. */
static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations
- (1 << kLoadStoreElimination) |
+ // (1 << kLoadStoreElimination) |
// (1 << kLoadHoisting) |
// (1 << kSuppressLoads) |
// (1 << kNullCheckElimination) |
// (1 << kClassInitCheckElimination) |
- (1 << kGlobalValueNumbering) |
+ // (1 << kGlobalValueNumbering) |
// (1 << kPromoteRegs) |
// (1 << kTrackLiveTemps) |
// (1 << kSafeOptimizations) |
@@ -81,542 +74,8 @@
// (1 << kDebugCodegenDump) |
0;
-COMPILE_ASSERT(0U == static_cast<size_t>(kNone), kNone_not_0);
-COMPILE_ASSERT(1U == static_cast<size_t>(kArm), kArm_not_1);
-COMPILE_ASSERT(2U == static_cast<size_t>(kArm64), kArm64_not_2);
-COMPILE_ASSERT(3U == static_cast<size_t>(kThumb2), kThumb2_not_3);
-COMPILE_ASSERT(4U == static_cast<size_t>(kX86), kX86_not_4);
-COMPILE_ASSERT(5U == static_cast<size_t>(kX86_64), kX86_64_not_5);
-COMPILE_ASSERT(6U == static_cast<size_t>(kMips), kMips_not_6);
-COMPILE_ASSERT(7U == static_cast<size_t>(kMips64), kMips64_not_7);
-
-// Additional disabled optimizations (over generally disabled) per instruction set.
-static constexpr uint32_t kDisabledOptimizationsPerISA[] = {
- // 0 = kNone.
- ~0U,
- // 1 = kArm, unused (will use kThumb2).
- ~0U,
- // 2 = kArm64.
- 0,
- // 3 = kThumb2.
- 0,
- // 4 = kX86.
- (1 << kLoadStoreElimination) |
- 0,
- // 5 = kX86_64.
- (1 << kLoadStoreElimination) |
- 0,
- // 6 = kMips.
- (1 << kLoadStoreElimination) |
- (1 << kLoadHoisting) |
- (1 << kSuppressLoads) |
- (1 << kNullCheckElimination) |
- (1 << kPromoteRegs) |
- (1 << kTrackLiveTemps) |
- (1 << kSafeOptimizations) |
- (1 << kBBOpt) |
- (1 << kMatch) |
- (1 << kPromoteCompilerTemps) |
- 0,
- // 7 = kMips64.
- ~0U
-};
-COMPILE_ASSERT(sizeof(kDisabledOptimizationsPerISA) == 8 * sizeof(uint32_t), kDisabledOpts_unexp);
-
-// Supported shorty types per instruction set. nullptr means that all are available.
-// Z : boolean
-// B : byte
-// S : short
-// C : char
-// I : int
-// J : long
-// F : float
-// D : double
-// L : reference(object, array)
-// V : void
-static const char* kSupportedTypes[] = {
- // 0 = kNone.
- "",
- // 1 = kArm, unused (will use kThumb2).
- "",
- // 2 = kArm64.
- nullptr,
- // 3 = kThumb2.
- nullptr,
- // 4 = kX86.
- nullptr,
- // 5 = kX86_64.
- nullptr,
- // 6 = kMips.
- nullptr,
- // 7 = kMips64.
- ""
-};
-COMPILE_ASSERT(sizeof(kSupportedTypes) == 8 * sizeof(char*), kSupportedTypes_unexp);
-
-static int kAllOpcodes[] = {
- Instruction::NOP,
- Instruction::MOVE,
- Instruction::MOVE_FROM16,
- Instruction::MOVE_16,
- Instruction::MOVE_WIDE,
- Instruction::MOVE_WIDE_FROM16,
- Instruction::MOVE_WIDE_16,
- Instruction::MOVE_OBJECT,
- Instruction::MOVE_OBJECT_FROM16,
- Instruction::MOVE_OBJECT_16,
- Instruction::MOVE_RESULT,
- Instruction::MOVE_RESULT_WIDE,
- Instruction::MOVE_RESULT_OBJECT,
- Instruction::MOVE_EXCEPTION,
- Instruction::RETURN_VOID,
- Instruction::RETURN,
- Instruction::RETURN_WIDE,
- Instruction::RETURN_OBJECT,
- Instruction::CONST_4,
- Instruction::CONST_16,
- Instruction::CONST,
- Instruction::CONST_HIGH16,
- Instruction::CONST_WIDE_16,
- Instruction::CONST_WIDE_32,
- Instruction::CONST_WIDE,
- Instruction::CONST_WIDE_HIGH16,
- Instruction::CONST_STRING,
- Instruction::CONST_STRING_JUMBO,
- Instruction::CONST_CLASS,
- Instruction::MONITOR_ENTER,
- Instruction::MONITOR_EXIT,
- Instruction::CHECK_CAST,
- Instruction::INSTANCE_OF,
- Instruction::ARRAY_LENGTH,
- Instruction::NEW_INSTANCE,
- Instruction::NEW_ARRAY,
- Instruction::FILLED_NEW_ARRAY,
- Instruction::FILLED_NEW_ARRAY_RANGE,
- Instruction::FILL_ARRAY_DATA,
- Instruction::THROW,
- Instruction::GOTO,
- Instruction::GOTO_16,
- Instruction::GOTO_32,
- Instruction::PACKED_SWITCH,
- Instruction::SPARSE_SWITCH,
- Instruction::CMPL_FLOAT,
- Instruction::CMPG_FLOAT,
- Instruction::CMPL_DOUBLE,
- Instruction::CMPG_DOUBLE,
- Instruction::CMP_LONG,
- Instruction::IF_EQ,
- Instruction::IF_NE,
- Instruction::IF_LT,
- Instruction::IF_GE,
- Instruction::IF_GT,
- Instruction::IF_LE,
- Instruction::IF_EQZ,
- Instruction::IF_NEZ,
- Instruction::IF_LTZ,
- Instruction::IF_GEZ,
- Instruction::IF_GTZ,
- Instruction::IF_LEZ,
- Instruction::UNUSED_3E,
- Instruction::UNUSED_3F,
- Instruction::UNUSED_40,
- Instruction::UNUSED_41,
- Instruction::UNUSED_42,
- Instruction::UNUSED_43,
- Instruction::AGET,
- Instruction::AGET_WIDE,
- Instruction::AGET_OBJECT,
- Instruction::AGET_BOOLEAN,
- Instruction::AGET_BYTE,
- Instruction::AGET_CHAR,
- Instruction::AGET_SHORT,
- Instruction::APUT,
- Instruction::APUT_WIDE,
- Instruction::APUT_OBJECT,
- Instruction::APUT_BOOLEAN,
- Instruction::APUT_BYTE,
- Instruction::APUT_CHAR,
- Instruction::APUT_SHORT,
- Instruction::IGET,
- Instruction::IGET_WIDE,
- Instruction::IGET_OBJECT,
- Instruction::IGET_BOOLEAN,
- Instruction::IGET_BYTE,
- Instruction::IGET_CHAR,
- Instruction::IGET_SHORT,
- Instruction::IPUT,
- Instruction::IPUT_WIDE,
- Instruction::IPUT_OBJECT,
- Instruction::IPUT_BOOLEAN,
- Instruction::IPUT_BYTE,
- Instruction::IPUT_CHAR,
- Instruction::IPUT_SHORT,
- Instruction::SGET,
- Instruction::SGET_WIDE,
- Instruction::SGET_OBJECT,
- Instruction::SGET_BOOLEAN,
- Instruction::SGET_BYTE,
- Instruction::SGET_CHAR,
- Instruction::SGET_SHORT,
- Instruction::SPUT,
- Instruction::SPUT_WIDE,
- Instruction::SPUT_OBJECT,
- Instruction::SPUT_BOOLEAN,
- Instruction::SPUT_BYTE,
- Instruction::SPUT_CHAR,
- Instruction::SPUT_SHORT,
- Instruction::INVOKE_VIRTUAL,
- Instruction::INVOKE_SUPER,
- Instruction::INVOKE_DIRECT,
- Instruction::INVOKE_STATIC,
- Instruction::INVOKE_INTERFACE,
- Instruction::RETURN_VOID_BARRIER,
- Instruction::INVOKE_VIRTUAL_RANGE,
- Instruction::INVOKE_SUPER_RANGE,
- Instruction::INVOKE_DIRECT_RANGE,
- Instruction::INVOKE_STATIC_RANGE,
- Instruction::INVOKE_INTERFACE_RANGE,
- Instruction::UNUSED_79,
- Instruction::UNUSED_7A,
- Instruction::NEG_INT,
- Instruction::NOT_INT,
- Instruction::NEG_LONG,
- Instruction::NOT_LONG,
- Instruction::NEG_FLOAT,
- Instruction::NEG_DOUBLE,
- Instruction::INT_TO_LONG,
- Instruction::INT_TO_FLOAT,
- Instruction::INT_TO_DOUBLE,
- Instruction::LONG_TO_INT,
- Instruction::LONG_TO_FLOAT,
- Instruction::LONG_TO_DOUBLE,
- Instruction::FLOAT_TO_INT,
- Instruction::FLOAT_TO_LONG,
- Instruction::FLOAT_TO_DOUBLE,
- Instruction::DOUBLE_TO_INT,
- Instruction::DOUBLE_TO_LONG,
- Instruction::DOUBLE_TO_FLOAT,
- Instruction::INT_TO_BYTE,
- Instruction::INT_TO_CHAR,
- Instruction::INT_TO_SHORT,
- Instruction::ADD_INT,
- Instruction::SUB_INT,
- Instruction::MUL_INT,
- Instruction::DIV_INT,
- Instruction::REM_INT,
- Instruction::AND_INT,
- Instruction::OR_INT,
- Instruction::XOR_INT,
- Instruction::SHL_INT,
- Instruction::SHR_INT,
- Instruction::USHR_INT,
- Instruction::ADD_LONG,
- Instruction::SUB_LONG,
- Instruction::MUL_LONG,
- Instruction::DIV_LONG,
- Instruction::REM_LONG,
- Instruction::AND_LONG,
- Instruction::OR_LONG,
- Instruction::XOR_LONG,
- Instruction::SHL_LONG,
- Instruction::SHR_LONG,
- Instruction::USHR_LONG,
- Instruction::ADD_FLOAT,
- Instruction::SUB_FLOAT,
- Instruction::MUL_FLOAT,
- Instruction::DIV_FLOAT,
- Instruction::REM_FLOAT,
- Instruction::ADD_DOUBLE,
- Instruction::SUB_DOUBLE,
- Instruction::MUL_DOUBLE,
- Instruction::DIV_DOUBLE,
- Instruction::REM_DOUBLE,
- Instruction::ADD_INT_2ADDR,
- Instruction::SUB_INT_2ADDR,
- Instruction::MUL_INT_2ADDR,
- Instruction::DIV_INT_2ADDR,
- Instruction::REM_INT_2ADDR,
- Instruction::AND_INT_2ADDR,
- Instruction::OR_INT_2ADDR,
- Instruction::XOR_INT_2ADDR,
- Instruction::SHL_INT_2ADDR,
- Instruction::SHR_INT_2ADDR,
- Instruction::USHR_INT_2ADDR,
- Instruction::ADD_LONG_2ADDR,
- Instruction::SUB_LONG_2ADDR,
- Instruction::MUL_LONG_2ADDR,
- Instruction::DIV_LONG_2ADDR,
- Instruction::REM_LONG_2ADDR,
- Instruction::AND_LONG_2ADDR,
- Instruction::OR_LONG_2ADDR,
- Instruction::XOR_LONG_2ADDR,
- Instruction::SHL_LONG_2ADDR,
- Instruction::SHR_LONG_2ADDR,
- Instruction::USHR_LONG_2ADDR,
- Instruction::ADD_FLOAT_2ADDR,
- Instruction::SUB_FLOAT_2ADDR,
- Instruction::MUL_FLOAT_2ADDR,
- Instruction::DIV_FLOAT_2ADDR,
- Instruction::REM_FLOAT_2ADDR,
- Instruction::ADD_DOUBLE_2ADDR,
- Instruction::SUB_DOUBLE_2ADDR,
- Instruction::MUL_DOUBLE_2ADDR,
- Instruction::DIV_DOUBLE_2ADDR,
- Instruction::REM_DOUBLE_2ADDR,
- Instruction::ADD_INT_LIT16,
- Instruction::RSUB_INT,
- Instruction::MUL_INT_LIT16,
- Instruction::DIV_INT_LIT16,
- Instruction::REM_INT_LIT16,
- Instruction::AND_INT_LIT16,
- Instruction::OR_INT_LIT16,
- Instruction::XOR_INT_LIT16,
- Instruction::ADD_INT_LIT8,
- Instruction::RSUB_INT_LIT8,
- Instruction::MUL_INT_LIT8,
- Instruction::DIV_INT_LIT8,
- Instruction::REM_INT_LIT8,
- Instruction::AND_INT_LIT8,
- Instruction::OR_INT_LIT8,
- Instruction::XOR_INT_LIT8,
- Instruction::SHL_INT_LIT8,
- Instruction::SHR_INT_LIT8,
- Instruction::USHR_INT_LIT8,
- Instruction::IGET_QUICK,
- Instruction::IGET_WIDE_QUICK,
- Instruction::IGET_OBJECT_QUICK,
- Instruction::IPUT_QUICK,
- Instruction::IPUT_WIDE_QUICK,
- Instruction::IPUT_OBJECT_QUICK,
- Instruction::INVOKE_VIRTUAL_QUICK,
- Instruction::INVOKE_VIRTUAL_RANGE_QUICK,
- Instruction::UNUSED_EB,
- Instruction::UNUSED_EC,
- Instruction::UNUSED_ED,
- Instruction::UNUSED_EE,
- Instruction::UNUSED_EF,
- Instruction::UNUSED_F0,
- Instruction::UNUSED_F1,
- Instruction::UNUSED_F2,
- Instruction::UNUSED_F3,
- Instruction::UNUSED_F4,
- Instruction::UNUSED_F5,
- Instruction::UNUSED_F6,
- Instruction::UNUSED_F7,
- Instruction::UNUSED_F8,
- Instruction::UNUSED_F9,
- Instruction::UNUSED_FA,
- Instruction::UNUSED_FB,
- Instruction::UNUSED_FC,
- Instruction::UNUSED_FD,
- Instruction::UNUSED_FE,
- Instruction::UNUSED_FF,
- // ----- ExtendedMIROpcode -----
- kMirOpPhi,
- kMirOpCopy,
- kMirOpFusedCmplFloat,
- kMirOpFusedCmpgFloat,
- kMirOpFusedCmplDouble,
- kMirOpFusedCmpgDouble,
- kMirOpFusedCmpLong,
- kMirOpNop,
- kMirOpNullCheck,
- kMirOpRangeCheck,
- kMirOpDivZeroCheck,
- kMirOpCheck,
- kMirOpCheckPart2,
- kMirOpSelect,
-};
-
-// Unsupported opcodes. nullptr can be used when everything is supported. Size of the lists is
-// recorded below.
-static const int* kUnsupportedOpcodes[] = {
- // 0 = kNone.
- kAllOpcodes,
- // 1 = kArm, unused (will use kThumb2).
- kAllOpcodes,
- // 2 = kArm64.
- nullptr,
- // 3 = kThumb2.
- nullptr,
- // 4 = kX86.
- nullptr,
- // 5 = kX86_64.
- nullptr,
- // 6 = kMips.
- nullptr,
- // 7 = kMips64.
- kAllOpcodes
-};
-COMPILE_ASSERT(sizeof(kUnsupportedOpcodes) == 8 * sizeof(int*), kUnsupportedOpcodes_unexp);
-
-// Size of the arrays stored above.
-static const size_t kUnsupportedOpcodesSize[] = {
- // 0 = kNone.
- arraysize(kAllOpcodes),
- // 1 = kArm, unused (will use kThumb2).
- arraysize(kAllOpcodes),
- // 2 = kArm64.
- 0,
- // 3 = kThumb2.
- 0,
- // 4 = kX86.
- 0,
- // 5 = kX86_64.
- 0,
- // 6 = kMips.
- 0,
- // 7 = kMips64.
- arraysize(kAllOpcodes),
-};
-COMPILE_ASSERT(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t),
- kUnsupportedOpcodesSize_unexp);
-
-// The maximum amount of Dalvik register in a method for which we will start compiling. Tries to
-// avoid an abort when we need to manage more SSA registers than we can.
-static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2;
-
-CompilationUnit::CompilationUnit(ArenaPool* pool)
- : compiler_driver(nullptr),
- class_linker(nullptr),
- dex_file(nullptr),
- class_loader(nullptr),
- class_def_idx(0),
- method_idx(0),
- code_item(nullptr),
- access_flags(0),
- invoke_type(kDirect),
- shorty(nullptr),
- disable_opt(0),
- enable_debug(0),
- verbose(false),
- compiler(nullptr),
- instruction_set(kNone),
- target64(false),
- num_dalvik_registers(0),
- insns(nullptr),
- num_ins(0),
- num_outs(0),
- num_regs(0),
- compiler_flip_match(false),
- arena(pool),
- arena_stack(pool),
- mir_graph(nullptr),
- cg(nullptr),
- timings("QuickCompiler", true, false),
- print_pass(false) {
-}
-
-CompilationUnit::~CompilationUnit() {
-}
-
-void CompilationUnit::StartTimingSplit(const char* label) {
- if (compiler_driver->GetDumpPasses()) {
- timings.StartTiming(label);
- }
-}
-
-void CompilationUnit::NewTimingSplit(const char* label) {
- if (compiler_driver->GetDumpPasses()) {
- timings.EndTiming();
- timings.StartTiming(label);
- }
-}
-
-void CompilationUnit::EndTiming() {
- if (compiler_driver->GetDumpPasses()) {
- timings.EndTiming();
- if (enable_debug & (1 << kDebugTimings)) {
- LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file);
- LOG(INFO) << Dumpable<TimingLogger>(timings);
- }
- }
-}
-
-static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) {
- const char* supported_types = kSupportedTypes[instruction_set];
- if (supported_types == nullptr) {
- // Everything available.
- return true;
- }
-
- uint32_t shorty_size = strlen(shorty);
- CHECK_GE(shorty_size, 1u);
-
- for (uint32_t i = 0; i < shorty_size; i++) {
- if (strchr(supported_types, shorty[i]) == nullptr) {
- return false;
- }
- }
- return true;
-};
-
-// Skip the method that we do not support currently.
-static bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
- CompilationUnit& cu) {
- // This is a limitation in mir_graph. See MirGraph::SetNumSSARegs.
- if (cu.num_dalvik_registers > kMaxAllowedDalvikRegisters) {
- VLOG(compiler) << "Too many dalvik registers : " << cu.num_dalvik_registers;
- return false;
- }
-
- // Check whether we do have limitations at all.
- if (kSupportedTypes[cu.instruction_set] == nullptr &&
- kUnsupportedOpcodesSize[cu.instruction_set] == 0U) {
- return true;
- }
-
- // Check if we can compile the prototype.
- const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
- if (!CanCompileShorty(shorty, cu.instruction_set)) {
- VLOG(compiler) << "Unsupported shorty : " << shorty;
- return false;
- }
-
- const int *unsupport_list = kUnsupportedOpcodes[cu.instruction_set];
- int unsupport_list_size = kUnsupportedOpcodesSize[cu.instruction_set];
-
- for (unsigned int idx = 0; idx < cu.mir_graph->GetNumBlocks(); idx++) {
- BasicBlock* bb = cu.mir_graph->GetBasicBlock(idx);
- if (bb == NULL) continue;
- if (bb->block_type == kDead) continue;
- for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
- int opcode = mir->dalvikInsn.opcode;
- // Check if we support the byte code.
- if (std::find(unsupport_list, unsupport_list + unsupport_list_size,
- opcode) != unsupport_list + unsupport_list_size) {
- if (!MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
- VLOG(compiler) << "Unsupported dalvik byte code : "
- << mir->dalvikInsn.opcode;
- } else {
- VLOG(compiler) << "Unsupported extended MIR opcode : "
- << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst];
- }
- return false;
- }
- // Check if it invokes a prototype that we cannot support.
- if (Instruction::INVOKE_VIRTUAL == opcode ||
- Instruction::INVOKE_SUPER == opcode ||
- Instruction::INVOKE_DIRECT == opcode ||
- Instruction::INVOKE_STATIC == opcode ||
- Instruction::INVOKE_INTERFACE == opcode) {
- uint32_t invoke_method_idx = mir->dalvikInsn.vB;
- const char* invoke_method_shorty = dex_file.GetMethodShorty(
- dex_file.GetMethodId(invoke_method_idx));
- if (!CanCompileShorty(invoke_method_shorty, cu.instruction_set)) {
- VLOG(compiler) << "Unsupported to invoke '"
- << PrettyMethod(invoke_method_idx, dex_file)
- << "' with shorty : " << invoke_method_shorty;
- return false;
- }
- }
- }
- }
- return true;
-}
-
static CompiledMethod* CompileMethod(CompilerDriver& driver,
- Compiler* compiler,
+ const Compiler* compiler,
const DexFile::CodeItem* code_item,
uint32_t access_flags, InvokeType invoke_type,
uint16_t class_def_idx, uint32_t method_idx,
@@ -662,8 +121,6 @@
(cu.instruction_set == kX86_64) ||
(cu.instruction_set == kMips));
- /* Adjust this value accordingly once inlining is performed */
- cu.num_dalvik_registers = code_item->registers_size_;
// TODO: set this from command line
cu.compiler_flip_match = false;
bool use_match = !cu.compiler_method_match.empty();
@@ -698,9 +155,6 @@
compiler->InitCompilationUnit(cu);
- // Disable optimizations according to instruction set.
- cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
-
cu.StartTimingSplit("BuildMIRGraph");
cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
@@ -720,7 +174,7 @@
cu.mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
class_loader, dex_file);
- if (!CanCompileMethod(method_idx, dex_file, cu)) {
+ if (!compiler->CanCompileMethod(method_idx, dex_file, &cu)) {
VLOG(compiler) << cu.instruction_set << ": Cannot compile method : "
<< PrettyMethod(method_idx, dex_file);
return nullptr;
@@ -802,8 +256,8 @@
return result;
}
-CompiledMethod* CompileOneMethod(CompilerDriver& driver,
- Compiler* compiler,
+CompiledMethod* CompileOneMethod(CompilerDriver* driver,
+ const Compiler* compiler,
const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
@@ -812,22 +266,8 @@
jobject class_loader,
const DexFile& dex_file,
void* compilation_unit) {
- return CompileMethod(driver, compiler, code_item, access_flags, invoke_type, class_def_idx,
+ return CompileMethod(*driver, compiler, code_item, access_flags, invoke_type, class_def_idx,
method_idx, class_loader, dex_file, compilation_unit);
}
} // namespace art
-
-extern "C" art::CompiledMethod*
- ArtQuickCompileMethod(art::CompilerDriver& driver,
- const art::DexFile::CodeItem* code_item,
- uint32_t access_flags, art::InvokeType invoke_type,
- uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
- const art::DexFile& dex_file) {
- // TODO: check method fingerprint here to determine appropriate backend type. Until then, use
- // build default.
- art::Compiler* compiler = driver.GetCompiler();
- return art::CompileOneMethod(driver, compiler, code_item, access_flags, invoke_type,
- class_def_idx, method_idx, class_loader, dex_file,
- NULL /* use thread llvm_info */);
-}
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
index f4cbdfb..51b6d68 100644
--- a/compiler/dex/frontend.h
+++ b/compiler/dex/frontend.h
@@ -20,16 +20,11 @@
#include "dex_file.h"
#include "invoke_type.h"
-namespace llvm {
- class Module;
- class LLVMContext;
-}
-
namespace art {
-namespace llvm {
- class IntrinsicHelper;
- class IRBuilder;
-}
+
+class CompiledMethod;
+class Compiler;
+class CompilerDriver;
/*
* Assembly is an iterative process, and usually terminates within
@@ -81,48 +76,17 @@
kDebugCodegenDump
};
-class LLVMInfo {
- public:
- LLVMInfo();
- ~LLVMInfo();
-
- ::llvm::LLVMContext* GetLLVMContext() {
- return llvm_context_.get();
- }
-
- ::llvm::Module* GetLLVMModule() {
- return llvm_module_;
- }
-
- art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
- return intrinsic_helper_.get();
- }
-
- art::llvm::IRBuilder* GetIRBuilder() {
- return ir_builder_.get();
- }
-
- private:
- std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
- ::llvm::Module* llvm_module_; // Managed by context_.
- std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
- std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
-};
-
-class CompiledMethod;
-class CompilerDriver;
+CompiledMethod* CompileOneMethod(CompilerDriver* driver,
+ const Compiler* compiler,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file,
+ void* compilation_unit);
} // namespace art
-extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver,
- const art::DexFile::CodeItem* code_item,
- uint32_t access_flags,
- art::InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const art::DexFile& dex_file);
-
-
-
#endif // ART_COMPILER_DEX_FRONTEND_H_
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc
index d7ef6f0..af57529 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -56,8 +56,11 @@
return nullptr;
}
if (UNLIKELY(bbs_processed_ == max_bbs_to_process_)) {
- last_value_ = kNoValue; // Make bad.
- return nullptr;
+ // If we're still trying to converge, stop now. Otherwise, proceed to apply optimizations.
+ if (!modifications_allowed_) {
+ last_value_ = kNoValue; // Make bad.
+ return nullptr;
+ }
}
if (allocator == nullptr) {
allocator = allocator_;
@@ -67,7 +70,7 @@
if (bb->block_type == kEntryBlock) {
if ((cu_->access_flags & kAccStatic) == 0) {
// If non-static method, mark "this" as non-null
- int this_reg = cu_->num_dalvik_registers - cu_->num_ins;
+ int this_reg = cu_->mir_graph->GetFirstInVR();
uint16_t value_name = work_lvn_->GetSRegValueName(this_reg);
work_lvn_->SetValueNameNullChecked(value_name);
}
@@ -86,21 +89,20 @@
// the loop head stack will also be empty and there will be nothing to merge anyway.
bool use_all_predecessors = true;
uint16_t loop_head_idx = 0u; // Used only if !use_all_predecessors.
- if (mir_graph_->GetTopologicalSortOrderLoopHeadStack()->Size() != 0) {
+ if (mir_graph_->GetTopologicalSortOrderLoopHeadStack()->size() != 0) {
// Full GVN inside a loop, see if we're at the loop head for the first time.
- auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->Peek();
+ auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->back();
loop_head_idx = top.first;
bool recalculating = top.second;
use_all_predecessors = recalculating ||
- loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()->Get(bb->id);
+ loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()[bb->id];
}
- GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
- for (BasicBlock* pred_bb = mir_graph_->GetBasicBlock(iter.Next());
- pred_bb != nullptr; pred_bb = mir_graph_->GetBasicBlock(iter.Next())) {
- if (lvns_[pred_bb->id] != nullptr &&
+ for (BasicBlockId pred_id : bb->predecessors) {
+ DCHECK_NE(pred_id, NullBasicBlockId);
+ if (lvns_[pred_id] != nullptr &&
(use_all_predecessors ||
- mir_graph_->GetTopologicalSortOrderIndexes()->Get(pred_bb->id) < loop_head_idx)) {
- merge_lvns_.push_back(lvns_[pred_bb->id]);
+ mir_graph_->GetTopologicalSortOrderIndexes()[pred_id] < loop_head_idx)) {
+ merge_lvns_.push_back(lvns_[pred_id]);
}
}
// Determine merge type.
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index c06ff6f..27183bf 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -79,7 +79,7 @@
static uint64_t BuildKey(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
return (static_cast<uint64_t>(op) << 48 | static_cast<uint64_t>(operand1) << 32 |
static_cast<uint64_t>(operand2) << 16 | static_cast<uint64_t>(modifier));
- };
+ }
// Look up a value in the global value map, adding a new entry if there was none before.
uint16_t LookupValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier) {
@@ -93,7 +93,7 @@
global_value_map_.PutBefore(lb, key, res);
}
return res;
- };
+ }
// Check if the exact value is stored in the global value map.
bool HasValue(uint16_t op, uint16_t operand1, uint16_t operand2, uint16_t modifier,
@@ -105,7 +105,7 @@
uint64_t key = BuildKey(op, operand1, operand2, modifier);
ValueMap::const_iterator it = global_value_map_.find(key);
return (it != global_value_map_.end() && it->second == value);
- };
+ }
// FieldReference represents a unique resolved field.
struct FieldReference {
@@ -214,7 +214,7 @@
static constexpr uint32_t kMaxBbsToProcessMultiplyFactor = 20u;
uint32_t bbs_processed_;
- uint32_t max_bbs_to_process_;
+ uint32_t max_bbs_to_process_; // Doesn't apply after the main GVN has converged.
// We have 32-bit last_value_ so that we can detect when we run out of value names, see Good().
// We usually don't check Good() until the end of LVN unless we're about to modify code.
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index e8501cd..1d9920d 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -129,8 +129,8 @@
{ bb, static_cast<Instruction::Code>(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } }
void DoPrepareIFields(const IFieldDef* defs, size_t count) {
- cu_.mir_graph->ifield_lowering_infos_.Reset();
- cu_.mir_graph->ifield_lowering_infos_.Resize(count);
+ cu_.mir_graph->ifield_lowering_infos_.clear();
+ cu_.mir_graph->ifield_lowering_infos_.reserve(count);
for (size_t i = 0u; i != count; ++i) {
const IFieldDef* def = &defs[i];
MirIFieldLoweringInfo field_info(def->field_idx);
@@ -140,7 +140,7 @@
field_info.flags_ = 0u | // Without kFlagIsStatic.
(def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u);
}
- cu_.mir_graph->ifield_lowering_infos_.Insert(field_info);
+ cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
}
}
@@ -150,8 +150,8 @@
}
void DoPrepareSFields(const SFieldDef* defs, size_t count) {
- cu_.mir_graph->sfield_lowering_infos_.Reset();
- cu_.mir_graph->sfield_lowering_infos_.Resize(count);
+ cu_.mir_graph->sfield_lowering_infos_.clear();
+ cu_.mir_graph->sfield_lowering_infos_.reserve(count);
for (size_t i = 0u; i != count; ++i) {
const SFieldDef* def = &defs[i];
MirSFieldLoweringInfo field_info(def->field_idx);
@@ -163,7 +163,7 @@
field_info.declaring_field_idx_ = def->declaring_field_idx;
field_info.flags_ |= (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u);
}
- cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
+ cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
}
}
@@ -174,41 +174,33 @@
void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
cu_.mir_graph->block_id_map_.clear();
- cu_.mir_graph->block_list_.Reset();
+ cu_.mir_graph->block_list_.clear();
ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block.
ASSERT_EQ(kNullBlock, defs[0].type);
ASSERT_EQ(kEntryBlock, defs[1].type);
ASSERT_EQ(kExitBlock, defs[2].type);
for (size_t i = 0u; i != count; ++i) {
const BBDef* def = &defs[i];
- BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
- cu_.mir_graph->block_list_.Insert(bb);
+ BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
if (def->num_successors <= 2) {
bb->successor_block_list_type = kNotUsed;
- bb->successor_blocks = nullptr;
bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
} else {
bb->successor_block_list_type = kPackedSwitch;
bb->fall_through = 0u;
bb->taken = 0u;
- bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
- &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+ bb->successor_blocks.reserve(def->num_successors);
for (size_t j = 0u; j != def->num_successors; ++j) {
SuccessorBlockInfo* successor_block_info =
static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
kArenaAllocSuccessor));
successor_block_info->block = j;
successor_block_info->key = 0u; // Not used by class init check elimination.
- bb->successor_blocks->Insert(successor_block_info);
+ bb->successor_blocks.push_back(successor_block_info);
}
}
- bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
- &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
- for (size_t j = 0u; j != def->num_predecessors; ++j) {
- ASSERT_NE(0u, def->predecessors[j]);
- bb->predecessors->Insert(def->predecessors[j]);
- }
+ bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
@@ -216,10 +208,10 @@
}
}
cu_.mir_graph->num_blocks_ = count;
- ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
- cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+ ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
+ cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
- cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+ cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
}
@@ -235,24 +227,23 @@
for (size_t i = 0u; i != count; ++i) {
const MIRDef* def = &defs[i];
MIR* mir = &mirs_[i];
- ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.Size());
- BasicBlock* bb = cu_.mir_graph->block_list_.Get(def->bbid);
+ ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size());
+ BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
bb->AppendMIR(mir);
mir->dalvikInsn.opcode = def->opcode;
mir->dalvikInsn.vB = static_cast<int32_t>(def->value);
mir->dalvikInsn.vB_wide = def->value;
if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
- ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.Size());
+ ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
mir->meta.ifield_lowering_info = def->field_info;
} else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
- ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.Size());
+ ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
mir->meta.sfield_lowering_info = def->field_info;
} else if (def->opcode == static_cast<Instruction::Code>(kMirOpPhi)) {
mir->meta.phi_incoming = static_cast<BasicBlockId*>(
allocator_->Alloc(def->num_uses * sizeof(BasicBlockId), kArenaAllocDFInfo));
- for (size_t i = 0; i != def->num_uses; ++i) {
- mir->meta.phi_incoming[i] = bb->predecessors->Get(i);
- }
+ ASSERT_EQ(def->num_uses, bb->predecessors.size());
+ std::copy(bb->predecessors.begin(), bb->predecessors.end(), mir->meta.phi_incoming);
}
mir->ssa_rep = &ssa_reps_[i];
mir->ssa_rep->num_uses = def->num_uses;
@@ -266,6 +257,10 @@
mir->optimization_flags = 0u;
}
mirs_[count - 1u].next = nullptr;
+ DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
+ cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
+ code_item->insns_size_in_code_units_ = 2u * count;
+ cu_.mir_graph->current_code_item_ = code_item;
}
template <size_t count>
@@ -341,11 +336,11 @@
allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack));
// Bind all possible sregs to live vregs for test purposes.
live_in_v_->SetInitialBits(kMaxSsaRegs);
- cu_.mir_graph->ssa_base_vregs_ = new (&cu_.arena) GrowableArray<int>(&cu_.arena, kMaxSsaRegs);
- cu_.mir_graph->ssa_subscripts_ = new (&cu_.arena) GrowableArray<int>(&cu_.arena, kMaxSsaRegs);
+ cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs);
+ cu_.mir_graph->ssa_subscripts_.reserve(kMaxSsaRegs);
for (unsigned int i = 0; i < kMaxSsaRegs; i++) {
- cu_.mir_graph->ssa_base_vregs_->Insert(i);
- cu_.mir_graph->ssa_subscripts_->Insert(0);
+ cu_.mir_graph->ssa_base_vregs_.push_back(i);
+ cu_.mir_graph->ssa_subscripts_.push_back(0);
}
}
@@ -434,12 +429,10 @@
// Add successor block info to the check block.
BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
check_bb->successor_block_list_type = kCatch;
- check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
- &cu_.arena, 2, kGrowableArraySuccessorBlocks);
SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
successor_block_info->block = catch_handler->id;
- check_bb->successor_blocks->Insert(successor_block_info);
+ check_bb->successor_blocks.push_back(successor_block_info);
}
class GlobalValueNumberingTestTwoConsecutiveLoops : public GlobalValueNumberingTest {
@@ -2116,12 +2109,10 @@
// Add successor block info to the check block.
BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
check_bb->successor_block_list_type = kCatch;
- check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
- &cu_.arena, 2, kGrowableArraySuccessorBlocks);
SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
successor_block_info->block = catch_handler->id;
- check_bb->successor_blocks->Insert(successor_block_info);
+ check_bb->successor_blocks.push_back(successor_block_info);
BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u);
std::swap(merge_block->taken, merge_block->fall_through);
PrepareMIRs(mirs);
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index 5997568..eb0806b 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -464,7 +464,7 @@
const MIR* mir = fall_through_bb->first_mir_insn;
DCHECK(mir != nullptr);
// Only INVOKEs can leak and clobber non-aliasing references if they throw.
- if ((Instruction::FlagsOf(mir->dalvikInsn.opcode) & Instruction::kInvoke) != 0) {
+ if ((mir->dalvikInsn.FlagsOf() & Instruction::kInvoke) != 0) {
for (uint16_t i = 0u; i != mir->ssa_rep->num_uses; ++i) {
uint16_t value_name = lvn->GetOperandValue(mir->ssa_rep->uses[i]);
non_aliasing_refs_.erase(value_name);
@@ -656,13 +656,37 @@
}
}
-void LocalValueNumbering::MergeNullChecked(const ValueNameSet::value_type& entry,
- ValueNameSet::iterator hint) {
- // Merge null_checked_ for this ref.
- merge_names_.clear();
- merge_names_.resize(gvn_->merge_lvns_.size(), entry);
- if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
- null_checked_.insert(hint, entry);
+void LocalValueNumbering::MergeNullChecked() {
+ DCHECK_GE(gvn_->merge_lvns_.size(), 2u);
+
+ // Find the LVN with the least entries in the set.
+ const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0];
+ for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
+ if (lvn->null_checked_.size() < least_entries_lvn->null_checked_.size()) {
+ least_entries_lvn = lvn;
+ }
+ }
+
+ // For each null-checked value name check if it's null-checked in all the LVNs.
+ for (const auto& value_name : least_entries_lvn->null_checked_) {
+ // Merge null_checked_ for this ref.
+ merge_names_.clear();
+ merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+ if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+ null_checked_.insert(null_checked_.end(), value_name);
+ }
+ }
+
+ // Now check if the least_entries_lvn has a null-check as the last insn.
+ const BasicBlock* least_entries_bb = gvn_->GetBasicBlock(least_entries_lvn->Id());
+ if (gvn_->HasNullCheckLastInsn(least_entries_bb, id_)) {
+ int s_reg = least_entries_bb->last_mir_insn->ssa_rep->uses[0];
+ uint32_t value_name = least_entries_lvn->GetSRegValueName(s_reg);
+ merge_names_.clear();
+ merge_names_.resize(gvn_->merge_lvns_.size(), value_name);
+ if (gvn_->NullCheckedInAllPredecessors(merge_names_)) {
+ null_checked_.insert(value_name);
+ }
}
}
@@ -896,8 +920,7 @@
IntersectSets<RangeCheckSet, &LocalValueNumbering::range_checked_>();
// Merge null_checked_. We may later insert more, such as merged object field values.
- MergeSets<ValueNameSet, &LocalValueNumbering::null_checked_,
- &LocalValueNumbering::MergeNullChecked>();
+ MergeNullChecked();
if (merge_type == kCatchMerge) {
// Memory is clobbered. New memory version already created, don't merge aliasing locations.
@@ -1146,8 +1169,9 @@
const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir);
uint16_t res;
if (!field_info.IsResolved() || field_info.IsVolatile()) {
- // Volatile fields always get a new memory version; field id is irrelevant.
// Unresolved fields may be volatile, so handle them as such to be safe.
+ HandleInvokeOrClInitOrAcquireOp(mir); // Volatile GETs have acquire semantics.
+ // Volatile fields always get a new memory version; field id is irrelevant.
// Use result s_reg - will be unique.
res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue);
} else {
@@ -1246,14 +1270,16 @@
uint16_t LocalValueNumbering::HandleSGet(MIR* mir, uint16_t opcode) {
const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir);
- if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
- // Class initialization can call arbitrary functions, we need to wipe aliasing values.
- HandleInvokeOrClInit(mir);
+ if (!field_info.IsResolved() || field_info.IsVolatile() ||
+ (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0)) {
+ // Volatile SGETs (and unresolved fields are potentially volatile) have acquire semantics
+ // and class initialization can call arbitrary functions, we need to wipe aliasing values.
+ HandleInvokeOrClInitOrAcquireOp(mir);
}
uint16_t res;
if (!field_info.IsResolved() || field_info.IsVolatile()) {
- // Volatile fields always get a new memory version; field id is irrelevant.
// Unresolved fields may be volatile, so handle them as such to be safe.
+ // Volatile fields always get a new memory version; field id is irrelevant.
// Use result s_reg - will be unique.
res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue);
} else {
@@ -1283,7 +1309,7 @@
const MirSFieldLoweringInfo& field_info = gvn_->GetMirGraph()->GetSFieldLoweringInfo(mir);
if (!field_info.IsInitialized() && (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0) {
// Class initialization can call arbitrary functions, we need to wipe aliasing values.
- HandleInvokeOrClInit(mir);
+ HandleInvokeOrClInitOrAcquireOp(mir);
}
uint16_t type = opcode - Instruction::SPUT;
if (!field_info.IsResolved()) {
@@ -1328,7 +1354,7 @@
}
}
-void LocalValueNumbering::HandleInvokeOrClInit(MIR* mir) {
+void LocalValueNumbering::HandleInvokeOrClInitOrAcquireOp(MIR* mir) {
// Use mir->offset as modifier; without elaborate inlining, it will be unique.
global_memory_version_ =
gvn_->LookupValue(kInvokeMemoryVersionBumpOp, 0u, 0u, mir->offset);
@@ -1381,9 +1407,7 @@
case Instruction::MONITOR_ENTER:
HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0]));
- // NOTE: Keeping all aliasing values intact. Programs that rely on loads/stores of the
- // same non-volatile locations outside and inside a synchronized block being different
- // contain races that we cannot fix.
+ HandleInvokeOrClInitOrAcquireOp(mir); // Acquire operation.
break;
case Instruction::MONITOR_EXIT:
@@ -1445,7 +1469,7 @@
uint16_t reg = GetOperandValue(mir->ssa_rep->uses[i]);
non_aliasing_refs_.erase(reg);
}
- HandleInvokeOrClInit(mir);
+ HandleInvokeOrClInitOrAcquireOp(mir);
}
break;
diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h
index 855d66d..c60da32 100644
--- a/compiler/dex/local_value_numbering.h
+++ b/compiler/dex/local_value_numbering.h
@@ -122,19 +122,19 @@
void SetOperandValue(uint16_t s_reg, uint16_t value) {
SetOperandValueImpl(s_reg, value, &sreg_value_map_);
- };
+ }
uint16_t GetOperandValue(int s_reg) const {
return GetOperandValueImpl(s_reg, &sreg_value_map_);
- };
+ }
void SetOperandValueWide(uint16_t s_reg, uint16_t value) {
SetOperandValueImpl(s_reg, value, &sreg_wide_value_map_);
- };
+ }
uint16_t GetOperandValueWide(int s_reg) const {
return GetOperandValueImpl(s_reg, &sreg_wide_value_map_);
- };
+ }
struct RangeCheckKey {
uint16_t array;
@@ -308,7 +308,7 @@
uint16_t HandleSGet(MIR* mir, uint16_t opcode);
void HandleSPut(MIR* mir, uint16_t opcode);
void RemoveSFieldsForType(uint16_t type);
- void HandleInvokeOrClInit(MIR* mir);
+ void HandleInvokeOrClInitOrAcquireOp(MIR* mir);
bool SameMemoryVersion(const LocalValueNumbering& other) const;
@@ -343,11 +343,11 @@
EscapedIFieldClobberSet::iterator hint);
void MergeEscapedArrayClobberSets(const EscapedArrayClobberSet::value_type& entry,
EscapedArrayClobberSet::iterator hint);
- void MergeNullChecked(const ValueNameSet::value_type& entry, ValueNameSet::iterator hint);
void MergeSFieldValues(const SFieldToValueMap::value_type& entry,
SFieldToValueMap::iterator hint);
void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry,
IFieldLocToValueMap::iterator hint);
+ void MergeNullChecked();
template <typename Map, Map LocalValueNumbering::*map_ptr, typename Versions>
void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint);
diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc
index e4e944e..067bea2 100644
--- a/compiler/dex/local_value_numbering_test.cc
+++ b/compiler/dex/local_value_numbering_test.cc
@@ -86,8 +86,8 @@
{ opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ...
void DoPrepareIFields(const IFieldDef* defs, size_t count) {
- cu_.mir_graph->ifield_lowering_infos_.Reset();
- cu_.mir_graph->ifield_lowering_infos_.Resize(count);
+ cu_.mir_graph->ifield_lowering_infos_.clear();
+ cu_.mir_graph->ifield_lowering_infos_.reserve(count);
for (size_t i = 0u; i != count; ++i) {
const IFieldDef* def = &defs[i];
MirIFieldLoweringInfo field_info(def->field_idx);
@@ -97,7 +97,7 @@
field_info.flags_ = 0u | // Without kFlagIsStatic.
(def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u);
}
- cu_.mir_graph->ifield_lowering_infos_.Insert(field_info);
+ cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
}
}
@@ -107,8 +107,8 @@
}
void DoPrepareSFields(const SFieldDef* defs, size_t count) {
- cu_.mir_graph->sfield_lowering_infos_.Reset();
- cu_.mir_graph->sfield_lowering_infos_.Resize(count);
+ cu_.mir_graph->sfield_lowering_infos_.clear();
+ cu_.mir_graph->sfield_lowering_infos_.reserve(count);
for (size_t i = 0u; i != count; ++i) {
const SFieldDef* def = &defs[i];
MirSFieldLoweringInfo field_info(def->field_idx);
@@ -120,7 +120,7 @@
field_info.declaring_field_idx_ = def->declaring_field_idx;
field_info.flags_ |= (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u);
}
- cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
+ cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
}
}
@@ -140,10 +140,10 @@
mir->dalvikInsn.vB = static_cast<int32_t>(def->value);
mir->dalvikInsn.vB_wide = def->value;
if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
- ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.Size());
+ ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
mir->meta.ifield_lowering_info = def->field_info;
} else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
- ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.Size());
+ ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
mir->meta.sfield_lowering_info = def->field_info;
}
mir->ssa_rep = &ssa_reps_[i];
@@ -170,8 +170,8 @@
}
void MakeSFieldUninitialized(uint32_t sfield_index) {
- CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.Size());
- cu_.mir_graph->sfield_lowering_infos_.GetRawStorage()[sfield_index].flags_ &=
+ CHECK_LT(sfield_index, cu_.mir_graph->sfield_lowering_infos_.size());
+ cu_.mir_graph->sfield_lowering_infos_[sfield_index].flags_ &=
~MirSFieldLoweringInfo::kFlagIsInitialized;
}
@@ -338,16 +338,19 @@
DEF_IGET(Instruction::IGET, 1u, 0u, 0u), // Non-volatile.
DEF_IGET(Instruction::IGET, 2u, 10u, 1u), // Volatile.
DEF_IGET(Instruction::IGET, 3u, 2u, 1u), // Non-volatile.
+ DEF_IGET(Instruction::IGET, 4u, 0u, 0u), // Non-volatile.
};
PrepareIFields(ifields);
PrepareMIRs(mirs);
PerformLVN();
- ASSERT_EQ(value_names_.size(), 4u);
+ ASSERT_EQ(value_names_.size(), 5u);
EXPECT_NE(value_names_[0], value_names_[2]); // Volatile has always different value name.
EXPECT_NE(value_names_[1], value_names_[3]); // Used different base because of volatile.
+ EXPECT_NE(value_names_[1], value_names_[4]); // Not guaranteed to be the same after "acquire".
+
for (size_t i = 0; i != arraysize(mirs); ++i) {
- EXPECT_EQ((i == 2u) ? MIR_IGNORE_NULL_CHECK : 0,
+ EXPECT_EQ((i == 2u || i == 4u) ? MIR_IGNORE_NULL_CHECK : 0,
mirs_[i].optimization_flags) << i;
}
}
@@ -363,7 +366,7 @@
DEF_IGET(Instruction::IGET, 1u, 20u, 0u), // Resolved field #1, unique object.
DEF_IGET(Instruction::IGET, 2u, 21u, 0u), // Resolved field #1.
DEF_IGET_WIDE(Instruction::IGET_WIDE, 3u, 21u, 1u), // Resolved field #2.
- DEF_IGET(Instruction::IGET, 4u, 22u, 2u), // IGET doesn't clobber anything.
+ DEF_IGET(Instruction::IGET, 4u, 22u, 2u), // Unresolved IGET can be "acquire".
DEF_IGET(Instruction::IGET, 5u, 20u, 0u), // Resolved field #1, unique object.
DEF_IGET(Instruction::IGET, 6u, 21u, 0u), // Resolved field #1.
DEF_IGET_WIDE(Instruction::IGET_WIDE, 7u, 21u, 1u), // Resolved field #2.
@@ -381,14 +384,15 @@
PrepareMIRs(mirs);
PerformLVN();
ASSERT_EQ(value_names_.size(), 16u);
- EXPECT_EQ(value_names_[1], value_names_[5]);
- EXPECT_EQ(value_names_[2], value_names_[6]);
- EXPECT_EQ(value_names_[3], value_names_[7]);
- EXPECT_EQ(value_names_[1], value_names_[9]);
- EXPECT_NE(value_names_[2], value_names_[10]); // This aliased with unresolved IPUT.
- EXPECT_EQ(value_names_[3], value_names_[11]);
- EXPECT_EQ(value_names_[12], value_names_[15]);
- EXPECT_NE(value_names_[1], value_names_[14]); // This aliased with unresolved IPUT.
+ // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics.
+ EXPECT_EQ(value_names_[1], value_names_[5]); // Unique object.
+ EXPECT_NE(value_names_[2], value_names_[6]); // Not guaranteed to be the same after "acquire".
+ EXPECT_NE(value_names_[3], value_names_[7]); // Not guaranteed to be the same after "acquire".
+ EXPECT_EQ(value_names_[1], value_names_[9]); // Unique object.
+ EXPECT_NE(value_names_[6], value_names_[10]); // This aliased with unresolved IPUT.
+ EXPECT_EQ(value_names_[7], value_names_[11]); // Still the same after "release".
+ EXPECT_EQ(value_names_[12], value_names_[15]); // Still the same after "release".
+ EXPECT_NE(value_names_[1], value_names_[14]); // This aliased with unresolved IPUT.
EXPECT_EQ(mirs_[0].optimization_flags, 0u);
EXPECT_EQ(mirs_[1].optimization_flags, MIR_IGNORE_NULL_CHECK);
EXPECT_EQ(mirs_[2].optimization_flags, 0u);
@@ -409,7 +413,7 @@
static const MIRDef mirs[] = {
DEF_SGET(Instruction::SGET, 0u, 0u), // Resolved field #1.
DEF_SGET_WIDE(Instruction::SGET_WIDE, 1u, 1u), // Resolved field #2.
- DEF_SGET(Instruction::SGET, 2u, 2u), // SGET doesn't clobber anything.
+ DEF_SGET(Instruction::SGET, 2u, 2u), // Unresolved SGET can be "acquire".
DEF_SGET(Instruction::SGET, 3u, 0u), // Resolved field #1.
DEF_SGET_WIDE(Instruction::SGET_WIDE, 4u, 1u), // Resolved field #2.
DEF_SPUT(Instruction::SPUT, 5u, 2u), // SPUT clobbers field #1 (#2 is wide).
@@ -421,10 +425,11 @@
PrepareMIRs(mirs);
PerformLVN();
ASSERT_EQ(value_names_.size(), 8u);
- EXPECT_EQ(value_names_[0], value_names_[3]);
- EXPECT_EQ(value_names_[1], value_names_[4]);
- EXPECT_NE(value_names_[0], value_names_[6]); // This aliased with unresolved IPUT.
- EXPECT_EQ(value_names_[1], value_names_[7]);
+ // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics.
+ EXPECT_NE(value_names_[0], value_names_[3]); // Not guaranteed to be the same after "acquire".
+ EXPECT_NE(value_names_[1], value_names_[4]); // Not guaranteed to be the same after "acquire".
+ EXPECT_NE(value_names_[3], value_names_[6]); // This aliased with unresolved IPUT.
+ EXPECT_EQ(value_names_[4], value_names_[7]); // Still the same after "release".
for (size_t i = 0u; i != mir_count_; ++i) {
EXPECT_EQ(0, mirs_[i].optimization_flags) << i;
}
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 3de4483..ee48796 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -29,818 +29,911 @@
namespace art {
- // Instruction characteristics used to statically identify computation-intensive methods.
-const uint32_t MIRGraph::analysis_attributes_[kMirOpLast] = {
+enum InstructionAnalysisAttributeOps : uint8_t {
+ kUninterestingOp = 0,
+ kArithmeticOp,
+ kFpOp,
+ kSingleOp,
+ kDoubleOp,
+ kIntOp,
+ kLongOp,
+ kBranchOp,
+ kInvokeOp,
+ kArrayOp,
+ kHeavyweightOp,
+ kSimpleConstOp,
+ kMoveOp,
+ kSwitch
+};
+
+enum InstructionAnalysisAttributeMasks : uint16_t {
+ kAnNone = 1 << kUninterestingOp,
+ kAnMath = 1 << kArithmeticOp,
+ kAnFp = 1 << kFpOp,
+ kAnLong = 1 << kLongOp,
+ kAnInt = 1 << kIntOp,
+ kAnSingle = 1 << kSingleOp,
+ kAnDouble = 1 << kDoubleOp,
+ kAnFloatMath = 1 << kFpOp,
+ kAnBranch = 1 << kBranchOp,
+ kAnInvoke = 1 << kInvokeOp,
+ kAnArrayOp = 1 << kArrayOp,
+ kAnHeavyWeight = 1 << kHeavyweightOp,
+ kAnSimpleConst = 1 << kSimpleConstOp,
+ kAnMove = 1 << kMoveOp,
+ kAnSwitch = 1 << kSwitch,
+ kAnComputational = kAnMath | kAnArrayOp | kAnMove | kAnSimpleConst,
+};
+
+// Instruction characteristics used to statically identify computation-intensive methods.
+static const uint16_t kAnalysisAttributes[kMirOpLast] = {
// 00 NOP
- AN_NONE,
+ kAnNone,
// 01 MOVE vA, vB
- AN_MOVE,
+ kAnMove,
// 02 MOVE_FROM16 vAA, vBBBB
- AN_MOVE,
+ kAnMove,
// 03 MOVE_16 vAAAA, vBBBB
- AN_MOVE,
+ kAnMove,
// 04 MOVE_WIDE vA, vB
- AN_MOVE,
+ kAnMove,
// 05 MOVE_WIDE_FROM16 vAA, vBBBB
- AN_MOVE,
+ kAnMove,
// 06 MOVE_WIDE_16 vAAAA, vBBBB
- AN_MOVE,
+ kAnMove,
// 07 MOVE_OBJECT vA, vB
- AN_MOVE,
+ kAnMove,
// 08 MOVE_OBJECT_FROM16 vAA, vBBBB
- AN_MOVE,
+ kAnMove,
// 09 MOVE_OBJECT_16 vAAAA, vBBBB
- AN_MOVE,
+ kAnMove,
// 0A MOVE_RESULT vAA
- AN_MOVE,
+ kAnMove,
// 0B MOVE_RESULT_WIDE vAA
- AN_MOVE,
+ kAnMove,
// 0C MOVE_RESULT_OBJECT vAA
- AN_MOVE,
+ kAnMove,
// 0D MOVE_EXCEPTION vAA
- AN_MOVE,
+ kAnMove,
// 0E RETURN_VOID
- AN_BRANCH,
+ kAnBranch,
// 0F RETURN vAA
- AN_BRANCH,
+ kAnBranch,
// 10 RETURN_WIDE vAA
- AN_BRANCH,
+ kAnBranch,
// 11 RETURN_OBJECT vAA
- AN_BRANCH,
+ kAnBranch,
// 12 CONST_4 vA, #+B
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 13 CONST_16 vAA, #+BBBB
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 14 CONST vAA, #+BBBBBBBB
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 15 CONST_HIGH16 VAA, #+BBBB0000
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 16 CONST_WIDE_16 vAA, #+BBBB
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 17 CONST_WIDE_32 vAA, #+BBBBBBBB
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 18 CONST_WIDE vAA, #+BBBBBBBBBBBBBBBB
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 19 CONST_WIDE_HIGH16 vAA, #+BBBB000000000000
- AN_SIMPLECONST,
+ kAnSimpleConst,
// 1A CONST_STRING vAA, string@BBBB
- AN_NONE,
+ kAnNone,
// 1B CONST_STRING_JUMBO vAA, string@BBBBBBBB
- AN_NONE,
+ kAnNone,
// 1C CONST_CLASS vAA, type@BBBB
- AN_NONE,
+ kAnNone,
// 1D MONITOR_ENTER vAA
- AN_NONE,
+ kAnNone,
// 1E MONITOR_EXIT vAA
- AN_NONE,
+ kAnNone,
// 1F CHK_CAST vAA, type@BBBB
- AN_NONE,
+ kAnNone,
// 20 INSTANCE_OF vA, vB, type@CCCC
- AN_NONE,
+ kAnNone,
// 21 ARRAY_LENGTH vA, vB
- AN_ARRAYOP,
+ kAnArrayOp,
// 22 NEW_INSTANCE vAA, type@BBBB
- AN_HEAVYWEIGHT,
+ kAnHeavyWeight,
// 23 NEW_ARRAY vA, vB, type@CCCC
- AN_HEAVYWEIGHT,
+ kAnHeavyWeight,
// 24 FILLED_NEW_ARRAY {vD, vE, vF, vG, vA}
- AN_HEAVYWEIGHT,
+ kAnHeavyWeight,
// 25 FILLED_NEW_ARRAY_RANGE {vCCCC .. vNNNN}, type@BBBB
- AN_HEAVYWEIGHT,
+ kAnHeavyWeight,
// 26 FILL_ARRAY_DATA vAA, +BBBBBBBB
- AN_NONE,
+ kAnNone,
// 27 THROW vAA
- AN_HEAVYWEIGHT | AN_BRANCH,
+ kAnHeavyWeight | kAnBranch,
// 28 GOTO
- AN_BRANCH,
+ kAnBranch,
// 29 GOTO_16
- AN_BRANCH,
+ kAnBranch,
// 2A GOTO_32
- AN_BRANCH,
+ kAnBranch,
// 2B PACKED_SWITCH vAA, +BBBBBBBB
- AN_SWITCH,
+ kAnSwitch,
// 2C SPARSE_SWITCH vAA, +BBBBBBBB
- AN_SWITCH,
+ kAnSwitch,
// 2D CMPL_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// 2E CMPG_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// 2F CMPL_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// 30 CMPG_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// 31 CMP_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 32 IF_EQ vA, vB, +CCCC
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 33 IF_NE vA, vB, +CCCC
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 34 IF_LT vA, vB, +CCCC
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 35 IF_GE vA, vB, +CCCC
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 36 IF_GT vA, vB, +CCCC
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 37 IF_LE vA, vB, +CCCC
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 38 IF_EQZ vAA, +BBBB
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 39 IF_NEZ vAA, +BBBB
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 3A IF_LTZ vAA, +BBBB
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 3B IF_GEZ vAA, +BBBB
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 3C IF_GTZ vAA, +BBBB
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 3D IF_LEZ vAA, +BBBB
- AN_MATH | AN_BRANCH | AN_INT,
+ kAnMath | kAnBranch | kAnInt,
// 3E UNUSED_3E
- AN_NONE,
+ kAnNone,
// 3F UNUSED_3F
- AN_NONE,
+ kAnNone,
// 40 UNUSED_40
- AN_NONE,
+ kAnNone,
// 41 UNUSED_41
- AN_NONE,
+ kAnNone,
// 42 UNUSED_42
- AN_NONE,
+ kAnNone,
// 43 UNUSED_43
- AN_NONE,
+ kAnNone,
// 44 AGET vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 45 AGET_WIDE vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 46 AGET_OBJECT vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 47 AGET_BOOLEAN vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 48 AGET_BYTE vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 49 AGET_CHAR vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 4A AGET_SHORT vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 4B APUT vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 4C APUT_WIDE vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 4D APUT_OBJECT vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 4E APUT_BOOLEAN vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 4F APUT_BYTE vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 50 APUT_CHAR vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 51 APUT_SHORT vAA, vBB, vCC
- AN_ARRAYOP,
+ kAnArrayOp,
// 52 IGET vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 53 IGET_WIDE vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 54 IGET_OBJECT vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 55 IGET_BOOLEAN vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 56 IGET_BYTE vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 57 IGET_CHAR vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 58 IGET_SHORT vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 59 IPUT vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 5A IPUT_WIDE vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 5B IPUT_OBJECT vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 5C IPUT_BOOLEAN vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 5D IPUT_BYTE vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 5E IPUT_CHAR vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 5F IPUT_SHORT vA, vB, field@CCCC
- AN_NONE,
+ kAnNone,
// 60 SGET vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 61 SGET_WIDE vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 62 SGET_OBJECT vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 63 SGET_BOOLEAN vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 64 SGET_BYTE vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 65 SGET_CHAR vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 66 SGET_SHORT vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 67 SPUT vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 68 SPUT_WIDE vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 69 SPUT_OBJECT vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 6A SPUT_BOOLEAN vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 6B SPUT_BYTE vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 6C SPUT_CHAR vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 6D SPUT_SHORT vAA, field@BBBB
- AN_NONE,
+ kAnNone,
// 6E INVOKE_VIRTUAL {vD, vE, vF, vG, vA}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 6F INVOKE_SUPER {vD, vE, vF, vG, vA}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 70 INVOKE_DIRECT {vD, vE, vF, vG, vA}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 71 INVOKE_STATIC {vD, vE, vF, vG, vA}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 73 UNUSED_73
- AN_NONE,
+ kAnNone,
// 74 INVOKE_VIRTUAL_RANGE {vCCCC .. vNNNN}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 75 INVOKE_SUPER_RANGE {vCCCC .. vNNNN}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 76 INVOKE_DIRECT_RANGE {vCCCC .. vNNNN}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 77 INVOKE_STATIC_RANGE {vCCCC .. vNNNN}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// 79 UNUSED_79
- AN_NONE,
+ kAnNone,
// 7A UNUSED_7A
- AN_NONE,
+ kAnNone,
// 7B NEG_INT vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 7C NOT_INT vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 7D NEG_LONG vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 7E NOT_LONG vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 7F NEG_FLOAT vA, vB
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// 80 NEG_DOUBLE vA, vB
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// 81 INT_TO_LONG vA, vB
- AN_MATH | AN_INT | AN_LONG,
+ kAnMath | kAnInt | kAnLong,
// 82 INT_TO_FLOAT vA, vB
- AN_MATH | AN_FP | AN_INT | AN_SINGLE,
+ kAnMath | kAnFp | kAnInt | kAnSingle,
// 83 INT_TO_DOUBLE vA, vB
- AN_MATH | AN_FP | AN_INT | AN_DOUBLE,
+ kAnMath | kAnFp | kAnInt | kAnDouble,
// 84 LONG_TO_INT vA, vB
- AN_MATH | AN_INT | AN_LONG,
+ kAnMath | kAnInt | kAnLong,
// 85 LONG_TO_FLOAT vA, vB
- AN_MATH | AN_FP | AN_LONG | AN_SINGLE,
+ kAnMath | kAnFp | kAnLong | kAnSingle,
// 86 LONG_TO_DOUBLE vA, vB
- AN_MATH | AN_FP | AN_LONG | AN_DOUBLE,
+ kAnMath | kAnFp | kAnLong | kAnDouble,
// 87 FLOAT_TO_INT vA, vB
- AN_MATH | AN_FP | AN_INT | AN_SINGLE,
+ kAnMath | kAnFp | kAnInt | kAnSingle,
// 88 FLOAT_TO_LONG vA, vB
- AN_MATH | AN_FP | AN_LONG | AN_SINGLE,
+ kAnMath | kAnFp | kAnLong | kAnSingle,
// 89 FLOAT_TO_DOUBLE vA, vB
- AN_MATH | AN_FP | AN_SINGLE | AN_DOUBLE,
+ kAnMath | kAnFp | kAnSingle | kAnDouble,
// 8A DOUBLE_TO_INT vA, vB
- AN_MATH | AN_FP | AN_INT | AN_DOUBLE,
+ kAnMath | kAnFp | kAnInt | kAnDouble,
// 8B DOUBLE_TO_LONG vA, vB
- AN_MATH | AN_FP | AN_LONG | AN_DOUBLE,
+ kAnMath | kAnFp | kAnLong | kAnDouble,
// 8C DOUBLE_TO_FLOAT vA, vB
- AN_MATH | AN_FP | AN_SINGLE | AN_DOUBLE,
+ kAnMath | kAnFp | kAnSingle | kAnDouble,
// 8D INT_TO_BYTE vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 8E INT_TO_CHAR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 8F INT_TO_SHORT vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 90 ADD_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 91 SUB_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 92 MUL_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 93 DIV_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 94 REM_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 95 AND_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 96 OR_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 97 XOR_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 98 SHL_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 99 SHR_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 9A USHR_INT vAA, vBB, vCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// 9B ADD_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 9C SUB_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 9D MUL_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 9E DIV_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// 9F REM_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A0 AND_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A1 OR_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A2 XOR_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A3 SHL_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A4 SHR_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A5 USHR_LONG vAA, vBB, vCC
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// A6 ADD_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// A7 SUB_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// A8 MUL_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// A9 DIV_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// AA REM_FLOAT vAA, vBB, vCC
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// AB ADD_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// AC SUB_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// AD MUL_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// AE DIV_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// AF REM_DOUBLE vAA, vBB, vCC
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// B0 ADD_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B1 SUB_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B2 MUL_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B3 DIV_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B4 REM_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B5 AND_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B6 OR_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B7 XOR_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B8 SHL_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// B9 SHR_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// BA USHR_INT_2ADDR vA, vB
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// BB ADD_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// BC SUB_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// BD MUL_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// BE DIV_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// BF REM_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C0 AND_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C1 OR_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C2 XOR_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C3 SHL_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C4 SHR_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C5 USHR_LONG_2ADDR vA, vB
- AN_MATH | AN_LONG,
+ kAnMath | kAnLong,
// C6 ADD_FLOAT_2ADDR vA, vB
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// C7 SUB_FLOAT_2ADDR vA, vB
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// C8 MUL_FLOAT_2ADDR vA, vB
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// C9 DIV_FLOAT_2ADDR vA, vB
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// CA REM_FLOAT_2ADDR vA, vB
- AN_MATH | AN_FP | AN_SINGLE,
+ kAnMath | kAnFp | kAnSingle,
// CB ADD_DOUBLE_2ADDR vA, vB
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// CC SUB_DOUBLE_2ADDR vA, vB
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// CD MUL_DOUBLE_2ADDR vA, vB
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// CE DIV_DOUBLE_2ADDR vA, vB
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// CF REM_DOUBLE_2ADDR vA, vB
- AN_MATH | AN_FP | AN_DOUBLE,
+ kAnMath | kAnFp | kAnDouble,
// D0 ADD_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D1 RSUB_INT vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D2 MUL_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D3 DIV_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D4 REM_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D5 AND_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D6 OR_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D7 XOR_INT_LIT16 vA, vB, #+CCCC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D8 ADD_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// D9 RSUB_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// DA MUL_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// DB DIV_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// DC REM_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// DD AND_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// DE OR_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// DF XOR_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// E0 SHL_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// E1 SHR_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// E2 USHR_INT_LIT8 vAA, vBB, #+CC
- AN_MATH | AN_INT,
+ kAnMath | kAnInt,
// E3 IGET_VOLATILE
- AN_NONE,
+ kAnNone,
// E4 IPUT_VOLATILE
- AN_NONE,
+ kAnNone,
// E5 SGET_VOLATILE
- AN_NONE,
+ kAnNone,
// E6 SPUT_VOLATILE
- AN_NONE,
+ kAnNone,
// E7 IGET_OBJECT_VOLATILE
- AN_NONE,
+ kAnNone,
// E8 IGET_WIDE_VOLATILE
- AN_NONE,
+ kAnNone,
// E9 IPUT_WIDE_VOLATILE
- AN_NONE,
+ kAnNone,
// EA SGET_WIDE_VOLATILE
- AN_NONE,
+ kAnNone,
// EB SPUT_WIDE_VOLATILE
- AN_NONE,
+ kAnNone,
// EC BREAKPOINT
- AN_NONE,
+ kAnNone,
// ED THROW_VERIFICATION_ERROR
- AN_HEAVYWEIGHT | AN_BRANCH,
+ kAnHeavyWeight | kAnBranch,
// EE EXECUTE_INLINE
- AN_NONE,
+ kAnNone,
// EF EXECUTE_INLINE_RANGE
- AN_NONE,
+ kAnNone,
// F0 INVOKE_OBJECT_INIT_RANGE
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// F1 RETURN_VOID_BARRIER
- AN_BRANCH,
+ kAnBranch,
// F2 IGET_QUICK
- AN_NONE,
+ kAnNone,
// F3 IGET_WIDE_QUICK
- AN_NONE,
+ kAnNone,
// F4 IGET_OBJECT_QUICK
- AN_NONE,
+ kAnNone,
// F5 IPUT_QUICK
- AN_NONE,
+ kAnNone,
// F6 IPUT_WIDE_QUICK
- AN_NONE,
+ kAnNone,
// F7 IPUT_OBJECT_QUICK
- AN_NONE,
+ kAnNone,
// F8 INVOKE_VIRTUAL_QUICK
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// F9 INVOKE_VIRTUAL_QUICK_RANGE
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// FA INVOKE_SUPER_QUICK
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// FB INVOKE_SUPER_QUICK_RANGE
- AN_INVOKE | AN_HEAVYWEIGHT,
+ kAnInvoke | kAnHeavyWeight,
// FC IPUT_OBJECT_VOLATILE
- AN_NONE,
+ kAnNone,
// FD SGET_OBJECT_VOLATILE
- AN_NONE,
+ kAnNone,
// FE SPUT_OBJECT_VOLATILE
- AN_NONE,
+ kAnNone,
// FF UNUSED_FF
- AN_NONE,
+ kAnNone,
// Beginning of extended MIR opcodes
// 100 MIR_PHI
- AN_NONE,
+ kAnNone,
// 101 MIR_COPY
- AN_NONE,
+ kAnNone,
// 102 MIR_FUSED_CMPL_FLOAT
- AN_NONE,
+ kAnNone,
// 103 MIR_FUSED_CMPG_FLOAT
- AN_NONE,
+ kAnNone,
// 104 MIR_FUSED_CMPL_DOUBLE
- AN_NONE,
+ kAnNone,
// 105 MIR_FUSED_CMPG_DOUBLE
- AN_NONE,
+ kAnNone,
// 106 MIR_FUSED_CMP_LONG
- AN_NONE,
+ kAnNone,
// 107 MIR_NOP
- AN_NONE,
+ kAnNone,
// 108 MIR_NULL_CHECK
- AN_NONE,
+ kAnNone,
// 109 MIR_RANGE_CHECK
- AN_NONE,
+ kAnNone,
- // 110 MIR_DIV_ZERO_CHECK
- AN_NONE,
+ // 10A MIR_DIV_ZERO_CHECK
+ kAnNone,
- // 111 MIR_CHECK
- AN_NONE,
+ // 10B MIR_CHECK
+ kAnNone,
- // 112 MIR_CHECKPART2
- AN_NONE,
+ // 10C MIR_CHECKPART2
+ kAnNone,
- // 113 MIR_SELECT
- AN_NONE,
+ // 10D MIR_SELECT
+ kAnNone,
+
+ // 10E MirOpConstVector
+ kAnNone,
+
+ // 10F MirOpMoveVector
+ kAnNone,
+
+ // 110 MirOpPackedMultiply
+ kAnNone,
+
+ // 111 MirOpPackedAddition
+ kAnNone,
+
+ // 112 MirOpPackedSubtract
+ kAnNone,
+
+ // 113 MirOpPackedShiftLeft
+ kAnNone,
+
+ // 114 MirOpPackedSignedShiftRight
+ kAnNone,
+
+ // 115 MirOpPackedUnsignedShiftRight
+ kAnNone,
+
+ // 116 MirOpPackedAnd
+ kAnNone,
+
+ // 117 MirOpPackedOr
+ kAnNone,
+
+ // 118 MirOpPackedXor
+ kAnNone,
+
+ // 119 MirOpPackedAddReduce
+ kAnNone,
+
+ // 11A MirOpPackedReduce
+ kAnNone,
+
+ // 11B MirOpPackedSet
+ kAnNone,
+
+ // 11C MirOpReserveVectorRegisters
+ kAnNone,
+
+ // 11D MirOpReturnVectorRegisters
+ kAnNone,
+
+ // 11E MirOpMemBarrier
+ kAnNone,
+
+ // 11F MirOpPackedArrayGet
+ kAnArrayOp,
+
+ // 120 MirOpPackedArrayPut
+ kAnArrayOp,
};
struct MethodStats {
@@ -872,10 +965,10 @@
*/
BasicBlock* ending_bb = bb;
if (ending_bb->last_mir_insn != NULL) {
- uint32_t ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
- while ((ending_flags & AN_BRANCH) == 0) {
+ uint32_t ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode];
+ while ((ending_flags & kAnBranch) == 0) {
ending_bb = GetBasicBlock(ending_bb->fall_through);
- ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
+ ending_flags = kAnalysisAttributes[ending_bb->last_mir_insn->dalvikInsn.opcode];
}
}
/*
@@ -906,27 +999,27 @@
// Skip any MIR pseudo-op.
continue;
}
- uint32_t flags = analysis_attributes_[mir->dalvikInsn.opcode];
+ uint16_t flags = kAnalysisAttributes[mir->dalvikInsn.opcode];
stats->dex_instructions += loop_scale_factor;
- if ((flags & AN_BRANCH) == 0) {
- computational_block &= ((flags & AN_COMPUTATIONAL) != 0);
+ if ((flags & kAnBranch) == 0) {
+ computational_block &= ((flags & kAnComputational) != 0);
} else {
stats->branch_ops += loop_scale_factor;
}
- if ((flags & AN_MATH) != 0) {
+ if ((flags & kAnMath) != 0) {
stats->math_ops += loop_scale_factor;
has_math = true;
}
- if ((flags & AN_FP) != 0) {
+ if ((flags & kAnFp) != 0) {
stats->fp_ops += loop_scale_factor;
}
- if ((flags & AN_ARRAYOP) != 0) {
+ if ((flags & kAnArrayOp) != 0) {
stats->array_ops += loop_scale_factor;
}
- if ((flags & AN_HEAVYWEIGHT) != 0) {
+ if ((flags & kAnHeavyWeight) != 0) {
stats->heavyweight_ops += loop_scale_factor;
}
- if ((flags & AN_SWITCH) != 0) {
+ if ((flags & kAnSwitch) != 0) {
stats->has_switch = true;
}
}
@@ -1037,6 +1130,7 @@
default_cutoff = compiler_options.GetSmallMethodThreshold();
break;
case CompilerOptions::kSpeed:
+ case CompilerOptions::kTime:
small_cutoff = compiler_options.GetHugeMethodThreshold();
default_cutoff = compiler_options.GetHugeMethodThreshold();
break;
@@ -1108,7 +1202,7 @@
void MIRGraph::DoCacheFieldLoweringInfo() {
// All IGET/IPUT/SGET/SPUT instructions take 2 code units and there must also be a RETURN.
- const uint32_t max_refs = (current_code_item_->insns_size_in_code_units_ - 1u) / 2u;
+ const uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 2u;
ScopedArenaAllocator allocator(&cu_->arena_stack);
uint16_t* field_idxs =
reinterpret_cast<uint16_t*>(allocator.Alloc(max_refs * sizeof(uint16_t), kArenaAllocMisc));
@@ -1124,12 +1218,11 @@
for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
if (mir->dalvikInsn.opcode >= Instruction::IGET &&
mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
- const Instruction* insn = Instruction::At(current_code_item_->insns_ + mir->offset);
// Get field index and try to find it among existing indexes. If found, it's usually among
// the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this
// is a linear search, it actually performs much better than map based approach.
if (mir->dalvikInsn.opcode <= Instruction::IPUT_SHORT) {
- uint16_t field_idx = insn->VRegC_22c();
+ uint16_t field_idx = mir->dalvikInsn.vC;
size_t i = ifield_pos;
while (i != 0u && field_idxs[i - 1] != field_idx) {
--i;
@@ -1141,7 +1234,7 @@
field_idxs[ifield_pos++] = field_idx;
}
} else {
- uint16_t field_idx = insn->VRegB_21c();
+ uint16_t field_idx = mir->dalvikInsn.vB;
size_t i = sfield_pos;
while (i != max_refs && field_idxs[i] != field_idx) {
++i;
@@ -1160,25 +1253,25 @@
if (ifield_pos != 0u) {
// Resolve instance field infos.
- DCHECK_EQ(ifield_lowering_infos_.Size(), 0u);
- ifield_lowering_infos_.Resize(ifield_pos);
+ DCHECK_EQ(ifield_lowering_infos_.size(), 0u);
+ ifield_lowering_infos_.reserve(ifield_pos);
for (size_t pos = 0u; pos != ifield_pos; ++pos) {
- ifield_lowering_infos_.Insert(MirIFieldLoweringInfo(field_idxs[pos]));
+ ifield_lowering_infos_.push_back(MirIFieldLoweringInfo(field_idxs[pos]));
}
MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(),
- ifield_lowering_infos_.GetRawStorage(), ifield_pos);
+ ifield_lowering_infos_.data(), ifield_pos);
}
if (sfield_pos != max_refs) {
// Resolve static field infos.
- DCHECK_EQ(sfield_lowering_infos_.Size(), 0u);
- sfield_lowering_infos_.Resize(max_refs - sfield_pos);
+ DCHECK_EQ(sfield_lowering_infos_.size(), 0u);
+ sfield_lowering_infos_.reserve(max_refs - sfield_pos);
for (size_t pos = max_refs; pos != sfield_pos;) {
--pos;
- sfield_lowering_infos_.Insert(MirSFieldLoweringInfo(field_idxs[pos]));
+ sfield_lowering_infos_.push_back(MirSFieldLoweringInfo(field_idxs[pos]));
}
MirSFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(),
- sfield_lowering_infos_.GetRawStorage(), max_refs - sfield_pos);
+ sfield_lowering_infos_.data(), max_refs - sfield_pos);
}
}
@@ -1221,7 +1314,7 @@
ScopedArenaAllocator allocator(&cu_->arena_stack);
// All INVOKE instructions take 3 code units and there must also be a RETURN.
- uint32_t max_refs = (current_code_item_->insns_size_in_code_units_ - 1u) / 3u;
+ uint32_t max_refs = (GetNumDalvikInsns() - 1u) / 3u;
// Map invoke key (see MapEntry) to lowering info index and vice versa.
// The invoke_map and sequential entries are essentially equivalent to Boost.MultiIndex's
@@ -1242,14 +1335,13 @@
mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE_RANGE &&
mir->dalvikInsn.opcode != Instruction::RETURN_VOID_BARRIER) {
// Decode target method index and invoke type.
- const Instruction* insn = Instruction::At(current_code_item_->insns_ + mir->offset);
uint16_t target_method_idx;
uint16_t invoke_type_idx;
if (mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE) {
- target_method_idx = insn->VRegB_35c();
+ target_method_idx = mir->dalvikInsn.vB;
invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL;
} else {
- target_method_idx = insn->VRegB_3rc();
+ target_method_idx = mir->dalvikInsn.vB;
invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL_RANGE;
}
@@ -1280,9 +1372,9 @@
}
// Prepare unique method infos, set method info indexes for their MIRs.
- DCHECK_EQ(method_lowering_infos_.Size(), 0u);
+ DCHECK_EQ(method_lowering_infos_.size(), 0u);
const size_t count = invoke_map.size();
- method_lowering_infos_.Resize(count);
+ method_lowering_infos_.reserve(count);
for (size_t pos = 0u; pos != count; ++pos) {
const MapEntry* entry = sequential_entries[pos];
MirMethodLoweringInfo method_info(entry->target_method_idx,
@@ -1290,10 +1382,10 @@
if (entry->devirt_target != nullptr) {
method_info.SetDevirtualizationTarget(*entry->devirt_target);
}
- method_lowering_infos_.Insert(method_info);
+ method_lowering_infos_.push_back(method_info);
}
MirMethodLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(),
- method_lowering_infos_.GetRawStorage(), count);
+ method_lowering_infos_.data(), count);
}
bool MIRGraph::SkipCompilationByName(const std::string& methodname) {
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index b82c5c7..246ae44 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -824,75 +824,84 @@
DF_NOP,
// 108 MIR_NULL_CHECK
- 0,
+ DF_UA | DF_REF_A | DF_NULL_CHK_0 | DF_LVN,
// 109 MIR_RANGE_CHECK
0,
- // 110 MIR_DIV_ZERO_CHECK
+ // 10A MIR_DIV_ZERO_CHECK
0,
- // 111 MIR_CHECK
+ // 10B MIR_CHECK
0,
- // 112 MIR_CHECKPART2
+ // 10C MIR_CHECKPART2
0,
- // 113 MIR_SELECT
+ // 10D MIR_SELECT
DF_DA | DF_UB,
- // 114 MirOpConstVector
- DF_DA,
-
- // 115 MirOpMoveVector
+ // 10E MirOpConstVector
0,
- // 116 MirOpPackedMultiply
+ // 10F MirOpMoveVector
0,
- // 117 MirOpPackedAddition
+ // 110 MirOpPackedMultiply
0,
- // 118 MirOpPackedSubtract
+ // 111 MirOpPackedAddition
0,
- // 119 MirOpPackedShiftLeft
+ // 112 MirOpPackedSubtract
0,
- // 120 MirOpPackedSignedShiftRight
+ // 113 MirOpPackedShiftLeft
0,
- // 121 MirOpPackedUnsignedShiftRight
+ // 114 MirOpPackedSignedShiftRight
0,
- // 122 MirOpPackedAnd
+ // 115 MirOpPackedUnsignedShiftRight
0,
- // 123 MirOpPackedOr
+ // 116 MirOpPackedAnd
0,
- // 124 MirOpPackedXor
+ // 117 MirOpPackedOr
0,
- // 125 MirOpPackedAddReduce
- DF_DA | DF_UA,
-
- // 126 MirOpPackedReduce
- DF_DA,
-
- // 127 MirOpPackedSet
- DF_UB,
-
- // 128 MirOpReserveVectorRegisters
+ // 118 MirOpPackedXor
0,
- // 129 MirOpReturnVectorRegisters
+ // 119 MirOpPackedAddReduce
+ DF_FORMAT_EXTENDED,
+
+ // 11A MirOpPackedReduce
+ DF_FORMAT_EXTENDED,
+
+ // 11B MirOpPackedSet
+ DF_FORMAT_EXTENDED,
+
+ // 11C MirOpReserveVectorRegisters
0,
+
+ // 11D MirOpReturnVectorRegisters
+ 0,
+
+ // 11E MirOpMemBarrier
+ 0,
+
+ // 11F MirOpPackedArrayGet
+ DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+
+ // 120 MirOpPackedArrayPut
+ DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
};
/* Return the base virtual register for a SSA name */
int MIRGraph::SRegToVReg(int ssa_reg) const {
- return ssa_base_vregs_->Get(ssa_reg);
+ return ssa_base_vregs_[ssa_reg];
}
/* Any register that is used before being defined is considered live-in */
@@ -912,7 +921,36 @@
void MIRGraph::HandleExtended(ArenaBitVector* use_v, ArenaBitVector* def_v,
ArenaBitVector* live_in_v,
const MIR::DecodedInstruction& d_insn) {
+ // For vector MIRs, vC contains type information
+ bool is_vector_type_wide = false;
+ int type_size = d_insn.vC >> 16;
+ if (type_size == k64 || type_size == kDouble) {
+ is_vector_type_wide = true;
+ }
+
switch (static_cast<int>(d_insn.opcode)) {
+ case kMirOpPackedAddReduce:
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA);
+ if (is_vector_type_wide == true) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vA + 1);
+ }
+ HandleDef(def_v, d_insn.vA);
+ if (is_vector_type_wide == true) {
+ HandleDef(def_v, d_insn.vA + 1);
+ }
+ break;
+ case kMirOpPackedReduce:
+ HandleDef(def_v, d_insn.vA);
+ if (is_vector_type_wide == true) {
+ HandleDef(def_v, d_insn.vA + 1);
+ }
+ break;
+ case kMirOpPackedSet:
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB);
+ if (is_vector_type_wide == true) {
+ HandleLiveInUse(use_v, def_v, live_in_v, d_insn.vB + 1);
+ }
+ break;
default:
LOG(ERROR) << "Unexpected Extended Opcode " << d_insn.opcode;
break;
@@ -930,11 +968,11 @@
if (bb->data_flow_info == NULL) return false;
use_v = bb->data_flow_info->use_v =
- new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapUse);
+ new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapUse);
def_v = bb->data_flow_info->def_v =
- new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapDef);
+ new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapDef);
live_in_v = bb->data_flow_info->live_in_v =
- new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapLiveIn);
+ new (arena_) ArenaBitVector(arena_, GetNumOfCodeAndTempVRs(), false, kBitMapLiveIn);
for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
uint64_t df_attributes = GetDataFlowAttributes(mir);
@@ -984,31 +1022,30 @@
}
int MIRGraph::AddNewSReg(int v_reg) {
- // Compiler temps always have a subscript of 0
- int subscript = (v_reg < 0) ? 0 : ++ssa_last_defs_[v_reg];
+ int subscript = ++ssa_last_defs_[v_reg];
uint32_t ssa_reg = GetNumSSARegs();
SetNumSSARegs(ssa_reg + 1);
- ssa_base_vregs_->Insert(v_reg);
- ssa_subscripts_->Insert(subscript);
- DCHECK_EQ(ssa_base_vregs_->Size(), ssa_subscripts_->Size());
+ ssa_base_vregs_.push_back(v_reg);
+ ssa_subscripts_.push_back(subscript);
+ DCHECK_EQ(ssa_base_vregs_.size(), ssa_subscripts_.size());
// If we are expanding very late, update use counts too.
- if (ssa_reg > 0 && use_counts_.Size() == ssa_reg) {
+ if (ssa_reg > 0 && use_counts_.size() == ssa_reg) {
// Need to expand the counts.
- use_counts_.Insert(0);
- raw_use_counts_.Insert(0);
+ use_counts_.push_back(0);
+ raw_use_counts_.push_back(0);
}
return ssa_reg;
}
/* Find out the latest SSA register for a given Dalvik register */
void MIRGraph::HandleSSAUse(int* uses, int dalvik_reg, int reg_index) {
- DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers));
+ DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast<int>(GetNumOfCodeAndTempVRs())));
uses[reg_index] = vreg_to_ssa_map_[dalvik_reg];
}
/* Setup a new SSA register for a given Dalvik register */
void MIRGraph::HandleSSADef(int* defs, int dalvik_reg, int reg_index) {
- DCHECK((dalvik_reg >= 0) && (dalvik_reg < cu_->num_dalvik_registers));
+ DCHECK((dalvik_reg >= 0) && (dalvik_reg < static_cast<int>(GetNumOfCodeAndTempVRs())));
int ssa_reg = AddNewSReg(dalvik_reg);
vreg_to_ssa_map_[dalvik_reg] = ssa_reg;
defs[reg_index] = ssa_reg;
@@ -1062,7 +1099,46 @@
}
void MIRGraph::DataFlowSSAFormatExtended(MIR* mir) {
+ const MIR::DecodedInstruction& d_insn = mir->dalvikInsn;
+ // For vector MIRs, vC contains type information
+ bool is_vector_type_wide = false;
+ int type_size = d_insn.vC >> 16;
+ if (type_size == k64 || type_size == kDouble) {
+ is_vector_type_wide = true;
+ }
+
switch (static_cast<int>(mir->dalvikInsn.opcode)) {
+ case kMirOpPackedAddReduce:
+ // We have one use, plus one more for wide
+ AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vA, 0);
+ if (is_vector_type_wide == true) {
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vA + 1, 1);
+ }
+
+ // We have a def, plus one more for wide
+ AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1);
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+ if (is_vector_type_wide == true) {
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1);
+ }
+ break;
+ case kMirOpPackedReduce:
+ // We have a def, plus one more for wide
+ AllocateSSADefData(mir, is_vector_type_wide ? 2 : 1);
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA, 0);
+ if (is_vector_type_wide == true) {
+ HandleSSADef(mir->ssa_rep->defs, d_insn.vA + 1, 1);
+ }
+ break;
+ case kMirOpPackedSet:
+ // We have one use, plus one more for wide
+ AllocateSSAUseData(mir, is_vector_type_wide ? 2 : 1);
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vB, 0);
+ if (is_vector_type_wide == true) {
+ HandleSSAUse(mir->ssa_rep->uses, d_insn.vB + 1, 1);
+ }
+ break;
default:
LOG(ERROR) << "Missing case for extended MIR: " << mir->dalvikInsn.opcode;
break;
@@ -1085,7 +1161,7 @@
// If not a pseudo-op, note non-leaf or can throw
if (!MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) {
- int flags = Instruction::FlagsOf(mir->dalvikInsn.opcode);
+ int flags = mir->dalvikInsn.FlagsOf();
if ((flags & Instruction::kInvoke) != 0 && (mir->optimization_flags & MIR_INLINED) == 0) {
attributes_ &= ~METHOD_IS_LEAF;
@@ -1188,70 +1264,23 @@
* predecessor blocks.
*/
bb->data_flow_info->vreg_to_ssa_map_exit =
- static_cast<int*>(arena_->Alloc(sizeof(int) * cu_->num_dalvik_registers,
+ static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumOfCodeAndTempVRs(),
kArenaAllocDFInfo));
memcpy(bb->data_flow_info->vreg_to_ssa_map_exit, vreg_to_ssa_map_,
- sizeof(int) * cu_->num_dalvik_registers);
+ sizeof(int) * GetNumOfCodeAndTempVRs());
return true;
}
-/* Setup the basic data structures for SSA conversion */
-void MIRGraph::CompilerInitializeSSAConversion() {
- size_t num_dalvik_reg = cu_->num_dalvik_registers;
-
- ssa_base_vregs_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128,
- kGrowableArraySSAtoDalvikMap);
- ssa_subscripts_ = new (arena_) GrowableArray<int>(arena_, num_dalvik_reg + GetDefCount() + 128,
- kGrowableArraySSAtoDalvikMap);
+void MIRGraph::InitializeBasicBlockDataFlow() {
/*
- * Initial number of SSA registers is equal to the number of Dalvik
- * registers.
+ * Allocate the BasicBlockDataFlow structure for the entry and code blocks.
*/
- SetNumSSARegs(num_dalvik_reg);
-
- /*
- * Initialize the SSA2Dalvik map list. For the first num_dalvik_reg elements,
- * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
- * into "(0 << 16) | i"
- */
- for (unsigned int i = 0; i < num_dalvik_reg; i++) {
- ssa_base_vregs_->Insert(i);
- ssa_subscripts_->Insert(0);
- }
-
- /*
- * Initialize the DalvikToSSAMap map. There is one entry for each
- * Dalvik register, and the SSA names for those are the same.
- */
- vreg_to_ssa_map_ =
- static_cast<int*>(arena_->Alloc(sizeof(int) * num_dalvik_reg,
- kArenaAllocDFInfo));
- /* Keep track of the higest def for each dalvik reg */
- ssa_last_defs_ =
- static_cast<int*>(arena_->Alloc(sizeof(int) * num_dalvik_reg,
- kArenaAllocDFInfo));
-
- for (unsigned int i = 0; i < num_dalvik_reg; i++) {
- vreg_to_ssa_map_[i] = i;
- ssa_last_defs_[i] = 0;
- }
-
- // Create a compiler temporary for Method*. This is done after SSA initialization.
- GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false);
-
- /*
- * Allocate the BasicBlockDataFlow structure for the entry and code blocks
- */
- GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
-
- while (true) {
- BasicBlock* bb = iterator.Next();
- if (bb == NULL) break;
+ for (BasicBlock* bb : block_list_) {
if (bb->hidden == true) continue;
if (bb->block_type == kDalvikByteCode ||
- bb->block_type == kEntryBlock ||
- bb->block_type == kExitBlock) {
+ bb->block_type == kEntryBlock ||
+ bb->block_type == kExitBlock) {
bb->data_flow_info =
static_cast<BasicBlockDataFlow*>(arena_->Alloc(sizeof(BasicBlockDataFlow),
kArenaAllocDFInfo));
@@ -1259,54 +1288,54 @@
}
}
-/*
- * This function will make a best guess at whether the invoke will
- * end up using Method*. It isn't critical to get it exactly right,
- * and attempting to do would involve more complexity than it's
- * worth.
- */
-bool MIRGraph::InvokeUsesMethodStar(MIR* mir) {
- InvokeType type;
- Instruction::Code opcode = mir->dalvikInsn.opcode;
- switch (opcode) {
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_STATIC_RANGE:
- type = kStatic;
- break;
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE:
- type = kDirect;
- break;
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- type = kVirtual;
- break;
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_INTERFACE_RANGE:
- return false;
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_SUPER:
- type = kSuper;
- break;
- default:
- LOG(WARNING) << "Unexpected invoke op: " << opcode;
- return false;
+/* Setup the basic data structures for SSA conversion */
+void MIRGraph::CompilerInitializeSSAConversion() {
+ size_t num_reg = GetNumOfCodeAndTempVRs();
+
+ ssa_base_vregs_.clear();
+ ssa_base_vregs_.reserve(num_reg + GetDefCount() + 128);
+ ssa_subscripts_.clear();
+ ssa_subscripts_.reserve(num_reg + GetDefCount() + 128);
+
+ /*
+ * Initial number of SSA registers is equal to the number of Dalvik
+ * registers.
+ */
+ SetNumSSARegs(num_reg);
+
+ /*
+ * Initialize the SSA2Dalvik map list. For the first num_reg elements,
+ * the subscript is 0 so we use the ENCODE_REG_SUB macro to encode the value
+ * into "(0 << 16) | i"
+ */
+ for (unsigned int i = 0; i < num_reg; i++) {
+ ssa_base_vregs_.push_back(i);
+ ssa_subscripts_.push_back(0);
}
- DexCompilationUnit m_unit(cu_);
- MethodReference target_method(cu_->dex_file, mir->dalvikInsn.vB);
- int vtable_idx;
- uintptr_t direct_code;
- uintptr_t direct_method;
- uint32_t current_offset = static_cast<uint32_t>(current_offset_);
- bool fast_path =
- cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset,
- false, true,
- &type, &target_method,
- &vtable_idx,
- &direct_code, &direct_method) &&
- !(cu_->enable_debug & (1 << kDebugSlowInvokePath));
- return (((type == kDirect) || (type == kStatic)) &&
- fast_path && ((direct_code == 0) || (direct_method == 0)));
+
+ /*
+ * Initialize the DalvikToSSAMap map. There is one entry for each
+ * Dalvik register, and the SSA names for those are the same.
+ */
+ vreg_to_ssa_map_ =
+ static_cast<int*>(arena_->Alloc(sizeof(int) * num_reg,
+ kArenaAllocDFInfo));
+ /* Keep track of the higest def for each dalvik reg */
+ ssa_last_defs_ =
+ static_cast<int*>(arena_->Alloc(sizeof(int) * num_reg,
+ kArenaAllocDFInfo));
+
+ for (unsigned int i = 0; i < num_reg; i++) {
+ vreg_to_ssa_map_[i] = i;
+ ssa_last_defs_[i] = 0;
+ }
+
+ // Create a compiler temporary for Method*. This is done after SSA initialization.
+ CompilerTemp* method_temp = GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false);
+ // The MIR graph keeps track of the sreg for method pointer specially, so record that now.
+ method_sreg_ = method_temp->s_reg_low;
+
+ InitializeBasicBlockDataFlow();
}
/*
@@ -1327,8 +1356,8 @@
}
for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
int s_reg = mir->ssa_rep->uses[i];
- raw_use_counts_.Increment(s_reg);
- use_counts_.Put(s_reg, use_counts_.Get(s_reg) + weight);
+ raw_use_counts_[s_reg] += 1u;
+ use_counts_[s_reg] += weight;
}
if (!(cu_->disable_opt & (1 << kPromoteCompilerTemps))) {
uint64_t df_attributes = GetDataFlowAttributes(mir);
@@ -1343,8 +1372,8 @@
* and save results for both here and GenInvoke. For now, go ahead
* and assume all invokes use method*.
*/
- raw_use_counts_.Increment(method_sreg_);
- use_counts_.Put(method_sreg_, use_counts_.Get(method_sreg_) + weight);
+ raw_use_counts_[method_sreg_] += 1u;
+ use_counts_[method_sreg_] += weight;
}
}
}
@@ -1352,21 +1381,16 @@
/* Verify if all the successor is connected with all the claimed predecessors */
bool MIRGraph::VerifyPredInfo(BasicBlock* bb) {
- GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
-
- while (true) {
- BasicBlock* pred_bb = GetBasicBlock(iter.Next());
- if (!pred_bb) break;
+ for (BasicBlockId pred_id : bb->predecessors) {
+ BasicBlock* pred_bb = GetBasicBlock(pred_id);
+ DCHECK(pred_bb != nullptr);
bool found = false;
if (pred_bb->taken == bb->id) {
found = true;
} else if (pred_bb->fall_through == bb->id) {
found = true;
} else if (pred_bb->successor_block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(pred_bb->successor_blocks);
- while (true) {
- SuccessorBlockInfo *successor_block_info = iterator.Next();
- if (successor_block_info == NULL) break;
+ for (SuccessorBlockInfo* successor_block_info : pred_bb->successor_blocks) {
BasicBlockId succ_bb = successor_block_info->block;
if (succ_bb == bb->id) {
found = true;
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 6aee563..dda9e77 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <queue>
+#include "base/bit_vector-inl.h"
#include "base/stl_util.h"
#include "compiler_internals.h"
#include "dex_file-inl.h"
@@ -28,6 +29,7 @@
#include "dex/quick/dex_file_method_inliner.h"
#include "leb128.h"
#include "pass_driver_me_post_opt.h"
+#include "stack.h"
#include "utils/scoped_arena_containers.h"
namespace art {
@@ -65,29 +67,32 @@
"PackedSet",
"ReserveVectorRegisters",
"ReturnVectorRegisters",
+ "MemBarrier",
+ "PackedArrayGet",
+ "PackedArrayPut",
};
MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
: reg_location_(NULL),
block_id_map_(std::less<unsigned int>(), arena->Adapter()),
cu_(cu),
- ssa_base_vregs_(NULL),
- ssa_subscripts_(NULL),
+ ssa_base_vregs_(arena->Adapter(kArenaAllocSSAToDalvikMap)),
+ ssa_subscripts_(arena->Adapter(kArenaAllocSSAToDalvikMap)),
vreg_to_ssa_map_(NULL),
ssa_last_defs_(NULL),
is_constant_v_(NULL),
constant_values_(NULL),
- use_counts_(arena, 256, kGrowableArrayMisc),
- raw_use_counts_(arena, 256, kGrowableArrayMisc),
+ use_counts_(arena->Adapter()),
+ raw_use_counts_(arena->Adapter()),
num_reachable_blocks_(0),
max_num_reachable_blocks_(0),
- dfs_order_(NULL),
- dfs_post_order_(NULL),
- dom_post_order_traversal_(NULL),
- topological_order_(nullptr),
- topological_order_loop_ends_(nullptr),
- topological_order_indexes_(nullptr),
- topological_order_loop_head_stack_(nullptr),
+ dfs_order_(arena->Adapter(kArenaAllocDfsPreOrder)),
+ dfs_post_order_(arena->Adapter(kArenaAllocDfsPostOrder)),
+ dom_post_order_traversal_(arena->Adapter(kArenaAllocDomPostOrder)),
+ topological_order_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
+ topological_order_loop_ends_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
+ topological_order_indexes_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
+ topological_order_loop_head_stack_(arena->Adapter(kArenaAllocTopologicalSortOrder)),
i_dom_list_(NULL),
def_block_matrix_(NULL),
temp_scoped_alloc_(),
@@ -95,13 +100,13 @@
temp_bit_vector_size_(0u),
temp_bit_vector_(nullptr),
temp_gvn_(),
- block_list_(arena, 100, kGrowableArrayBlockList),
+ block_list_(arena->Adapter(kArenaAllocBBList)),
try_block_addr_(NULL),
entry_block_(NULL),
exit_block_(NULL),
num_blocks_(0),
current_code_item_(NULL),
- dex_pc_to_block_map_(arena, 0, kGrowableArrayMisc),
+ dex_pc_to_block_map_(arena->Adapter()),
m_units_(arena->Adapter()),
method_stack_(arena->Adapter()),
current_method_(kInvalidEntry),
@@ -116,21 +121,38 @@
arena_(arena),
backward_branches_(0),
forward_branches_(0),
- compiler_temps_(arena, 6, kGrowableArrayMisc),
num_non_special_compiler_temps_(0),
- max_available_non_special_compiler_temps_(0),
+ max_available_special_compiler_temps_(1), // We only need the method ptr as a special temp for now.
+ requested_backend_temp_(false),
+ compiler_temps_committed_(false),
punt_to_interpreter_(false),
merged_df_flags_(0u),
- ifield_lowering_infos_(arena, 0u),
- sfield_lowering_infos_(arena, 0u),
- method_lowering_infos_(arena, 0u),
- gen_suspend_test_list_(arena, 0u) {
+ ifield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
+ sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
+ method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)),
+ gen_suspend_test_list_(arena->Adapter()) {
+ use_counts_.reserve(256);
+ raw_use_counts_.reserve(256);
+ block_list_.reserve(100);
try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */);
- max_available_special_compiler_temps_ = std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg))
- - std::abs(static_cast<int>(kVRegTempBaseReg));
+
+
+ if (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64) {
+ // X86 requires a temp to keep track of the method address.
+ // TODO For x86_64, addressing can be done with RIP. When that is implemented,
+ // this needs to be updated to reserve 0 temps for BE.
+ max_available_non_special_compiler_temps_ = cu_->target64 ? 2 : 1;
+ reserved_temps_for_backend_ = max_available_non_special_compiler_temps_;
+ } else {
+ // Other architectures do not have a known lower bound for non-special temps.
+ // We allow the update of the max to happen at BE initialization stage and simply set 0 for now.
+ max_available_non_special_compiler_temps_ = 0;
+ reserved_temps_for_backend_ = 0;
+ }
}
MIRGraph::~MIRGraph() {
+ STLDeleteElements(&block_list_);
STLDeleteElements(&m_units_);
}
@@ -165,50 +187,54 @@
if (insn == NULL) {
LOG(FATAL) << "Break split failed";
}
- BasicBlock* bottom_block = NewMemBB(kDalvikByteCode, num_blocks_++);
- block_list_.Insert(bottom_block);
+ BasicBlock* bottom_block = CreateNewBB(kDalvikByteCode);
bottom_block->start_offset = code_offset;
bottom_block->first_mir_insn = insn;
bottom_block->last_mir_insn = orig_block->last_mir_insn;
- /* If this block was terminated by a return, the flag needs to go with the bottom block */
+ /* If this block was terminated by a return, conditional branch or throw,
+ * the flag needs to go with the bottom block
+ */
bottom_block->terminated_by_return = orig_block->terminated_by_return;
orig_block->terminated_by_return = false;
+ bottom_block->conditional_branch = orig_block->conditional_branch;
+ orig_block->conditional_branch = false;
+
+ bottom_block->explicit_throw = orig_block->explicit_throw;
+ orig_block->explicit_throw = false;
+
/* Handle the taken path */
bottom_block->taken = orig_block->taken;
if (bottom_block->taken != NullBasicBlockId) {
orig_block->taken = NullBasicBlockId;
BasicBlock* bb_taken = GetBasicBlock(bottom_block->taken);
- bb_taken->predecessors->Delete(orig_block->id);
- bb_taken->predecessors->Insert(bottom_block->id);
+ bb_taken->ErasePredecessor(orig_block->id);
+ bb_taken->predecessors.push_back(bottom_block->id);
}
/* Handle the fallthrough path */
bottom_block->fall_through = orig_block->fall_through;
orig_block->fall_through = bottom_block->id;
- bottom_block->predecessors->Insert(orig_block->id);
+ bottom_block->predecessors.push_back(orig_block->id);
if (bottom_block->fall_through != NullBasicBlockId) {
BasicBlock* bb_fall_through = GetBasicBlock(bottom_block->fall_through);
- bb_fall_through->predecessors->Delete(orig_block->id);
- bb_fall_through->predecessors->Insert(bottom_block->id);
+ bb_fall_through->ErasePredecessor(orig_block->id);
+ bb_fall_through->predecessors.push_back(bottom_block->id);
}
/* Handle the successor list */
if (orig_block->successor_block_list_type != kNotUsed) {
bottom_block->successor_block_list_type = orig_block->successor_block_list_type;
- bottom_block->successor_blocks = orig_block->successor_blocks;
+ bottom_block->successor_blocks.swap(orig_block->successor_blocks);
orig_block->successor_block_list_type = kNotUsed;
- orig_block->successor_blocks = nullptr;
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_blocks);
- while (true) {
- SuccessorBlockInfo* successor_block_info = iterator.Next();
- if (successor_block_info == nullptr) break;
+ DCHECK(orig_block->successor_blocks.empty()); // Empty after the swap() above.
+ for (SuccessorBlockInfo* successor_block_info : bottom_block->successor_blocks) {
BasicBlock* bb = GetBasicBlock(successor_block_info->block);
if (bb != nullptr) {
- bb->predecessors->Delete(orig_block->id);
- bb->predecessors->Insert(bottom_block->id);
+ bb->ErasePredecessor(orig_block->id);
+ bb->predecessors.push_back(bottom_block->id);
}
}
}
@@ -232,9 +258,9 @@
DCHECK_EQ(insn->offset, bottom_block->start_offset);
DCHECK(static_cast<int>(insn->dalvikInsn.opcode) == kMirOpCheck ||
!MIR::DecodedInstruction::IsPseudoMirOp(insn->dalvikInsn.opcode));
- DCHECK_EQ(dex_pc_to_block_map_.Get(insn->offset), orig_block->id);
+ DCHECK_EQ(dex_pc_to_block_map_[insn->offset], orig_block->id);
MIR* p = insn;
- dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+ dex_pc_to_block_map_[p->offset] = bottom_block->id;
while (p != bottom_block->last_mir_insn) {
p = p->next;
DCHECK(p != nullptr);
@@ -247,8 +273,8 @@
* the first in a BasicBlock, we can't hit it here.
*/
if ((opcode == kMirOpCheck) || !MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
- DCHECK_EQ(dex_pc_to_block_map_.Get(p->offset), orig_block->id);
- dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+ DCHECK_EQ(dex_pc_to_block_map_[p->offset], orig_block->id);
+ dex_pc_to_block_map_[p->offset] = bottom_block->id;
}
}
@@ -265,12 +291,12 @@
*/
BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool split, bool create,
BasicBlock** immed_pred_block_p) {
- if (code_offset >= cu_->code_item->insns_size_in_code_units_) {
+ if (code_offset >= current_code_item_->insns_size_in_code_units_) {
return NULL;
}
- int block_id = dex_pc_to_block_map_.Get(code_offset);
- BasicBlock* bb = (block_id == 0) ? NULL : block_list_.Get(block_id);
+ int block_id = dex_pc_to_block_map_[code_offset];
+ BasicBlock* bb = GetBasicBlock(block_id);
if ((bb != NULL) && (bb->start_offset == code_offset)) {
// Does this containing block start with the desired instruction?
@@ -288,10 +314,9 @@
}
// Create a new block.
- bb = NewMemBB(kDalvikByteCode, num_blocks_++);
- block_list_.Insert(bb);
+ bb = CreateNewBB(kDalvikByteCode);
bb->start_offset = code_offset;
- dex_pc_to_block_map_.Put(bb->start_offset, bb->id);
+ dex_pc_to_block_map_[bb->start_offset] = bb->id;
return bb;
}
@@ -339,10 +364,10 @@
// (We don't want to ignore all monitor-exit catches since one could enclose a synchronized
// block in a try-block and catch the NPE, Error or Throwable and we should let it through;
// even though a throwing monitor-exit certainly indicates a bytecode error.)
- const Instruction* monitor_exit = Instruction::At(cu_->code_item->insns_ + monitor_exit_offset);
+ const Instruction* monitor_exit = Instruction::At(current_code_item_->insns_ + monitor_exit_offset);
DCHECK(monitor_exit->Opcode() == Instruction::MONITOR_EXIT);
int monitor_reg = monitor_exit->VRegA_11x();
- const Instruction* check_insn = Instruction::At(cu_->code_item->insns_ + catch_offset);
+ const Instruction* check_insn = Instruction::At(current_code_item_->insns_ + catch_offset);
DCHECK(check_insn->Opcode() == Instruction::MOVE_EXCEPTION);
if (check_insn->VRegA_11x() == monitor_reg) {
// Unexpected move-exception to the same register. Probably not the pattern we're looking for.
@@ -431,7 +456,7 @@
BasicBlock* taken_block = FindBlock(target, /* split */ true, /* create */ true,
/* immed_pred_block_p */ &cur_block);
cur_block->taken = taken_block->id;
- taken_block->predecessors->Insert(cur_block->id);
+ taken_block->predecessors.push_back(cur_block->id);
/* Always terminate the current block for conditional branches */
if (flags & Instruction::kContinue) {
@@ -454,7 +479,7 @@
/* immed_pred_block_p */
&cur_block);
cur_block->fall_through = fallthrough_block->id;
- fallthrough_block->predecessors->Insert(cur_block->id);
+ fallthrough_block->predecessors.push_back(cur_block->id);
} else if (code_ptr < code_end) {
FindBlock(cur_offset + width, /* split */ false, /* create */ true,
/* immed_pred_block_p */ NULL);
@@ -513,8 +538,7 @@
}
cur_block->successor_block_list_type =
(insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? kPackedSwitch : kSparseSwitch;
- cur_block->successor_blocks =
- new (arena_) GrowableArray<SuccessorBlockInfo*>(arena_, size, kGrowableArraySuccessorBlocks);
+ cur_block->successor_blocks.reserve(size);
for (i = 0; i < size; i++) {
BasicBlock* case_block = FindBlock(cur_offset + target_table[i], /* split */ true,
@@ -526,15 +550,15 @@
successor_block_info->key =
(insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
first_key + i : keyTable[i];
- cur_block->successor_blocks->Insert(successor_block_info);
- case_block->predecessors->Insert(cur_block->id);
+ cur_block->successor_blocks.push_back(successor_block_info);
+ case_block->predecessors.push_back(cur_block->id);
}
/* Fall-through case */
BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* split */ false,
/* create */ true, /* immed_pred_block_p */ NULL);
cur_block->fall_through = fallthrough_block->id;
- fallthrough_block->predecessors->Insert(cur_block->id);
+ fallthrough_block->predecessors.push_back(cur_block->id);
return cur_block;
}
@@ -567,8 +591,6 @@
}
if (cur_block->successor_block_list_type == kNotUsed) {
cur_block->successor_block_list_type = kCatch;
- cur_block->successor_blocks = new (arena_) GrowableArray<SuccessorBlockInfo*>(
- arena_, 2, kGrowableArraySuccessorBlocks);
}
catch_block->catch_entry = true;
if (kIsDebugBuild) {
@@ -578,17 +600,16 @@
(arena_->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
successor_block_info->block = catch_block->id;
successor_block_info->key = iterator.GetHandlerTypeIndex();
- cur_block->successor_blocks->Insert(successor_block_info);
- catch_block->predecessors->Insert(cur_block->id);
+ cur_block->successor_blocks.push_back(successor_block_info);
+ catch_block->predecessors.push_back(cur_block->id);
}
in_try_block = (cur_block->successor_block_list_type != kNotUsed);
}
if (!in_try_block && build_all_edges) {
- BasicBlock* eh_block = NewMemBB(kExceptionHandling, num_blocks_++);
+ BasicBlock* eh_block = CreateNewBB(kExceptionHandling);
cur_block->taken = eh_block->id;
- block_list_.Insert(eh_block);
eh_block->start_offset = cur_offset;
- eh_block->predecessors->Insert(cur_block->id);
+ eh_block->predecessors.push_back(cur_block->id);
}
if (is_throw) {
@@ -631,11 +652,10 @@
* Note also that the dex_pc_to_block_map_ entry for the potentially
* throwing instruction will refer to the original basic block.
*/
- BasicBlock* new_block = NewMemBB(kDalvikByteCode, num_blocks_++);
- block_list_.Insert(new_block);
+ BasicBlock* new_block = CreateNewBB(kDalvikByteCode);
new_block->start_offset = insn->offset;
cur_block->fall_through = new_block->id;
- new_block->predecessors->Insert(cur_block->id);
+ new_block->predecessors.push_back(cur_block->id);
MIR* new_insn = NewMIR();
*new_insn = *insn;
insn->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpCheck);
@@ -663,8 +683,8 @@
// TODO: need to rework expansion of block list & try_block_addr when inlining activated.
// TUNING: use better estimate of basic blocks for following resize.
- block_list_.Resize(block_list_.Size() + current_code_item_->insns_size_in_code_units_);
- dex_pc_to_block_map_.SetSize(dex_pc_to_block_map_.Size() + current_code_item_->insns_size_in_code_units_);
+ block_list_.reserve(block_list_.size() + current_code_item_->insns_size_in_code_units_);
+ dex_pc_to_block_map_.resize(dex_pc_to_block_map_.size() + current_code_item_->insns_size_in_code_units_);
// TODO: replace with explicit resize routine. Using automatic extension side effect for now.
try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_);
@@ -676,14 +696,11 @@
DCHECK(exit_block_ == NULL);
DCHECK_EQ(num_blocks_, 0U);
// Use id 0 to represent a null block.
- BasicBlock* null_block = NewMemBB(kNullBlock, num_blocks_++);
+ BasicBlock* null_block = CreateNewBB(kNullBlock);
DCHECK_EQ(null_block->id, NullBasicBlockId);
null_block->hidden = true;
- block_list_.Insert(null_block);
- entry_block_ = NewMemBB(kEntryBlock, num_blocks_++);
- block_list_.Insert(entry_block_);
- exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
- block_list_.Insert(exit_block_);
+ entry_block_ = CreateNewBB(kEntryBlock);
+ exit_block_ = CreateNewBB(kExitBlock);
// TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
cu_->dex_file = &dex_file;
cu_->class_def_idx = class_def_idx;
@@ -691,12 +708,6 @@
cu_->access_flags = access_flags;
cu_->invoke_type = invoke_type;
cu_->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
- cu_->num_ins = current_code_item_->ins_size_;
- cu_->num_regs = current_code_item_->registers_size_ - cu_->num_ins;
- cu_->num_outs = current_code_item_->outs_size_;
- cu_->num_dalvik_registers = current_code_item_->registers_size_;
- cu_->insns = current_code_item_->insns_;
- cu_->code_item = current_code_item_;
} else {
UNIMPLEMENTED(FATAL) << "Nested inlining not implemented.";
/*
@@ -706,13 +717,12 @@
}
/* Current block to record parsed instructions */
- BasicBlock* cur_block = NewMemBB(kDalvikByteCode, num_blocks_++);
+ BasicBlock* cur_block = CreateNewBB(kDalvikByteCode);
DCHECK_EQ(current_offset_, 0U);
cur_block->start_offset = current_offset_;
- block_list_.Insert(cur_block);
// TODO: for inlining support, insert at the insert point rather than entry block.
entry_block_->fall_through = cur_block->id;
- cur_block->predecessors->Insert(entry_block_->id);
+ cur_block->predecessors.push_back(entry_block_->id);
/* Identify code range in try blocks and set up the empty catch blocks */
ProcessTryCatchBlocks();
@@ -730,7 +740,7 @@
opcode_count_[static_cast<int>(opcode)]++;
}
- int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode);
+ int flags = insn->dalvikInsn.FlagsOf();
int verify_flags = Instruction::VerifyFlagsOf(insn->dalvikInsn.opcode);
uint64_t df_flags = GetDataFlowAttributes(insn);
@@ -770,7 +780,7 @@
}
// Associate the starting dex_pc for this opcode with its containing basic block.
- dex_pc_to_block_map_.Put(insn->offset, cur_block->id);
+ dex_pc_to_block_map_[insn->offset] = cur_block->id;
code_ptr += width;
@@ -780,7 +790,7 @@
} else if (flags & Instruction::kReturn) {
cur_block->terminated_by_return = true;
cur_block->fall_through = exit_block_->id;
- exit_block_->predecessors->Insert(cur_block->id);
+ exit_block_->predecessors.push_back(cur_block->id);
/*
* Terminate the current block if there are instructions
* afterwards.
@@ -829,7 +839,7 @@
if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) {
cur_block->fall_through = next_block->id;
- next_block->predecessors->Insert(cur_block->id);
+ next_block->predecessors.push_back(cur_block->id);
}
cur_block = next_block;
}
@@ -894,7 +904,7 @@
int idx;
for (idx = 0; idx < num_blocks; idx++) {
- int block_idx = all_blocks ? idx : dfs_order_->Get(idx);
+ int block_idx = all_blocks ? idx : dfs_order_[idx];
BasicBlock* bb = GetBasicBlock(block_idx);
if (bb == NULL) continue;
if (bb->block_type == kDead) continue;
@@ -911,27 +921,7 @@
bb->first_mir_insn ? " | " : " ");
for (mir = bb->first_mir_insn; mir; mir = mir->next) {
int opcode = mir->dalvikInsn.opcode;
- if (opcode > kMirOpSelect && opcode < kMirOpLast) {
- if (opcode == kMirOpConstVector) {
- fprintf(file, " {%04x %s %d %d %d %d %d %d\\l}%s\\\n", mir->offset,
- extended_mir_op_names_[kMirOpConstVector - kMirOpFirst],
- mir->dalvikInsn.vA,
- mir->dalvikInsn.vB,
- mir->dalvikInsn.arg[0],
- mir->dalvikInsn.arg[1],
- mir->dalvikInsn.arg[2],
- mir->dalvikInsn.arg[3],
- mir->next ? " | " : " ");
- } else {
- fprintf(file, " {%04x %s %d %d %d\\l}%s\\\n", mir->offset,
- extended_mir_op_names_[opcode - kMirOpFirst],
- mir->dalvikInsn.vA,
- mir->dalvikInsn.vB,
- mir->dalvikInsn.vC,
- mir->next ? " | " : " ");
- }
- } else {
- fprintf(file, " {%04x %s %s %s %s\\l}%s\\\n", mir->offset,
+ fprintf(file, " {%04x %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset,
mir->ssa_rep ? GetDalvikDisassembly(mir) :
!MIR::DecodedInstruction::IsPseudoMirOp(opcode) ?
Instruction::Name(mir->dalvikInsn.opcode) :
@@ -939,8 +929,10 @@
(mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ",
(mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ",
(mir->optimization_flags & MIR_IGNORE_SUSPEND_CHECK) != 0 ? " no_suspendcheck" : " ",
+ (mir->optimization_flags & MIR_STORE_NON_TEMPORAL) != 0 ? " non_temporal" : " ",
+ (mir->optimization_flags & MIR_CALLEE) != 0 ? " inlined" : " ",
+ (mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) != 0 ? " no_clinit" : " ",
mir->next ? " | " : " ");
- }
}
fprintf(file, " }\"];\n\n");
} else if (bb->block_type == kExceptionHandling) {
@@ -968,23 +960,17 @@
fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n",
bb->start_offset, bb->id,
(bb->successor_block_list_type == kCatch) ? "Mrecord" : "record");
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
- SuccessorBlockInfo* successor_block_info = iterator.Next();
+ int last_succ_id = static_cast<int>(bb->successor_blocks.size() - 1u);
int succ_id = 0;
- while (true) {
- if (successor_block_info == NULL) break;
-
+ for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
BasicBlock* dest_block = GetBasicBlock(successor_block_info->block);
- SuccessorBlockInfo *next_successor_block_info = iterator.Next();
-
fprintf(file, " {<f%d> %04x: %04x\\l}%s\\\n",
- succ_id++,
+ succ_id,
successor_block_info->key,
dest_block->start_offset,
- (next_successor_block_info != NULL) ? " | " : " ");
-
- successor_block_info = next_successor_block_info;
+ (succ_id != last_succ_id) ? " | " : " ");
+ ++succ_id;
}
fprintf(file, " }\"];\n\n");
@@ -993,13 +979,8 @@
block_name1, bb->start_offset, bb->id);
// Link the successor pseudo-block with all of its potential targets.
- GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
-
succ_id = 0;
- while (true) {
- SuccessorBlockInfo* successor_block_info = iter.Next();
- if (successor_block_info == NULL) break;
-
+ for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
BasicBlock* dest_block = GetBasicBlock(successor_block_info->block);
GetBlockName(dest_block, block_name2);
@@ -1172,7 +1153,7 @@
}
// Remove the BB information and also find the after_list.
- for (MIR* mir = first_list_mir; mir != last_list_mir; mir = mir->next) {
+ for (MIR* mir = first_list_mir; mir != last_list_mir->next; mir = mir->next) {
mir->bb = NullBasicBlockId;
}
@@ -1210,6 +1191,198 @@
return next_mir;
}
+static void FillTypeSizeString(uint32_t type_size, std::string* decoded_mir) {
+ DCHECK(decoded_mir != nullptr);
+ OpSize type = static_cast<OpSize>(type_size >> 16);
+ uint16_t vect_size = (type_size & 0xFFFF);
+
+ // Now print the type and vector size.
+ std::stringstream ss;
+ ss << " (type:";
+ ss << type;
+ ss << " vectsize:";
+ ss << vect_size;
+ ss << ")";
+
+ decoded_mir->append(ss.str());
+}
+
+void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir) {
+ DCHECK(decoded_mir != nullptr);
+ int opcode = mir->dalvikInsn.opcode;
+ SSARepresentation* ssa_rep = mir->ssa_rep;
+ int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0;
+ int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0;
+
+ decoded_mir->append(extended_mir_op_names_[opcode - kMirOpFirst]);
+
+ switch (opcode) {
+ case kMirOpPhi: {
+ if (defs > 0 && uses > 0) {
+ BasicBlockId* incoming = mir->meta.phi_incoming;
+ decoded_mir->append(StringPrintf(" %s = (%s",
+ GetSSANameWithConst(ssa_rep->defs[0], true).c_str(),
+ GetSSANameWithConst(ssa_rep->uses[0], true).c_str()));
+ decoded_mir->append(StringPrintf(":%d", incoming[0]));
+ for (int i = 1; i < uses; i++) {
+ decoded_mir->append(StringPrintf(", %s:%d", GetSSANameWithConst(ssa_rep->uses[i], true).c_str(), incoming[i]));
+ }
+ decoded_mir->append(")");
+ }
+ break;
+ }
+ case kMirOpCopy:
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(" ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+ if (defs > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+ }
+ decoded_mir->append(" = ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false));
+ if (uses > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false));
+ }
+ } else {
+ decoded_mir->append(StringPrintf(" v%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ }
+ break;
+ case kMirOpFusedCmplFloat:
+ case kMirOpFusedCmpgFloat:
+ case kMirOpFusedCmplDouble:
+ case kMirOpFusedCmpgDouble:
+ case kMirOpFusedCmpLong:
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(" ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[0], false));
+ for (int i = 1; i < uses; i++) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[i], false));
+ }
+ } else {
+ decoded_mir->append(StringPrintf(" v%d, v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ }
+ break;
+ case kMirOpMoveVector:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedAddition:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d + vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedMultiply:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d * vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedSubtract:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedAnd:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d & vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedOr:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d \\| vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedXor:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d ^ vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedShiftLeft:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d \\<\\< %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedUnsignedShiftRight:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedSignedShiftRight:
+ decoded_mir->append(StringPrintf(" vect%d = vect%d \\>\\> %d", mir->dalvikInsn.vA, mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpConstVector:
+ decoded_mir->append(StringPrintf(" vect%d = %x, %x, %x, %x", mir->dalvikInsn.vA, mir->dalvikInsn.arg[0],
+ mir->dalvikInsn.arg[1], mir->dalvikInsn.arg[2], mir->dalvikInsn.arg[3]));
+ break;
+ case kMirOpPackedSet:
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(StringPrintf(" vect%d = %s", mir->dalvikInsn.vA,
+ GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
+ if (uses > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false));
+ }
+ } else {
+ decoded_mir->append(StringPrintf(" vect%d = v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ }
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedAddReduce:
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(" ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+ if (defs > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+ }
+ decoded_mir->append(StringPrintf(" = vect%d + %s", mir->dalvikInsn.vB,
+ GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
+ if (uses > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->uses[1], false));
+ }
+ } else {
+ decoded_mir->append(StringPrintf("v%d = vect%d + v%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB, mir->dalvikInsn.vA));
+ }
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpPackedReduce:
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(" ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[0], false));
+ if (defs > 1) {
+ decoded_mir->append(", ");
+ decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false));
+ }
+ decoded_mir->append(StringPrintf(" = vect%d", mir->dalvikInsn.vB));
+ } else {
+ decoded_mir->append(StringPrintf(" v%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ }
+ FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir);
+ break;
+ case kMirOpReserveVectorRegisters:
+ case kMirOpReturnVectorRegisters:
+ decoded_mir->append(StringPrintf(" vect%d - vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB));
+ break;
+ case kMirOpMemBarrier: {
+ decoded_mir->append(" type:");
+ std::stringstream ss;
+ ss << static_cast<MemBarrierKind>(mir->dalvikInsn.vA);
+ decoded_mir->append(ss.str());
+ break;
+ }
+ case kMirOpPackedArrayGet:
+ case kMirOpPackedArrayPut:
+ decoded_mir->append(StringPrintf(" vect%d", mir->dalvikInsn.vA));
+ if (ssa_rep != nullptr) {
+ decoded_mir->append(StringPrintf(", %s[%s]",
+ GetSSANameWithConst(ssa_rep->uses[0], false).c_str(),
+ GetSSANameWithConst(ssa_rep->uses[1], false).c_str()));
+ } else {
+ decoded_mir->append(StringPrintf(", v%d[v%d]", mir->dalvikInsn.vB, mir->dalvikInsn.vC));
+ }
+ FillTypeSizeString(mir->dalvikInsn.arg[0], decoded_mir);
+ break;
+ default:
+ break;
+ }
+}
+
char* MIRGraph::GetDalvikDisassembly(const MIR* mir) {
MIR::DecodedInstruction insn = mir->dalvikInsn;
std::string str;
@@ -1219,120 +1392,114 @@
bool nop = false;
SSARepresentation* ssa_rep = mir->ssa_rep;
Instruction::Format dalvik_format = Instruction::k10x; // Default to no-operand format.
- int defs = (ssa_rep != NULL) ? ssa_rep->num_defs : 0;
- int uses = (ssa_rep != NULL) ? ssa_rep->num_uses : 0;
- // Handle special cases.
+ // Handle special cases that recover the original dalvik instruction.
if ((opcode == kMirOpCheck) || (opcode == kMirOpCheckPart2)) {
str.append(extended_mir_op_names_[opcode - kMirOpFirst]);
str.append(": ");
// Recover the original Dex instruction.
insn = mir->meta.throw_insn->dalvikInsn;
ssa_rep = mir->meta.throw_insn->ssa_rep;
- defs = ssa_rep->num_defs;
- uses = ssa_rep->num_uses;
opcode = insn.opcode;
} else if (opcode == kMirOpNop) {
str.append("[");
- // Recover original opcode.
- insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode();
- opcode = insn.opcode;
+ if (mir->offset < current_code_item_->insns_size_in_code_units_) {
+ // Recover original opcode.
+ insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode();
+ opcode = insn.opcode;
+ }
nop = true;
}
+ int defs = (ssa_rep != NULL) ? ssa_rep->num_defs : 0;
+ int uses = (ssa_rep != NULL) ? ssa_rep->num_uses : 0;
if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
- str.append(extended_mir_op_names_[opcode - kMirOpFirst]);
+ // Note that this does not check the MIR's opcode in all cases. In cases where it
+ // recovered dalvik instruction, it uses opcode of that instead of the extended one.
+ DisassembleExtendedInstr(mir, &str);
} else {
dalvik_format = Instruction::FormatOf(insn.opcode);
- flags = Instruction::FlagsOf(insn.opcode);
+ flags = insn.FlagsOf();
str.append(Instruction::Name(insn.opcode));
- }
- if (opcode == kMirOpPhi) {
- BasicBlockId* incoming = mir->meta.phi_incoming;
- str.append(StringPrintf(" %s = (%s",
- GetSSANameWithConst(ssa_rep->defs[0], true).c_str(),
- GetSSANameWithConst(ssa_rep->uses[0], true).c_str()));
- str.append(StringPrintf(":%d", incoming[0]));
- int i;
- for (i = 1; i < uses; i++) {
- str.append(StringPrintf(", %s:%d",
- GetSSANameWithConst(ssa_rep->uses[i], true).c_str(),
- incoming[i]));
- }
- str.append(")");
- } else if ((flags & Instruction::kBranch) != 0) {
- // For branches, decode the instructions to print out the branch targets.
- int offset = 0;
- switch (dalvik_format) {
- case Instruction::k21t:
- str.append(StringPrintf(" %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str()));
- offset = insn.vB;
- break;
- case Instruction::k22t:
- str.append(StringPrintf(" %s, %s,", GetSSANameWithConst(ssa_rep->uses[0], false).c_str(),
- GetSSANameWithConst(ssa_rep->uses[1], false).c_str()));
- offset = insn.vC;
- break;
- case Instruction::k10t:
- case Instruction::k20t:
- case Instruction::k30t:
- offset = insn.vA;
- break;
- default:
- LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode;
- }
- str.append(StringPrintf(" 0x%x (%c%x)", mir->offset + offset,
- offset > 0 ? '+' : '-', offset > 0 ? offset : -offset));
- } else {
// For invokes-style formats, treat wide regs as a pair of singles.
bool show_singles = ((dalvik_format == Instruction::k35c) ||
(dalvik_format == Instruction::k3rc));
if (defs != 0) {
- str.append(StringPrintf(" %s", GetSSANameWithConst(ssa_rep->defs[0], false).c_str()));
+ str.append(" ");
+ str.append(GetSSANameWithConst(ssa_rep->defs[0], false));
+ if (defs > 1) {
+ str.append(", ");
+ str.append(GetSSANameWithConst(ssa_rep->defs[1], false));
+ }
if (uses != 0) {
str.append(", ");
}
}
for (int i = 0; i < uses; i++) {
- str.append(
- StringPrintf(" %s", GetSSANameWithConst(ssa_rep->uses[i], show_singles).c_str()));
+ str.append(" ");
+ str.append(GetSSANameWithConst(ssa_rep->uses[i], show_singles));
if (!show_singles && (reg_location_ != NULL) && reg_location_[i].wide) {
// For the listing, skip the high sreg.
i++;
}
- if (i != (uses -1)) {
+ if (i != (uses - 1)) {
str.append(",");
}
}
+
switch (dalvik_format) {
case Instruction::k11n: // Add one immediate from vB.
case Instruction::k21s:
case Instruction::k31i:
case Instruction::k21h:
- str.append(StringPrintf(", #%d", insn.vB));
+ str.append(StringPrintf(", #0x%x", insn.vB));
break;
case Instruction::k51l: // Add one wide immediate.
str.append(StringPrintf(", #%" PRId64, insn.vB_wide));
break;
case Instruction::k21c: // One register, one string/type/method index.
case Instruction::k31c:
- str.append(StringPrintf(", index #%d", insn.vB));
+ str.append(StringPrintf(", index #0x%x", insn.vB));
break;
case Instruction::k22c: // Two registers, one string/type/method index.
- str.append(StringPrintf(", index #%d", insn.vC));
+ str.append(StringPrintf(", index #0x%x", insn.vC));
break;
case Instruction::k22s: // Add one immediate from vC.
case Instruction::k22b:
- str.append(StringPrintf(", #%d", insn.vC));
+ str.append(StringPrintf(", #0x%x", insn.vC));
break;
- default: {
+ default:
// Nothing left to print.
- }
+ break;
}
- }
- if (nop) {
- str.append("]--optimized away");
+
+ if ((flags & Instruction::kBranch) != 0) {
+ // For branches, decode the instructions to print out the branch targets.
+ int offset = 0;
+ switch (dalvik_format) {
+ case Instruction::k21t:
+ offset = insn.vB;
+ break;
+ case Instruction::k22t:
+ offset = insn.vC;
+ break;
+ case Instruction::k10t:
+ case Instruction::k20t:
+ case Instruction::k30t:
+ offset = insn.vA;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected branch format " << dalvik_format << " from " << insn.opcode;
+ break;
+ }
+ str.append(StringPrintf(", 0x%x (%c%x)", mir->offset + offset,
+ offset > 0 ? '+' : '-', offset > 0 ? offset : -offset));
+ }
+
+ if (nop) {
+ str.append("]--optimized away");
+ }
}
int length = str.length() + 1;
ret = static_cast<char*>(arena_->Alloc(length, kArenaAllocDFInfo));
@@ -1355,7 +1522,12 @@
// TODO: This value is needed for LLVM and debugging. Currently, we compute this and then copy to
// the arena. We should be smarter and just place straight into the arena, or compute the
// value more lazily.
- return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ int vreg = SRegToVReg(ssa_reg);
+ if (vreg >= static_cast<int>(GetFirstTempVR())) {
+ return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ } else {
+ return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ }
}
// Similar to GetSSAName, but if ssa name represents an immediate show that as well.
@@ -1373,7 +1545,12 @@
ConstantValue(reg_location_[ssa_reg]));
}
} else {
- return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ int vreg = SRegToVReg(ssa_reg);
+ if (vreg >= static_cast<int>(GetFirstTempVR())) {
+ return StringPrintf("t%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ } else {
+ return StringPrintf("v%d_%d", SRegToVReg(ssa_reg), GetSSASubscript(ssa_reg));
+ }
}
}
@@ -1406,7 +1583,6 @@
/* Debug Utility - dump a compilation unit */
void MIRGraph::DumpMIRGraph() {
- BasicBlock* bb;
const char* block_type_names[] = {
"Null Block",
"Entry Block",
@@ -1417,13 +1593,10 @@
};
LOG(INFO) << "Compiling " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
- LOG(INFO) << cu_->insns << " insns";
+ LOG(INFO) << GetInsns(0) << " insns";
LOG(INFO) << GetNumBlocks() << " blocks in total";
- GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
- while (true) {
- bb = iterator.Next();
- if (bb == NULL) break;
+ for (BasicBlock* bb : block_list_) {
LOG(INFO) << StringPrintf("Block %d (%s) (insn %04x - %04x%s)",
bb->id,
block_type_names[bb->block_type],
@@ -1481,15 +1654,10 @@
// Allocate a new basic block.
BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) {
- BasicBlock* bb = new (arena_) BasicBlock();
+ BasicBlock* bb = new (arena_) BasicBlock(block_id, block_type, arena_);
- bb->block_type = block_type;
- bb->id = block_id;
// TUNING: better estimate of the exit block predecessors?
- bb->predecessors = new (arena_) GrowableArray<BasicBlockId>(arena_,
- (block_type == kExitBlock) ? 2048 : 2,
- kGrowableArrayPredecessors);
- bb->successor_block_list_type = kNotUsed;
+ bb->predecessors.reserve((block_type == kExitBlock) ? 2048 : 2);
block_id_map_.Put(block_id, block_id);
return bb;
}
@@ -1502,24 +1670,20 @@
void MIRGraph::InitializeMethodUses() {
// The gate starts by initializing the use counts.
int num_ssa_regs = GetNumSSARegs();
- use_counts_.Resize(num_ssa_regs + 32);
- raw_use_counts_.Resize(num_ssa_regs + 32);
- // Initialize list.
- for (int i = 0; i < num_ssa_regs; i++) {
- use_counts_.Insert(0);
- raw_use_counts_.Insert(0);
- }
+ use_counts_.clear();
+ use_counts_.reserve(num_ssa_regs + 32);
+ use_counts_.resize(num_ssa_regs, 0u);
+ raw_use_counts_.clear();
+ raw_use_counts_.reserve(num_ssa_regs + 32);
+ raw_use_counts_.resize(num_ssa_regs, 0u);
}
void MIRGraph::SSATransformationStart() {
DCHECK(temp_scoped_alloc_.get() == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- temp_bit_vector_size_ = cu_->num_dalvik_registers;
+ temp_bit_vector_size_ = GetNumOfCodeAndTempVRs();
temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapRegisterV);
-
- // Update the maximum number of reachable blocks.
- max_num_reachable_blocks_ = num_reachable_blocks_;
}
void MIRGraph::SSATransformationEnd() {
@@ -1532,6 +1696,36 @@
temp_bit_vector_ = nullptr;
DCHECK(temp_scoped_alloc_.get() != nullptr);
temp_scoped_alloc_.reset();
+
+ // Update the maximum number of reachable blocks.
+ max_num_reachable_blocks_ = num_reachable_blocks_;
+}
+
+size_t MIRGraph::GetNumDalvikInsns() const {
+ size_t cumulative_size = 0u;
+ bool counted_current_item = false;
+ const uint8_t size_for_null_code_item = 2u;
+
+ for (auto it : m_units_) {
+ const DexFile::CodeItem* code_item = it->GetCodeItem();
+ // Even if the code item is null, we still count non-zero value so that
+ // each m_unit is counted as having impact.
+ cumulative_size += (code_item == nullptr ?
+ size_for_null_code_item : code_item->insns_size_in_code_units_);
+ if (code_item == current_code_item_) {
+ counted_current_item = true;
+ }
+ }
+
+ // If the current code item was not counted yet, count it now.
+ // This can happen for example in unit tests where some fields like m_units_
+ // are not initialized.
+ if (counted_current_item == false) {
+ cumulative_size += (current_code_item_ == nullptr ?
+ size_for_null_code_item : current_code_item_->insns_size_in_code_units_);
+ }
+
+ return cumulative_size;
}
static BasicBlock* SelectTopologicalSortOrderFallBack(
@@ -1600,9 +1794,9 @@
tmp_stack->pop_back();
BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id);
DCHECK(current_bb != nullptr);
- GrowableArray<BasicBlockId>::Iterator iter(current_bb->predecessors);
- BasicBlock* pred_bb = mir_graph->GetBasicBlock(iter.Next());
- for ( ; pred_bb != nullptr; pred_bb = mir_graph->GetBasicBlock(iter.Next())) {
+ for (BasicBlockId pred_id : current_bb->predecessors) {
+ BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
+ DCHECK(pred_bb != nullptr);
if (!pred_bb->visited && !reachable->IsBitSet(pred_bb->id)) {
reachable->SetBit(pred_bb->id);
tmp_stack->push_back(pred_bb->id);
@@ -1623,36 +1817,27 @@
loop_exit_blocks.ClearAllBits();
// Count the number of blocks to process and add the entry block(s).
- GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
unsigned int num_blocks_to_process = 0u;
- for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) {
+ for (BasicBlock* bb : block_list_) {
if (bb->hidden == true) {
continue;
}
num_blocks_to_process += 1u;
- if (bb->predecessors->Size() == 0u) {
+ if (bb->predecessors.size() == 0u) {
// Add entry block to the queue.
q.push(bb);
}
}
- // Create the topological order if need be.
- if (topological_order_ == nullptr) {
- topological_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, num_blocks);
- topological_order_loop_ends_ = new (arena_) GrowableArray<uint16_t>(arena_, num_blocks);
- topological_order_indexes_ = new (arena_) GrowableArray<uint16_t>(arena_, num_blocks);
- }
- topological_order_->Reset();
- topological_order_loop_ends_->Reset();
- topological_order_indexes_->Reset();
- topological_order_loop_ends_->Resize(num_blocks);
- topological_order_indexes_->Resize(num_blocks);
- for (BasicBlockId i = 0; i != num_blocks; ++i) {
- topological_order_loop_ends_->Insert(0u);
- topological_order_indexes_->Insert(static_cast<uint16_t>(-1));
- }
+ // Clear the topological order arrays.
+ topological_order_.clear();
+ topological_order_.reserve(num_blocks);
+ topological_order_loop_ends_.clear();
+ topological_order_loop_ends_.resize(num_blocks, 0u);
+ topological_order_indexes_.clear();
+ topological_order_indexes_.resize(num_blocks, static_cast<uint16_t>(-1));
// Mark all blocks as unvisited.
ClearAllVisitedFlags();
@@ -1675,8 +1860,8 @@
if (bb->visited) {
// Loop head: it was already processed, mark end and copy exit blocks to the queue.
DCHECK(q.empty()) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
- uint16_t idx = static_cast<uint16_t>(topological_order_->Size());
- topological_order_loop_ends_->Put(topological_order_indexes_->Get(bb->id), idx);
+ uint16_t idx = static_cast<uint16_t>(topological_order_.size());
+ topological_order_loop_ends_[topological_order_indexes_[bb->id]] = idx;
DCHECK_EQ(loop_head_stack.back(), bb->id);
loop_head_stack.pop_back();
ArenaBitVector* reachable =
@@ -1719,15 +1904,16 @@
continue;
}
- GrowableArray<BasicBlockId>::Iterator pred_iter(candidate->predecessors);
- BasicBlock* pred_bb = GetBasicBlock(pred_iter.Next());
- for ( ; pred_bb != nullptr; pred_bb = GetBasicBlock(pred_iter.Next())) {
+ for (BasicBlockId pred_id : candidate->predecessors) {
+ BasicBlock* pred_bb = GetBasicBlock(pred_id);
+ DCHECK(pred_bb != nullptr);
if (pred_bb != candidate && !pred_bb->visited &&
!pred_bb->dominators->IsBitSet(candidate->id)) {
- break; // Keep non-null pred_bb to indicate failure.
+ candidate = nullptr; // Set candidate to null to indicate failure.
+ break;
}
}
- if (pred_bb == nullptr) {
+ if (candidate != nullptr) {
bb = candidate;
break;
}
@@ -1747,9 +1933,9 @@
bb->visited = true;
// Now add the basic block.
- uint16_t idx = static_cast<uint16_t>(topological_order_->Size());
- topological_order_indexes_->Put(bb->id, idx);
- topological_order_->Insert(bb->id);
+ uint16_t idx = static_cast<uint16_t>(topological_order_.size());
+ topological_order_indexes_[bb->id] = idx;
+ topological_order_.push_back(bb->id);
// Update visited_cnt_values for children.
ChildBlockIterator succIter(bb, this);
@@ -1761,7 +1947,7 @@
// One more predecessor was visited.
visited_cnt_values[successor->id] += 1u;
- if (visited_cnt_values[successor->id] == successor->predecessors->Size()) {
+ if (visited_cnt_values[successor->id] == successor->predecessors.size()) {
if (loop_head_stack.empty() ||
loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(successor->id)) {
q.push(successor);
@@ -1774,8 +1960,8 @@
}
// Prepare the loop head stack for iteration.
- topological_order_loop_head_stack_ =
- new (arena_) GrowableArray<std::pair<uint16_t, bool>>(arena_, max_nested_loops);
+ topological_order_loop_head_stack_.clear();
+ topological_order_loop_head_stack_.reserve(max_nested_loops);
}
bool BasicBlock::IsExceptionBlock() const {
@@ -1792,8 +1978,8 @@
return false;
int idx;
- for (idx = gen_suspend_test_list_.Size() - 1; idx >= 0; idx--) {
- BasicBlock* bb = gen_suspend_test_list_.Get(idx);
+ for (idx = gen_suspend_test_list_.size() - 1; idx >= 0; idx--) {
+ BasicBlock* bb = gen_suspend_test_list_[idx];
if (bb == source)
return true; // The block has been inserted by a suspend check before.
if (source->dominators->IsBitSet(bb->id) && bb->dominators->IsBitSet(target_id))
@@ -1809,7 +1995,7 @@
// Check if we actually do have successors.
if (basic_block_ != 0 && basic_block_->successor_block_list_type != kNotUsed) {
have_successors_ = true;
- successor_iter_.Reset(basic_block_->successor_blocks);
+ successor_iter_ = basic_block_->successor_blocks.cbegin();
}
}
@@ -1842,9 +2028,10 @@
// We visited both taken and fallthrough. Now check if we have successors we need to visit.
if (have_successors_ == true) {
// Get information about next successor block.
- for (SuccessorBlockInfo* successor_block_info = successor_iter_.Next();
- successor_block_info != nullptr;
- successor_block_info = successor_iter_.Next()) {
+ auto end = basic_block_->successor_blocks.cend();
+ while (successor_iter_ != end) {
+ SuccessorBlockInfo* successor_block_info = *successor_iter_;
+ ++successor_iter_;
// If block was replaced by zero block, take next one.
if (successor_block_info->block != NullBasicBlockId) {
return mir_graph_->GetBasicBlock(successor_block_info->block);
@@ -1875,17 +2062,12 @@
result_bb->successor_block_list_type = successor_block_list_type;
if (result_bb->successor_block_list_type != kNotUsed) {
- size_t size = successor_blocks->Size();
- result_bb->successor_blocks = new (arena) GrowableArray<SuccessorBlockInfo*>(arena, size, kGrowableArraySuccessorBlocks);
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(successor_blocks);
- while (true) {
- SuccessorBlockInfo* sbi_old = iterator.Next();
- if (sbi_old == nullptr) {
- break;
- }
- SuccessorBlockInfo* sbi_new = static_cast<SuccessorBlockInfo*>(arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+ result_bb->successor_blocks.reserve(successor_blocks.size());
+ for (SuccessorBlockInfo* sbi_old : successor_blocks) {
+ SuccessorBlockInfo* sbi_new = static_cast<SuccessorBlockInfo*>(
+ arena->Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
memcpy(sbi_new, sbi_old, sizeof(SuccessorBlockInfo));
- result_bb->successor_blocks->Insert(sbi_new);
+ result_bb->successor_blocks.push_back(sbi_new);
}
}
@@ -1934,6 +2116,10 @@
case Instruction::IPUT_SHORT:
case Instruction::IPUT_QUICK:
case Instruction::IPUT_OBJECT_QUICK:
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ case Instruction::IPUT_BYTE_QUICK:
+ case Instruction::IPUT_CHAR_QUICK:
+ case Instruction::IPUT_SHORT_QUICK:
case Instruction::APUT:
case Instruction::APUT_OBJECT:
case Instruction::APUT_BOOLEAN:
@@ -2040,14 +2226,10 @@
first_mir_insn = nullptr;
last_mir_insn = nullptr;
- GrowableArray<BasicBlockId>::Iterator iterator(predecessors);
-
MIRGraph* mir_graph = c_unit->mir_graph.get();
- while (true) {
- BasicBlock* pred_bb = mir_graph->GetBasicBlock(iterator.Next());
- if (pred_bb == nullptr) {
- break;
- }
+ for (BasicBlockId pred_id : predecessors) {
+ BasicBlock* pred_bb = mir_graph->GetBasicBlock(pred_id);
+ DCHECK(pred_bb != nullptr);
// Sadly we have to go through the children by hand here.
pred_bb->ReplaceChild(id, NullBasicBlockId);
@@ -2057,9 +2239,14 @@
ChildBlockIterator successorChildIter(this, mir_graph);
for (BasicBlock* childPtr = successorChildIter.Next(); childPtr != 0; childPtr = successorChildIter.Next()) {
- // Replace child with null child.
- childPtr->predecessors->Delete(id);
+ // Erase this predecessor from child.
+ childPtr->ErasePredecessor(id);
}
+
+ // Remove link to children.
+ taken = NullBasicBlockId;
+ fall_through = NullBasicBlockId;
+ successor_block_list_type = kNotUsed;
}
bool BasicBlock::IsSSALiveOut(const CompilationUnit* c_unit, int ssa_reg) {
@@ -2119,12 +2306,7 @@
}
if (successor_block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(successor_blocks);
- while (true) {
- SuccessorBlockInfo* successor_block_info = iterator.Next();
- if (successor_block_info == nullptr) {
- break;
- }
+ for (SuccessorBlockInfo* successor_block_info : successor_blocks) {
if (successor_block_info->block == old_bb) {
successor_block_info->block = new_bb;
found = true;
@@ -2135,28 +2317,20 @@
return found;
}
-void BasicBlock::UpdatePredecessor(BasicBlockId old_parent, BasicBlockId new_parent) {
- GrowableArray<BasicBlockId>::Iterator iterator(predecessors);
- bool found = false;
+void BasicBlock::ErasePredecessor(BasicBlockId old_pred) {
+ auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
+ DCHECK(pos != predecessors.end());
+ predecessors.erase(pos);
+}
- while (true) {
- BasicBlockId pred_bb_id = iterator.Next();
-
- if (pred_bb_id == NullBasicBlockId) {
- break;
- }
-
- if (pred_bb_id == old_parent) {
- size_t idx = iterator.GetIndex() - 1;
- predecessors->Put(idx, new_parent);
- found = true;
- break;
- }
- }
-
- // If not found, add it.
- if (found == false) {
- predecessors->Insert(new_parent);
+void BasicBlock::UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred) {
+ DCHECK_NE(new_pred, NullBasicBlockId);
+ auto pos = std::find(predecessors.begin(), predecessors.end(), old_pred);
+ if (pos != predecessors.end()) {
+ *pos = new_pred;
+ } else {
+ // If not found, add it.
+ predecessors.push_back(new_pred);
}
}
@@ -2164,7 +2338,7 @@
// post-incremented.
BasicBlock* MIRGraph::CreateNewBB(BBType block_type) {
BasicBlock* res = NewMemBB(block_type, num_blocks_++);
- block_list_.Insert(res);
+ block_list_.push_back(res);
return res;
}
@@ -2174,7 +2348,89 @@
}
void MIRGraph::InitializeBasicBlockData() {
- num_blocks_ = block_list_.Size();
+ num_blocks_ = block_list_.size();
}
+int MIR::DecodedInstruction::FlagsOf() const {
+ // Calculate new index.
+ int idx = static_cast<int>(opcode) - kNumPackedOpcodes;
+
+ // Check if it is an extended or not.
+ if (idx < 0) {
+ return Instruction::FlagsOf(opcode);
+ }
+
+ // For extended, we use a switch.
+ switch (static_cast<int>(opcode)) {
+ case kMirOpPhi:
+ return Instruction::kContinue;
+ case kMirOpCopy:
+ return Instruction::kContinue;
+ case kMirOpFusedCmplFloat:
+ return Instruction::kContinue | Instruction::kBranch;
+ case kMirOpFusedCmpgFloat:
+ return Instruction::kContinue | Instruction::kBranch;
+ case kMirOpFusedCmplDouble:
+ return Instruction::kContinue | Instruction::kBranch;
+ case kMirOpFusedCmpgDouble:
+ return Instruction::kContinue | Instruction::kBranch;
+ case kMirOpFusedCmpLong:
+ return Instruction::kContinue | Instruction::kBranch;
+ case kMirOpNop:
+ return Instruction::kContinue;
+ case kMirOpNullCheck:
+ return Instruction::kContinue | Instruction::kThrow;
+ case kMirOpRangeCheck:
+ return Instruction::kContinue | Instruction::kThrow;
+ case kMirOpDivZeroCheck:
+ return Instruction::kContinue | Instruction::kThrow;
+ case kMirOpCheck:
+ return Instruction::kContinue | Instruction::kThrow;
+ case kMirOpCheckPart2:
+ return Instruction::kContinue;
+ case kMirOpSelect:
+ return Instruction::kContinue;
+ case kMirOpConstVector:
+ return Instruction::kContinue;
+ case kMirOpMoveVector:
+ return Instruction::kContinue;
+ case kMirOpPackedMultiply:
+ return Instruction::kContinue;
+ case kMirOpPackedAddition:
+ return Instruction::kContinue;
+ case kMirOpPackedSubtract:
+ return Instruction::kContinue;
+ case kMirOpPackedShiftLeft:
+ return Instruction::kContinue;
+ case kMirOpPackedSignedShiftRight:
+ return Instruction::kContinue;
+ case kMirOpPackedUnsignedShiftRight:
+ return Instruction::kContinue;
+ case kMirOpPackedAnd:
+ return Instruction::kContinue;
+ case kMirOpPackedOr:
+ return Instruction::kContinue;
+ case kMirOpPackedXor:
+ return Instruction::kContinue;
+ case kMirOpPackedAddReduce:
+ return Instruction::kContinue;
+ case kMirOpPackedReduce:
+ return Instruction::kContinue;
+ case kMirOpPackedSet:
+ return Instruction::kContinue;
+ case kMirOpReserveVectorRegisters:
+ return Instruction::kContinue;
+ case kMirOpReturnVectorRegisters:
+ return Instruction::kContinue;
+ case kMirOpMemBarrier:
+ return Instruction::kContinue;
+ case kMirOpPackedArrayGet:
+ return Instruction::kContinue | Instruction::kThrow;
+ case kMirOpPackedArrayPut:
+ return Instruction::kContinue | Instruction::kThrow;
+ default:
+ LOG(WARNING) << "ExtendedFlagsOf: Unhandled case: " << static_cast<int> (opcode);
+ return 0;
+ }
+}
} // namespace art
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 5817f92..f14b187 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -19,14 +19,14 @@
#include <stdint.h>
+#include "compiler_ir.h"
#include "dex_file.h"
#include "dex_instruction.h"
-#include "compiler_ir.h"
+#include "driver/dex_compilation_unit.h"
#include "invoke_type.h"
#include "mir_field_info.h"
#include "mir_method_info.h"
#include "utils/arena_bit_vector.h"
-#include "utils/growable_array.h"
#include "utils/arena_containers.h"
#include "utils/scoped_arena_containers.h"
#include "reg_location.h"
@@ -36,40 +36,6 @@
class GlobalValueNumbering;
-enum InstructionAnalysisAttributePos {
- kUninterestingOp = 0,
- kArithmeticOp,
- kFPOp,
- kSingleOp,
- kDoubleOp,
- kIntOp,
- kLongOp,
- kBranchOp,
- kInvokeOp,
- kArrayOp,
- kHeavyweightOp,
- kSimpleConstOp,
- kMoveOp,
- kSwitch
-};
-
-#define AN_NONE (1 << kUninterestingOp)
-#define AN_MATH (1 << kArithmeticOp)
-#define AN_FP (1 << kFPOp)
-#define AN_LONG (1 << kLongOp)
-#define AN_INT (1 << kIntOp)
-#define AN_SINGLE (1 << kSingleOp)
-#define AN_DOUBLE (1 << kDoubleOp)
-#define AN_FLOATMATH (1 << kFPOp)
-#define AN_BRANCH (1 << kBranchOp)
-#define AN_INVOKE (1 << kInvokeOp)
-#define AN_ARRAYOP (1 << kArrayOp)
-#define AN_HEAVYWEIGHT (1 << kHeavyweightOp)
-#define AN_SIMPLECONST (1 << kSimpleConstOp)
-#define AN_MOVE (1 << kMoveOp)
-#define AN_SWITCH (1 << kSwitch)
-#define AN_COMPUTATIONAL (AN_MATH | AN_ARRAYOP | AN_MOVE | AN_SIMPLECONST)
-
enum DataFlowAttributePos {
kUA = 0,
kUB,
@@ -194,6 +160,7 @@
#define MIR_CALLEE (1 << kMIRCallee)
#define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck)
#define MIR_DUP (1 << kMIRDup)
+#define MIR_STORE_NON_TEMPORAL (1 << kMIRStoreNonTemporal)
#define BLOCK_NAME_LEN 80
@@ -215,6 +182,7 @@
enum CompilerTempType {
kCompilerTempVR, // A virtual register temporary.
kCompilerTempSpecialMethodPtr, // Temporary that keeps track of current method pointer.
+ kCompilerTempBackend, // Temporary that is used by backend.
};
// When debug option enabled, records effectiveness of null and range check elimination.
@@ -297,37 +265,37 @@
}
bool IsInvoke() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kInvoke) == Instruction::kInvoke);
+ return ((FlagsOf() & Instruction::kInvoke) == Instruction::kInvoke);
}
bool IsStore() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kStore) == Instruction::kStore);
+ return ((FlagsOf() & Instruction::kStore) == Instruction::kStore);
}
bool IsLoad() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kLoad) == Instruction::kLoad);
+ return ((FlagsOf() & Instruction::kLoad) == Instruction::kLoad);
}
bool IsConditionalBranch() const {
- return !IsPseudoMirOp(opcode) && (Instruction::FlagsOf(opcode) == (Instruction::kContinue | Instruction::kBranch));
+ return (FlagsOf() == (Instruction::kContinue | Instruction::kBranch));
}
/**
* @brief Is the register C component of the decoded instruction a constant?
*/
bool IsCFieldOrConstant() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant);
+ return ((FlagsOf() & Instruction::kRegCFieldOrConstant) == Instruction::kRegCFieldOrConstant);
}
/**
* @brief Is the register C component of the decoded instruction a constant?
*/
bool IsBFieldOrConstant() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant);
+ return ((FlagsOf() & Instruction::kRegBFieldOrConstant) == Instruction::kRegBFieldOrConstant);
}
bool IsCast() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kCast) == Instruction::kCast);
+ return ((FlagsOf() & Instruction::kCast) == Instruction::kCast);
}
/**
@@ -337,12 +305,14 @@
* when crossing such an instruction.
*/
bool Clobbers() const {
- return !IsPseudoMirOp(opcode) && ((Instruction::FlagsOf(opcode) & Instruction::kClobber) == Instruction::kClobber);
+ return ((FlagsOf() & Instruction::kClobber) == Instruction::kClobber);
}
bool IsLinear() const {
- return !IsPseudoMirOp(opcode) && (Instruction::FlagsOf(opcode) & (Instruction::kAdd | Instruction::kSubtract)) != 0;
+ return (FlagsOf() & (Instruction::kAdd | Instruction::kSubtract)) != 0;
}
+
+ int FlagsOf() const;
} dalvikInsn;
NarrowDexOffset offset; // Offset of the instruction in code units.
@@ -389,6 +359,17 @@
struct SuccessorBlockInfo;
struct BasicBlock {
+ BasicBlock(BasicBlockId block_id, BBType type, ArenaAllocator* allocator)
+ : id(block_id),
+ dfs_id(), start_offset(), fall_through(), taken(), i_dom(), nesting_depth(),
+ block_type(type),
+ successor_block_list_type(kNotUsed),
+ visited(), hidden(), catch_entry(), explicit_throw(), conditional_branch(),
+ terminated_by_return(), dominates_return(), use_lvn(), first_mir_insn(),
+ last_mir_insn(), data_flow_info(), dominators(), i_dominated(), dom_frontier(),
+ predecessors(allocator->Adapter(kArenaAllocBBPredecessors)),
+ successor_blocks(allocator->Adapter(kArenaAllocSuccessor)) {
+ }
BasicBlockId id;
BasicBlockId dfs_id;
NarrowDexOffset start_offset; // Offset in code units.
@@ -412,8 +393,8 @@
ArenaBitVector* dominators;
ArenaBitVector* i_dominated; // Set nodes being immediately dominated.
ArenaBitVector* dom_frontier; // Dominance frontier.
- GrowableArray<BasicBlockId>* predecessors;
- GrowableArray<SuccessorBlockInfo*>* successor_blocks;
+ ArenaVector<BasicBlockId> predecessors;
+ ArenaVector<SuccessorBlockInfo*> successor_blocks;
void AppendMIR(MIR* mir);
void AppendMIRList(MIR* first_list_mir, MIR* last_list_mir);
@@ -441,7 +422,7 @@
* @brief Hide the BasicBlock.
* @details Set it to kDalvikByteCode, set hidden to true, remove all MIRs,
* remove itself from any predecessor edges, remove itself from any
- * child's predecessor growable array.
+ * child's predecessor array.
*/
void Hide(CompilationUnit* c_unit);
@@ -456,7 +437,12 @@
bool ReplaceChild(BasicBlockId old_bb, BasicBlockId new_bb);
/**
- * @brief Update the predecessor growable array from old_pred to new_pred.
+ * @brief Erase the predecessor old_pred.
+ */
+ void ErasePredecessor(BasicBlockId old_pred);
+
+ /**
+ * @brief Update the predecessor array from old_pred to new_pred.
*/
void UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred);
@@ -507,7 +493,7 @@
bool visited_fallthrough_;
bool visited_taken_;
bool have_successors_;
- GrowableArray<SuccessorBlockInfo*>::Iterator successor_iter_;
+ ArenaVector<SuccessorBlockInfo*>::const_iterator successor_iter_;
};
/*
@@ -539,7 +525,7 @@
class MIRGraph {
public:
MIRGraph(CompilationUnit* cu, ArenaAllocator* arena);
- ~MIRGraph();
+ virtual ~MIRGraph();
/*
* Examine the graph to determine whether it's worthwile to spend the time compiling
@@ -569,17 +555,36 @@
return current_code_item_->insns_;
}
+ /**
+ * @brief Used to obtain the raw dex bytecode instruction pointer.
+ * @param m_unit_index The method index in MIRGraph (caused by having multiple methods).
+ * This is guaranteed to contain index 0 which is the base method being compiled.
+ * @return Returns the raw instruction pointer.
+ */
const uint16_t* GetInsns(int m_unit_index) const {
return m_units_[m_unit_index]->GetCodeItem()->insns_;
}
+ /**
+ * @brief Used to obtain the raw data table.
+ * @param mir sparse switch, packed switch, of fill-array-data
+ * @param table_offset The table offset from start of method.
+ * @return Returns the raw table pointer.
+ */
+ const uint16_t* GetTable(MIR* mir, uint32_t table_offset) const {
+ return GetInsns(mir->m_unit_index) + mir->offset + table_offset;
+ }
+
unsigned int GetNumBlocks() const {
return num_blocks_;
}
- size_t GetNumDalvikInsns() const {
- return cu_->code_item->insns_size_in_code_units_;
- }
+ /**
+ * @brief Provides the total size in code units of all instructions in MIRGraph.
+ * @details Includes the sizes of all methods in compilation unit.
+ * @return Returns the cumulative sum of all insn sizes (in code units).
+ */
+ size_t GetNumDalvikInsns() const;
ArenaBitVector* GetTryBlockAddr() const {
return try_block_addr_;
@@ -594,26 +599,27 @@
}
BasicBlock* GetBasicBlock(unsigned int block_id) const {
- return (block_id == NullBasicBlockId) ? NULL : block_list_.Get(block_id);
+ DCHECK_LT(block_id, block_list_.size()); // NOTE: NullBasicBlockId is 0.
+ return (block_id == NullBasicBlockId) ? NULL : block_list_[block_id];
}
size_t GetBasicBlockListCount() const {
- return block_list_.Size();
+ return block_list_.size();
}
- GrowableArray<BasicBlock*>* GetBlockList() {
- return &block_list_;
+ const ArenaVector<BasicBlock*>& GetBlockList() {
+ return block_list_;
}
- GrowableArray<BasicBlockId>* GetDfsOrder() {
+ const ArenaVector<BasicBlockId>& GetDfsOrder() {
return dfs_order_;
}
- GrowableArray<BasicBlockId>* GetDfsPostOrder() {
+ const ArenaVector<BasicBlockId>& GetDfsPostOrder() {
return dfs_post_order_;
}
- GrowableArray<BasicBlockId>* GetDomPostOrder() {
+ const ArenaVector<BasicBlockId>& GetDomPostOrder() {
return dom_post_order_traversal_;
}
@@ -660,20 +666,20 @@
void DoCacheFieldLoweringInfo();
const MirIFieldLoweringInfo& GetIFieldLoweringInfo(MIR* mir) const {
- DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.Size());
- return ifield_lowering_infos_.GetRawStorage()[mir->meta.ifield_lowering_info];
+ DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.size());
+ return ifield_lowering_infos_[mir->meta.ifield_lowering_info];
}
const MirSFieldLoweringInfo& GetSFieldLoweringInfo(MIR* mir) const {
- DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.Size());
- return sfield_lowering_infos_.GetRawStorage()[mir->meta.sfield_lowering_info];
+ DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.size());
+ return sfield_lowering_infos_[mir->meta.sfield_lowering_info];
}
void DoCacheMethodLoweringInfo();
const MirMethodLoweringInfo& GetMethodLoweringInfo(MIR* mir) {
- DCHECK_LT(mir->meta.method_lowering_info, method_lowering_infos_.Size());
- return method_lowering_infos_.GetRawStorage()[mir->meta.method_lowering_info];
+ DCHECK_LT(mir->meta.method_lowering_info, method_lowering_infos_.size());
+ return method_lowering_infos_[mir->meta.method_lowering_info];
}
void ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput);
@@ -686,24 +692,24 @@
void BasicBlockOptimization();
- GrowableArray<BasicBlockId>* GetTopologicalSortOrder() {
- DCHECK(topological_order_ != nullptr);
+ const ArenaVector<BasicBlockId>& GetTopologicalSortOrder() {
+ DCHECK(!topological_order_.empty());
return topological_order_;
}
- GrowableArray<BasicBlockId>* GetTopologicalSortOrderLoopEnds() {
- DCHECK(topological_order_loop_ends_ != nullptr);
+ const ArenaVector<BasicBlockId>& GetTopologicalSortOrderLoopEnds() {
+ DCHECK(!topological_order_loop_ends_.empty());
return topological_order_loop_ends_;
}
- GrowableArray<BasicBlockId>* GetTopologicalSortOrderIndexes() {
- DCHECK(topological_order_indexes_ != nullptr);
+ const ArenaVector<BasicBlockId>& GetTopologicalSortOrderIndexes() {
+ DCHECK(!topological_order_indexes_.empty());
return topological_order_indexes_;
}
- GrowableArray<std::pair<uint16_t, bool>>* GetTopologicalSortOrderLoopHeadStack() {
- DCHECK(topological_order_loop_head_stack_ != nullptr);
- return topological_order_loop_head_stack_;
+ ArenaVector<std::pair<uint16_t, bool>>* GetTopologicalSortOrderLoopHeadStack() {
+ DCHECK(!topological_order_.empty()); // Checking the main array, not the stack.
+ return &topological_order_loop_head_stack_;
}
bool IsConst(int32_t s_reg) const {
@@ -724,6 +730,19 @@
return constant_values_[s_reg];
}
+ /**
+ * @brief Used to obtain 64-bit value of a pair of ssa registers.
+ * @param s_reg_low The ssa register representing the low bits.
+ * @param s_reg_high The ssa register representing the high bits.
+ * @return Retusn the 64-bit constant value.
+ */
+ int64_t ConstantValueWide(int32_t s_reg_low, int32_t s_reg_high) const {
+ DCHECK(IsConst(s_reg_low));
+ DCHECK(IsConst(s_reg_high));
+ return (static_cast<int64_t>(constant_values_[s_reg_high]) << 32) |
+ Low32Bits(static_cast<int64_t>(constant_values_[s_reg_low]));
+ }
+
int64_t ConstantValueWide(RegLocation loc) const {
DCHECK(IsConst(loc));
DCHECK(!loc.high_word); // Do not allow asking for the high partner.
@@ -732,6 +751,20 @@
Low32Bits(static_cast<int64_t>(constant_values_[loc.orig_sreg]));
}
+ /**
+ * @brief Used to mark ssa register as being constant.
+ * @param ssa_reg The ssa register.
+ * @param value The constant value of ssa register.
+ */
+ void SetConstant(int32_t ssa_reg, int32_t value);
+
+ /**
+ * @brief Used to mark ssa register and its wide counter-part as being constant.
+ * @param ssa_reg The ssa register.
+ * @param value The 64-bit constant value of ssa register and its pair.
+ */
+ void SetConstantWide(int32_t ssa_reg, int64_t value);
+
bool IsConstantNullRef(RegLocation loc) const {
return loc.ref && loc.is_const && (ConstantValue(loc) == 0);
}
@@ -754,16 +787,19 @@
return num_reachable_blocks_;
}
- int GetUseCount(int vreg) const {
- return use_counts_.Get(vreg);
+ uint32_t GetUseCount(int sreg) const {
+ DCHECK_LT(static_cast<size_t>(sreg), use_counts_.size());
+ return use_counts_[sreg];
}
- int GetRawUseCount(int vreg) const {
- return raw_use_counts_.Get(vreg);
+ uint32_t GetRawUseCount(int sreg) const {
+ DCHECK_LT(static_cast<size_t>(sreg), raw_use_counts_.size());
+ return raw_use_counts_[sreg];
}
int GetSSASubscript(int ssa_reg) const {
- return ssa_subscripts_->Get(ssa_reg);
+ DCHECK_LT(static_cast<size_t>(ssa_reg), ssa_subscripts_.size());
+ return ssa_subscripts_[ssa_reg];
}
RegLocation GetRawSrc(MIR* mir, int num) {
@@ -815,9 +851,26 @@
* @return Returns the number of compiler temporaries.
*/
size_t GetNumUsedCompilerTemps() const {
- size_t total_num_temps = compiler_temps_.Size();
- DCHECK_LE(num_non_special_compiler_temps_, total_num_temps);
- return total_num_temps;
+ // Assume that the special temps will always be used.
+ return GetNumNonSpecialCompilerTemps() + max_available_special_compiler_temps_;
+ }
+
+ /**
+ * @brief Used to obtain number of bytes needed for special temps.
+ * @details This space is always needed because temps have special location on stack.
+ * @return Returns number of bytes for the special temps.
+ */
+ size_t GetNumBytesForSpecialTemps() const;
+
+ /**
+ * @brief Used by backend as a hint for maximum number of bytes for non-special temps.
+ * @details Returns 4 bytes for each temp because that is the maximum amount needed
+ * for storing each temp. The BE could be smarter though and allocate a smaller
+ * spill region.
+ * @return Returns the maximum number of bytes needed for non-special temps.
+ */
+ size_t GetMaximumBytesForNonSpecialTemps() const {
+ return GetNumNonSpecialCompilerTemps() * sizeof(uint32_t);
}
/**
@@ -835,7 +888,9 @@
* @return Returns true if the max was set and false if failed to set.
*/
bool SetMaxAvailableNonSpecialCompilerTemps(size_t new_max) {
- if (new_max < GetNumNonSpecialCompilerTemps()) {
+ // Make sure that enough temps still exist for backend and also that the
+ // new max can still keep around all of the already requested temps.
+ if (new_max < (GetNumNonSpecialCompilerTemps() + reserved_temps_for_backend_)) {
return false;
} else {
max_available_non_special_compiler_temps_ = new_max;
@@ -844,21 +899,12 @@
}
/**
- * @brief Provides the number of non-special compiler temps available.
+ * @brief Provides the number of non-special compiler temps available for use by ME.
* @details Even if this returns zero, special compiler temps are guaranteed to be available.
+ * Additionally, this makes sure to not use any temps reserved for BE only.
* @return Returns the number of available temps.
*/
- size_t GetNumAvailableNonSpecialCompilerTemps();
-
- /**
- * @brief Used to obtain an existing compiler temporary.
- * @param index The index of the temporary which must be strictly less than the
- * number of temporaries.
- * @return Returns the temporary that was asked for.
- */
- CompilerTemp* GetCompilerTemp(size_t index) const {
- return compiler_temps_.Get(index);
- }
+ size_t GetNumAvailableVRTemps();
/**
* @brief Used to obtain the maximum number of compiler temporaries that can be requested.
@@ -869,7 +915,22 @@
}
/**
+ * @brief Used to signal that the compiler temps have been committed.
+ * @details This should be used once the number of temps can no longer change,
+ * such as after frame size is committed and cannot be changed.
+ */
+ void CommitCompilerTemps() {
+ compiler_temps_committed_ = true;
+ }
+
+ /**
* @brief Used to obtain a new unique compiler temporary.
+ * @details Two things are done for convenience when allocating a new compiler
+ * temporary. The ssa register is automatically requested and the information
+ * about reg location is filled. This helps when the temp is requested post
+ * ssa initialization, such as when temps are requested by the backend.
+ * @warning If the temp requested will be used for ME and have multiple versions,
+ * the sreg provided by the temp will be invalidated on next ssa recalculation.
* @param ct_type Type of compiler temporary requested.
* @param wide Whether we should allocate a wide temporary.
* @return Returns the newly created compiler temporary.
@@ -911,8 +972,49 @@
}
// Is this vreg in the in set?
- bool IsInVReg(int vreg) {
- return (vreg >= cu_->num_regs);
+ bool IsInVReg(uint32_t vreg) {
+ return (vreg >= GetFirstInVR()) && (vreg < GetFirstTempVR());
+ }
+
+ uint32_t GetNumOfCodeVRs() const {
+ return current_code_item_->registers_size_;
+ }
+
+ uint32_t GetNumOfCodeAndTempVRs() const {
+ // Include all of the possible temps so that no structures overflow when initialized.
+ return GetNumOfCodeVRs() + GetMaxPossibleCompilerTemps();
+ }
+
+ uint32_t GetNumOfLocalCodeVRs() const {
+ // This also refers to the first "in" VR.
+ return GetNumOfCodeVRs() - current_code_item_->ins_size_;
+ }
+
+ uint32_t GetNumOfInVRs() const {
+ return current_code_item_->ins_size_;
+ }
+
+ uint32_t GetNumOfOutVRs() const {
+ return current_code_item_->outs_size_;
+ }
+
+ uint32_t GetFirstInVR() const {
+ return GetNumOfLocalCodeVRs();
+ }
+
+ uint32_t GetFirstTempVR() const {
+ // Temp VRs immediately follow code VRs.
+ return GetNumOfCodeVRs();
+ }
+
+ uint32_t GetFirstSpecialTempVR() const {
+ // Special temps appear first in the ordering before non special temps.
+ return GetFirstTempVR();
+ }
+
+ uint32_t GetFirstNonSpecialTempVR() const {
+ // We always leave space for all the special temps before the non-special ones.
+ return GetFirstSpecialTempVR() + max_available_special_compiler_temps_;
}
void DumpCheckStats();
@@ -965,6 +1067,7 @@
punt_to_interpreter_ = val;
}
+ void DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir);
char* GetDalvikDisassembly(const MIR* mir);
void ReplaceSpecialChars(std::string& str);
std::string GetSSAName(int ssa_reg);
@@ -1044,6 +1147,7 @@
void ComputeDefBlockMatrix();
void ComputeDominators();
void CompilerInitializeSSAConversion();
+ virtual void InitializeBasicBlockDataFlow();
void InsertPhiNodes();
void DoDFSPreOrderSSARename(BasicBlock* block);
@@ -1058,16 +1162,15 @@
ArenaSafeMap<unsigned int, unsigned int> block_id_map_; // Block collapse lookup cache.
static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst];
- static const uint32_t analysis_attributes_[kMirOpLast];
void HandleSSADef(int* defs, int dalvik_reg, int reg_index);
bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed);
// Used for removing redudant suspend tests
void AppendGenSuspendTestList(BasicBlock* bb) {
- if (gen_suspend_test_list_.Size() == 0 ||
- gen_suspend_test_list_.Get(gen_suspend_test_list_.Size() - 1) != bb) {
- gen_suspend_test_list_.Insert(bb);
+ if (gen_suspend_test_list_.size() == 0 ||
+ gen_suspend_test_list_.back() != bb) {
+ gen_suspend_test_list_.push_back(bb);
}
}
@@ -1088,7 +1191,6 @@
ArenaBitVector* live_in_v,
const MIR::DecodedInstruction& d_insn);
bool DoSSAConversion(BasicBlock* bb);
- bool InvokeUsesMethodStar(MIR* mir);
int ParseInsn(const uint16_t* code_ptr, MIR::DecodedInstruction* decoded_instruction);
bool ContentIsInsn(const uint16_t* code_ptr);
BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block,
@@ -1116,8 +1218,6 @@
void MarkPreOrder(BasicBlock* bb);
void RecordDFSOrders(BasicBlock* bb);
void ComputeDomPostOrderTraversal(BasicBlock* bb);
- void SetConstant(int32_t ssa_reg, int value);
- void SetConstantWide(int ssa_reg, int64_t value);
int GetSSAUseCount(int s_reg);
bool BasicBlockOpt(BasicBlock* bb);
bool BuildExtendedBBList(struct BasicBlock* bb);
@@ -1135,45 +1235,45 @@
std::string* skip_message);
CompilationUnit* const cu_;
- GrowableArray<int>* ssa_base_vregs_;
- GrowableArray<int>* ssa_subscripts_;
+ ArenaVector<int> ssa_base_vregs_;
+ ArenaVector<int> ssa_subscripts_;
// Map original Dalvik virtual reg i to the current SSA name.
int* vreg_to_ssa_map_; // length == method->registers_size
int* ssa_last_defs_; // length == method->registers_size
ArenaBitVector* is_constant_v_; // length == num_ssa_reg
int* constant_values_; // length == num_ssa_reg
// Use counts of ssa names.
- GrowableArray<uint32_t> use_counts_; // Weighted by nesting depth
- GrowableArray<uint32_t> raw_use_counts_; // Not weighted
+ ArenaVector<uint32_t> use_counts_; // Weighted by nesting depth
+ ArenaVector<uint32_t> raw_use_counts_; // Not weighted
unsigned int num_reachable_blocks_;
unsigned int max_num_reachable_blocks_;
- GrowableArray<BasicBlockId>* dfs_order_;
- GrowableArray<BasicBlockId>* dfs_post_order_;
- GrowableArray<BasicBlockId>* dom_post_order_traversal_;
- GrowableArray<BasicBlockId>* topological_order_;
+ ArenaVector<BasicBlockId> dfs_order_;
+ ArenaVector<BasicBlockId> dfs_post_order_;
+ ArenaVector<BasicBlockId> dom_post_order_traversal_;
+ ArenaVector<BasicBlockId> topological_order_;
// Indexes in topological_order_ need to be only as big as the BasicBlockId.
COMPILE_ASSERT(sizeof(BasicBlockId) == sizeof(uint16_t), assuming_16_bit_BasicBlockId);
// For each loop head, remember the past-the-end index of the end of the loop. 0 if not loop head.
- GrowableArray<uint16_t>* topological_order_loop_ends_;
+ ArenaVector<uint16_t> topological_order_loop_ends_;
// Map BB ids to topological_order_ indexes. 0xffff if not included (hidden or null block).
- GrowableArray<uint16_t>* topological_order_indexes_;
+ ArenaVector<uint16_t> topological_order_indexes_;
// Stack of the loop head indexes and recalculation flags for RepeatingTopologicalSortIterator.
- GrowableArray<std::pair<uint16_t, bool>>* topological_order_loop_head_stack_;
+ ArenaVector<std::pair<uint16_t, bool>> topological_order_loop_head_stack_;
int* i_dom_list_;
- ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks.
+ ArenaBitVector** def_block_matrix_; // original num registers x num_blocks.
std::unique_ptr<ScopedArenaAllocator> temp_scoped_alloc_;
uint16_t* temp_insn_data_;
uint32_t temp_bit_vector_size_;
ArenaBitVector* temp_bit_vector_;
std::unique_ptr<GlobalValueNumbering> temp_gvn_;
static const int kInvalidEntry = -1;
- GrowableArray<BasicBlock*> block_list_;
+ ArenaVector<BasicBlock*> block_list_;
ArenaBitVector* try_block_addr_;
BasicBlock* entry_block_;
BasicBlock* exit_block_;
unsigned int num_blocks_;
const DexFile::CodeItem* current_code_item_;
- GrowableArray<uint16_t> dex_pc_to_block_map_; // FindBlock lookup cache.
+ ArenaVector<uint16_t> dex_pc_to_block_map_; // FindBlock lookup cache.
ArenaVector<DexCompilationUnit*> m_units_; // List of methods included in this graph
typedef std::pair<int, int> MIRLocation; // Insert point, (m_unit_ index, offset)
ArenaVector<MIRLocation> method_stack_; // Include stack
@@ -1189,17 +1289,19 @@
ArenaAllocator* arena_;
int backward_branches_;
int forward_branches_;
- GrowableArray<CompilerTemp*> compiler_temps_;
- size_t num_non_special_compiler_temps_;
- size_t max_available_non_special_compiler_temps_;
- size_t max_available_special_compiler_temps_;
- bool punt_to_interpreter_; // Difficult or not worthwhile - just interpret.
+ size_t num_non_special_compiler_temps_; // Keeps track of allocated non-special compiler temps. These are VRs that are in compiler temp region on stack.
+ size_t max_available_non_special_compiler_temps_; // Keeps track of maximum available non-special temps.
+ size_t max_available_special_compiler_temps_; // Keeps track of maximum available special temps.
+ bool requested_backend_temp_; // Keeps track whether BE temps have been requested.
+ size_t reserved_temps_for_backend_; // Keeps track of the remaining temps that are reserved for BE.
+ bool compiler_temps_committed_; // Keeps track whether number of temps has been frozen (for example post frame size calculation).
+ bool punt_to_interpreter_; // Difficult or not worthwhile - just interpret.
uint64_t merged_df_flags_;
- GrowableArray<MirIFieldLoweringInfo> ifield_lowering_infos_;
- GrowableArray<MirSFieldLoweringInfo> sfield_lowering_infos_;
- GrowableArray<MirMethodLoweringInfo> method_lowering_infos_;
+ ArenaVector<MirIFieldLoweringInfo> ifield_lowering_infos_;
+ ArenaVector<MirSFieldLoweringInfo> sfield_lowering_infos_;
+ ArenaVector<MirMethodLoweringInfo> method_lowering_infos_;
static const uint64_t oat_data_flow_attributes_[kMirOpLast];
- GrowableArray<BasicBlock*> gen_suspend_test_list_; // List of blocks containing suspend tests
+ ArenaVector<BasicBlock*> gen_suspend_test_list_; // List of blocks containing suspend tests
friend class ClassInitCheckEliminationTest;
friend class GlobalValueNumberingTest;
diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc
index 932f453..a96cd84 100644
--- a/compiler/dex/mir_graph_test.cc
+++ b/compiler/dex/mir_graph_test.cc
@@ -57,52 +57,48 @@
void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
cu_.mir_graph->block_id_map_.clear();
- cu_.mir_graph->block_list_.Reset();
+ cu_.mir_graph->block_list_.clear();
ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block.
ASSERT_EQ(kNullBlock, defs[0].type);
ASSERT_EQ(kEntryBlock, defs[1].type);
ASSERT_EQ(kExitBlock, defs[2].type);
for (size_t i = 0u; i != count; ++i) {
const BBDef* def = &defs[i];
- BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
- cu_.mir_graph->block_list_.Insert(bb);
+ BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
if (def->num_successors <= 2) {
bb->successor_block_list_type = kNotUsed;
- bb->successor_blocks = nullptr;
bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
} else {
bb->successor_block_list_type = kPackedSwitch;
bb->fall_through = 0u;
bb->taken = 0u;
- bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
- &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+ bb->successor_blocks.reserve(def->num_successors);
for (size_t j = 0u; j != def->num_successors; ++j) {
SuccessorBlockInfo* successor_block_info =
static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
kArenaAllocSuccessor));
successor_block_info->block = j;
successor_block_info->key = 0u; // Not used by class init check elimination.
- bb->successor_blocks->Insert(successor_block_info);
+ bb->successor_blocks.push_back(successor_block_info);
}
}
- bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
- &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
- for (size_t j = 0u; j != def->num_predecessors; ++j) {
- ASSERT_NE(0u, def->predecessors[j]);
- bb->predecessors->Insert(def->predecessors[j]);
- }
+ bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
}
}
cu_.mir_graph->num_blocks_ = count;
- ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
- cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+ ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
+ cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
- cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+ cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
+
+ DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(cu_.arena.Alloc(sizeof(DexFile::CodeItem),
+ kArenaAllocMisc));
+ cu_.mir_graph->current_code_item_ = code_item;
}
template <size_t count>
@@ -116,21 +112,21 @@
cu_.mir_graph->ComputeDominators();
cu_.mir_graph->ComputeTopologicalSortOrder();
cu_.mir_graph->SSATransformationEnd();
- ASSERT_NE(cu_.mir_graph->topological_order_, nullptr);
- ASSERT_NE(cu_.mir_graph->topological_order_loop_ends_, nullptr);
- ASSERT_NE(cu_.mir_graph->topological_order_indexes_, nullptr);
- ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_->Size());
- for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder()->Size(); i != size; ++i) {
- ASSERT_LT(cu_.mir_graph->topological_order_->Get(i), cu_.mir_graph->GetNumBlocks());
- BasicBlockId id = cu_.mir_graph->topological_order_->Get(i);
- EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_->Get(id));
+ ASSERT_FALSE(cu_.mir_graph->topological_order_.empty());
+ ASSERT_FALSE(cu_.mir_graph->topological_order_loop_ends_.empty());
+ ASSERT_FALSE(cu_.mir_graph->topological_order_indexes_.empty());
+ ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_.size());
+ for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder().size(); i != size; ++i) {
+ ASSERT_LT(cu_.mir_graph->topological_order_[i], cu_.mir_graph->GetNumBlocks());
+ BasicBlockId id = cu_.mir_graph->topological_order_[i];
+ EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_[id]);
}
}
void DoCheckOrder(const BasicBlockId* ids, size_t count) {
- ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder()->Size());
+ ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder().size());
for (size_t i = 0; i != count; ++i) {
- EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()->Get(i)) << i;
+ EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()[i]) << i;
}
}
@@ -141,8 +137,8 @@
void DoCheckLoopEnds(const uint16_t* ends, size_t count) {
for (size_t i = 0; i != count; ++i) {
- ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds()->Size());
- EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()->Get(i)) << i;
+ ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds().size());
+ EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()[i]) << i;
}
}
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 23ceb56..dac71f6 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/bit_vector-inl.h"
#include "compiler_internals.h"
#include "global_value_numbering.h"
#include "local_value_numbering.h"
@@ -21,21 +22,22 @@
#include "dex/global_value_numbering.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "stack.h"
#include "utils/scoped_arena_containers.h"
namespace art {
static unsigned int Predecessors(BasicBlock* bb) {
- return bb->predecessors->Size();
+ return bb->predecessors.size();
}
/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
-void MIRGraph::SetConstant(int32_t ssa_reg, int value) {
+void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) {
is_constant_v_->SetBit(ssa_reg);
constant_values_[ssa_reg] = value;
}
-void MIRGraph::SetConstantWide(int ssa_reg, int64_t value) {
+void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
is_constant_v_->SetBit(ssa_reg);
is_constant_v_->SetBit(ssa_reg + 1);
constant_values_[ssa_reg] = Low32Bits(value);
@@ -228,30 +230,63 @@
COMPILE_ASSERT(ConditionCodeForIfCcZ(Instruction::IF_LEZ) == kCondLe, check_if_lez_ccode);
int MIRGraph::GetSSAUseCount(int s_reg) {
- return raw_use_counts_.Get(s_reg);
+ DCHECK_LT(static_cast<size_t>(s_reg), ssa_subscripts_.size());
+ return raw_use_counts_[s_reg];
}
-size_t MIRGraph::GetNumAvailableNonSpecialCompilerTemps() {
- if (num_non_special_compiler_temps_ >= max_available_non_special_compiler_temps_) {
+size_t MIRGraph::GetNumBytesForSpecialTemps() const {
+ // This logic is written with assumption that Method* is only special temp.
+ DCHECK_EQ(max_available_special_compiler_temps_, 1u);
+ return sizeof(StackReference<mirror::ArtMethod>);
+}
+
+size_t MIRGraph::GetNumAvailableVRTemps() {
+ // First take into account all temps reserved for backend.
+ if (max_available_non_special_compiler_temps_ < reserved_temps_for_backend_) {
+ return 0;
+ }
+
+ // Calculate remaining ME temps available.
+ size_t remaining_me_temps = max_available_non_special_compiler_temps_ - reserved_temps_for_backend_;
+
+ if (num_non_special_compiler_temps_ >= remaining_me_temps) {
return 0;
} else {
- return max_available_non_special_compiler_temps_ - num_non_special_compiler_temps_;
+ return remaining_me_temps - num_non_special_compiler_temps_;
}
}
-
// FIXME - will probably need to revisit all uses of this, as type not defined.
static const RegLocation temp_loc = {kLocCompilerTemp,
0, 1 /*defined*/, 0, 0, 0, 0, 0, 1 /*home*/,
RegStorage(), INVALID_SREG, INVALID_SREG};
CompilerTemp* MIRGraph::GetNewCompilerTemp(CompilerTempType ct_type, bool wide) {
- // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded.
- if (ct_type == kCompilerTempVR) {
- size_t available_temps = GetNumAvailableNonSpecialCompilerTemps();
- if (available_temps <= 0 || (available_temps <= 1 && wide)) {
- return 0;
+ // Once the compiler temps have been committed, new ones cannot be requested anymore.
+ DCHECK_EQ(compiler_temps_committed_, false);
+ // Make sure that reserved for BE set is sane.
+ DCHECK_LE(reserved_temps_for_backend_, max_available_non_special_compiler_temps_);
+
+ bool verbose = cu_->verbose;
+ const char* ct_type_str = nullptr;
+
+ if (verbose) {
+ switch (ct_type) {
+ case kCompilerTempBackend:
+ ct_type_str = "backend";
+ break;
+ case kCompilerTempSpecialMethodPtr:
+ ct_type_str = "method*";
+ break;
+ case kCompilerTempVR:
+ ct_type_str = "VR";
+ break;
+ default:
+ ct_type_str = "unknown";
+ break;
}
+ LOG(INFO) << "CompilerTemps: A compiler temp of type " << ct_type_str << " that is "
+ << (wide ? "wide is being requested." : "not wide is being requested.");
}
CompilerTemp *compiler_temp = static_cast<CompilerTemp *>(arena_->Alloc(sizeof(CompilerTemp),
@@ -260,51 +295,100 @@
// Create the type of temp requested. Special temps need special handling because
// they have a specific virtual register assignment.
if (ct_type == kCompilerTempSpecialMethodPtr) {
+ // This has a special location on stack which is 32-bit or 64-bit depending
+ // on mode. However, we don't want to overlap with non-special section
+ // and thus even for 64-bit, we allow only a non-wide temp to be requested.
DCHECK_EQ(wide, false);
- compiler_temp->v_reg = static_cast<int>(kVRegMethodPtrBaseReg);
- compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
- // The MIR graph keeps track of the sreg for method pointer specially, so record that now.
- method_sreg_ = compiler_temp->s_reg_low;
+ // The vreg is always the first special temp for method ptr.
+ compiler_temp->v_reg = GetFirstSpecialTempVR();
+
+ } else if (ct_type == kCompilerTempBackend) {
+ requested_backend_temp_ = true;
+
+ // Make sure that we are not exceeding temps reserved for BE.
+ // Since VR temps cannot be requested once the BE temps are requested, we
+ // allow reservation of VR temps as well for BE. We
+ size_t available_temps = reserved_temps_for_backend_ + GetNumAvailableVRTemps();
+ if (available_temps <= 0 || (available_temps <= 1 && wide)) {
+ if (verbose) {
+ LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str << " are available.";
+ }
+ return nullptr;
+ }
+
+ // Update the remaining reserved temps since we have now used them.
+ // Note that the code below is actually subtracting to remove them from reserve
+ // once they have been claimed. It is careful to not go below zero.
+ if (reserved_temps_for_backend_ >= 1) {
+ reserved_temps_for_backend_--;
+ }
+ if (wide && reserved_temps_for_backend_ >= 1) {
+ reserved_temps_for_backend_--;
+ }
+
+ // The new non-special compiler temp must receive a unique v_reg.
+ compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_;
+ num_non_special_compiler_temps_++;
+ } else if (ct_type == kCompilerTempVR) {
+ // Once we start giving out BE temps, we don't allow anymore ME temps to be requested.
+ // This is done in order to prevent problems with ssa since these structures are allocated
+ // and managed by the ME.
+ DCHECK_EQ(requested_backend_temp_, false);
+
+ // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded.
+ size_t available_temps = GetNumAvailableVRTemps();
+ if (available_temps <= 0 || (available_temps <= 1 && wide)) {
+ if (verbose) {
+ LOG(INFO) << "CompilerTemps: Not enough temp(s) of type " << ct_type_str << " are available.";
+ }
+ return nullptr;
+ }
+
+ // The new non-special compiler temp must receive a unique v_reg.
+ compiler_temp->v_reg = GetFirstNonSpecialTempVR() + num_non_special_compiler_temps_;
+ num_non_special_compiler_temps_++;
} else {
- DCHECK_EQ(ct_type, kCompilerTempVR);
+ UNIMPLEMENTED(FATAL) << "No handling for compiler temp type " << ct_type_str << ".";
+ }
- // The new non-special compiler temp must receive a unique v_reg with a negative value.
- compiler_temp->v_reg = static_cast<int>(kVRegNonSpecialTempBaseReg) -
- num_non_special_compiler_temps_;
- compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+ // We allocate an sreg as well to make developer life easier.
+ // However, if this is requested from an ME pass that will recalculate ssa afterwards,
+ // this sreg is no longer valid. The caller should be aware of this.
+ compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+
+ if (verbose) {
+ LOG(INFO) << "CompilerTemps: New temp of type " << ct_type_str << " with v" << compiler_temp->v_reg
+ << " and s" << compiler_temp->s_reg_low << " has been created.";
+ }
+
+ if (wide) {
+ // Only non-special temps are handled as wide for now.
+ // Note that the number of non special temps is incremented below.
+ DCHECK(ct_type == kCompilerTempBackend || ct_type == kCompilerTempVR);
+
+ // Ensure that the two registers are consecutive.
+ int ssa_reg_low = compiler_temp->s_reg_low;
+ int ssa_reg_high = AddNewSReg(compiler_temp->v_reg + 1);
num_non_special_compiler_temps_++;
- if (wide) {
- // Create a new CompilerTemp for the high part.
- CompilerTemp *compiler_temp_high =
- static_cast<CompilerTemp *>(arena_->Alloc(sizeof(CompilerTemp), kArenaAllocRegAlloc));
- compiler_temp_high->v_reg = compiler_temp->v_reg;
- compiler_temp_high->s_reg_low = compiler_temp->s_reg_low;
- compiler_temps_.Insert(compiler_temp_high);
+ if (verbose) {
+ LOG(INFO) << "CompilerTemps: The wide part of temp of type " << ct_type_str << " is v"
+ << compiler_temp->v_reg + 1 << " and s" << ssa_reg_high << ".";
+ }
- // Ensure that the two registers are consecutive. Since the virtual registers used for temps
- // grow in a negative fashion, we need the smaller to refer to the low part. Thus, we
- // redefine the v_reg and s_reg_low.
- compiler_temp->v_reg--;
- int ssa_reg_high = compiler_temp->s_reg_low;
- compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
- int ssa_reg_low = compiler_temp->s_reg_low;
-
- // If needed initialize the register location for the high part.
- // The low part is handled later in this method on a common path.
- if (reg_location_ != nullptr) {
- reg_location_[ssa_reg_high] = temp_loc;
- reg_location_[ssa_reg_high].high_word = 1;
- reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low;
- reg_location_[ssa_reg_high].wide = true;
- }
-
- num_non_special_compiler_temps_++;
+ if (reg_location_ != nullptr) {
+ reg_location_[ssa_reg_high] = temp_loc;
+ reg_location_[ssa_reg_high].high_word = true;
+ reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low;
+ reg_location_[ssa_reg_high].wide = true;
}
}
- // Have we already allocated the register locations?
+ // If the register locations have already been allocated, add the information
+ // about the temp. We will not overflow because they have been initialized
+ // to support the maximum number of temps. For ME temps that have multiple
+ // ssa versions, the structures below will be expanded on the post pass cleanup.
if (reg_location_ != nullptr) {
int ssa_reg_low = compiler_temp->s_reg_low;
reg_location_[ssa_reg_low] = temp_loc;
@@ -312,7 +396,6 @@
reg_location_[ssa_reg_low].wide = wide;
}
- compiler_temps_.Insert(compiler_temp);
return compiler_temp;
}
@@ -596,26 +679,41 @@
}
}
-/* Try to make common case the fallthrough path */
+/* Try to make common case the fallthrough path. */
bool MIRGraph::LayoutBlocks(BasicBlock* bb) {
- // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback
+ // TODO: For now, just looking for direct throws. Consider generalizing for profile feedback.
if (!bb->explicit_throw) {
return false;
}
+
+ // If we visited it, we are done.
+ if (bb->visited) {
+ return false;
+ }
+ bb->visited = true;
+
BasicBlock* walker = bb;
while (true) {
- // Check termination conditions
+ // Check termination conditions.
if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) {
break;
}
- BasicBlock* prev = GetBasicBlock(walker->predecessors->Get(0));
+ DCHECK(!walker->predecessors.empty());
+ BasicBlock* prev = GetBasicBlock(walker->predecessors[0]);
+
+ // If we visited the predecessor, we are done.
+ if (prev->visited) {
+ return false;
+ }
+ prev->visited = true;
+
if (prev->conditional_branch) {
if (GetBasicBlock(prev->fall_through) == walker) {
- // Already done - return
+ // Already done - return.
break;
}
DCHECK_EQ(walker, GetBasicBlock(prev->taken));
- // Got one. Flip it and exit
+ // Got one. Flip it and exit.
Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode;
switch (opcode) {
case Instruction::IF_EQ: opcode = Instruction::IF_NE; break;
@@ -639,6 +737,10 @@
break;
}
walker = prev;
+
+ if (walker->visited) {
+ break;
+ }
}
return false;
}
@@ -684,7 +786,7 @@
*bb->last_mir_insn = *throw_insn;
// Use the successor info from the next block
bb->successor_block_list_type = bb_next->successor_block_list_type;
- bb->successor_blocks = bb_next->successor_blocks;
+ bb->successor_blocks.swap(bb_next->successor_blocks); // Swap instead of copying.
// Use the ending block linkage from the next block
bb->fall_through = bb_next->fall_through;
GetBasicBlock(bb->taken)->block_type = kDead; // Kill the unused exception block
@@ -692,10 +794,13 @@
// Include the rest of the instructions
bb->last_mir_insn = bb_next->last_mir_insn;
/*
- * If lower-half of pair of blocks to combine contained a return, move the flag
- * to the newly combined block.
+ * If lower-half of pair of blocks to combine contained
+ * a return or a conditional branch or an explicit throw,
+ * move the flag to the newly combined block.
*/
bb->terminated_by_return = bb_next->terminated_by_return;
+ bb->conditional_branch = bb_next->conditional_branch;
+ bb->explicit_throw = bb_next->explicit_throw;
/*
* NOTE: we aren't updating all dataflow info here. Should either make sure this pass
@@ -744,17 +849,17 @@
if (bb->block_type == kEntryBlock) {
ssa_regs_to_check->ClearAllBits();
// Assume all ins are objects.
- for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins;
- in_reg < cu_->num_dalvik_registers; in_reg++) {
+ for (uint16_t in_reg = GetFirstInVR();
+ in_reg < GetNumOfCodeVRs(); in_reg++) {
ssa_regs_to_check->SetBit(in_reg);
}
if ((cu_->access_flags & kAccStatic) == 0) {
// If non-static method, mark "this" as non-null
- int this_reg = cu_->num_dalvik_registers - cu_->num_ins;
+ int this_reg = GetFirstInVR();
ssa_regs_to_check->ClearBit(this_reg);
}
- } else if (bb->predecessors->Size() == 1) {
- BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
+ } else if (bb->predecessors.size() == 1) {
+ BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
// pred_bb must have already been processed at least once.
DCHECK(pred_bb->data_flow_info->ending_check_v != nullptr);
ssa_regs_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
@@ -780,25 +885,22 @@
}
} else {
// Starting state is union of all incoming arcs
- GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
- BasicBlock* pred_bb = GetBasicBlock(iter.Next());
- CHECK(pred_bb != NULL);
- while (pred_bb->data_flow_info->ending_check_v == nullptr) {
- pred_bb = GetBasicBlock(iter.Next());
- // At least one predecessor must have been processed before this bb.
+ bool copied_first = false;
+ for (BasicBlockId pred_id : bb->predecessors) {
+ BasicBlock* pred_bb = GetBasicBlock(pred_id);
DCHECK(pred_bb != nullptr);
DCHECK(pred_bb->data_flow_info != nullptr);
- }
- ssa_regs_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
- while (true) {
- pred_bb = GetBasicBlock(iter.Next());
- if (!pred_bb) break;
- DCHECK(pred_bb->data_flow_info != nullptr);
if (pred_bb->data_flow_info->ending_check_v == nullptr) {
continue;
}
- ssa_regs_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+ if (!copied_first) {
+ copied_first = true;
+ ssa_regs_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
+ } else {
+ ssa_regs_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+ }
}
+ DCHECK(copied_first); // At least one predecessor must have been processed before this bb.
}
// At this point, ssa_regs_to_check shows which sregs have an object definition with
// no intervening uses.
@@ -964,7 +1066,7 @@
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
// Each insn we use here has at least 2 code units, offset/2 will be a unique index.
- const size_t end = (cu_->code_item->insns_size_in_code_units_ + 1u) / 2u;
+ const size_t end = (GetNumDalvikInsns() + 1u) / 2u;
temp_insn_data_ = static_cast<uint16_t*>(
temp_scoped_alloc_->Alloc(end * sizeof(*temp_insn_data_), kArenaAllocGrowableArray));
@@ -1053,35 +1155,31 @@
DCHECK(classes_to_check != nullptr);
if (bb->block_type == kEntryBlock) {
classes_to_check->SetInitialBits(temp_bit_vector_size_);
- } else if (bb->predecessors->Size() == 1) {
- BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
+ } else if (bb->predecessors.size() == 1) {
+ BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
// pred_bb must have already been processed at least once.
DCHECK(pred_bb != nullptr);
DCHECK(pred_bb->data_flow_info != nullptr);
DCHECK(pred_bb->data_flow_info->ending_check_v != nullptr);
classes_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
} else {
- // Starting state is union of all incoming arcs
- GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
- BasicBlock* pred_bb = GetBasicBlock(iter.Next());
- DCHECK(pred_bb != NULL);
- DCHECK(pred_bb->data_flow_info != NULL);
- while (pred_bb->data_flow_info->ending_check_v == nullptr) {
- pred_bb = GetBasicBlock(iter.Next());
- // At least one predecessor must have been processed before this bb.
+ // Starting state is union of all incoming arcs.
+ bool copied_first = false;
+ for (BasicBlockId pred_id : bb->predecessors) {
+ BasicBlock* pred_bb = GetBasicBlock(pred_id);
DCHECK(pred_bb != nullptr);
DCHECK(pred_bb->data_flow_info != nullptr);
- }
- classes_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
- while (true) {
- pred_bb = GetBasicBlock(iter.Next());
- if (!pred_bb) break;
- DCHECK(pred_bb->data_flow_info != nullptr);
if (pred_bb->data_flow_info->ending_check_v == nullptr) {
continue;
}
- classes_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+ if (!copied_first) {
+ copied_first = true;
+ classes_to_check->Copy(pred_bb->data_flow_info->ending_check_v);
+ } else {
+ classes_to_check->Union(pred_bb->data_flow_info->ending_check_v);
+ }
}
+ DCHECK(copied_first); // At least one predecessor must have been processed before this bb.
}
// At this point, classes_to_check shows which classes need clinit checks.
@@ -1209,8 +1307,8 @@
MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, &inlined_unit, &inlined_field_info, 1u);
DCHECK(inlined_field_info.IsResolved());
- uint32_t field_info_index = ifield_lowering_infos_.Size();
- ifield_lowering_infos_.Insert(inlined_field_info);
+ uint32_t field_info_index = ifield_lowering_infos_.size();
+ ifield_lowering_infos_.push_back(inlined_field_info);
temp_bit_vector_->SetBit(method_index);
temp_insn_data_[method_index] = field_info_index;
iget_or_iput->meta.ifield_lowering_info = field_info_index;
@@ -1218,7 +1316,7 @@
bool MIRGraph::InlineSpecialMethodsGate() {
if ((cu_->disable_opt & (1 << kSuppressMethodInlining)) != 0 ||
- method_lowering_infos_.Size() == 0u) {
+ method_lowering_infos_.size() == 0u) {
return false;
}
if (cu_->compiler_driver->GetMethodInlinerMap() == nullptr) {
@@ -1234,7 +1332,7 @@
DCHECK(temp_scoped_alloc_.get() == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- temp_bit_vector_size_ = method_lowering_infos_.Size();
+ temp_bit_vector_size_ = method_lowering_infos_.size();
temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapMisc);
temp_bit_vector_->ClearAllBits();
@@ -1250,18 +1348,27 @@
if (MIR::DecodedInstruction::IsPseudoMirOp(mir->dalvikInsn.opcode)) {
continue;
}
- if (!(Instruction::FlagsOf(mir->dalvikInsn.opcode) & Instruction::kInvoke)) {
+ if (!(mir->dalvikInsn.FlagsOf() & Instruction::kInvoke)) {
continue;
}
const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir);
if (!method_info.FastPath()) {
continue;
}
+
InvokeType sharp_type = method_info.GetSharpType();
- if ((sharp_type != kDirect) &&
- (sharp_type != kStatic || method_info.NeedsClassInitialization())) {
+ if ((sharp_type != kDirect) && (sharp_type != kStatic)) {
continue;
}
+
+ if (sharp_type == kStatic) {
+ bool needs_clinit = method_info.NeedsClassInitialization() &&
+ ((mir->optimization_flags & MIR_IGNORE_CLINIT_CHECK) == 0);
+ if (needs_clinit) {
+ continue;
+ }
+ }
+
DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
MethodReference target = method_info.GetTargetMethod();
if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(target.dex_file)
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index c510b52..55e547e 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -76,8 +76,8 @@
{ opcode, bb, field_info }
void DoPrepareSFields(const SFieldDef* defs, size_t count) {
- cu_.mir_graph->sfield_lowering_infos_.Reset();
- cu_.mir_graph->sfield_lowering_infos_.Resize(count);
+ cu_.mir_graph->sfield_lowering_infos_.clear();
+ cu_.mir_graph->sfield_lowering_infos_.reserve(count);
for (size_t i = 0u; i != count; ++i) {
const SFieldDef* def = &defs[i];
MirSFieldLoweringInfo field_info(def->field_idx);
@@ -89,7 +89,7 @@
}
ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
ASSERT_FALSE(field_info.IsInitialized());
- cu_.mir_graph->sfield_lowering_infos_.Insert(field_info);
+ cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
}
}
@@ -100,51 +100,43 @@
void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
cu_.mir_graph->block_id_map_.clear();
- cu_.mir_graph->block_list_.Reset();
+ cu_.mir_graph->block_list_.clear();
ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block.
ASSERT_EQ(kNullBlock, defs[0].type);
ASSERT_EQ(kEntryBlock, defs[1].type);
ASSERT_EQ(kExitBlock, defs[2].type);
for (size_t i = 0u; i != count; ++i) {
const BBDef* def = &defs[i];
- BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
- cu_.mir_graph->block_list_.Insert(bb);
+ BasicBlock* bb = cu_.mir_graph->CreateNewBB(def->type);
if (def->num_successors <= 2) {
bb->successor_block_list_type = kNotUsed;
- bb->successor_blocks = nullptr;
bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
} else {
bb->successor_block_list_type = kPackedSwitch;
bb->fall_through = 0u;
bb->taken = 0u;
- bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
- &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+ bb->successor_blocks.reserve(def->num_successors);
for (size_t j = 0u; j != def->num_successors; ++j) {
SuccessorBlockInfo* successor_block_info =
static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
kArenaAllocSuccessor));
successor_block_info->block = j;
successor_block_info->key = 0u; // Not used by class init check elimination.
- bb->successor_blocks->Insert(successor_block_info);
+ bb->successor_blocks.push_back(successor_block_info);
}
}
- bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
- &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
- for (size_t j = 0u; j != def->num_predecessors; ++j) {
- ASSERT_NE(0u, def->predecessors[j]);
- bb->predecessors->Insert(def->predecessors[j]);
- }
+ bb->predecessors.assign(def->predecessors, def->predecessors + def->num_predecessors);
if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
}
}
cu_.mir_graph->num_blocks_ = count;
- ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
- cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+ ASSERT_EQ(count, cu_.mir_graph->block_list_.size());
+ cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_[1];
ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
- cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+ cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_[2];
ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
}
@@ -161,11 +153,11 @@
const MIRDef* def = &defs[i];
MIR* mir = &mirs_[i];
mir->dalvikInsn.opcode = def->opcode;
- ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.Size());
- BasicBlock* bb = cu_.mir_graph->block_list_.Get(def->bbid);
+ ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size());
+ BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
bb->AppendMIR(mir);
if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
- ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.Size());
+ ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size());
mir->meta.sfield_lowering_info = def->field_or_method_info;
}
mir->ssa_rep = nullptr;
@@ -179,7 +171,7 @@
cu_.arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
memset(code_item_, 0, sizeof(DexFile::CodeItem));
code_item_->insns_size_in_code_units_ = 2u * count;
- cu_.mir_graph->current_code_item_ = cu_.code_item = code_item_;
+ cu_.mir_graph->current_code_item_ = code_item_;
}
template <size_t count>
@@ -408,12 +400,10 @@
// Add successor block info to the check block.
BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
check_bb->successor_block_list_type = kCatch;
- check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
- &cu_.arena, 2, kGrowableArraySuccessorBlocks);
SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
(cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
successor_block_info->block = catch_handler->id;
- check_bb->successor_blocks->Insert(successor_block_info);
+ check_bb->successor_blocks.push_back(successor_block_info);
PrepareMIRs(mirs);
PerformClassInitCheckElimination();
ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
diff --git a/compiler/dex/pass.h b/compiler/dex/pass.h
index dbb5366..c377426 100644
--- a/compiler/dex/pass.h
+++ b/compiler/dex/pass.h
@@ -81,7 +81,7 @@
* @param data the object containing data necessary for the pass.
* @return whether or not there is a change when walking the BasicBlock
*/
- virtual bool Worker(const PassDataHolder* data) const {
+ virtual bool Worker(PassDataHolder* data) const {
// Unused parameter.
UNUSED(data);
diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h
index bd8f53c..8a3eae1 100644
--- a/compiler/dex/pass_driver.h
+++ b/compiler/dex/pass_driver.h
@@ -161,6 +161,17 @@
print_pass_list_ = list;
}
+ /**
+ * @brief Used to set a string that contains the overridden pass options.
+ * @details An overridden pass option means that the pass uses this option
+ * instead of using its default option.
+ * @param s The string passed by user with overridden options. The string is in format
+ * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
+ */
+ static void SetOverriddenPassOptions(const std::string& s) {
+ overridden_pass_options_list_ = s;
+ }
+
void SetDefaultPasses() {
pass_list_ = PassDriver<PassDriverType>::g_default_pass_list;
}
@@ -206,6 +217,9 @@
/** @brief What are the passes we want to be dumping the CFG? */
static std::string dump_pass_list_;
+
+ /** @brief String of all options that should be overridden for selected passes */
+ static std::string overridden_pass_options_list_;
};
} // namespace art
diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h
index 133593c..537ceb6 100644
--- a/compiler/dex/pass_driver_me.h
+++ b/compiler/dex/pass_driver_me.h
@@ -17,6 +17,8 @@
#ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_H_
#define ART_COMPILER_DEX_PASS_DRIVER_ME_H_
+#include <cstdlib>
+#include <cstring>
#include "bb_optimizations.h"
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
@@ -94,19 +96,27 @@
c_unit->NewTimingSplit(pass->GetName());
}
+ // First, work on determining pass verbosity.
+ bool old_print_pass = c_unit->print_pass;
+ c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
+ const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
+ if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
+ c_unit->print_pass = true;
+ }
+
+ // Next, check if there are any overridden settings for the pass that change default configuration.
+ c_unit->overridden_pass_options.clear();
+ FillOverriddenPassSettings(pass->GetName(), c_unit->overridden_pass_options);
+ if (c_unit->print_pass) {
+ for (auto setting_it : c_unit->overridden_pass_options) {
+ LOG(INFO) << "Overridden option \"" << setting_it.first << ":"
+ << setting_it.second << "\" for pass \"" << pass->GetName() << "\"";
+ }
+ }
+
// Check the pass gate first.
bool should_apply_pass = pass->Gate(&pass_me_data_holder_);
if (should_apply_pass) {
- bool old_print_pass = c_unit->print_pass;
-
- c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
-
- const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
-
- if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
- c_unit->print_pass = true;
- }
-
// Applying the pass: first start, doWork, and end calls.
this->ApplyPass(&pass_me_data_holder_, pass);
@@ -137,10 +147,11 @@
}
}
}
-
- c_unit->print_pass = old_print_pass;
}
+ // Before wrapping up with this pass, restore old pass verbosity flag.
+ c_unit->print_pass = old_print_pass;
+
// If the pass gate passed, we can declare success.
return should_apply_pass;
}
@@ -149,6 +160,18 @@
return dump_cfg_folder_;
}
+ static void PrintPassOptions() {
+ for (auto pass : PassDriver<PassDriverType>::g_default_pass_list) {
+ const PassME* me_pass = down_cast<const PassME*>(pass);
+ if (me_pass->HasOptions()) {
+ LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:";
+ SafeMap<const std::string, int> overridden_settings;
+ FillOverriddenPassSettings(me_pass->GetName(), overridden_settings);
+ me_pass->PrintPassOptions(overridden_settings);
+ }
+ }
+ }
+
protected:
/** @brief The data holder that contains data needed for the PassDriverME. */
PassMEDataHolder pass_me_data_holder_;
@@ -175,6 +198,97 @@
Iterator iterator(c_unit->mir_graph.get());
DoWalkBasicBlocks(data, pass, &iterator);
}
+
+ /**
+ * @brief Fills the settings_to_fill by finding all of the applicable options in the overridden_pass_options_list_.
+ * @param pass_name The pass name for which to fill settings.
+ * @param settings_to_fill Fills the options to contain the mapping of name of option to the new configuration.
+ */
+ static void FillOverriddenPassSettings(const char* pass_name, SafeMap<const std::string, int>& settings_to_fill) {
+ const std::string& settings = PassDriver<PassDriverType>::overridden_pass_options_list_;
+ const size_t settings_len = settings.size();
+
+ // Before anything, check if we care about anything right now.
+ if (settings_len == 0) {
+ return;
+ }
+
+ const size_t pass_name_len = strlen(pass_name);
+ const size_t min_setting_size = 4; // 2 delimiters, 1 setting name, 1 setting
+ size_t search_pos = 0;
+
+ // If there is no room for pass options, exit early.
+ if (settings_len < pass_name_len + min_setting_size) {
+ return;
+ }
+
+ do {
+ search_pos = settings.find(pass_name, search_pos);
+
+ // Check if we found this pass name in rest of string.
+ if (search_pos == std::string::npos) {
+ // No more settings for this pass.
+ break;
+ }
+
+ // The string contains the pass name. Now check that there is
+ // room for the settings: at least one char for setting name,
+ // two chars for two delimiter, and at least one char for setting.
+ if (search_pos + pass_name_len + min_setting_size >= settings_len) {
+ // No more settings for this pass.
+ break;
+ }
+
+ // Update the current search position to not include the pass name.
+ search_pos += pass_name_len;
+
+ // The format must be "PassName:SettingName:#" where # is the setting.
+ // Thus look for the first ":" which must exist.
+ if (settings[search_pos] != ':') {
+ // Missing delimiter right after pass name.
+ continue;
+ } else {
+ search_pos += 1;
+ }
+
+ // Now look for the actual setting by finding the next ":" delimiter.
+ const size_t setting_name_pos = search_pos;
+ size_t setting_pos = settings.find(':', setting_name_pos);
+
+ if (setting_pos == std::string::npos) {
+ // Missing a delimiter that would capture where setting starts.
+ continue;
+ } else if (setting_pos == setting_name_pos) {
+ // Missing setting thus did not move from setting name
+ continue;
+ } else {
+ // Skip the delimiter.
+ setting_pos += 1;
+ }
+
+ // Look for the terminating delimiter which must be a comma.
+ size_t next_configuration_separator = settings.find(',', setting_pos);
+ if (next_configuration_separator == std::string::npos) {
+ next_configuration_separator = settings_len;
+ }
+
+ // Prevent end of string errors.
+ if (next_configuration_separator == setting_pos) {
+ continue;
+ }
+
+ // Get the actual setting itself. Strtol is being used to convert because it is
+ // exception safe. If the input is not sane, it will set a setting of 0.
+ std::string setting_string = settings.substr(setting_pos, next_configuration_separator - setting_pos);
+ int setting = std::strtol(setting_string.c_str(), 0, 0);
+
+ std::string setting_name = settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
+
+ settings_to_fill.Put(setting_name, setting);
+
+ search_pos = next_configuration_separator;
+ } while (true);
+ }
};
} // namespace art
#endif // ART_COMPILER_DEX_PASS_DRIVER_ME_H_
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index c72a4a6..6281062 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -69,20 +69,30 @@
template<>
bool PassDriver<PassDriverMEOpts>::default_print_passes_ = false;
-void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
- // First call the base class' version.
- PassDriver::ApplyPass(data, pass);
+// By default, there are no overridden pass settings.
+template<>
+std::string PassDriver<PassDriverMEOpts>::overridden_pass_options_list_ = std::string();
+void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
const PassME* pass_me = down_cast<const PassME*> (pass);
DCHECK(pass_me != nullptr);
PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+ // Set to dirty.
+ pass_me_data_holder->dirty = true;
+
+ // First call the base class' version.
+ PassDriver::ApplyPass(data, pass);
+
// Now we care about flags.
if ((pass_me->GetFlag(kOptimizationBasicBlockChange) == true) ||
(pass_me->GetFlag(kOptimizationDefUsesChange) == true)) {
- CompilationUnit* c_unit = pass_me_data_holder->c_unit;
- c_unit->mir_graph.get()->CalculateBasicBlockInformation();
+ // Is it dirty at least?
+ if (pass_me_data_holder->dirty == true) {
+ CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+ c_unit->mir_graph.get()->CalculateBasicBlockInformation();
+ }
}
}
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 14108af..4acab6c 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -73,4 +73,8 @@
template<>
bool PassDriver<PassDriverMEPostOpt>::default_print_passes_ = false;
+// By default, there are no overridden pass settings.
+template<>
+std::string PassDriver<PassDriverMEPostOpt>::overridden_pass_options_list_ = std::string();
+
} // namespace art
diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h
index c7276eb..8242cb8 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -42,7 +42,8 @@
public:
CompilationUnit* c_unit;
BasicBlock* bb;
- void* data;
+ void* data; /**< @brief Any data the pass wants to use */
+ bool dirty; /**< @brief Has the pass rendered the CFG dirty, requiring post-opt? */
};
enum DataFlowAnalysisMode {
@@ -80,12 +81,53 @@
}
~PassME() {
+ default_options_.clear();
}
virtual DataFlowAnalysisMode GetTraversal() const {
return traversal_type_;
}
+ /**
+ * @return Returns whether the pass has any configurable options.
+ */
+ bool HasOptions() const {
+ return default_options_.size() != 0;
+ }
+
+ /**
+ * @brief Prints the pass options along with default settings if there are any.
+ * @details The printing is done using LOG(INFO).
+ */
+ void PrintPassDefaultOptions() const {
+ for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
+ LOG(INFO) << "\t" << option_it->first << ":" << std::dec << option_it->second;
+ }
+ }
+
+ /**
+ * @brief Prints the pass options along with either default or overridden setting.
+ * @param overridden_options The overridden settings for this pass.
+ */
+ void PrintPassOptions(SafeMap<const std::string, int>& overridden_options) const {
+ // We walk through the default options only to get the pass names. We use GetPassOption to
+ // also consider the overridden ones.
+ for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
+ LOG(INFO) << "\t" << option_it->first << ":" << std::dec << GetPassOption(option_it->first, overridden_options);
+ }
+ }
+
+ /**
+ * @brief Used to obtain the option for a pass.
+ * @details Will return the overridden option if it exists or default one.
+ * @param option_name The name of option whose setting to look for.
+ * @param c_unit The compilation unit currently being handled.
+ * @return Returns the setting for the pass option.
+ */
+ int GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
+ return GetPassOption(option_name, c_unit->overridden_pass_options);
+ }
+
const char* GetDumpCFGFolder() const {
return dump_cfg_folder_;
}
@@ -95,6 +137,25 @@
}
protected:
+ int GetPassOption(const char* option_name, const SafeMap<const std::string, int>& overridden_options) const {
+ // First check if there are any overridden settings.
+ auto overridden_it = overridden_options.find(std::string(option_name));
+ if (overridden_it != overridden_options.end()) {
+ return overridden_it->second;
+ }
+
+ // Next check the default options.
+ auto default_it = default_options_.find(option_name);
+
+ if (default_it == default_options_.end()) {
+ // An invalid option is being requested.
+ DCHECK(false);
+ return 0;
+ }
+
+ return default_it->second;
+ }
+
/** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */
const DataFlowAnalysisMode traversal_type_;
@@ -103,6 +164,13 @@
/** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */
const char* const dump_cfg_folder_;
+
+ /**
+ * @brief Contains a map of options with the default settings.
+ * @details The constructor of the specific pass instance should fill this
+ * with default options.
+ * */
+ SafeMap<const char*, int> default_options_;
};
} // namespace art
#endif // ART_COMPILER_DEX_PASS_ME_H_
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
index fd67608..ba255e0 100644
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -64,7 +64,7 @@
}
::llvm::Value* MirConverter::GetLLVMValue(int s_reg) {
- return llvm_values_.Get(s_reg);
+ return llvm_values_[s_reg];
}
void MirConverter::SetVregOnValue(::llvm::Value* val, int s_reg) {
@@ -87,7 +87,7 @@
}
placeholder->replaceAllUsesWith(val);
val->takeName(placeholder);
- llvm_values_.Put(s_reg, val);
+ llvm_values_[s_reg] = val;
::llvm::Instruction* inst = ::llvm::dyn_cast< ::llvm::Instruction>(placeholder);
DCHECK(inst != NULL);
inst->eraseFromParent();
@@ -140,11 +140,11 @@
return GetLLVMBlock(bb->id);
}
-void MirConverter::ConvertPackedSwitch(BasicBlock* bb,
+void MirConverter::ConvertPackedSwitch(BasicBlock* bb, MIR* mir,
int32_t table_offset, RegLocation rl_src) {
const Instruction::PackedSwitchPayload* payload =
reinterpret_cast<const Instruction::PackedSwitchPayload*>(
- cu_->insns + current_dalvik_offset_ + table_offset);
+ mir_graph_->GetTable(mir, table_offset));
::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
@@ -164,11 +164,11 @@
bb->fall_through = NullBasicBlockId;
}
-void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
+void MirConverter::ConvertSparseSwitch(BasicBlock* bb, MIR* mir,
int32_t table_offset, RegLocation rl_src) {
const Instruction::SparseSwitchPayload* payload =
reinterpret_cast<const Instruction::SparseSwitchPayload*>(
- cu_->insns + current_dalvik_offset_ + table_offset);
+ mir_graph_->GetTable(mir, table_offset));
const int32_t* keys = payload->GetKeys();
const int32_t* targets = payload->GetTargets();
@@ -1536,9 +1536,9 @@
::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(id);
::llvm::Instruction* inst = irb_->CreateCall(intr);
::llvm::SmallVector< ::llvm::Value*, 2> reg_info;
- reg_info.push_back(irb_->getInt32(cu_->num_ins));
- reg_info.push_back(irb_->getInt32(cu_->num_regs));
- reg_info.push_back(irb_->getInt32(cu_->num_outs));
+ reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfInVRs()));
+ reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfLocalCodeVRs()));
+ reg_info.push_back(irb_->getInt32(mir_graph_->GetNumOfOutVRs()));
reg_info.push_back(irb_->getInt32(mir_graph_->GetNumUsedCompilerTemps()));
reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs()));
::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info);
@@ -1669,12 +1669,12 @@
art::llvm::IntrinsicHelper::IntrinsicId id =
art::llvm::IntrinsicHelper::AllocaShadowFrame;
::llvm::Function* func = intrinsic_helper_->GetIntrinsicFunction(id);
- ::llvm::Value* entries = irb_->getInt32(cu_->num_dalvik_registers);
+ ::llvm::Value* entries = irb_->getInt32(mir_graph_->GetNumOfCodeVRs());
irb_->CreateCall(func, entries);
}
{ // Store arguments to vregs.
- uint16_t arg_reg = cu_->num_regs;
+ uint16_t arg_reg = mir_graph_->GetFirstInVR();
::llvm::Function::arg_iterator arg_iter(func_->arg_begin());
@@ -1740,15 +1740,12 @@
art::llvm::IntrinsicHelper::CatchTargets);
::llvm::Value* switch_key =
irb_->CreateCall(intr, irb_->getInt32(mir->offset));
- GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
// New basic block to use for work half
::llvm::BasicBlock* work_bb =
::llvm::BasicBlock::Create(*context_, "", func_);
::llvm::SwitchInst* sw =
- irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks->Size());
- while (true) {
- SuccessorBlockInfo *successor_block_info = iter.Next();
- if (successor_block_info == NULL) break;
+ irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks.size());
+ for (SuccessorBlockInfo *successor_block_info : bb->successor_blocks) {
::llvm::BasicBlock *target =
GetLLVMBlock(successor_block_info->block);
int type_index = successor_block_info->key;
@@ -1843,7 +1840,7 @@
arg_iter->setName("method");
++arg_iter;
- int start_sreg = cu_->num_regs;
+ int start_sreg = mir_graph_->GetFirstInVR();
for (unsigned i = 0; arg_iter != arg_end; ++i, ++arg_iter) {
arg_iter->setName(StringPrintf("v%i_0", start_sreg));
@@ -1908,18 +1905,18 @@
::llvm::Value* val;
RegLocation rl_temp = mir_graph_->reg_location_[i];
if ((mir_graph_->SRegToVReg(i) < 0) || rl_temp.high_word) {
- llvm_values_.Insert(0);
- } else if ((i < cu_->num_regs) ||
- (i >= (cu_->num_regs + cu_->num_ins))) {
+ llvm_values_.push_back(0);
+ } else if ((i < mir_graph_->GetFirstInVR()) ||
+ (i >= (mir_graph_->GetFirstTempVR()))) {
::llvm::Constant* imm_value = mir_graph_->reg_location_[i].wide ?
irb_->getJLong(0) : irb_->getJInt(0);
val = EmitConst(imm_value, mir_graph_->reg_location_[i]);
val->setName(mir_graph_->GetSSAName(i));
- llvm_values_.Insert(val);
+ llvm_values_.push_back(val);
} else {
// Recover previously-created argument values
::llvm::Value* arg_val = arg_iter++;
- llvm_values_.Insert(arg_val);
+ llvm_values_.push_back(arg_val);
}
}
@@ -1966,7 +1963,7 @@
if (::llvm::verifyFunction(*func_, ::llvm::PrintMessageAction)) {
LOG(INFO) << "Bitcode verification FAILED for "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file)
- << " of size " << cu_->code_item->insns_size_in_code_units_;
+ << " of size " << mir_graph_->GetNumDalvikInsns();
cu_->enable_debug |= (1 << kDebugDumpBitcodeFile);
}
}
diff --git a/compiler/dex/portable/mir_to_gbc.h b/compiler/dex/portable/mir_to_gbc.h
index e97634c..94ae3f7 100644
--- a/compiler/dex/portable/mir_to_gbc.h
+++ b/compiler/dex/portable/mir_to_gbc.h
@@ -31,9 +31,48 @@
#include "llvm/intrinsic_helper.h"
#include "llvm/llvm_compilation_unit.h"
#include "safe_map.h"
+#include "utils/arena_containers.h"
+
+namespace llvm {
+ class Module;
+ class LLVMContext;
+}
namespace art {
+namespace llvm {
+ class IntrinsicHelper;
+ class IRBuilder;
+}
+
+class LLVMInfo {
+ public:
+ LLVMInfo();
+ ~LLVMInfo();
+
+ ::llvm::LLVMContext* GetLLVMContext() {
+ return llvm_context_.get();
+ }
+
+ ::llvm::Module* GetLLVMModule() {
+ return llvm_module_;
+ }
+
+ art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
+ return intrinsic_helper_.get();
+ }
+
+ art::llvm::IRBuilder* GetIRBuilder() {
+ return ir_builder_.get();
+ }
+
+ private:
+ std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
+ ::llvm::Module* llvm_module_; // Managed by context_.
+ std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
+ std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
+};
+
struct BasicBlock;
struct CallInfo;
struct CompilationUnit;
@@ -66,9 +105,10 @@
placeholder_bb_(NULL),
entry_bb_(NULL),
entry_target_bb_(NULL),
- llvm_values_(arena, mir_graph->GetNumSSARegs()),
+ llvm_values_(arena->Adapter()),
temp_name_(0),
current_dalvik_offset_(0) {
+ llvm_values_.reserve(mir_graph->GetNumSSARegs());
if (kIsDebugBuild) {
cu->enable_debug |= (1 << kDebugVerifyBitcode);
}
@@ -91,9 +131,9 @@
::llvm::Type* LlvmTypeFromLocRec(RegLocation loc);
void InitIR();
::llvm::BasicBlock* FindCaseTarget(uint32_t vaddr);
- void ConvertPackedSwitch(BasicBlock* bb, int32_t table_offset,
+ void ConvertPackedSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
RegLocation rl_src);
- void ConvertSparseSwitch(BasicBlock* bb, int32_t table_offset,
+ void ConvertSparseSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
RegLocation rl_src);
void ConvertSget(int32_t field_index,
art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
@@ -190,7 +230,7 @@
::llvm::BasicBlock* entry_bb_;
::llvm::BasicBlock* entry_target_bb_;
std::string bitcode_filename_;
- GrowableArray< ::llvm::Value*> llvm_values_;
+ ArenaVector< ::llvm::Value*> llvm_values_;
int32_t temp_name_;
SafeMap<int32_t, ::llvm::BasicBlock*> id_to_block_map_; // block id -> llvm bb.
int current_dalvik_offset_;
diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc
index 1371652..675dbcf 100644
--- a/compiler/dex/post_opt_passes.cc
+++ b/compiler/dex/post_opt_passes.cc
@@ -36,9 +36,9 @@
return res;
}
-bool MethodUseCount::Worker(const PassDataHolder* data) const {
+bool MethodUseCount::Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
CompilationUnit* c_unit = pass_me_data_holder->c_unit;
DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
@@ -49,9 +49,9 @@
}
-bool ClearPhiInstructions::Worker(const PassDataHolder* data) const {
+bool ClearPhiInstructions::Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
+ PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
CompilationUnit* c_unit = pass_me_data_holder->c_unit;
DCHECK(c_unit != nullptr);
BasicBlock* bb = pass_me_data_holder->bb;
@@ -84,7 +84,7 @@
// First clear all predecessors.
AllNodesIterator first(mir_graph);
for (BasicBlock* bb = first.Next(); bb != nullptr; bb = first.Next()) {
- bb->predecessors->Reset();
+ bb->predecessors.clear();
}
// Now calculate all predecessors.
@@ -100,7 +100,7 @@
// Now iterate through the children to set the predecessor bits.
for (BasicBlock* child = child_iter.Next(); child != nullptr; child = child_iter.Next()) {
- child->predecessors->Insert(bb->id);
+ child->predecessors.push_back(bb->id);
}
}
}
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index a1b0df4..e7805ba 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_DEX_POST_OPT_PASSES_H_
#define ART_COMPILER_DEX_POST_OPT_PASSES_H_
+#include "dex/quick/mir_to_lir.h"
#include "compiler_internals.h"
#include "pass_me.h"
@@ -52,7 +53,7 @@
MethodUseCount() : PassME("UseCount") {
}
- bool Worker(const PassDataHolder* data) const;
+ bool Worker(PassDataHolder* data) const;
bool Gate(const PassDataHolder* data) const;
};
@@ -66,7 +67,7 @@
ClearPhiInstructions() : PassME("ClearPhiInstructions") {
}
- bool Worker(const PassDataHolder* data) const;
+ bool Worker(PassDataHolder* data) const;
};
/**
@@ -222,11 +223,11 @@
PhiNodeOperands() : PassME("PhiNodeOperands", kPreOrderDFSTraversal) {
}
- bool Worker(const PassDataHolder* data) const {
+ bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
- BasicBlock* bb = down_cast<const PassMEDataHolder*>(data)->bb;
+ BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
DCHECK(bb != nullptr);
c_unit->mir_graph->InsertPhiNodeOperands(bb);
// No need of repeating, so just return false.
@@ -260,11 +261,11 @@
ConstantPropagation() : PassME("ConstantPropagation") {
}
- bool Worker(const PassDataHolder* data) const {
+ bool Worker(PassDataHolder* data) const {
DCHECK(data != nullptr);
- CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+ CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(c_unit != nullptr);
- BasicBlock* bb = down_cast<const PassMEDataHolder*>(data)->bb;
+ BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
DCHECK(bb != nullptr);
c_unit->mir_graph->DoConstantPropagation(bb);
// No need of repeating, so just return false.
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 6272555..d935bc3 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -528,6 +528,7 @@
kThumb2Vldms, // vldms rd, <list>.
kThumb2Vstms, // vstms rd, <list>.
kThumb2BUncond, // b <label>.
+ kThumb2Bl, // bl with linker fixup. [11110] S imm10 [11] J1 [1] J2 imm11.
kThumb2MovImm16H, // similar to kThumb2MovImm16, but target high hw.
kThumb2AddPCR, // Thumb2 2-operand add with hard-coded PC target.
kThumb2Adr, // Special purpose encoding of ADR for switch tables.
@@ -556,22 +557,24 @@
// Instruction assembly field_loc kind.
enum ArmEncodingKind {
- kFmtUnused, // Unused field and marks end of formats.
- kFmtBitBlt, // Bit string using end/start.
- kFmtDfp, // Double FP reg.
- kFmtSfp, // Single FP reg.
- kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
- kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
- kFmtImm6, // Encoded branch target using [9,7..3]0.
- kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
- kFmtShift, // Shift descriptor, [14..12,7..4].
- kFmtLsb, // least significant bit using [14..12][7..6].
- kFmtBWidth, // bit-field width, encoded as width-1.
- kFmtShift5, // Shift count, [14..12,7..6].
- kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
- kFmtFPImm, // Encoded floating point immediate.
- kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
- kFmtSkip, // Unused field, but continue to next.
+ kFmtUnused, // Unused field and marks end of formats.
+ kFmtBitBlt, // Bit string using end/start.
+ kFmtLdmRegList, // Load multiple register list using [15,14,12..0].
+ kFmtStmRegList, // Store multiple register list using [14,12..0].
+ kFmtDfp, // Double FP reg.
+ kFmtSfp, // Single FP reg.
+ kFmtModImm, // Shifted 8-bit immed using [26,14..12,7..0].
+ kFmtImm16, // Zero-extended immed using [26,19..16,14..12,7..0].
+ kFmtImm6, // Encoded branch target using [9,7..3]0.
+ kFmtImm12, // Zero-extended immediate using [26,14..12,7..0].
+ kFmtShift, // Shift descriptor, [14..12,7..4].
+ kFmtLsb, // least significant bit using [14..12][7..6].
+ kFmtBWidth, // bit-field width, encoded as width-1.
+ kFmtShift5, // Shift count, [14..12,7..6].
+ kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
+ kFmtFPImm, // Encoded floating point immediate.
+ kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
+ kFmtSkip, // Unused field, but continue to next.
};
// Struct used to define the snippet positions for each Thumb opcode.
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 35c3597..cf34948 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -427,7 +427,7 @@
REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4, kFixupVLoad),
ENCODING_MAP(kThumb2Vldrd, 0xed900b00,
kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
- kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF |
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF4 |
REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4, kFixupVLoad),
ENCODING_MAP(kThumb2Vmuls, 0xee200a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
@@ -560,12 +560,12 @@
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
"vmov.f64 ", " !0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
"ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2Stmia, 0xe8800000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
"stmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -935,7 +935,7 @@
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD_OFF,
"ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Stm, 0xe9000000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtStmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
"stm", "!0C, <!1R>", 4, kFixupNone),
@@ -968,6 +968,10 @@
kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
"b", "!0t", 4, kFixupT2Branch),
+ ENCODING_MAP(kThumb2Bl, 0xf000d000,
+ kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+ "bl", "!0T", 4, kFixupLabel),
ENCODING_MAP(kThumb2MovImm16H, 0xf2c00000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0,
@@ -992,7 +996,7 @@
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
"movt", "!0C, #!1M", 4, kFixupMovImmHST),
ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
- kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
+ kFmtBitBlt, 19, 16, kFmtLdmRegList, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
"ldmia", "!0C!!, <!1R>", 4, kFixupNone),
@@ -1094,6 +1098,19 @@
bits |= value;
} else {
switch (encoder->field_loc[i].kind) {
+ case kFmtLdmRegList:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ DCHECK_EQ((bits & (1 << 13)), 0u);
+ break;
+ case kFmtStmRegList:
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ DCHECK_EQ((bits & (1 << 13)), 0u);
+ DCHECK_EQ((bits & (1 << 15)), 0u);
+ break;
case kFmtSkip:
break; // Nothing to do, but continue to next.
case kFmtUnused:
diff --git a/compiler/dex/quick/arm/backend_arm.h b/compiler/dex/quick/arm/backend_arm.h
new file mode 100644
index 0000000..42a9bca
--- /dev/null
+++ b/compiler/dex/quick/arm/backend_arm.h
@@ -0,0 +1,32 @@
+/*
+ * 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_COMPILER_DEX_QUICK_ARM_BACKEND_ARM_H_
+#define ART_COMPILER_DEX_QUICK_ARM_BACKEND_ARM_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_QUICK_ARM_BACKEND_ARM_H_
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 4ba3c4b..b721e02 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -20,6 +20,8 @@
#include "codegen_arm.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "gc/accounting/card_table.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
namespace art {
@@ -44,7 +46,7 @@
* cbnz r_idx, lp
*/
void ArmMir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
@@ -55,7 +57,7 @@
tab_rec->vaddr = current_dalvik_offset_;
uint32_t size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
@@ -92,7 +94,7 @@
void ArmMir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
@@ -104,7 +106,7 @@
uint32_t size = table[1];
tab_rec->targets =
static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
@@ -147,8 +149,8 @@
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void ArmMir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
// Add the table to the list - we'll process it later
FillArrayData *tab_rec =
static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
@@ -158,7 +160,7 @@
uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
tab_rec->size = (size * width) + 8;
- fill_array_data_.Insert(tab_rec);
+ fill_array_data_.push_back(tab_rec);
// Making a call - use explicit registers
FlushAllRegs(); /* Everything to home location */
@@ -499,4 +501,117 @@
NewLIR1(kThumbBx, rs_rARM_LR.GetReg());
}
+static bool ArmUseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
+ // Emit relative calls only within a dex file due to the limited range of the BL insn.
+ return cu->dex_file == target_method.dex_file;
+}
+
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t unused,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ if (direct_code != 0 && direct_method != 0) {
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ if (direct_code != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else if (ArmUseRelativeCall(cu, target_method)) {
+ // Defer to linker patch.
+ } else {
+ cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+ }
+ if (direct_method != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+ } else {
+ cg->LoadMethodAddress(target_method, type, kArg0);
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ // TUNING: we can save a reg copy if Method* has been promoted.
+ cg->LoadCurrMethodDirect(arg0_ref);
+ break;
+ case 1: // Get method->dex_cache_resolved_methods_
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ // Set up direct code if known.
+ if (direct_code != 0) {
+ if (direct_code != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else if (ArmUseRelativeCall(cu, target_method)) {
+ // Defer to linker patch.
+ } else {
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+ cg->LoadCodeAddress(target_method, type, kInvokeTgt);
+ }
+ }
+ break;
+ case 2: // Grab target method*
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+ target_method.dex_method_index).Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ break;
+ case 3: // Grab the code from the method*
+ if (direct_code == 0) {
+ // kInvokeTgt := arg0_ref->entrypoint
+ cg->LoadWordDisp(arg0_ref,
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(),
+ cg->TargetPtrReg(kInvokeTgt));
+ }
+ break;
+ default:
+ return -1;
+ }
+ }
+ return state + 1;
+}
+
+NextCallInsn ArmMir2Lir::GetNextSDCallInsn() {
+ return ArmNextSDCallInsn;
+}
+
+LIR* ArmMir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
+ // For ARM, just generate a relative BL instruction that will be filled in at 'link time'.
+ // If the target turns out to be too far, the linker will generate a thunk for dispatch.
+ int target_method_idx = target_method.dex_method_index;
+ const DexFile* target_dex_file = target_method.dex_file;
+
+ // Generate the call instruction and save index, dex_file, and type.
+ // NOTE: Method deduplication takes linker patches into account, so we can just pass 0
+ // as a placeholder for the offset.
+ LIR* call = RawLIR(current_dalvik_offset_, kThumb2Bl, 0,
+ target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
+ AppendLIR(call);
+ call_method_insns_.push_back(call);
+ return call;
+}
+
+LIR* ArmMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+ LIR* call_insn;
+ if (method_info.FastPath() && ArmUseRelativeCall(cu_, method_info.GetTargetMethod()) &&
+ (method_info.GetSharpType() == kDirect || method_info.GetSharpType() == kStatic) &&
+ method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
+ call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
+ } else {
+ call_insn = OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+ }
+ return call_insn;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index cd6c9cc..932dd87 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -19,6 +19,8 @@
#include "arm_lir.h"
#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
+#include "utils/arena_containers.h"
namespace art {
@@ -116,7 +118,7 @@
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
void GenSpecialExitSequence();
- void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
+ void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
@@ -184,6 +186,28 @@
return false; // Wide FPRs are formed by pairing.
}
+ NextCallInsn GetNextSDCallInsn() OVERRIDE;
+
+ /*
+ * @brief Generate a relative call to the method that will be patched at link time.
+ * @param target_method The MethodReference of the method to be invoked.
+ * @param type How the method will be invoked.
+ * @returns Call instruction
+ */
+ LIR* CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+
+ /*
+ * @brief Generate the actual call insn based on the method info.
+ * @param method_info the lowering info for the method call.
+ * @returns Call instruction
+ */
+ LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
+
+ /*
+ * @brief Handle ARM specific literals.
+ */
+ void InstallLiteralPools() OVERRIDE;
+
LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
size_t GetInstructionOffset(LIR* lir);
@@ -214,6 +238,8 @@
static constexpr ResourceMask GetRegMaskArm(RegStorage reg);
static constexpr ResourceMask EncodeArmRegList(int reg_list);
static constexpr ResourceMask EncodeArmRegFpcsList(int reg_list);
+
+ ArenaVector<LIR*> call_method_insns_;
};
} // namespace art
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 0de2a44..1a4b23e 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -377,7 +377,7 @@
* TODO: consider interspersing slowpaths in code following unconditional branches.
*/
bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget));
- skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64);
+ skip &= ((mir_graph_->GetNumDalvikInsns() - current_dalvik_offset_) > 64);
if (!skip && reg.Low8() && (check_value == 0)) {
if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 0509ad3..dd8f7fe 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -20,6 +20,7 @@
#include <string>
+#include "backend_arm.h"
#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir-inl.h"
@@ -451,6 +452,11 @@
reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1),
lir->target);
break;
+ case 'T':
+ snprintf(tbuf, arraysize(tbuf), "%s", PrettyMethod(
+ static_cast<uint32_t>(lir->operands[1]),
+ *reinterpret_cast<const DexFile*>(UnwrapPointer(lir->operands[2]))).c_str());
+ break;
case 'u': {
int offset_1 = lir->operands[0];
int offset_2 = NEXT_LIR(lir)->operands[0];
@@ -550,7 +556,9 @@
}
ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
- : Mir2Lir(cu, mir_graph, arena) {
+ : Mir2Lir(cu, mir_graph, arena),
+ call_method_insns_(arena->Adapter()) {
+ call_method_insns_.reserve(100);
// Sanity check - make sure encoding map lines up.
for (int i = 0; i < kArmLast; i++) {
if (ArmMir2Lir::EncodingMap[i].opcode != i) {
@@ -567,16 +575,16 @@
}
void ArmMir2Lir::CompilerInitializeRegAlloc() {
- reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */, sp_regs,
- dp_regs, reserved_regs, empty_pool /* reserved64 */,
- core_temps, empty_pool /* core64_temps */, sp_temps,
- dp_temps);
+ reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */,
+ sp_regs, dp_regs,
+ reserved_regs, empty_pool /* reserved64 */,
+ core_temps, empty_pool /* core64_temps */,
+ sp_temps, dp_temps));
// Target-specific adjustments.
// Alias single precision floats to appropriate half of overlapping double.
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->sp_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->sp_regs_) {
int sp_reg_num = info->GetReg().GetRegNum();
int dp_reg_num = sp_reg_num >> 1;
RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num);
@@ -783,8 +791,7 @@
* TODO: until runtime support is in, make sure we avoid promoting the same vreg to
* different underlying physical registers.
*/
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->dp_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->dp_regs_) {
if (!info->IsTemp() && !info->InUse()) {
res = info->GetReg();
info->MarkInUse();
@@ -808,8 +815,7 @@
// Reserve a callee-save sp single register.
RegStorage ArmMir2Lir::AllocPreservedSingle(int s_reg) {
RegStorage res;
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->sp_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->sp_regs_) {
if (!info->IsTemp() && !info->InUse()) {
res = info->GetReg();
int p_map_idx = SRegToPMap(s_reg);
@@ -824,4 +830,21 @@
return res;
}
+void ArmMir2Lir::InstallLiteralPools() {
+ // PC-relative calls to methods.
+ patches_.reserve(call_method_insns_.size());
+ for (LIR* p : call_method_insns_) {
+ DCHECK_EQ(p->opcode, kThumb2Bl);
+ uint32_t target_method_idx = p->operands[1];
+ const DexFile* target_dex_file =
+ reinterpret_cast<const DexFile*>(UnwrapPointer(p->operands[2]));
+
+ patches_.push_back(LinkerPatch::RelativeCodePatch(p->offset,
+ target_dex_file, target_method_idx));
+ }
+
+ // And do the normal processing.
+ Mir2Lir::InstallLiteralPools();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h
index a449cbd..ab71921 100644
--- a/compiler/dex/quick/arm64/arm64_lir.h
+++ b/compiler/dex/quick/arm64/arm64_lir.h
@@ -22,77 +22,75 @@
namespace art {
/*
- * TODO(Arm64): the comments below are outdated.
- *
* Runtime register usage conventions.
*
- * r0-r3: Argument registers in both Dalvik and C/C++ conventions.
- * However, for Dalvik->Dalvik calls we'll pass the target's Method*
- * pointer in r0 as a hidden arg0. Otherwise used as codegen scratch
- * registers.
- * r0-r1: As in C/C++ r0 is 32-bit return register and r0/r1 is 64-bit
- * r4 : (rA64_SUSPEND) is reserved (suspend check/debugger assist)
- * r5 : Callee save (promotion target)
- * r6 : Callee save (promotion target)
- * r7 : Callee save (promotion target)
- * r8 : Callee save (promotion target)
- * r9 : (rA64_SELF) is reserved (pointer to thread-local storage)
- * r10 : Callee save (promotion target)
- * r11 : Callee save (promotion target)
- * r12 : Scratch, may be trashed by linkage stubs
- * r13 : (sp) is reserved
- * r14 : (lr) is reserved
- * r15 : (pc) is reserved
+ * r0 : As in C/C++ w0 is 32-bit return register and x0 is 64-bit.
+ * r0-r7 : Argument registers in both Dalvik and C/C++ conventions.
+ * However, for Dalvik->Dalvik calls we'll pass the target's Method*
+ * pointer in x0 as a hidden arg0. Otherwise used as codegen scratch
+ * registers.
+ * r8-r15 : Caller save registers (used as temporary registers).
+ * r16-r17: Also known as ip0-ip1, respectively. Used as scratch registers by
+ * the linker, by the trampolines and other stubs (the backend uses
+ * these as temporary registers).
+ * r18 : (rxSELF) is reserved (pointer to thread-local storage).
+ * r19 : (rwSUSPEND) is reserved (suspend check/debugger assist).
+ * r20-r29: Callee save registers (promotion targets).
+ * r30 : (lr) is reserved (the link register).
+ * rsp : (sp) is reserved (the stack pointer).
+ * rzr : (zr) is reserved (the zero register).
*
- * 5 core temps that codegen can use (r0, r1, r2, r3, r12)
- * 7 core registers that can be used for promotion
+ * 18 core temps that codegen can use (r0-r17).
+ * 10 core registers that can be used for promotion.
*
- * Floating pointer registers
- * s0-s31
- * d0-d15, where d0={s0,s1}, d1={s2,s3}, ... , d15={s30,s31}
+ * Floating-point registers
+ * v0-v31
*
- * s16-s31 (d8-d15) preserved across C calls
- * s0-s15 (d0-d7) trashed across C calls
+ * v0 : s0 is return register for singles (32-bit) and d0 for doubles (64-bit).
+ * This is analogous to the C/C++ (hard-float) calling convention.
+ * v0-v7 : Floating-point argument registers in both Dalvik and C/C++ conventions.
+ * Also used as temporary and codegen scratch registers.
*
- * s0-s15/d0-d7 used as codegen temp/scratch
- * s16-s31/d8-d31 can be used for promotion.
+ * v0-v7 and v16-v31 : trashed across C calls.
+ * v8-v15 : bottom 64-bits preserved across C calls (d8-d15 are preserved).
*
- * Calling convention
- * o On a call to a Dalvik method, pass target's Method* in r0
- * o r1-r3 will be used for up to the first 3 words of arguments
- * o Arguments past the first 3 words will be placed in appropriate
+ * v16-v31: Used as codegen temp/scratch.
+ * v8-v15 : Can be used for promotion.
+ *
+ * Calling convention (Hard-float)
+ * o On a call to a Dalvik method, pass target's Method* in x0
+ * o r1-r7, v0-v7 will be used for the first 7+8 arguments
+ * o Arguments which cannot be put in registers are placed in appropriate
* out slots by the caller.
- * o If a 64-bit argument would span the register/memory argument
- * boundary, it will instead be fully passed in the frame.
* o Maintain a 16-byte stack alignment
*
* Stack frame diagram (stack grows down, higher addresses at top):
*
- * +------------------------+
- * | IN[ins-1] | {Note: resides in caller's frame}
- * | . |
- * | IN[0] |
- * | caller's Method* |
- * +========================+ {Note: start of callee's frame}
- * | spill region | {variable sized - will include lr if non-leaf.}
- * +------------------------+
- * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
- * +------------------------+
- * | V[locals-1] |
- * | V[locals-2] |
- * | . |
- * | . |
- * | V[1] |
- * | V[0] |
- * +------------------------+
- * | 0 to 3 words padding |
- * +------------------------+
- * | OUT[outs-1] |
- * | OUT[outs-2] |
- * | . |
- * | OUT[0] |
- * | cur_method* | <<== sp w/ 16-byte alignment
- * +========================+
+ * +--------------------------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's method (StackReference<ArtMethod>)| {This is a compressed (4-bytes) reference}
+ * +============================================+ {Note: start of callee's frame}
+ * | spill region | {variable sized - will include lr if non-leaf}
+ * +--------------------------------------------+
+ * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long}
+ * +--------------------------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . |
+ * | V[1] |
+ * | V[0] |
+ * +--------------------------------------------+
+ * | 0 to 3 words padding |
+ * +--------------------------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | current method (StackReference<ArtMethod>) | <<== sp w/ 16-byte alignment
+ * +============================================+
*/
// First FP callee save.
@@ -103,12 +101,12 @@
#define A64_REG_IS_ZR(reg_num) ((reg_num) == rwzr || (reg_num) == rxzr)
#define A64_REGSTORAGE_IS_SP_OR_ZR(rs) (((rs).GetRegNum() & 0x1f) == 0x1f)
-enum Arm64ResourceEncodingPos {
- kArm64GPReg0 = 0,
- kArm64RegLR = 30,
- kArm64RegSP = 31,
- kArm64FPReg0 = 32,
- kArm64RegEnd = 64,
+enum A64ResourceEncodingPos {
+ kA64GPReg0 = 0,
+ kA64RegLR = 30,
+ kA64RegSP = 31,
+ kA64FPReg0 = 32,
+ kA64RegEnd = 64,
};
#define IS_SIGNED_IMM(size, value) \
@@ -116,6 +114,7 @@
#define IS_SIGNED_IMM7(value) IS_SIGNED_IMM(7, value)
#define IS_SIGNED_IMM9(value) IS_SIGNED_IMM(9, value)
#define IS_SIGNED_IMM12(value) IS_SIGNED_IMM(12, value)
+#define IS_SIGNED_IMM14(value) IS_SIGNED_IMM(14, value)
#define IS_SIGNED_IMM19(value) IS_SIGNED_IMM(19, value)
#define IS_SIGNED_IMM21(value) IS_SIGNED_IMM(21, value)
@@ -185,15 +184,15 @@
constexpr RegStorage rs_wLR(RegStorage::kValid | rwLR);
// RegisterLocation templates return values (following the hard-float calling convention).
-const RegLocation arm_loc_c_return =
+const RegLocation a64_loc_c_return =
{kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_w0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_ref =
+const RegLocation a64_loc_c_return_ref =
{kLocPhysReg, 0, 0, 0, 0, 0, 1, 0, 1, rs_x0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_wide =
+const RegLocation a64_loc_c_return_wide =
{kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_x0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_float =
+const RegLocation a64_loc_c_return_float =
{kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, rs_f0, INVALID_SREG, INVALID_SREG};
-const RegLocation arm_loc_c_return_double =
+const RegLocation a64_loc_c_return_double =
{kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, rs_d0, INVALID_SREG, INVALID_SREG};
/**
@@ -227,7 +226,7 @@
* assembler. Their corresponding EncodingMap positions will be defined in
* assemble_arm64.cc.
*/
-enum ArmOpcode {
+enum A64Opcode {
kA64First = 0,
kA64Adc3rrr = kA64First, // adc [00011010000] rm[20-16] [000000] rn[9-5] rd[4-0].
kA64Add4RRdT, // add [s001000100] imm_12[21-10] rn[9-5] rd[4-0].
@@ -355,7 +354,10 @@
kA64Sub4rrro, // sub [s1001011000] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0].
kA64Sub4RRre, // sub [s1001011001] rm[20-16] option[15-13] imm_3[12-10] rn[9-5] rd[4-0].
kA64Subs3rRd, // subs[s111000100] imm_12[21-10] rn[9-5] rd[4-0].
+ kA64Tst2rl, // tst alias of "ands rzr, rn, #imm".
kA64Tst3rro, // tst alias of "ands rzr, arg1, arg2, arg3".
+ kA64Tbnz3rht, // tbnz imm_6_b5[31] [0110111] imm_6_b40[23-19] imm_14[18-5] rt[4-0].
+ kA64Tbz3rht, // tbz imm_6_b5[31] [0110110] imm_6_b40[23-19] imm_14[18-5] rt[4-0].
kA64Ubfm4rrdd, // ubfm[s10100110] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0].
kA64Last,
kA64NotWide = 0, // Flag used to select the first instruction variant.
@@ -371,22 +373,13 @@
*/
// Return the wide and no-wide variants of the given opcode.
-#define WIDE(op) ((ArmOpcode)((op) | kA64Wide))
-#define UNWIDE(op) ((ArmOpcode)((op) & ~kA64Wide))
+#define WIDE(op) ((A64Opcode)((op) | kA64Wide))
+#define UNWIDE(op) ((A64Opcode)((op) & ~kA64Wide))
// Whether the given opcode is wide.
#define IS_WIDE(op) (((op) & kA64Wide) != 0)
-/*
- * Floating point variants. These are just aliases of the macros above which we use for floating
- * point instructions, just for readibility reasons.
- * TODO(Arm64): should we remove these and use the original macros?
- */
-#define FWIDE WIDE
-#define FUNWIDE UNWIDE
-#define IS_FWIDE IS_WIDE
-
-enum ArmOpDmbOptions {
+enum A64OpDmbOptions {
kSY = 0xf,
kST = 0xe,
kISH = 0xb,
@@ -397,38 +390,39 @@
};
// Instruction assembly field_loc kind.
-enum ArmEncodingKind {
+enum A64EncodingKind {
// All the formats below are encoded in the same way (as a kFmtBitBlt).
// These are grouped together, for fast handling (e.g. "if (LIKELY(fmt <= kFmtBitBlt)) ...").
- kFmtRegW = 0, // Word register (w) or wzr.
- kFmtRegX, // Extended word register (x) or xzr.
- kFmtRegR, // Register with same width as the instruction or zr.
- kFmtRegWOrSp, // Word register (w) or wsp.
- kFmtRegXOrSp, // Extended word register (x) or sp.
- kFmtRegROrSp, // Register with same width as the instruction or sp.
- kFmtRegS, // Single FP reg.
- kFmtRegD, // Double FP reg.
- kFmtRegF, // Single/double FP reg depending on the instruction width.
- kFmtBitBlt, // Bit string using end/start.
+ kFmtRegW = 0, // Word register (w) or wzr.
+ kFmtRegX, // Extended word register (x) or xzr.
+ kFmtRegR, // Register with same width as the instruction or zr.
+ kFmtRegWOrSp, // Word register (w) or wsp.
+ kFmtRegXOrSp, // Extended word register (x) or sp.
+ kFmtRegROrSp, // Register with same width as the instruction or sp.
+ kFmtRegS, // Single FP reg.
+ kFmtRegD, // Double FP reg.
+ kFmtRegF, // Single/double FP reg depending on the instruction width.
+ kFmtBitBlt, // Bit string using end/start.
// Less likely formats.
- kFmtUnused, // Unused field and marks end of formats.
- kFmtImm21, // Sign-extended immediate using [23..5,30..29].
- kFmtShift, // Register shift, 9-bit at [23..21, 15..10]..
- kFmtExtend, // Register extend, 9-bit at [23..21, 15..10].
- kFmtSkip, // Unused field, but continue to next.
+ kFmtUnused, // Unused field and marks end of formats.
+ kFmtImm6Shift, // Shift immediate, 6-bit at [31, 23..19].
+ kFmtImm21, // Sign-extended immediate using [23..5,30..29].
+ kFmtShift, // Register shift, 9-bit at [23..21, 15..10]..
+ kFmtExtend, // Register extend, 9-bit at [23..21, 15..10].
+ kFmtSkip, // Unused field, but continue to next.
};
// Struct used to define the snippet positions for each A64 opcode.
-struct ArmEncodingMap {
+struct A64EncodingMap {
uint32_t wskeleton;
uint32_t xskeleton;
struct {
- ArmEncodingKind kind;
+ A64EncodingKind kind;
int end; // end for kFmtBitBlt, 1-bit slice end for FP regs.
int start; // start for kFmtBitBlt, 4-bit slice end for FP regs.
} field_loc[4];
- ArmOpcode opcode; // can be WIDE()-ned to indicate it has a wide variant.
+ A64Opcode opcode; // can be WIDE()-ned to indicate it has a wide variant.
uint64_t flags;
const char* name;
const char* fmt;
@@ -436,25 +430,6 @@
FixupKind fixup;
};
-#if 0
-// TODO(Arm64): try the following alternative, which fits exactly in one cache line (64 bytes).
-struct ArmEncodingMap {
- uint32_t wskeleton;
- uint32_t xskeleton;
- uint64_t flags;
- const char* name;
- const char* fmt;
- struct {
- uint8_t kind;
- int8_t end; // end for kFmtBitBlt, 1-bit slice end for FP regs.
- int8_t start; // start for kFmtBitBlt, 4-bit slice end for FP regs.
- } field_loc[4];
- uint32_t fixup;
- uint32_t opcode; // can be WIDE()-ned to indicate it has a wide variant.
- uint32_t padding[3];
-};
-#endif
-
} // namespace art
#endif // ART_COMPILER_DEX_QUICK_ARM64_ARM64_LIR_H_
diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc
index 15c89f2..b1cf279 100644
--- a/compiler/dex/quick/arm64/assemble_arm64.cc
+++ b/compiler/dex/quick/arm64/assemble_arm64.cc
@@ -47,7 +47,7 @@
CUSTOM_VARIANTS(type00_skeleton, (type00_skeleton | 0x00400000))
/*
- * opcode: ArmOpcode enum
+ * opcode: A64Opcode enum
* variants: instruction skeletons supplied via CUSTOM_VARIANTS or derived macros.
* a{n}k: key to applying argument {n} \
* a{n}s: argument {n} start bit position | n = 0, 1, 2, 3
@@ -89,6 +89,7 @@
* M -> 16-bit shift expression ("" or ", lsl #16" or ", lsl #32"...)
* B -> dmb option string (sy, st, ish, ishst, nsh, hshst)
* H -> operand shift
+ * h -> 6-bit shift immediate
* T -> register shift (either ", lsl #0" or ", lsl #12")
* e -> register extend (e.g. uxtb #1)
* o -> register shift (e.g. lsl #1) for Word registers
@@ -101,8 +102,8 @@
*
* [!] escape. To insert "!", use "!!"
*/
-/* NOTE: must be kept in sync with enum ArmOpcode from arm64_lir.h */
-const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = {
+/* NOTE: must be kept in sync with enum A64Opcode from arm64_lir.h */
+const A64EncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = {
ENCODING_MAP(WIDE(kA64Adc3rrr), SF_VARIANTS(0x1a000000),
kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
@@ -228,27 +229,27 @@
kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16,
kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE12,
"extr", "!0r, !1r, !2r, #!3d", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fabs2ff), FLOAT_VARIANTS(0x1e20c000),
+ ENCODING_MAP(WIDE(kA64Fabs2ff), FLOAT_VARIANTS(0x1e20c000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP| REG_DEF0_USE1,
"fabs", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fadd3fff), FLOAT_VARIANTS(0x1e202800),
+ ENCODING_MAP(WIDE(kA64Fadd3fff), FLOAT_VARIANTS(0x1e202800),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"fadd", "!0f, !1f, !2f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fcmp1f), FLOAT_VARIANTS(0x1e202008),
+ ENCODING_MAP(WIDE(kA64Fcmp1f), FLOAT_VARIANTS(0x1e202008),
kFmtRegF, 9, 5, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE0 | SETS_CCODES,
"fcmp", "!0f, #0", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fcmp2ff), FLOAT_VARIANTS(0x1e202000),
+ ENCODING_MAP(WIDE(kA64Fcmp2ff), FLOAT_VARIANTS(0x1e202000),
kFmtRegF, 9, 5, kFmtRegF, 20, 16, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
"fcmp", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fcvtzs2wf), FLOAT_VARIANTS(0x1e380000),
+ ENCODING_MAP(WIDE(kA64Fcvtzs2wf), FLOAT_VARIANTS(0x1e380000),
kFmtRegW, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"fcvtzs", "!0w, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fcvtzs2xf), FLOAT_VARIANTS(0x9e380000),
+ ENCODING_MAP(WIDE(kA64Fcvtzs2xf), FLOAT_VARIANTS(0x9e380000),
kFmtRegX, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"fcvtzs", "!0x, !1f", kFixupNone),
@@ -268,23 +269,23 @@
kFmtRegX, 4, 0, kFmtRegD, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"fcvtms", "!0x, !1S", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fdiv3fff), FLOAT_VARIANTS(0x1e201800),
+ ENCODING_MAP(WIDE(kA64Fdiv3fff), FLOAT_VARIANTS(0x1e201800),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"fdiv", "!0f, !1f, !2f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fmax3fff), FLOAT_VARIANTS(0x1e204800),
+ ENCODING_MAP(WIDE(kA64Fmax3fff), FLOAT_VARIANTS(0x1e204800),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"fmax", "!0f, !1f, !2f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fmin3fff), FLOAT_VARIANTS(0x1e205800),
+ ENCODING_MAP(WIDE(kA64Fmin3fff), FLOAT_VARIANTS(0x1e205800),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"fmin", "!0f, !1f, !2f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fmov2ff), FLOAT_VARIANTS(0x1e204000),
+ ENCODING_MAP(WIDE(kA64Fmov2ff), FLOAT_VARIANTS(0x1e204000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1 | IS_MOVE,
"fmov", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fmov2fI), FLOAT_VARIANTS(0x1e201000),
+ ENCODING_MAP(WIDE(kA64Fmov2fI), FLOAT_VARIANTS(0x1e201000),
kFmtRegF, 4, 0, kFmtBitBlt, 20, 13, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
"fmov", "!0f, #!1I", kFixupNone),
@@ -304,35 +305,35 @@
kFmtRegX, 4, 0, kFmtRegD, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"fmov", "!0x, !1S", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fmul3fff), FLOAT_VARIANTS(0x1e200800),
+ ENCODING_MAP(WIDE(kA64Fmul3fff), FLOAT_VARIANTS(0x1e200800),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"fmul", "!0f, !1f, !2f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fneg2ff), FLOAT_VARIANTS(0x1e214000),
+ ENCODING_MAP(WIDE(kA64Fneg2ff), FLOAT_VARIANTS(0x1e214000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"fneg", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Frintp2ff), FLOAT_VARIANTS(0x1e24c000),
+ ENCODING_MAP(WIDE(kA64Frintp2ff), FLOAT_VARIANTS(0x1e24c000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"frintp", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Frintm2ff), FLOAT_VARIANTS(0x1e254000),
+ ENCODING_MAP(WIDE(kA64Frintm2ff), FLOAT_VARIANTS(0x1e254000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"frintm", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Frintn2ff), FLOAT_VARIANTS(0x1e244000),
+ ENCODING_MAP(WIDE(kA64Frintn2ff), FLOAT_VARIANTS(0x1e244000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"frintn", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Frintz2ff), FLOAT_VARIANTS(0x1e25c000),
+ ENCODING_MAP(WIDE(kA64Frintz2ff), FLOAT_VARIANTS(0x1e25c000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"frintz", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fsqrt2ff), FLOAT_VARIANTS(0x1e61c000),
+ ENCODING_MAP(WIDE(kA64Fsqrt2ff), FLOAT_VARIANTS(0x1e61c000),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"fsqrt", "!0f, !1f", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Fsub3fff), FLOAT_VARIANTS(0x1e203800),
+ ENCODING_MAP(WIDE(kA64Fsub3fff), FLOAT_VARIANTS(0x1e203800),
kFmtRegF, 4, 0, kFmtRegF, 9, 5, kFmtRegF, 20, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
"fsub", "!0f, !1f, !2f", kFixupNone),
@@ -368,7 +369,7 @@
kFmtRegR, 4, 0, kFmtRegXOrSp, 9, 5, kFmtRegX, 20, 16,
kFmtBitBlt, 12, 12, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD_OFF,
"ldrsh", "!0r, [!1X, !2x, lsl #!3d]", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Ldr2fp), SIZE_VARIANTS(0x1c000000),
+ ENCODING_MAP(WIDE(kA64Ldr2fp), SIZE_VARIANTS(0x1c000000),
kFmtRegF, 4, 0, kFmtBitBlt, 23, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
@@ -378,7 +379,7 @@
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
"ldr", "!0r, !1p", kFixupLoad),
- ENCODING_MAP(FWIDE(kA64Ldr3fXD), SIZE_VARIANTS(0xbd400000),
+ ENCODING_MAP(WIDE(kA64Ldr3fXD), SIZE_VARIANTS(0xbd400000),
kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 10,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF,
"ldr", "!0f, [!1X, #!2D]", kFixupNone),
@@ -386,7 +387,7 @@
kFmtRegR, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 10,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD_OFF,
"ldr", "!0r, [!1X, #!2D]", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Ldr4fXxG), SIZE_VARIANTS(0xbc606800),
+ ENCODING_MAP(WIDE(kA64Ldr4fXxG), SIZE_VARIANTS(0xbc606800),
kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtRegX, 20, 16,
kFmtBitBlt, 12, 12, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
"ldr", "!0f, [!1X, !2x!3G]", kFixupNone),
@@ -410,7 +411,7 @@
kFmtRegR, 4, 0, kFmtRegR, 14, 10, kFmtRegXOrSp, 9, 5,
kFmtBitBlt, 21, 15, IS_QUAD_OP | REG_USE2 | REG_DEF012 | IS_LOAD,
"ldp", "!0r, !1r, [!2X], #!3D", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Ldur3fXd), CUSTOM_VARIANTS(0xbc400000, 0xfc400000),
+ ENCODING_MAP(WIDE(kA64Ldur3fXd), CUSTOM_VARIANTS(0xbc400000, 0xfc400000),
kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 20, 12,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
"ldur", "!0f, [!1X, #!2d]", kFixupNone),
@@ -506,11 +507,11 @@
kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 21, 16,
kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE1,
"sbfm", "!0r, !1r, #!2d, #!3d", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Scvtf2fw), FLOAT_VARIANTS(0x1e220000),
+ ENCODING_MAP(WIDE(kA64Scvtf2fw), FLOAT_VARIANTS(0x1e220000),
kFmtRegF, 4, 0, kFmtRegW, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"scvtf", "!0f, !1w", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Scvtf2fx), FLOAT_VARIANTS(0x9e220000),
+ ENCODING_MAP(WIDE(kA64Scvtf2fx), FLOAT_VARIANTS(0x9e220000),
kFmtRegF, 4, 0, kFmtRegX, 9, 5, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
"scvtf", "!0f, !1x", kFixupNone),
@@ -546,11 +547,11 @@
kFmtRegR, 4, 0, kFmtRegR, 14, 10, kFmtRegXOrSp, 9, 5,
kFmtBitBlt, 21, 15, IS_QUAD_OP | REG_DEF2 | REG_USE012 | IS_STORE,
"stp", "!0r, !1r, [!2X, #!3D]!!", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Str3fXD), CUSTOM_VARIANTS(0xbd000000, 0xfd000000),
+ ENCODING_MAP(WIDE(kA64Str3fXD), CUSTOM_VARIANTS(0xbd000000, 0xfd000000),
kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 21, 10,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE_OFF,
"str", "!0f, [!1X, #!2D]", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Str4fXxG), CUSTOM_VARIANTS(0xbc206800, 0xfc206800),
+ ENCODING_MAP(WIDE(kA64Str4fXxG), CUSTOM_VARIANTS(0xbc206800, 0xfc206800),
kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtRegX, 20, 16,
kFmtBitBlt, 12, 12, IS_QUAD_OP | REG_USE012 | IS_STORE,
"str", "!0f, [!1X, !2x!3G]", kFixupNone),
@@ -582,7 +583,7 @@
kFmtRegR, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 20, 12,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | REG_DEF1 | IS_STORE,
"str", "!0r, [!1X], #!2d", kFixupNone),
- ENCODING_MAP(FWIDE(kA64Stur3fXd), CUSTOM_VARIANTS(0xbc000000, 0xfc000000),
+ ENCODING_MAP(WIDE(kA64Stur3fXd), CUSTOM_VARIANTS(0xbc000000, 0xfc000000),
kFmtRegF, 4, 0, kFmtRegXOrSp, 9, 5, kFmtBitBlt, 20, 12,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
"stur", "!0f, [!1X, #!2d]", kFixupNone),
@@ -614,10 +615,24 @@
kFmtRegR, 4, 0, kFmtRegROrSp, 9, 5, kFmtBitBlt, 21, 10,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
"subs", "!0r, !1R, #!2d", kFixupNone),
- ENCODING_MAP(WIDE(kA64Tst3rro), SF_VARIANTS(0x6a000000),
+ ENCODING_MAP(WIDE(kA64Tst2rl), SF_VARIANTS(0x7200001f),
+ kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | SETS_CCODES,
+ "tst", "!0r, !1l", kFixupNone),
+ ENCODING_MAP(WIDE(kA64Tst3rro), SF_VARIANTS(0x6a00001f),
kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtShift, -1, -1,
- kFmtUnused, -1, -1, IS_QUAD_OP | REG_USE01 | SETS_CCODES,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
"tst", "!0r, !1r!2o", kFixupNone),
+ // NOTE: Tbz/Tbnz does not require SETS_CCODES, but it may be replaced by some other LIRs
+ // which require SETS_CCODES in the fix-up stage.
+ ENCODING_MAP(WIDE(kA64Tbnz3rht), CUSTOM_VARIANTS(0x37000000, 0x37000000),
+ kFmtRegR, 4, 0, kFmtImm6Shift, -1, -1, kFmtBitBlt, 18, 5, kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP | SETS_CCODES,
+ "tbnz", "!0r, #!1h, !2t", kFixupTBxZ),
+ ENCODING_MAP(WIDE(kA64Tbz3rht), CUSTOM_VARIANTS(0x36000000, 0x36000000),
+ kFmtRegR, 4, 0, kFmtImm6Shift, -1, -1, kFmtBitBlt, 18, 5, kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP | SETS_CCODES,
+ "tbz", "!0r, #!1h, !2t", kFixupTBxZ),
ENCODING_MAP(WIDE(kA64Ubfm4rrdd), SF_N_VARIANTS(0x53000000),
kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 21, 16,
kFmtBitBlt, 15, 10, IS_QUAD_OP | REG_DEF0_USE1,
@@ -652,21 +667,21 @@
uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) {
for (; lir != nullptr; lir = NEXT_LIR(lir)) {
bool opcode_is_wide = IS_WIDE(lir->opcode);
- ArmOpcode opcode = UNWIDE(lir->opcode);
+ A64Opcode opcode = UNWIDE(lir->opcode);
if (UNLIKELY(IsPseudoLirOp(opcode))) {
continue;
}
if (LIKELY(!lir->flags.is_nop)) {
- const ArmEncodingMap *encoder = &EncodingMap[opcode];
+ const A64EncodingMap *encoder = &EncodingMap[opcode];
// Select the right variant of the skeleton.
uint32_t bits = opcode_is_wide ? encoder->xskeleton : encoder->wskeleton;
DCHECK(!opcode_is_wide || IS_WIDE(encoder->opcode));
for (int i = 0; i < 4; i++) {
- ArmEncodingKind kind = encoder->field_loc[i].kind;
+ A64EncodingKind kind = encoder->field_loc[i].kind;
uint32_t operand = lir->operands[i];
uint32_t value;
@@ -787,6 +802,11 @@
value |= ((operand & 0x1ffffc) >> 2) << 5;
bits |= value;
break;
+ case kFmtImm6Shift:
+ value = (operand & 0x1f) << 19;
+ value |= ((operand & 0x20) >> 5) << 31;
+ bits |= value;
+ break;
default:
LOG(FATAL) << "Bad fmt for arg. " << i << " in " << encoder->name
<< " (" << kind << ")";
@@ -827,11 +847,6 @@
*/
int generation = 0;
while (true) {
- // TODO(Arm64): check whether passes and offset adjustments are really necessary.
- // Currently they aren't, as - in the fixups below - LIR are never inserted.
- // Things can be different if jump ranges above 1 MB need to be supported.
- // If they are not, then we can get rid of the assembler retry logic.
-
offset_adjustment = 0;
AssemblerStatus res = kSuccess; // Assume success
generation ^= 1;
@@ -839,13 +854,9 @@
lir = first_fixup_;
prev_lir = NULL;
while (lir != NULL) {
- /*
- * NOTE: the lir being considered here will be encoded following the switch (so long as
- * we're not in a retry situation). However, any new non-pc_rel instructions inserted
- * due to retry must be explicitly encoded at the time of insertion. Note that
- * inserted instructions don't need use/def flags, but do need size and pc-rel status
- * properly updated.
- */
+ // NOTE: Any new non-pc_rel instructions inserted due to retry must be explicitly encoded at
+ // the time of insertion. Note that inserted instructions don't need use/def flags, but do
+ // need size and pc-rel status properly updated.
lir->offset += offset_adjustment;
// During pass, allows us to tell whether a node has been updated with offset_adjustment yet.
lir->flags.generation = generation;
@@ -861,7 +872,8 @@
CodeOffset target = target_lir->offset +
((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
int32_t delta = target - pc;
- if (!((delta & 0x3) == 0 && IS_SIGNED_IMM19(delta >> 2))) {
+ DCHECK_EQ(delta & 0x3, 0);
+ if (!IS_SIGNED_IMM19(delta >> 2)) {
LOG(FATAL) << "Invalid jump range in kFixupT1Branch";
}
lir->operands[0] = delta >> 2;
@@ -876,12 +888,75 @@
CodeOffset target = target_lir->offset +
((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
int32_t delta = target - pc;
- if (!((delta & 0x3) == 0 && IS_SIGNED_IMM19(delta >> 2))) {
+ DCHECK_EQ(delta & 0x3, 0);
+ if (!IS_SIGNED_IMM19(delta >> 2)) {
LOG(FATAL) << "Invalid jump range in kFixupLoad";
}
lir->operands[1] = delta >> 2;
break;
}
+ case kFixupTBxZ: {
+ int16_t opcode = lir->opcode;
+ RegStorage reg(lir->operands[0] | RegStorage::kValid);
+ int32_t imm = lir->operands[1];
+ DCHECK_EQ(IS_WIDE(opcode), reg.Is64Bit());
+ DCHECK_LT(imm, 64);
+ if (imm >= 32) {
+ DCHECK(IS_WIDE(opcode));
+ } else if (kIsDebugBuild && IS_WIDE(opcode)) {
+ // "tbz/tbnz x0, #imm(<32)" is the same with "tbz/tbnz w0, #imm(<32)", but GCC/oatdump
+ // will disassemble it as "tbz/tbnz w0, #imm(<32)". So unwide the LIR to make the
+ // compiler log behave the same with those disassembler in debug build.
+ // This will also affect tst instruction if it need to be replaced, but there is no
+ // performance difference between "tst Xt" and "tst Wt".
+ lir->opcode = UNWIDE(opcode);
+ lir->operands[0] = As32BitReg(reg).GetReg();
+ }
+
+ // Fix-up branch offset.
+ LIR *target_lir = lir->target;
+ DCHECK(target_lir);
+ CodeOffset pc = lir->offset;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ DCHECK_EQ(delta & 0x3, 0);
+ // Check if branch offset can be encoded in tbz/tbnz.
+ if (!IS_SIGNED_IMM14(delta >> 2)) {
+ DexOffset dalvik_offset = lir->dalvik_offset;
+ int16_t opcode = lir->opcode;
+ LIR* target = lir->target;
+ // "tbz/tbnz Rt, #imm, label" -> "tst Rt, #(1<<imm)".
+ offset_adjustment -= lir->flags.size;
+ int32_t imm = EncodeLogicalImmediate(IS_WIDE(opcode), 1 << lir->operands[1]);
+ DCHECK_NE(imm, -1);
+ lir->opcode = IS_WIDE(opcode) ? WIDE(kA64Tst2rl) : kA64Tst2rl;
+ lir->operands[1] = imm;
+ lir->target = nullptr;
+ lir->flags.fixup = EncodingMap[kA64Tst2rl].fixup;
+ lir->flags.size = EncodingMap[kA64Tst2rl].size;
+ offset_adjustment += lir->flags.size;
+ // Insert "beq/bneq label".
+ opcode = UNWIDE(opcode);
+ DCHECK(opcode == kA64Tbz3rht || opcode == kA64Tbnz3rht);
+ LIR* new_lir = RawLIR(dalvik_offset, kA64B2ct,
+ opcode == kA64Tbz3rht ? kArmCondEq : kArmCondNe, 0, 0, 0, 0, target);
+ InsertLIRAfter(lir, new_lir);
+ new_lir->offset = lir->offset + lir->flags.size;
+ new_lir->flags.generation = generation;
+ new_lir->flags.fixup = EncodingMap[kA64B2ct].fixup;
+ new_lir->flags.size = EncodingMap[kA64B2ct].size;
+ offset_adjustment += new_lir->flags.size;
+ // lir no longer pcrel, unlink and link in new_lir.
+ ReplaceFixup(prev_lir, lir, new_lir);
+ prev_lir = new_lir; // Continue with the new instruction.
+ lir = new_lir->u.a.pcrel_next;
+ res = kRetryAll;
+ continue;
+ }
+ lir->operands[2] = delta >> 2;
+ break;
+ }
case kFixupAdr: {
LIR* target_lir = lir->target;
int32_t delta;
@@ -910,6 +985,7 @@
}
if (res == kSuccess) {
+ DCHECK_EQ(offset_adjustment, 0);
break;
} else {
assembler_retries++;
@@ -951,7 +1027,7 @@
}
size_t Arm64Mir2Lir::GetInsnSize(LIR* lir) {
- ArmOpcode opcode = UNWIDE(lir->opcode);
+ A64Opcode opcode = UNWIDE(lir->opcode);
DCHECK(!IsPseudoLirOp(opcode));
return EncodingMap[opcode].size;
}
@@ -962,7 +1038,7 @@
LIR* last_fixup = NULL;
for (LIR* lir = head_lir; lir != end_lir; lir = NEXT_LIR(lir)) {
- ArmOpcode opcode = UNWIDE(lir->opcode);
+ A64Opcode opcode = UNWIDE(lir->opcode);
if (!lir->flags.is_nop) {
if (lir->flags.fixup != kFixupNone) {
if (!IsPseudoLirOp(opcode)) {
diff --git a/compiler/dex/quick/arm64/backend_arm64.h b/compiler/dex/quick/arm64/backend_arm64.h
new file mode 100644
index 0000000..53650c4
--- /dev/null
+++ b/compiler/dex/quick/arm64/backend_arm64.h
@@ -0,0 +1,32 @@
+/*
+ * 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_COMPILER_DEX_QUICK_ARM64_BACKEND_ARM64_H_
+#define ART_COMPILER_DEX_QUICK_ARM64_BACKEND_ARM64_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* Arm64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_QUICK_ARM64_BACKEND_ARM64_H_
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index eddc3a3..6081f28 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -44,7 +44,7 @@
* quit:
*/
void Arm64Mir2Lir::GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
@@ -55,7 +55,7 @@
tab_rec->vaddr = current_dalvik_offset_;
uint32_t size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
@@ -96,7 +96,7 @@
void Arm64Mir2Lir::GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
@@ -108,7 +108,7 @@
uint32_t size = table[1];
tab_rec->targets =
static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
@@ -156,8 +156,8 @@
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void Arm64Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void Arm64Mir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
// Add the table to the list - we'll process it later
FillArrayData *tab_rec =
static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
@@ -167,7 +167,7 @@
uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
tab_rec->size = (size * width) + 8;
- fill_array_data_.Insert(tab_rec);
+ fill_array_data_.push_back(tab_rec);
// Making a call - use explicit registers
FlushAllRegs(); /* Everything to home location */
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 3e1c18b..55cc938 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -19,6 +19,7 @@
#include "arm64_lir.h"
#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
#include <map>
@@ -167,6 +168,7 @@
bool GenInlinedRound(CallInfo* info, bool is_double) OVERRIDE;
bool GenInlinedPeek(CallInfo* info, OpSize size) OVERRIDE;
bool GenInlinedPoke(CallInfo* info, OpSize size) OVERRIDE;
+ bool GenInlinedAbsInt(CallInfo* info) OVERRIDE;
bool GenInlinedAbsLong(CallInfo* info) OVERRIDE;
bool GenInlinedArrayCopyCharArray(CallInfo* info) OVERRIDE;
void GenIntToLong(RegLocation rl_dest, RegLocation rl_src) OVERRIDE;
@@ -181,7 +183,7 @@
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
void GenExitSequence() OVERRIDE;
void GenSpecialExitSequence() OVERRIDE;
- void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) OVERRIDE;
+ void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) OVERRIDE;
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) OVERRIDE;
void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
@@ -393,7 +395,7 @@
RegLocation rl_src2, bool is_div);
InToRegStorageMapping in_to_reg_storage_mapping_;
- static const ArmEncodingMap EncodingMap[kA64Last];
+ static const A64EncodingMap EncodingMap[kA64Last];
};
} // namespace art
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index 7268d70..db24d12 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -112,7 +112,7 @@
rl_result = EvalLoc(rl_dest, kFPReg, true);
DCHECK(rl_dest.wide);
DCHECK(rl_result.wide);
- NewLIR3(FWIDE(op), rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
+ NewLIR3(WIDE(op), rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
StoreValueWide(rl_dest, rl_result);
}
@@ -145,17 +145,17 @@
dst_reg_class = kFPReg;
break;
case Instruction::INT_TO_DOUBLE:
- op = FWIDE(kA64Scvtf2fw);
+ op = WIDE(kA64Scvtf2fw);
src_reg_class = kCoreReg;
dst_reg_class = kFPReg;
break;
case Instruction::DOUBLE_TO_INT:
- op = FWIDE(kA64Fcvtzs2wf);
+ op = WIDE(kA64Fcvtzs2wf);
src_reg_class = kFPReg;
dst_reg_class = kCoreReg;
break;
case Instruction::LONG_TO_DOUBLE:
- op = FWIDE(kA64Scvtf2fx);
+ op = WIDE(kA64Scvtf2fx);
src_reg_class = kCoreReg;
dst_reg_class = kFPReg;
break;
@@ -170,7 +170,7 @@
dst_reg_class = kFPReg;
break;
case Instruction::DOUBLE_TO_LONG:
- op = FWIDE(kA64Fcvtzs2xf);
+ op = WIDE(kA64Fcvtzs2xf);
src_reg_class = kFPReg;
dst_reg_class = kCoreReg;
break;
@@ -208,7 +208,7 @@
rl_src2 = mir_graph_->GetSrcWide(mir, 2);
rl_src1 = LoadValueWide(rl_src1, kFPReg);
rl_src2 = LoadValueWide(rl_src2, kFPReg);
- NewLIR2(FWIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
+ NewLIR2(WIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
} else {
rl_src1 = mir_graph_->GetSrc(mir, 0);
rl_src2 = mir_graph_->GetSrc(mir, 1);
@@ -281,7 +281,7 @@
ClobberSReg(rl_dest.s_reg_low);
rl_result = EvalLoc(rl_dest, kCoreReg, true);
LoadConstant(rl_result.reg, default_result);
- NewLIR2(FWIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
+ NewLIR2(WIDE(kA64Fcmp2ff), rl_src1.reg.GetReg(), rl_src2.reg.GetReg());
} else {
rl_src1 = LoadValue(rl_src1, kFPReg);
rl_src2 = LoadValue(rl_src2, kFPReg);
@@ -318,7 +318,7 @@
RegLocation rl_result;
rl_src = LoadValueWide(rl_src, kFPReg);
rl_result = EvalLoc(rl_dest, kFPReg, true);
- NewLIR2(FWIDE(kA64Fneg2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(WIDE(kA64Fneg2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
StoreValueWide(rl_dest, rl_result);
}
@@ -353,7 +353,8 @@
if (reg_class == kFPReg) {
NewLIR2(kA64Fabs2ff, rl_result.reg.GetReg(), rl_src.reg.GetReg());
} else {
- NewLIR4(kA64Ubfm4rrdd, rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 30);
+ // Clear the sign bit in an integer register.
+ OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
}
StoreValue(rl_dest, rl_result);
return true;
@@ -369,9 +370,10 @@
rl_src = LoadValueWide(rl_src, reg_class);
RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
if (reg_class == kFPReg) {
- NewLIR2(FWIDE(kA64Fabs2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(WIDE(kA64Fabs2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
} else {
- NewLIR4(WIDE(kA64Ubfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 62);
+ // Clear the sign bit in an integer register.
+ OpRegRegImm64(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffffffffffff);
}
StoreValueWide(rl_dest, rl_result);
return true;
@@ -382,7 +384,7 @@
RegLocation rl_dest = InlineTargetWide(info); // double place for result
rl_src = LoadValueWide(rl_src, kFPReg);
RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
- NewLIR2(FWIDE(kA64Fsqrt2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(WIDE(kA64Fsqrt2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
StoreValueWide(rl_dest, rl_result);
return true;
}
@@ -392,7 +394,7 @@
RegLocation rl_dest = InlineTargetWide(info);
rl_src = LoadValueWide(rl_src, kFPReg);
RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
- NewLIR2(FWIDE(kA64Frintp2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(WIDE(kA64Frintp2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
StoreValueWide(rl_dest, rl_result);
return true;
}
@@ -402,7 +404,7 @@
RegLocation rl_dest = InlineTargetWide(info);
rl_src = LoadValueWide(rl_src, kFPReg);
RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
- NewLIR2(FWIDE(kA64Frintm2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(WIDE(kA64Frintm2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
StoreValueWide(rl_dest, rl_result);
return true;
}
@@ -412,14 +414,14 @@
RegLocation rl_dest = InlineTargetWide(info);
rl_src = LoadValueWide(rl_src, kFPReg);
RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
- NewLIR2(FWIDE(kA64Frintn2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ NewLIR2(WIDE(kA64Frintn2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
StoreValueWide(rl_dest, rl_result);
return true;
}
bool Arm64Mir2Lir::GenInlinedRound(CallInfo* info, bool is_double) {
int32_t encoded_imm = EncodeImmSingle(bit_cast<float, uint32_t>(0.5f));
- ArmOpcode wide = (is_double) ? FWIDE(0) : FUNWIDE(0);
+ A64Opcode wide = (is_double) ? WIDE(0) : UNWIDE(0);
RegLocation rl_src = info->args[0];
RegLocation rl_dest = (is_double) ? InlineTargetWide(info) : InlineTarget(info);
rl_src = (is_double) ? LoadValueWide(rl_src, kFPReg) : LoadValue(rl_src, kFPReg);
@@ -437,7 +439,7 @@
bool Arm64Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
DCHECK_EQ(cu_->instruction_set, kArm64);
int op = (is_min) ? kA64Fmin3fff : kA64Fmax3fff;
- ArmOpcode wide = (is_double) ? FWIDE(0) : FUNWIDE(0);
+ A64Opcode wide = (is_double) ? WIDE(0) : UNWIDE(0);
RegLocation rl_src1 = info->args[0];
RegLocation rl_src2 = (is_double) ? info->args[2] : info->args[1];
rl_src1 = (is_double) ? LoadValueWide(rl_src1, kFPReg) : LoadValue(rl_src1, kFPReg);
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 1e97a32..305a6c9 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -262,17 +262,21 @@
ArmConditionCode arm_cond = ArmConditionEncoding(cond);
if (check_value == 0) {
if (arm_cond == kArmCondEq || arm_cond == kArmCondNe) {
- ArmOpcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
- ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+ A64Opcode opcode = (arm_cond == kArmCondEq) ? kA64Cbz2rt : kA64Cbnz2rt;
+ A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
} else if (arm_cond == kArmCondLs) {
// kArmCondLs is an unsigned less or equal. A comparison r <= 0 is then the same as cbz.
// This case happens for a bounds check of array[0].
- ArmOpcode opcode = kA64Cbz2rt;
- ArmOpcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+ A64Opcode opcode = kA64Cbz2rt;
+ A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
branch = NewLIR2(opcode | wide, reg.GetReg(), 0);
+ } else if (arm_cond == kArmCondLt || arm_cond == kArmCondGe) {
+ A64Opcode opcode = (arm_cond == kArmCondLt) ? kA64Tbnz3rht : kA64Tbz3rht;
+ A64Opcode wide = reg.Is64Bit() ? WIDE(0) : UNWIDE(0);
+ int value = reg.Is64Bit() ? 63 : 31;
+ branch = NewLIR3(opcode | wide, reg.GetReg(), value, 0);
}
- // TODO: Use tbz/tbnz for < 0 or >= 0.
}
if (branch == nullptr) {
@@ -301,7 +305,7 @@
LIR* Arm64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) {
bool dest_is_fp = r_dest.IsFloat();
bool src_is_fp = r_src.IsFloat();
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
LIR* res;
if (LIKELY(dest_is_fp == src_is_fp)) {
@@ -329,7 +333,7 @@
DCHECK_EQ(dest_is_double, src_is_double);
// Homogeneous float/float copy.
- opcode = (dest_is_double) ? FWIDE(kA64Fmov2ff) : kA64Fmov2ff;
+ opcode = (dest_is_double) ? WIDE(kA64Fmov2ff) : kA64Fmov2ff;
}
} else {
// Inhomogeneous register copy.
@@ -625,7 +629,7 @@
// temp = r_src1 / r_src2
// dest = r_src1 - temp * r_src2
RegStorage temp;
- ArmOpcode wide;
+ A64Opcode wide;
if (rl_result.reg.Is64Bit()) {
temp = AllocTempWide();
wide = WIDE(0);
@@ -641,16 +645,32 @@
return rl_result;
}
+bool Arm64Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
+ RegLocation rl_src = info->args[0];
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+ // Compare the source value with zero. Write the negated value to the result if
+ // negative, otherwise write the original value.
+ OpRegImm(kOpCmp, rl_src.reg, 0);
+ NewLIR4(kA64Csneg4rrrc, rl_result.reg.GetReg(), rl_src.reg.GetReg(), rl_src.reg.GetReg(),
+ kArmCondPl);
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
bool Arm64Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
RegLocation rl_src = info->args[0];
rl_src = LoadValueWide(rl_src, kCoreReg);
RegLocation rl_dest = InlineTargetWide(info);
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- RegStorage sign_reg = AllocTempWide();
- // abs(x) = y<=x>>63, (x+y)^y.
- OpRegRegImm(kOpAsr, sign_reg, rl_src.reg, 63);
- OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, sign_reg);
- OpRegReg(kOpXor, rl_result.reg, sign_reg);
+
+ // Compare the source value with zero. Write the negated value to the result if
+ // negative, otherwise write the original value.
+ OpRegImm(kOpCmp, rl_src.reg, 0);
+ NewLIR4(WIDE(kA64Csneg4rrrc), rl_result.reg.GetReg(), rl_src.reg.GetReg(),
+ rl_src.reg.GetReg(), kArmCondPl);
StoreValueWide(rl_dest, rl_result);
return true;
}
@@ -749,7 +769,7 @@
RegStorage r_tmp;
RegStorage r_tmp_stored;
RegStorage rl_new_value_stored = rl_new_value.reg;
- ArmOpcode wide = UNWIDE(0);
+ A64Opcode wide = UNWIDE(0);
if (is_long) {
r_tmp_stored = r_tmp = AllocTempWide();
wide = WIDE(0);
@@ -855,16 +875,14 @@
OpRegRegImm(kOpLsl, rs_length, rs_length, 1);
// Copy one element.
- OpRegRegImm(kOpAnd, rs_tmp, As32BitReg(rs_length), 2);
- LIR* jmp_to_copy_two = OpCmpImmBranch(kCondEq, rs_tmp, 0, nullptr);
+ LIR* jmp_to_copy_two = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 1, 0);
OpRegImm(kOpSub, rs_length, 2);
LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, kSignedHalf);
StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, kSignedHalf);
// Copy two elements.
LIR *copy_two = NewLIR0(kPseudoTargetLabel);
- OpRegRegImm(kOpAnd, rs_tmp, As32BitReg(rs_length), 4);
- LIR* jmp_to_copy_four = OpCmpImmBranch(kCondEq, rs_tmp, 0, nullptr);
+ LIR* jmp_to_copy_four = NewLIR3(WIDE(kA64Tbz3rht), rs_length.GetReg(), 2, 0);
OpRegImm(kOpSub, rs_length, 4);
LoadBaseIndexed(rs_src, rs_length, rs_tmp, 0, k32);
StoreBaseIndexed(rs_dst, rs_length, rs_tmp, 0, k32);
@@ -943,7 +961,7 @@
// Combine sub & test using sub setflags encoding here. We need to make sure a
// subtract form that sets carry is used, so generate explicitly.
// TODO: might be best to add a new op, kOpSubs, and handle it generically.
- ArmOpcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
+ A64Opcode opcode = reg.Is64Bit() ? WIDE(kA64Subs3rRd) : UNWIDE(kA64Subs3rRd);
NewLIR3(opcode, reg.GetReg(), reg.GetReg(), 1); // For value == 1, this should set flags.
DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
return OpCondBranch(c_code, target);
@@ -1440,7 +1458,7 @@
for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
if (UNLIKELY(reg2 < 0)) {
- m2l->NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
+ m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
offset);
} else {
m2l->NewLIR4(WIDE(kA64Stp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
@@ -1551,7 +1569,7 @@
// Have some FP regs to do.
fp_reg_mask = GenPairWise(fp_reg_mask, ®1, ®2);
if (UNLIKELY(reg2 < 0)) {
- m2l->NewLIR3(FWIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
+ m2l->NewLIR3(WIDE(kA64Str3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
cur_offset);
// Do not increment offset here, as the second half will be filled by a core reg.
} else {
@@ -1624,7 +1642,7 @@
for (offset = (offset >> reg_log2_size); reg_mask; offset += 2) {
reg_mask = GenPairWise(reg_mask, & reg1, & reg2);
if (UNLIKELY(reg2 < 0)) {
- m2l->NewLIR3(FWIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
+ m2l->NewLIR3(WIDE(kA64Ldr3fXD), RegStorage::FloatSolo64(reg1).GetReg(), base.GetReg(),
offset);
} else {
m2l->NewLIR4(WIDE(kA64Ldp4ffXD), RegStorage::FloatSolo64(reg2).GetReg(),
@@ -1686,13 +1704,13 @@
}
bool Arm64Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
- ArmOpcode wide = (size == k64) ? WIDE(0) : UNWIDE(0);
+ A64Opcode wide = IsWide(size) ? WIDE(0) : UNWIDE(0);
RegLocation rl_src_i = info->args[0];
- RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info); // result reg
+ RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
+ RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
NewLIR2(kA64Rbit2rr | wide, rl_result.reg.GetReg(), rl_i.reg.GetReg());
- (size == k64) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
+ IsWide(size) ? StoreValueWide(rl_dest, rl_result) : StoreValue(rl_dest, rl_result);
return true;
}
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 9b4546a..0462530 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -20,6 +20,7 @@
#include <string>
+#include "backend_arm64.h"
#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
@@ -83,23 +84,23 @@
static constexpr ArrayRef<const RegStorage> dp_temps(dp_temps_arr);
RegLocation Arm64Mir2Lir::LocCReturn() {
- return arm_loc_c_return;
+ return a64_loc_c_return;
}
RegLocation Arm64Mir2Lir::LocCReturnRef() {
- return arm_loc_c_return_ref;
+ return a64_loc_c_return_ref;
}
RegLocation Arm64Mir2Lir::LocCReturnWide() {
- return arm_loc_c_return_wide;
+ return a64_loc_c_return_wide;
}
RegLocation Arm64Mir2Lir::LocCReturnFloat() {
- return arm_loc_c_return_float;
+ return a64_loc_c_return_float;
}
RegLocation Arm64Mir2Lir::LocCReturnDouble() {
- return arm_loc_c_return_double;
+ return a64_loc_c_return_double;
}
// Return a target-dependent special register.
@@ -152,7 +153,7 @@
return ResourceMask::Bit(
// FP register starts at bit position 32.
- (reg.IsFloat() ? kArm64FPReg0 : 0) + reg.GetRegNum());
+ (reg.IsFloat() ? kA64FPReg0 : 0) + reg.GetRegNum());
}
ResourceMask Arm64Mir2Lir::GetPCUseDefEncoding() const {
@@ -172,15 +173,15 @@
// These flags are somewhat uncommon - bypass if we can.
if ((flags & (REG_DEF_SP | REG_USE_SP | REG_DEF_LR)) != 0) {
if (flags & REG_DEF_SP) {
- def_mask->SetBit(kArm64RegSP);
+ def_mask->SetBit(kA64RegSP);
}
if (flags & REG_USE_SP) {
- use_mask->SetBit(kArm64RegSP);
+ use_mask->SetBit(kA64RegSP);
}
if (flags & REG_DEF_LR) {
- def_mask->SetBit(kArm64RegLR);
+ def_mask->SetBit(kA64RegLR);
}
}
}
@@ -248,19 +249,22 @@
}
}
-#define BIT_MASK(w) ((UINT64_C(1) << (w)) - UINT64_C(1))
+static uint64_t bit_mask(unsigned width) {
+ DCHECK_LE(width, 64U);
+ return (width == 64) ? static_cast<uint64_t>(-1) : ((UINT64_C(1) << (width)) - UINT64_C(1));
+}
static uint64_t RotateRight(uint64_t value, unsigned rotate, unsigned width) {
DCHECK_LE(width, 64U);
rotate &= 63;
- value = value & BIT_MASK(width);
- return ((value & BIT_MASK(rotate)) << (width - rotate)) | (value >> rotate);
+ value = value & bit_mask(width);
+ return ((value & bit_mask(rotate)) << (width - rotate)) | (value >> rotate);
}
static uint64_t RepeatBitsAcrossReg(bool is_wide, uint64_t value, unsigned width) {
unsigned i;
unsigned reg_size = (is_wide) ? 64 : 32;
- uint64_t result = value & BIT_MASK(width);
+ uint64_t result = value & bit_mask(width);
for (i = width; i < reg_size; i *= 2) {
result |= (result << i);
}
@@ -299,7 +303,7 @@
if (n == 1) {
DCHECK_NE(imm_s, 0x3fU);
- uint64_t bits = BIT_MASK(imm_s + 1);
+ uint64_t bits = bit_mask(imm_s + 1);
return RotateRight(bits, imm_r, 64);
} else {
DCHECK_NE((imm_s >> 1), 0x1fU);
@@ -307,7 +311,7 @@
if ((imm_s & width) == 0) {
unsigned mask = (unsigned)(width - 1);
DCHECK_NE((imm_s & mask), mask);
- uint64_t bits = BIT_MASK((imm_s & mask) + 1);
+ uint64_t bits = bit_mask((imm_s & mask) + 1);
return RepeatBitsAcrossReg(is_wide, RotateRight(bits, imm_r & mask, width), width);
}
}
@@ -404,7 +408,7 @@
snprintf(tbuf, arraysize(tbuf), "d%d", operand & RegStorage::kRegNumMask);
break;
case 'f':
- snprintf(tbuf, arraysize(tbuf), "%c%d", (IS_FWIDE(lir->opcode)) ? 'd' : 's',
+ snprintf(tbuf, arraysize(tbuf), "%c%d", (IS_WIDE(lir->opcode)) ? 'd' : 's',
operand & RegStorage::kRegNumMask);
break;
case 'l': {
@@ -504,6 +508,9 @@
else
strcpy(tbuf, ", DecodeError3");
break;
+ case 'h':
+ snprintf(tbuf, arraysize(tbuf), "%d", operand);
+ break;
default:
strcpy(tbuf, "DecodeError1");
break;
@@ -527,7 +534,7 @@
char num[8];
int i;
- for (i = 0; i < kArm64RegEnd; i++) {
+ for (i = 0; i < kA64RegEnd; i++) {
if (mask.HasBit(i)) {
snprintf(num, arraysize(num), "%d ", i);
strcat(buf, num);
@@ -595,14 +602,13 @@
}
void Arm64Mir2Lir::CompilerInitializeRegAlloc() {
- reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs, core64_regs, sp_regs, dp_regs,
- reserved_regs, reserved64_regs, core_temps, core64_temps,
- sp_temps, dp_temps);
+ reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, core64_regs, sp_regs, dp_regs,
+ reserved_regs, reserved64_regs,
+ core_temps, core64_temps, sp_temps, dp_temps));
// Target-specific adjustments.
// Alias single precision float registers to corresponding double registers.
- GrowableArray<RegisterInfo*>::Iterator fp_it(®_pool_->sp_regs_);
- for (RegisterInfo* info = fp_it.Next(); info != nullptr; info = fp_it.Next()) {
+ for (RegisterInfo* info : reg_pool_->sp_regs_) {
int fp_reg_num = info->GetReg().GetRegNum();
RegStorage dp_reg = RegStorage::FloatSolo64(fp_reg_num);
RegisterInfo* dp_reg_info = GetRegInfo(dp_reg);
@@ -615,8 +621,7 @@
}
// Alias 32bit W registers to corresponding 64bit X registers.
- GrowableArray<RegisterInfo*>::Iterator w_it(®_pool_->core_regs_);
- for (RegisterInfo* info = w_it.Next(); info != nullptr; info = w_it.Next()) {
+ for (RegisterInfo* info : reg_pool_->core_regs_) {
int x_reg_num = info->GetReg().GetRegNum();
RegStorage x_reg = RegStorage::Solo64(x_reg_num);
RegisterInfo* x_reg_info = GetRegInfo(x_reg);
@@ -889,11 +894,11 @@
RegStorage Arm64Mir2Lir::GetArgMappingToPhysicalReg(int arg_num) {
if (!in_to_reg_storage_mapping_.IsInitialized()) {
- int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+ int start_vreg = mir_graph_->GetFirstInVR();
RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
InToRegStorageArm64Mapper mapper;
- in_to_reg_storage_mapping_.Initialize(arg_locs, cu_->num_ins, &mapper);
+ in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
}
return in_to_reg_storage_mapping_.Get(arg_num);
}
@@ -927,14 +932,14 @@
StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile);
}
- if (cu_->num_ins == 0) {
+ if (mir_graph_->GetNumOfInVRs() == 0) {
return;
}
// Handle dalvik registers.
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
- for (int i = 0; i < cu_->num_ins; i++) {
+ int start_vreg = mir_graph_->GetFirstInVR();
+ for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
RegLocation* t_loc = &ArgLocs[i];
OpSize op_size;
RegStorage reg = GetArgPhysicalReg(t_loc, &num_gpr_used, &num_fpr_used, &op_size);
@@ -1077,9 +1082,6 @@
}
}
- // Logic below assumes that Method pointer is at offset zero from SP.
- DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0);
-
// The rest can be copied together
int start_offset = SRegOffset(info->args[last_mapped_in + 1].s_reg_low);
int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + 1,
diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc
index f58f830..9266d5c 100644
--- a/compiler/dex/quick/arm64/utility_arm64.cc
+++ b/compiler/dex/quick/arm64/utility_arm64.cc
@@ -89,9 +89,9 @@
size_t Arm64Mir2Lir::GetLoadStoreSize(LIR* lir) {
bool opcode_is_wide = IS_WIDE(lir->opcode);
- ArmOpcode opcode = UNWIDE(lir->opcode);
+ A64Opcode opcode = UNWIDE(lir->opcode);
DCHECK(!IsPseudoLirOp(opcode));
- const ArmEncodingMap *encoder = &EncodingMap[opcode];
+ const A64EncodingMap *encoder = &EncodingMap[opcode];
uint32_t bits = opcode_is_wide ? encoder->xskeleton : encoder->wskeleton;
return (bits >> 30);
}
@@ -138,7 +138,7 @@
} else {
int32_t encoded_imm = EncodeImmDouble(value);
if (encoded_imm >= 0) {
- return NewLIR2(FWIDE(kA64Fmov2fI), r_dest.GetReg(), encoded_imm);
+ return NewLIR2(WIDE(kA64Fmov2fI), r_dest.GetReg(), encoded_imm);
}
}
@@ -151,7 +151,7 @@
}
ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
- LIR* load_pc_rel = RawLIR(current_dalvik_offset_, FWIDE(kA64Ldr2fp),
+ LIR* load_pc_rel = RawLIR(current_dalvik_offset_, WIDE(kA64Ldr2fp),
r_dest.GetReg(), 0, 0, 0, 0, data_target);
AppendLIR(load_pc_rel);
return load_pc_rel;
@@ -415,7 +415,7 @@
// 1 instruction is enough to load the immediate.
if (LIKELY(low_bits == high_bits)) {
// Value is either 0 or -1: we can just use wzr.
- ArmOpcode opcode = LIKELY(low_bits == 0) ? kA64Mov2rr : kA64Mvn2rr;
+ A64Opcode opcode = LIKELY(low_bits == 0) ? kA64Mov2rr : kA64Mvn2rr;
res = NewLIR2(opcode, r_dest.GetReg(), rwzr);
} else {
uint16_t uniform_bits, useful_bits;
@@ -466,7 +466,7 @@
if (LIKELY(value == INT64_C(0) || value == INT64_C(-1))) {
// value is either 0 or -1: we can just use xzr.
- ArmOpcode opcode = LIKELY(value == 0) ? WIDE(kA64Mov2rr) : WIDE(kA64Mvn2rr);
+ A64Opcode opcode = LIKELY(value == 0) ? WIDE(kA64Mov2rr) : WIDE(kA64Mvn2rr);
return NewLIR2(opcode, r_dest.GetReg(), rxzr);
}
@@ -486,7 +486,7 @@
if (num_slow_halfwords <= max_num_ops_per_const_load) {
// We can encode the number using a movz/movn followed by one or more movk.
- ArmOpcode op;
+ A64Opcode op;
uint16_t background;
LIR* res = nullptr;
@@ -548,15 +548,11 @@
}
LIR* Arm64Mir2Lir::OpReg(OpKind op, RegStorage r_dest_src) {
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
switch (op) {
case kOpBlx:
opcode = kA64Blr1x;
break;
- // TODO(Arm64): port kThumbBx.
- // case kOpBx:
- // opcode = kThumbBx;
- // break;
default:
LOG(FATAL) << "Bad opcode " << op;
}
@@ -564,9 +560,9 @@
}
LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r_src2, int shift) {
- ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+ A64Opcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
CHECK_EQ(r_dest_src1.Is64Bit(), r_src2.Is64Bit());
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
switch (op) {
case kOpCmn:
@@ -621,7 +617,7 @@
DCHECK_EQ(shift, ENCODE_NO_SHIFT);
return NewLIR2(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg());
} else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
- ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
+ A64EncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
if (kind == kFmtShift) {
return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), shift);
}
@@ -633,8 +629,8 @@
LIR* Arm64Mir2Lir::OpRegRegExtend(OpKind op, RegStorage r_dest_src1, RegStorage r_src2,
A64RegExtEncodings ext, uint8_t amount) {
- ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+ A64Opcode opcode = kA64Brk1d;
switch (op) {
case kOpCmn:
@@ -655,7 +651,7 @@
DCHECK(!IsPseudoLirOp(opcode));
if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
- ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
+ A64EncodingKind kind = EncodingMap[opcode].field_loc[2].kind;
if (kind == kFmtExtend) {
return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(),
EncodeExtend(ext, amount));
@@ -694,7 +690,7 @@
LIR* Arm64Mir2Lir::OpRegRegRegShift(OpKind op, RegStorage r_dest, RegStorage r_src1,
RegStorage r_src2, int shift) {
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
switch (op) {
case kOpAdd:
@@ -747,7 +743,7 @@
// The instructions above belong to two kinds:
// - 4-operands instructions, where the last operand is a shift/extend immediate,
// - 3-operands instructions with no shift/extend.
- ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
+ A64Opcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
CHECK_EQ(r_dest.Is64Bit(), r_src1.Is64Bit());
CHECK_EQ(r_dest.Is64Bit(), r_src2.Is64Bit());
if (EncodingMap[opcode].flags & IS_QUAD_OP) {
@@ -762,7 +758,7 @@
LIR* Arm64Mir2Lir::OpRegRegRegExtend(OpKind op, RegStorage r_dest, RegStorage r_src1,
RegStorage r_src2, A64RegExtEncodings ext, uint8_t amount) {
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
switch (op) {
case kOpAdd:
@@ -775,7 +771,7 @@
LOG(FATAL) << "Unimplemented opcode: " << op;
break;
}
- ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
+ A64Opcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode;
if (r_dest.Is64Bit()) {
CHECK(r_src1.Is64Bit());
@@ -810,11 +806,11 @@
LIR* res;
bool neg = (value < 0);
uint64_t abs_value = (neg & !(value == LLONG_MIN)) ? -value : value;
- ArmOpcode opcode = kA64Brk1d;
- ArmOpcode alt_opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
+ A64Opcode alt_opcode = kA64Brk1d;
bool is_logical = false;
bool is_wide = r_dest.Is64Bit();
- ArmOpcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
+ A64Opcode wide = (is_wide) ? WIDE(0) : UNWIDE(0);
int info = 0;
switch (op) {
@@ -937,9 +933,9 @@
}
LIR* Arm64Mir2Lir::OpRegImm64(OpKind op, RegStorage r_dest_src1, int64_t value) {
- ArmOpcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
- ArmOpcode opcode = kA64Brk1d;
- ArmOpcode neg_opcode = kA64Brk1d;
+ A64Opcode wide = (r_dest_src1.Is64Bit()) ? WIDE(0) : UNWIDE(0);
+ A64Opcode opcode = kA64Brk1d;
+ A64Opcode neg_opcode = kA64Brk1d;
bool shift;
bool neg = (value < 0);
uint64_t abs_value = (neg & !(value == LLONG_MIN)) ? -value : value;
@@ -1025,7 +1021,7 @@
int scale, OpSize size) {
LIR* load;
int expected_scale = 0;
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
r_base = Check64BitReg(r_base);
// TODO(Arm64): The sign extension of r_index should be carried out by using an extended
@@ -1040,7 +1036,7 @@
if (r_dest.IsDouble()) {
DCHECK(size == k64 || size == kDouble);
expected_scale = 3;
- opcode = FWIDE(kA64Ldr4fXxG);
+ opcode = WIDE(kA64Ldr4fXxG);
} else {
DCHECK(r_dest.IsSingle());
DCHECK(size == k32 || size == kSingle);
@@ -1113,7 +1109,7 @@
int scale, OpSize size) {
LIR* store;
int expected_scale = 0;
- ArmOpcode opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
r_base = Check64BitReg(r_base);
// TODO(Arm64): The sign extension of r_index should be carried out by using an extended
@@ -1128,7 +1124,7 @@
if (r_src.IsDouble()) {
DCHECK(size == k64 || size == kDouble);
expected_scale = 3;
- opcode = FWIDE(kA64Str4fXxG);
+ opcode = WIDE(kA64Str4fXxG);
} else {
DCHECK(r_src.IsSingle());
DCHECK(size == k32 || size == kSingle);
@@ -1197,8 +1193,8 @@
LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest,
OpSize size) {
LIR* load = NULL;
- ArmOpcode opcode = kA64Brk1d;
- ArmOpcode alt_opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
+ A64Opcode alt_opcode = kA64Brk1d;
int scale = 0;
switch (size) {
@@ -1209,8 +1205,8 @@
scale = 3;
if (r_dest.IsFloat()) {
DCHECK(r_dest.IsDouble());
- opcode = FWIDE(kA64Ldr3fXD);
- alt_opcode = FWIDE(kA64Ldur3fXd);
+ opcode = WIDE(kA64Ldr3fXD);
+ alt_opcode = WIDE(kA64Ldur3fXd);
} else {
opcode = WIDE(kA64Ldr3rXD);
alt_opcode = WIDE(kA64Ldur3rXd);
@@ -1294,8 +1290,8 @@
LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src,
OpSize size) {
LIR* store = NULL;
- ArmOpcode opcode = kA64Brk1d;
- ArmOpcode alt_opcode = kA64Brk1d;
+ A64Opcode opcode = kA64Brk1d;
+ A64Opcode alt_opcode = kA64Brk1d;
int scale = 0;
switch (size) {
@@ -1306,11 +1302,11 @@
scale = 3;
if (r_src.IsFloat()) {
DCHECK(r_src.IsDouble());
- opcode = FWIDE(kA64Str3fXD);
- alt_opcode = FWIDE(kA64Stur3fXd);
+ opcode = WIDE(kA64Str3fXD);
+ alt_opcode = WIDE(kA64Stur3fXd);
} else {
- opcode = FWIDE(kA64Str3rXD);
- alt_opcode = FWIDE(kA64Stur3rXd);
+ opcode = WIDE(kA64Str3rXD);
+ alt_opcode = WIDE(kA64Stur3rXd);
}
break;
case kSingle: // Intentional fall-through.
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index ee1c467..f305017 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -15,6 +15,7 @@
*/
#include "dex/compiler_internals.h"
+#include "driver/compiler_options.h"
#include "dex_file-inl.h"
#include "gc_map.h"
#include "gc_map_builder.h"
@@ -274,8 +275,8 @@
}
void Mir2Lir::DumpPromotionMap() {
- int num_regs = cu_->num_dalvik_registers + mir_graph_->GetNumUsedCompilerTemps();
- for (int i = 0; i < num_regs; i++) {
+ uint32_t num_regs = mir_graph_->GetNumOfCodeAndTempVRs();
+ for (uint32_t i = 0; i < num_regs; i++) {
PromotionMap v_reg_map = promotion_map_[i];
std::string buf;
if (v_reg_map.fp_location == kLocPhysReg) {
@@ -283,12 +284,13 @@
}
std::string buf3;
- if (i < cu_->num_dalvik_registers) {
+ if (i < mir_graph_->GetNumOfCodeVRs()) {
StringAppendF(&buf3, "%02d", i);
- } else if (i == mir_graph_->GetMethodSReg()) {
+ } else if (i == mir_graph_->GetNumOfCodeVRs()) {
buf3 = "Method*";
} else {
- StringAppendF(&buf3, "ct%d", i - cu_->num_dalvik_registers);
+ uint32_t diff = i - mir_graph_->GetNumOfCodeVRs();
+ StringAppendF(&buf3, "ct%d", diff);
}
LOG(INFO) << StringPrintf("V[%s] -> %s%d%s", buf3.c_str(),
@@ -317,11 +319,11 @@
LOG(INFO) << "Dumping LIR insns for "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file);
LIR* lir_insn;
- int insns_size = cu_->code_item->insns_size_in_code_units_;
+ int insns_size = mir_graph_->GetNumDalvikInsns();
- LOG(INFO) << "Regs (excluding ins) : " << cu_->num_regs;
- LOG(INFO) << "Ins : " << cu_->num_ins;
- LOG(INFO) << "Outs : " << cu_->num_outs;
+ LOG(INFO) << "Regs (excluding ins) : " << mir_graph_->GetNumOfLocalCodeVRs();
+ LOG(INFO) << "Ins : " << mir_graph_->GetNumOfInVRs();
+ LOG(INFO) << "Outs : " << mir_graph_->GetNumOfOutVRs();
LOG(INFO) << "CoreSpills : " << num_core_spills_;
LOG(INFO) << "FPSpills : " << num_fp_spills_;
LOG(INFO) << "CompilerTemps : " << mir_graph_->GetNumUsedCompilerTemps();
@@ -401,6 +403,18 @@
return nullptr;
}
+/* Search the existing constants in the literal pool for an exact class match */
+LIR* Mir2Lir::ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx) {
+ while (data_target) {
+ if (static_cast<uint32_t>(data_target->operands[0]) == type_idx &&
+ UnwrapPointer(data_target->operands[1]) == &dex_file) {
+ return data_target;
+ }
+ data_target = data_target->next;
+ }
+ return nullptr;
+}
+
/*
* The following are building blocks to insert constants into the pool or
* instruction streams.
@@ -458,20 +472,15 @@
Push32(code_buffer_, data_lir->operands[0]);
data_lir = NEXT_LIR(data_lir);
}
+ // TODO: patches_.reserve() as needed.
// Push code and method literals, record offsets for the compiler to patch.
data_lir = code_literal_list_;
while (data_lir != NULL) {
uint32_t target_method_idx = data_lir->operands[0];
const DexFile* target_dex_file =
reinterpret_cast<const DexFile*>(UnwrapPointer(data_lir->operands[1]));
- cu_->compiler_driver->AddCodePatch(cu_->dex_file,
- cu_->class_def_idx,
- cu_->method_idx,
- cu_->invoke_type,
- target_method_idx,
- target_dex_file,
- static_cast<InvokeType>(data_lir->operands[2]),
- code_buffer_.size());
+ patches_.push_back(LinkerPatch::CodePatch(code_buffer_.size(),
+ target_dex_file, target_method_idx));
const DexFile::MethodId& target_method_id = target_dex_file->GetMethodId(target_method_idx);
// unique value based on target to ensure code deduplication works
PushPointer(code_buffer_, &target_method_id, cu_->target64);
@@ -482,14 +491,8 @@
uint32_t target_method_idx = data_lir->operands[0];
const DexFile* target_dex_file =
reinterpret_cast<const DexFile*>(UnwrapPointer(data_lir->operands[1]));
- cu_->compiler_driver->AddMethodPatch(cu_->dex_file,
- cu_->class_def_idx,
- cu_->method_idx,
- cu_->invoke_type,
- target_method_idx,
- target_dex_file,
- static_cast<InvokeType>(data_lir->operands[2]),
- code_buffer_.size());
+ patches_.push_back(LinkerPatch::MethodPatch(code_buffer_.size(),
+ target_dex_file, target_method_idx));
const DexFile::MethodId& target_method_id = target_dex_file->GetMethodId(target_method_idx);
// unique value based on target to ensure code deduplication works
PushPointer(code_buffer_, &target_method_id, cu_->target64);
@@ -498,13 +501,12 @@
// Push class literals.
data_lir = class_literal_list_;
while (data_lir != NULL) {
- uint32_t target_method_idx = data_lir->operands[0];
- cu_->compiler_driver->AddClassPatch(cu_->dex_file,
- cu_->class_def_idx,
- cu_->method_idx,
- target_method_idx,
- code_buffer_.size());
- const DexFile::TypeId& target_method_id = cu_->dex_file->GetTypeId(target_method_idx);
+ uint32_t target_type_idx = data_lir->operands[0];
+ const DexFile* class_dex_file =
+ reinterpret_cast<const DexFile*>(UnwrapPointer(data_lir->operands[1]));
+ patches_.push_back(LinkerPatch::TypePatch(code_buffer_.size(),
+ class_dex_file, target_type_idx));
+ const DexFile::TypeId& target_method_id = class_dex_file->GetTypeId(target_type_idx);
// unique value based on target to ensure code deduplication works
PushPointer(code_buffer_, &target_method_id, cu_->target64);
data_lir = NEXT_LIR(data_lir);
@@ -513,10 +515,7 @@
/* Write the switch tables to the output stream */
void Mir2Lir::InstallSwitchTables() {
- GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
- while (true) {
- Mir2Lir::SwitchTable* tab_rec = iterator.Next();
- if (tab_rec == NULL) break;
+ for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
AlignBuffer(code_buffer_, tab_rec->offset);
/*
* For Arm, our reference point is the address of the bx
@@ -573,10 +572,7 @@
/* Write the fill array dta to the output stream */
void Mir2Lir::InstallFillArrayData() {
- GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
- while (true) {
- Mir2Lir::FillArrayData *tab_rec = iterator.Next();
- if (tab_rec == NULL) break;
+ for (Mir2Lir::FillArrayData* tab_rec : fill_array_data_) {
AlignBuffer(code_buffer_, tab_rec->offset);
for (int i = 0; i < (tab_rec->size + 1) / 2; i++) {
code_buffer_.push_back(tab_rec->table[i] & 0xFF);
@@ -640,15 +636,19 @@
void Mir2Lir::CreateMappingTables() {
+ bool generate_src_map = cu_->compiler_driver->GetCompilerOptions().GetIncludeDebugSymbols();
+
uint32_t pc2dex_data_size = 0u;
uint32_t pc2dex_entries = 0u;
uint32_t pc2dex_offset = 0u;
uint32_t pc2dex_dalvik_offset = 0u;
+ uint32_t pc2dex_src_entries = 0u;
uint32_t dex2pc_data_size = 0u;
uint32_t dex2pc_entries = 0u;
uint32_t dex2pc_offset = 0u;
uint32_t dex2pc_dalvik_offset = 0u;
for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+ pc2dex_src_entries++;
if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
pc2dex_entries += 1;
DCHECK(pc2dex_offset <= tgt_lir->offset);
@@ -669,6 +669,10 @@
}
}
+ if (generate_src_map) {
+ src_mapping_table_.reserve(pc2dex_src_entries);
+ }
+
uint32_t total_entries = pc2dex_entries + dex2pc_entries;
uint32_t hdr_data_size = UnsignedLeb128Size(total_entries) + UnsignedLeb128Size(pc2dex_entries);
uint32_t data_size = hdr_data_size + pc2dex_data_size + dex2pc_data_size;
@@ -684,6 +688,10 @@
dex2pc_offset = 0u;
dex2pc_dalvik_offset = 0u;
for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
+ if (generate_src_map && !tgt_lir->flags.is_nop) {
+ src_mapping_table_.push_back(SrcMapElem({tgt_lir->offset,
+ static_cast<int32_t>(tgt_lir->dalvik_offset)}));
+ }
if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
DCHECK(pc2dex_offset <= tgt_lir->offset);
write_pos = EncodeUnsignedLeb128(write_pos, tgt_lir->offset - pc2dex_offset);
@@ -772,10 +780,7 @@
}
int Mir2Lir::AssignSwitchTablesOffset(CodeOffset offset) {
- GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
- while (true) {
- Mir2Lir::SwitchTable* tab_rec = iterator.Next();
- if (tab_rec == NULL) break;
+ for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
tab_rec->offset = offset;
if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
offset += tab_rec->table[1] * (sizeof(int) * 2);
@@ -789,15 +794,12 @@
}
int Mir2Lir::AssignFillArrayDataOffset(CodeOffset offset) {
- GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
- while (true) {
- Mir2Lir::FillArrayData *tab_rec = iterator.Next();
- if (tab_rec == NULL) break;
+ for (Mir2Lir::FillArrayData* tab_rec : fill_array_data_) {
tab_rec->offset = offset;
offset += tab_rec->size;
// word align
offset = RoundUp(offset, 4);
- }
+ }
return offset;
}
@@ -849,10 +851,7 @@
}
void Mir2Lir::ProcessSwitchTables() {
- GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
- while (true) {
- Mir2Lir::SwitchTable *tab_rec = iterator.Next();
- if (tab_rec == NULL) break;
+ for (Mir2Lir::SwitchTable* tab_rec : switch_tables_) {
if (tab_rec->table[0] == Instruction::kPackedSwitchSignature) {
MarkPackedCaseLabels(tab_rec);
} else if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
@@ -977,21 +976,22 @@
first_fixup_(NULL),
cu_(cu),
mir_graph_(mir_graph),
- switch_tables_(arena, 4, kGrowableArraySwitchTables),
- fill_array_data_(arena, 4, kGrowableArrayFillArrayData),
- tempreg_info_(arena, 20, kGrowableArrayMisc),
- reginfo_map_(arena, RegStorage::kMaxRegs, kGrowableArrayMisc),
- pointer_storage_(arena, 128, kGrowableArrayMisc),
+ switch_tables_(arena->Adapter(kArenaAllocSwitchTable)),
+ fill_array_data_(arena->Adapter(kArenaAllocFillArrayData)),
+ tempreg_info_(arena->Adapter()),
+ reginfo_map_(arena->Adapter()),
+ pointer_storage_(arena->Adapter()),
data_offset_(0),
total_size_(0),
block_label_list_(NULL),
promotion_map_(NULL),
current_dalvik_offset_(0),
estimated_native_code_size_(0),
- reg_pool_(NULL),
+ reg_pool_(nullptr),
live_sreg_(0),
core_vmap_table_(mir_graph->GetArena()->Adapter()),
fp_vmap_table_(mir_graph->GetArena()->Adapter()),
+ patches_(mir_graph->GetArena()->Adapter()),
num_core_spills_(0),
num_fp_spills_(0),
frame_size_(0),
@@ -999,9 +999,15 @@
fp_spill_mask_(0),
first_lir_insn_(NULL),
last_lir_insn_(NULL),
- slow_paths_(arena, 32, kGrowableArraySlowPaths),
+ slow_paths_(arena->Adapter(kArenaAllocSlowPaths)),
mem_ref_type_(ResourceMask::kHeapRef),
mask_cache_(arena) {
+ switch_tables_.reserve(4);
+ fill_array_data_.reserve(4);
+ tempreg_info_.reserve(20);
+ reginfo_map_.reserve(RegStorage::kMaxRegs);
+ pointer_storage_.reserve(128);
+ slow_paths_.reserve(32);
// Reserve pointer id 0 for NULL.
size_t null_idx = WrapPointer(NULL);
DCHECK_EQ(null_idx, 0U);
@@ -1077,11 +1083,17 @@
vmap_encoder.PushBackUnsigned(0u); // Size is 0.
}
- std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnCallFrameInformation());
+ // Sort patches by literal offset for better deduplication.
+ std::sort(patches_.begin(), patches_.end(), [](const LinkerPatch& lhs, const LinkerPatch& rhs) {
+ return lhs.LiteralOffset() < rhs.LiteralOffset();
+ });
+
+ std::unique_ptr<std::vector<uint8_t>> cfi_info(ReturnFrameDescriptionEntry());
CompiledMethod* result =
new CompiledMethod(cu_->compiler_driver, cu_->instruction_set, code_buffer_, frame_size_,
- core_spill_mask_, fp_spill_mask_, encoded_mapping_table_,
- vmap_encoder.GetData(), native_gc_map_, cfi_info.get());
+ core_spill_mask_, fp_spill_mask_, &src_mapping_table_, encoded_mapping_table_,
+ vmap_encoder.GetData(), native_gc_map_, cfi_info.get(),
+ ArrayRef<LinkerPatch>(patches_));
return result;
}
@@ -1096,7 +1108,8 @@
// By default assume that the Mir2Lir will need one slot for each temporary.
// If the backend can better determine temps that have non-overlapping ranges and
// temps that do not need spilled, it can actually provide a small region.
- return (mir_graph_->GetNumUsedCompilerTemps() * sizeof(uint32_t));
+ mir_graph_->CommitCompilerTemps();
+ return mir_graph_->GetNumBytesForSpecialTemps() + mir_graph_->GetMaximumBytesForNonSpecialTemps();
}
int Mir2Lir::ComputeFrameSize() {
@@ -1104,7 +1117,8 @@
uint32_t size = num_core_spills_ * GetBytesPerGprSpillLocation(cu_->instruction_set)
+ num_fp_spills_ * GetBytesPerFprSpillLocation(cu_->instruction_set)
+ sizeof(uint32_t) // Filler.
- + (cu_->num_regs + cu_->num_outs) * sizeof(uint32_t)
+ + mir_graph_->GetNumOfLocalCodeVRs() * sizeof(uint32_t)
+ + mir_graph_->GetNumOfOutVRs() * sizeof(uint32_t)
+ GetNumBytesForCompilerTempSpillRegion();
/* Align and set */
return RoundUp(size, kStackAlignment);
@@ -1192,7 +1206,8 @@
}
void Mir2Lir::AddSlowPath(LIRSlowPath* slowpath) {
- slow_paths_.Insert(slowpath);
+ slow_paths_.push_back(slowpath);
+ ResetDefTracking();
}
void Mir2Lir::LoadCodeAddress(const MethodReference& target_method, InvokeType type,
@@ -1229,18 +1244,20 @@
DCHECK_NE(cu_->instruction_set, kMips) << reinterpret_cast<void*>(data_target);
}
-void Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) {
+void Mir2Lir::LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+ SpecialTargetRegister symbolic_reg) {
// Use the literal pool and a PC-relative load from a data word.
- LIR* data_target = ScanLiteralPool(class_literal_list_, type_idx, 0);
+ LIR* data_target = ScanLiteralPoolClass(class_literal_list_, dex_file, type_idx);
if (data_target == nullptr) {
data_target = AddWordData(&class_literal_list_, type_idx);
+ data_target->operands[1] = WrapPointer(const_cast<DexFile*>(&dex_file));
}
// Loads a Class pointer, which is a reference as it lives in the heap.
LIR* load_pc_rel = OpPcRelLoad(TargetReg(symbolic_reg, kRef), data_target);
AppendLIR(load_pc_rel);
}
-std::vector<uint8_t>* Mir2Lir::ReturnCallFrameInformation() {
+std::vector<uint8_t>* Mir2Lir::ReturnFrameDescriptionEntry() {
// Default case is to do nothing.
return nullptr;
}
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 34ed0f4..2523380 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -25,8 +25,10 @@
#include "thread.h"
#include "thread-inl.h"
#include "dex/mir_graph.h"
+#include "dex/quick/mir_to_lir.h"
#include "dex_instruction.h"
#include "dex_instruction-inl.h"
+#include "driver/dex_compilation_unit.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
@@ -561,9 +563,25 @@
break;
default:
LOG(FATAL) << "Unexpected inline op: " << method.opcode;
+ break;
}
if (result) {
invoke->optimization_flags |= MIR_INLINED;
+ // If the invoke has not been eliminated yet, check now whether we should do it.
+ // This is done so that dataflow analysis does not get tripped up seeing nop invoke.
+ if (static_cast<int>(invoke->dalvikInsn.opcode) != kMirOpNop) {
+ bool is_static = invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC ||
+ invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE;
+ if (is_static || (invoke->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) {
+ // No null object register involved here so we can eliminate the invoke.
+ invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
+ } else {
+ // Invoke was kept around because null check needed to be done.
+ invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNullCheck);
+ // For invokes, the object register is in vC. For null check mir, it is in vA.
+ invoke->dalvikInsn.vA = invoke->dalvikInsn.vC;
+ }
+ }
if (move_result != nullptr) {
move_result->optimization_flags |= MIR_INLINED;
move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index f6c77fc..9f7a881 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -361,7 +361,7 @@
&direct_type_ptr, &is_finalizable)) {
// The fast path.
if (!use_direct_type_ptr) {
- LoadClassType(type_idx, kArg0);
+ LoadClassType(*dex_file, type_idx, kArg0);
CallRuntimeHelperRegMethodRegLocation(kQuickAllocArrayResolved, TargetReg(kArg0, kNotWide),
rl_src, true);
} else {
@@ -524,11 +524,9 @@
const RegStorage r_base_;
};
-void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double,
- bool is_object) {
+void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, OpSize size) {
const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass());
- OpSize store_size = LoadStoreOpSize(is_long_or_double, is_object);
if (!SLOW_FIELD_PATH && field_info.FastPut()) {
DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
RegStorage r_base;
@@ -587,37 +585,59 @@
FreeTemp(r_method);
}
// rBase now holds static storage base
- RegisterClass reg_class = RegClassForFieldLoadStore(store_size, field_info.IsVolatile());
- if (is_long_or_double) {
+ RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
+ if (IsWide(size)) {
rl_src = LoadValueWide(rl_src, reg_class);
} else {
rl_src = LoadValue(rl_src, reg_class);
}
- if (is_object) {
+ if (IsRef(size)) {
StoreRefDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg,
field_info.IsVolatile() ? kVolatile : kNotVolatile);
} else {
- StoreBaseDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, store_size,
+ StoreBaseDisp(r_base, field_info.FieldOffset().Int32Value(), rl_src.reg, size,
field_info.IsVolatile() ? kVolatile : kNotVolatile);
}
- if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
+ if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
MarkGCCard(rl_src.reg, r_base);
}
FreeTemp(r_base);
} else {
FlushAllRegs(); // Everything to home locations
- QuickEntrypointEnum target =
- is_long_or_double ? kQuickSet64Static
- : (is_object ? kQuickSetObjStatic : kQuickSet32Static);
+ QuickEntrypointEnum target;
+ switch (size) {
+ case kReference:
+ target = kQuickSetObjStatic;
+ break;
+ case k64:
+ case kDouble:
+ target = kQuickSet64Static;
+ break;
+ case k32:
+ case kSingle:
+ target = kQuickSet32Static;
+ break;
+ case kSignedHalf:
+ case kUnsignedHalf:
+ target = kQuickSet16Static;
+ break;
+ case kSignedByte:
+ case kUnsignedByte:
+ target = kQuickSet8Static;
+ break;
+ case kWord: // Intentional fallthrough.
+ default:
+ LOG(FATAL) << "Can't determine entrypoint for: " << size;
+ target = kQuickSet32Static;
+ }
CallRuntimeHelperImmRegLocation(target, field_info.FieldIndex(), rl_src, true);
}
}
-void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest,
- bool is_long_or_double, bool is_object) {
+void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, OpSize size, Primitive::Type type) {
const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir);
cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass());
- OpSize load_size = LoadStoreOpSize(is_long_or_double, is_object);
+
if (!SLOW_FIELD_PATH && field_info.FastGet()) {
DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
RegStorage r_base;
@@ -668,33 +688,62 @@
FreeTemp(r_method);
}
// r_base now holds static storage base
- RegisterClass reg_class = RegClassForFieldLoadStore(load_size, field_info.IsVolatile());
+ RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
int field_offset = field_info.FieldOffset().Int32Value();
- if (is_object) {
+ if (IsRef(size)) {
+ // TODO: DCHECK?
LoadRefDisp(r_base, field_offset, rl_result.reg, field_info.IsVolatile() ? kVolatile :
kNotVolatile);
} else {
- LoadBaseDisp(r_base, field_offset, rl_result.reg, load_size, field_info.IsVolatile() ?
+ LoadBaseDisp(r_base, field_offset, rl_result.reg, size, field_info.IsVolatile() ?
kVolatile : kNotVolatile);
}
FreeTemp(r_base);
- if (is_long_or_double) {
+ if (IsWide(size)) {
StoreValueWide(rl_dest, rl_result);
} else {
StoreValue(rl_dest, rl_result);
}
} else {
+ DCHECK(SizeMatchesTypeForEntrypoint(size, type));
FlushAllRegs(); // Everything to home locations
- QuickEntrypointEnum target =
- is_long_or_double ? kQuickGet64Static
- : (is_object ? kQuickGetObjStatic : kQuickGet32Static);
+ QuickEntrypointEnum target;
+ switch (type) {
+ case Primitive::kPrimNot:
+ target = kQuickGetObjStatic;
+ break;
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ target = kQuickGet64Static;
+ break;
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ target = kQuickGet32Static;
+ break;
+ case Primitive::kPrimShort:
+ target = kQuickGetShortStatic;
+ break;
+ case Primitive::kPrimChar:
+ target = kQuickGetCharStatic;
+ break;
+ case Primitive::kPrimByte:
+ target = kQuickGetByteStatic;
+ break;
+ case Primitive::kPrimBoolean:
+ target = kQuickGetBooleanStatic;
+ break;
+ case Primitive::kPrimVoid: // Intentional fallthrough.
+ default:
+ LOG(FATAL) << "Can't determine entrypoint for: " << type;
+ target = kQuickGet32Static;
+ }
CallRuntimeHelperImm(target, field_info.FieldIndex(), true);
// FIXME: pGetXXStatic always return an int or int64 regardless of rl_dest.fp.
- if (is_long_or_double) {
+ if (IsWide(size)) {
RegLocation rl_result = GetReturnWide(kCoreReg);
StoreValueWide(rl_dest, rl_result);
} else {
@@ -708,21 +757,18 @@
void Mir2Lir::HandleSlowPaths() {
// We should check slow_paths_.Size() every time, because a new slow path
// may be created during slowpath->Compile().
- for (size_t i = 0; i < slow_paths_.Size(); ++i) {
- LIRSlowPath* slowpath = slow_paths_.Get(i);
+ for (LIRSlowPath* slowpath : slow_paths_) {
slowpath->Compile();
}
- slow_paths_.Reset();
+ slow_paths_.clear();
}
-void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size,
- RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double,
- bool is_object) {
+void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, Primitive::Type type,
+ RegLocation rl_dest, RegLocation rl_obj) {
const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet());
- OpSize load_size = LoadStoreOpSize(is_long_or_double, is_object);
if (!SLOW_FIELD_PATH && field_info.FastGet()) {
- RegisterClass reg_class = RegClassForFieldLoadStore(load_size, field_info.IsVolatile());
+ RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
// A load of the class will lead to an iget with offset 0.
DCHECK_GE(field_info.FieldOffset().Int32Value(), 0);
rl_obj = LoadValue(rl_obj, kRefReg);
@@ -730,29 +776,57 @@
RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
int field_offset = field_info.FieldOffset().Int32Value();
LIR* load_lir;
- if (is_object) {
+ if (IsRef(size)) {
load_lir = LoadRefDisp(rl_obj.reg, field_offset, rl_result.reg, field_info.IsVolatile() ?
kVolatile : kNotVolatile);
} else {
- load_lir = LoadBaseDisp(rl_obj.reg, field_offset, rl_result.reg, load_size,
+ load_lir = LoadBaseDisp(rl_obj.reg, field_offset, rl_result.reg, size,
field_info.IsVolatile() ? kVolatile : kNotVolatile);
}
MarkPossibleNullPointerExceptionAfter(opt_flags, load_lir);
- if (is_long_or_double) {
+ if (IsWide(size)) {
StoreValueWide(rl_dest, rl_result);
} else {
StoreValue(rl_dest, rl_result);
}
} else {
- QuickEntrypointEnum target =
- is_long_or_double ? kQuickGet64Instance
- : (is_object ? kQuickGetObjInstance : kQuickGet32Instance);
+ DCHECK(SizeMatchesTypeForEntrypoint(size, type));
+ QuickEntrypointEnum target;
+ switch (type) {
+ case Primitive::kPrimNot:
+ target = kQuickGetObjInstance;
+ break;
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ target = kQuickGet64Instance;
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimInt:
+ target = kQuickGet32Instance;
+ break;
+ case Primitive::kPrimShort:
+ target = kQuickGetShortInstance;
+ break;
+ case Primitive::kPrimChar:
+ target = kQuickGetCharInstance;
+ break;
+ case Primitive::kPrimByte:
+ target = kQuickGetByteInstance;
+ break;
+ case Primitive::kPrimBoolean:
+ target = kQuickGetBooleanInstance;
+ break;
+ case Primitive::kPrimVoid: // Intentional fallthrough.
+ default:
+ LOG(FATAL) << "Can't determine entrypoint for: " << type;
+ target = kQuickGet32Instance;
+ }
// Second argument of pGetXXInstance is always a reference.
DCHECK_EQ(static_cast<unsigned int>(rl_obj.wide), 0U);
CallRuntimeHelperImmRegLocation(target, field_info.FieldIndex(), rl_obj, true);
// FIXME: pGetXXInstance always return an int or int64 regardless of rl_dest.fp.
- if (is_long_or_double) {
+ if (IsWide(size)) {
RegLocation rl_result = GetReturnWide(kCoreReg);
StoreValueWide(rl_dest, rl_result);
} else {
@@ -763,18 +837,16 @@
}
void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size,
- RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double,
- bool is_object) {
+ RegLocation rl_src, RegLocation rl_obj) {
const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir);
cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut());
- OpSize store_size = LoadStoreOpSize(is_long_or_double, is_object);
if (!SLOW_FIELD_PATH && field_info.FastPut()) {
- RegisterClass reg_class = RegClassForFieldLoadStore(store_size, field_info.IsVolatile());
+ RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile());
// Dex code never writes to the class field.
DCHECK_GE(static_cast<uint32_t>(field_info.FieldOffset().Int32Value()),
sizeof(mirror::HeapReference<mirror::Class>));
rl_obj = LoadValue(rl_obj, kRefReg);
- if (is_long_or_double) {
+ if (IsWide(size)) {
rl_src = LoadValueWide(rl_src, reg_class);
} else {
rl_src = LoadValue(rl_src, reg_class);
@@ -782,21 +854,44 @@
GenNullCheck(rl_obj.reg, opt_flags);
int field_offset = field_info.FieldOffset().Int32Value();
LIR* store;
- if (is_object) {
+ if (IsRef(size)) {
store = StoreRefDisp(rl_obj.reg, field_offset, rl_src.reg, field_info.IsVolatile() ?
kVolatile : kNotVolatile);
} else {
- store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, store_size,
+ store = StoreBaseDisp(rl_obj.reg, field_offset, rl_src.reg, size,
field_info.IsVolatile() ? kVolatile : kNotVolatile);
}
MarkPossibleNullPointerExceptionAfter(opt_flags, store);
- if (is_object && !mir_graph_->IsConstantNullRef(rl_src)) {
+ if (IsRef(size) && !mir_graph_->IsConstantNullRef(rl_src)) {
MarkGCCard(rl_src.reg, rl_obj.reg);
}
} else {
- QuickEntrypointEnum target =
- is_long_or_double ? kQuickSet64Instance
- : (is_object ? kQuickSetObjInstance : kQuickSet32Instance);
+ QuickEntrypointEnum target;
+ switch (size) {
+ case kReference:
+ target = kQuickSetObjInstance;
+ break;
+ case k64:
+ case kDouble:
+ target = kQuickSet64Instance;
+ break;
+ case k32:
+ case kSingle:
+ target = kQuickSet32Instance;
+ break;
+ case kSignedHalf:
+ case kUnsignedHalf:
+ target = kQuickSet16Instance;
+ break;
+ case kSignedByte:
+ case kUnsignedByte:
+ target = kQuickSet8Instance;
+ break;
+ case kWord: // Intentional fallthrough.
+ default:
+ LOG(FATAL) << "Can't determine entrypoint for: " << size;
+ target = kQuickSet32Instance;
+ }
CallRuntimeHelperImmRegLocationRegLocation(target, field_info.FieldIndex(), rl_obj, rl_src,
true);
}
@@ -961,7 +1056,7 @@
!is_finalizable) {
// The fast path.
if (!use_direct_type_ptr) {
- LoadClassType(type_idx, kArg0);
+ LoadClassType(*dex_file, type_idx, kArg0);
if (!is_type_initialized) {
CallRuntimeHelperRegMethod(kQuickAllocObjectResolved, TargetReg(kArg0, kRef), true);
} else {
@@ -2009,7 +2104,7 @@
}
void Mir2Lir::GenSmallPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
const uint16_t entries = table[1];
// Chained cmp-and-branch.
const int32_t* as_int32 = reinterpret_cast<const int32_t*>(&table[2]);
@@ -2052,7 +2147,7 @@
}
void Mir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
@@ -2067,7 +2162,7 @@
}
void Mir2Lir::GenSmallSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
const uint16_t entries = table[1];
// Chained cmp-and-branch.
const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
@@ -2082,7 +2177,7 @@
}
void Mir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
@@ -2096,4 +2191,28 @@
}
}
+bool Mir2Lir::SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type) {
+ switch (size) {
+ case kReference:
+ return type == Primitive::kPrimNot;
+ case k64:
+ case kDouble:
+ return type == Primitive::kPrimLong || type == Primitive::kPrimDouble;
+ case k32:
+ case kSingle:
+ return type == Primitive::kPrimInt || type == Primitive::kPrimFloat;
+ case kSignedHalf:
+ return type == Primitive::kPrimShort;
+ case kUnsignedHalf:
+ return type == Primitive::kPrimChar;
+ case kSignedByte:
+ return type == Primitive::kPrimByte;
+ case kUnsignedByte:
+ return type == Primitive::kPrimBoolean;
+ case kWord: // Intentional fallthrough.
+ default:
+ return false; // There are no sane types with this op size.
+ }
+}
+
} // namespace art
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 7958886..67a75cb 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "arm/codegen_arm.h"
#include "dex/compiler_ir.h"
#include "dex/frontend.h"
#include "dex/quick/dex_file_method_inliner.h"
@@ -25,11 +26,9 @@
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/object_array-inl.h"
-#include "mirror/reference-inl.h"
#include "mirror/string.h"
#include "mir_to_lir-inl.h"
#include "scoped_thread_state_change.h"
-#include "x86/codegen_x86.h"
namespace art {
@@ -383,11 +382,11 @@
StoreRefDisp(TargetPtrReg(kSp), 0, rl_src.reg, kNotVolatile);
}
- if (cu_->num_ins == 0) {
+ if (mir_graph_->GetNumOfInVRs() == 0) {
return;
}
- int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+ int start_vreg = mir_graph_->GetFirstInVR();
/*
* Copy incoming arguments to their proper home locations.
* NOTE: an older version of dx had an issue in which
@@ -401,7 +400,7 @@
* half to memory as well.
*/
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- for (int i = 0; i < cu_->num_ins; i++) {
+ for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
PromotionMap* v_map = &promotion_map_[start_vreg + i];
RegStorage reg = GetArgMappingToPhysicalReg(i);
@@ -495,15 +494,15 @@
uint32_t unused,
uintptr_t direct_code, uintptr_t direct_method,
InvokeType type) {
+ DCHECK(cu->instruction_set != kX86 && cu->instruction_set != kX86_64 &&
+ cu->instruction_set != kThumb2 && cu->instruction_set != kArm);
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_code != static_cast<uintptr_t>(-1)) {
- if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
- cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
- }
- } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+ cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
+ } else {
cg->LoadCodeAddress(target_method, type, kInvokeTgt);
}
if (direct_method != static_cast<uintptr_t>(-1)) {
@@ -531,7 +530,7 @@
if (direct_code != 0) {
if (direct_code != static_cast<uintptr_t>(-1)) {
cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
- } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+ } else {
CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
cg->LoadCodeAddress(target_method, type, kInvokeTgt);
}
@@ -549,7 +548,7 @@
if (CommonCallCodeLoadCodePointerIntoInvokeTgt(info, &arg0_ref, cu, cg)) {
break; // kInvokeTgt := arg0_ref->entrypoint
}
- } else if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) {
+ } else {
break;
}
// Intentional fallthrough for x86
@@ -935,9 +934,6 @@
}
}
- // Logic below assumes that Method pointer is at offset zero from SP.
- DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0);
-
// The first 3 arguments are passed via registers.
// TODO: For 64-bit, instead of hardcoding 4 for Method* size, we should either
// get size of uintptr_t or size of object reference according to model being used.
@@ -1112,9 +1108,12 @@
RegLocation Mir2Lir::InlineTarget(CallInfo* info) {
RegLocation res;
if (info->result.location == kLocInvalid) {
- res = GetReturn(LocToRegClass(info->result));
+ // If result is unused, return a sink target based on type of invoke target.
+ res = GetReturn(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
} else {
res = info->result;
+ DCHECK_EQ(LocToRegClass(res),
+ ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
}
return res;
}
@@ -1122,9 +1121,12 @@
RegLocation Mir2Lir::InlineTargetWide(CallInfo* info) {
RegLocation res;
if (info->result.location == kLocInvalid) {
- res = GetReturnWide(kCoreReg);
+ // If result is unused, return a sink target based on type of invoke target.
+ res = GetReturnWide(ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
} else {
res = info->result;
+ DCHECK_EQ(LocToRegClass(res),
+ ShortyToRegClass(mir_graph_->GetShortyFromTargetIdx(info->index)[0]));
}
return res;
}
@@ -1135,68 +1137,37 @@
return false;
}
- // the refrence class is stored in the image dex file which might not be the same as the cu's
- // dex file. Query the reference class for the image dex file then reset to starting dex file
- // in after loading class type.
- uint16_t type_idx = 0;
- const DexFile* ref_dex_file = nullptr;
- {
- ScopedObjectAccess soa(Thread::Current());
- type_idx = mirror::Reference::GetJavaLangRefReference()->GetDexTypeIndex();
- ref_dex_file = mirror::Reference::GetJavaLangRefReference()->GetDexCache()->GetDexFile();
- }
- CHECK(LIKELY(ref_dex_file != nullptr));
-
- // address is either static within the image file, or needs to be patched up after compilation.
- bool unused_type_initialized;
bool use_direct_type_ptr;
uintptr_t direct_type_ptr;
- bool is_finalizable;
- const DexFile* old_dex = cu_->dex_file;
- cu_->dex_file = ref_dex_file;
+ ClassReference ref;
+ if (!cu_->compiler_driver->CanEmbedReferenceTypeInCode(&ref,
+ &use_direct_type_ptr, &direct_type_ptr)) {
+ return false;
+ }
+
RegStorage reg_class = TargetReg(kArg1, kRef);
Clobber(reg_class);
LockTemp(reg_class);
- if (!cu_->compiler_driver->CanEmbedTypeInCode(*ref_dex_file, type_idx, &unused_type_initialized,
- &use_direct_type_ptr, &direct_type_ptr,
- &is_finalizable) || is_finalizable) {
- cu_->dex_file = old_dex;
- // address is not known and post-compile patch is not possible, cannot insert intrinsic.
- return false;
- }
if (use_direct_type_ptr) {
LoadConstant(reg_class, direct_type_ptr);
- } else if (cu_->dex_file == old_dex) {
- // TODO: Bug 16656190 If cu_->dex_file != old_dex the patching could retrieve the wrong class
- // since the load class is indexed only by the type_idx. We should include which dex file a
- // class is from in the LoadClassType LIR.
- LoadClassType(type_idx, kArg1);
} else {
- cu_->dex_file = old_dex;
- return false;
+ uint16_t type_idx = ref.first->GetClassDef(ref.second).class_idx_;
+ LoadClassType(*ref.first, type_idx, kArg1);
}
- cu_->dex_file = old_dex;
- // get the offset for flags in reference class.
- uint32_t slow_path_flag_offset = 0;
- uint32_t disable_flag_offset = 0;
- {
- ScopedObjectAccess soa(Thread::Current());
- mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference();
- slow_path_flag_offset = reference_class->GetSlowPathFlagOffset().Uint32Value();
- disable_flag_offset = reference_class->GetDisableIntrinsicFlagOffset().Uint32Value();
- }
+ uint32_t slow_path_flag_offset = cu_->compiler_driver->GetReferenceSlowFlagOffset();
+ uint32_t disable_flag_offset = cu_->compiler_driver->GetReferenceDisableFlagOffset();
CHECK(slow_path_flag_offset && disable_flag_offset &&
(slow_path_flag_offset != disable_flag_offset));
// intrinsic logic start.
RegLocation rl_obj = info->args[0];
- rl_obj = LoadValue(rl_obj);
+ rl_obj = LoadValue(rl_obj, kRefReg);
RegStorage reg_slow_path = AllocTemp();
RegStorage reg_disabled = AllocTemp();
- Load32Disp(reg_class, slow_path_flag_offset, reg_slow_path);
- Load32Disp(reg_class, disable_flag_offset, reg_disabled);
+ Load8Disp(reg_class, slow_path_flag_offset, reg_slow_path);
+ Load8Disp(reg_class, disable_flag_offset, reg_disabled);
FreeTemp(reg_class);
LIR* or_inst = OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled);
FreeTemp(reg_disabled);
@@ -1330,10 +1301,10 @@
return false;
}
RegLocation rl_src_i = info->args[0];
- RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
- RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info); // result reg
+ RegLocation rl_i = IsWide(size) ? LoadValueWide(rl_src_i, kCoreReg) : LoadValue(rl_src_i, kCoreReg);
+ RegLocation rl_dest = IsWide(size) ? InlineTargetWide(info) : InlineTarget(info); // result reg
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- if (size == k64) {
+ if (IsWide(size)) {
if (cu_->instruction_set == kArm64 || cu_->instruction_set == kX86_64) {
OpRegReg(kOpRev, rl_result.reg, rl_i.reg);
StoreValueWide(rl_dest, rl_result);
@@ -1713,31 +1684,6 @@
GenInvokeNoInline(info);
}
-static LIR* GenInvokeNoInlineCall(Mir2Lir* mir_to_lir, InvokeType type) {
- QuickEntrypointEnum trampoline;
- switch (type) {
- case kInterface:
- trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
- break;
- case kDirect:
- trampoline = kQuickInvokeDirectTrampolineWithAccessCheck;
- break;
- case kStatic:
- trampoline = kQuickInvokeStaticTrampolineWithAccessCheck;
- break;
- case kSuper:
- trampoline = kQuickInvokeSuperTrampolineWithAccessCheck;
- break;
- case kVirtual:
- trampoline = kQuickInvokeVirtualTrampolineWithAccessCheck;
- break;
- default:
- LOG(FATAL) << "Unexpected invoke type";
- trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
- }
- return mir_to_lir->InvokeTrampoline(kOpBlx, RegStorage::InvalidReg(), trampoline);
-}
-
void Mir2Lir::GenInvokeNoInline(CallInfo* info) {
int call_state = 0;
LIR* null_ck;
@@ -1751,7 +1697,7 @@
cu_->compiler_driver->ProcessedInvoke(method_info.GetInvokeType(), method_info.StatsFlags());
BeginInvoke(info);
InvokeType original_type = static_cast<InvokeType>(method_info.GetInvokeType());
- info->type = static_cast<InvokeType>(method_info.GetSharpType());
+ info->type = method_info.GetSharpType();
bool fast_path = method_info.FastPath();
bool skip_this;
if (info->type == kInterface) {
@@ -1761,10 +1707,10 @@
if (fast_path) {
p_null_ck = &null_ck;
}
- next_call_insn = fast_path ? NextSDCallInsn : NextDirectCallInsnSP;
+ next_call_insn = fast_path ? GetNextSDCallInsn() : NextDirectCallInsnSP;
skip_this = false;
} else if (info->type == kStatic) {
- next_call_insn = fast_path ? NextSDCallInsn : NextStaticCallInsnSP;
+ next_call_insn = fast_path ? GetNextSDCallInsn() : NextStaticCallInsnSP;
skip_this = false;
} else if (info->type == kSuper) {
DCHECK(!fast_path); // Fast path is a direct call.
@@ -1792,25 +1738,9 @@
call_state = next_call_insn(cu_, info, call_state, target_method, method_info.VTableIndex(),
method_info.DirectCode(), method_info.DirectMethod(), original_type);
}
- LIR* call_inst;
- if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) {
- call_inst = OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
- } else {
- if (fast_path) {
- if (method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
- // We can have the linker fixup a call relative.
- call_inst =
- reinterpret_cast<X86Mir2Lir*>(this)->CallWithLinkerFixup(target_method, info->type);
- } else {
- call_inst = OpMem(kOpBlx, TargetReg(kArg0, kRef),
- mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
- }
- } else {
- call_inst = GenInvokeNoInlineCall(this, info->type);
- }
- }
+ LIR* call_insn = GenCallInsn(method_info);
EndInvoke(info);
- MarkSafepointPC(call_inst);
+ MarkSafepointPC(call_insn);
ClobberCallerSave();
if (info->result.location != kLocInvalid) {
@@ -1825,4 +1755,14 @@
}
}
+NextCallInsn Mir2Lir::GetNextSDCallInsn() {
+ return NextSDCallInsn;
+}
+
+LIR* Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+ DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64 &&
+ cu_->instruction_set != kThumb2 && cu_->instruction_set != kArm);
+ return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
+}
+
} // namespace art
diff --git a/compiler/dex/quick/mips/backend_mips.h b/compiler/dex/quick/mips/backend_mips.h
new file mode 100644
index 0000000..f65e984
--- /dev/null
+++ b/compiler/dex/quick/mips/backend_mips.h
@@ -0,0 +1,32 @@
+/*
+ * 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_COMPILER_DEX_QUICK_MIPS_BACKEND_MIPS_H_
+#define ART_COMPILER_DEX_QUICK_MIPS_BACKEND_MIPS_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_QUICK_MIPS_BACKEND_MIPS_H_
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index e8cb356..6536c41 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -62,7 +62,7 @@
*
*/
void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
@@ -74,7 +74,7 @@
int elements = table[1];
tab_rec->targets =
static_cast<LIR**>(arena_->Alloc(elements * sizeof(LIR*), kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// The table is composed of 8-byte key/disp pairs
int byte_size = elements * 8;
@@ -139,7 +139,7 @@
* done:
*/
void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
@@ -151,7 +151,7 @@
int size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
@@ -220,8 +220,8 @@
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void MipsMir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
// Add the table to the list - we'll process it later
FillArrayData* tab_rec =
reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
@@ -232,7 +232,7 @@
uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
tab_rec->size = (size * width) + 8;
- fill_array_data_.Insert(tab_rec);
+ fill_array_data_.push_back(tab_rec);
// Making a call - use explicit registers
FlushAllRegs(); /* Everything to home location */
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 43cbde7..be94019 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_
#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
#include "mips_lir.h"
namespace art {
@@ -115,7 +116,7 @@
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
void GenSpecialExitSequence();
- void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index ea56989..95c1262 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -220,9 +220,9 @@
int dest_reg_class) {
// Implement as a branch-over.
// TODO: Conditional move?
- LoadConstant(rs_dest, false_val); // Favors false.
- LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
LoadConstant(rs_dest, true_val);
+ LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
+ LoadConstant(rs_dest, false_val);
LIR* target_label = NewLIR0(kPseudoTargetLabel);
ne_branchover->target = target_label;
}
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index bc91fbc..d3719ab 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -20,6 +20,7 @@
#include <string>
+#include "backend_mips.h"
#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "mips_lir.h"
@@ -429,16 +430,16 @@
}
void MipsMir2Lir::CompilerInitializeRegAlloc() {
- reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */, sp_regs,
- dp_regs, reserved_regs, empty_pool /* reserved64 */,
- core_temps, empty_pool /* core64_temps */, sp_temps,
- dp_temps);
+ reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */,
+ sp_regs, dp_regs,
+ reserved_regs, empty_pool /* reserved64 */,
+ core_temps, empty_pool /* core64_temps */,
+ sp_temps, dp_temps));
// Target-specific adjustments.
// Alias single precision floats to appropriate half of overlapping double.
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->sp_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->sp_regs_) {
int sp_reg_num = info->GetReg().GetRegNum();
#if (FR_BIT == 0)
int dp_reg_num = sp_reg_num & ~1;
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 2e4e292..0aefc2d 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -97,7 +97,7 @@
}
inline LIR* Mir2Lir::NewLIR2NoDest(int opcode, int src, int info) {
- DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -142,8 +142,9 @@
*/
inline void Mir2Lir::SetupRegMask(ResourceMask* mask, int reg) {
DCHECK_EQ((reg & ~RegStorage::kRegValMask), 0);
- DCHECK(reginfo_map_.Get(reg) != nullptr) << "No info for 0x" << reg;
- *mask = mask->Union(reginfo_map_.Get(reg)->DefUseMask());
+ DCHECK_LT(static_cast<size_t>(reg), reginfo_map_.size());
+ DCHECK(reginfo_map_[reg] != nullptr) << "No info for 0x" << reg;
+ *mask = mask->Union(reginfo_map_[reg]->DefUseMask());
}
/*
@@ -151,8 +152,9 @@
*/
inline void Mir2Lir::ClearRegMask(ResourceMask* mask, int reg) {
DCHECK_EQ((reg & ~RegStorage::kRegValMask), 0);
- DCHECK(reginfo_map_.Get(reg) != nullptr) << "No info for 0x" << reg;
- *mask = mask->ClearBits(reginfo_map_.Get(reg)->DefUseMask());
+ DCHECK_LT(static_cast<size_t>(reg), reginfo_map_.size());
+ DCHECK(reginfo_map_[reg] != nullptr) << "No info for 0x" << reg;
+ *mask = mask->ClearBits(reginfo_map_[reg]->DefUseMask());
}
/*
@@ -256,8 +258,7 @@
}
inline art::Mir2Lir::RegisterInfo* Mir2Lir::GetRegInfo(RegStorage reg) {
- RegisterInfo* res = reg.IsPair() ? reginfo_map_.Get(reg.GetLowReg()) :
- reginfo_map_.Get(reg.GetReg());
+ RegisterInfo* res = reg.IsPair() ? reginfo_map_[reg.GetLowReg()] : reginfo_map_[reg.GetReg()];
DCHECK(res != nullptr);
return res;
}
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index e519011..6942c0f 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -18,6 +18,7 @@
#include "dex/dataflow_iterator-inl.h"
#include "dex/quick/dex_file_method_inliner.h"
#include "mir_to_lir-inl.h"
+#include "primitive.h"
#include "thread-inl.h"
namespace art {
@@ -223,9 +224,27 @@
return false;
}
- bool wide = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE));
- bool ref = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT));
- OpSize size = LoadStoreOpSize(wide, ref);
+ OpSize size = k32;
+ switch (data.op_variant) {
+ case InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT):
+ size = kReference;
+ break;
+ case InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE):
+ size = k64;
+ break;
+ case InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT):
+ size = kSignedHalf;
+ break;
+ case InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR):
+ size = kUnsignedHalf;
+ break;
+ case InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE):
+ size = kSignedByte;
+ break;
+ case InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN):
+ size = kUnsignedByte;
+ break;
+ }
// Point of no return - no aborts after this
GenPrintLabel(mir);
@@ -233,20 +252,20 @@
RegStorage reg_obj = LoadArg(data.object_arg, kRefReg);
RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile);
RegisterClass ret_reg_class = ShortyToRegClass(cu_->shorty[0]);
- RegLocation rl_dest = wide ? GetReturnWide(ret_reg_class) : GetReturn(ret_reg_class);
+ RegLocation rl_dest = IsWide(size) ? GetReturnWide(ret_reg_class) : GetReturn(ret_reg_class);
RegStorage r_result = rl_dest.reg;
if (!RegClassMatches(reg_class, r_result)) {
- r_result = wide ? AllocTypedTempWide(rl_dest.fp, reg_class)
- : AllocTypedTemp(rl_dest.fp, reg_class);
+ r_result = IsWide(size) ? AllocTypedTempWide(rl_dest.fp, reg_class)
+ : AllocTypedTemp(rl_dest.fp, reg_class);
}
- if (ref) {
+ if (IsRef(size)) {
LoadRefDisp(reg_obj, data.field_offset, r_result, data.is_volatile ? kVolatile : kNotVolatile);
} else {
LoadBaseDisp(reg_obj, data.field_offset, r_result, size, data.is_volatile ? kVolatile :
kNotVolatile);
}
if (r_result.NotExactlyEquals(rl_dest.reg)) {
- if (wide) {
+ if (IsWide(size)) {
OpRegCopyWide(rl_dest.reg, r_result);
} else {
OpRegCopy(rl_dest.reg, r_result);
@@ -267,24 +286,42 @@
return false;
}
- bool wide = (data.op_variant == InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE));
- bool ref = (data.op_variant == InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT));
- OpSize size = LoadStoreOpSize(wide, ref);
+ OpSize size = k32;
+ switch (data.op_variant) {
+ case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT):
+ size = kReference;
+ break;
+ case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE):
+ size = k64;
+ break;
+ case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT):
+ size = kSignedHalf;
+ break;
+ case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR):
+ size = kUnsignedHalf;
+ break;
+ case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE):
+ size = kSignedByte;
+ break;
+ case InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN):
+ size = kUnsignedByte;
+ break;
+ }
// Point of no return - no aborts after this
GenPrintLabel(mir);
LockArg(data.object_arg);
- LockArg(data.src_arg, wide);
+ LockArg(data.src_arg, IsWide(size));
RegStorage reg_obj = LoadArg(data.object_arg, kRefReg);
RegisterClass reg_class = RegClassForFieldLoadStore(size, data.is_volatile);
- RegStorage reg_src = LoadArg(data.src_arg, reg_class, wide);
- if (ref) {
+ RegStorage reg_src = LoadArg(data.src_arg, reg_class, IsWide(size));
+ if (IsRef(size)) {
StoreRefDisp(reg_obj, data.field_offset, reg_src, data.is_volatile ? kVolatile : kNotVolatile);
} else {
StoreBaseDisp(reg_obj, data.field_offset, reg_src, size, data.is_volatile ? kVolatile :
kNotVolatile);
}
- if (ref) {
+ if (IsRef(size)) {
MarkGCCard(reg_src, reg_obj);
}
return true;
@@ -562,7 +599,7 @@
break;
case Instruction::FILL_ARRAY_DATA:
- GenFillArrayData(vB, rl_src[0]);
+ GenFillArrayData(mir, vB, rl_src[0]);
break;
case Instruction::FILLED_NEW_ARRAY:
@@ -720,84 +757,112 @@
break;
case Instruction::IGET_OBJECT:
- GenIGet(mir, opt_flags, kReference, rl_dest, rl_src[0], false, true);
+ GenIGet(mir, opt_flags, kReference, Primitive::kPrimNot, rl_dest, rl_src[0]);
break;
case Instruction::IGET_WIDE:
- GenIGet(mir, opt_flags, k64, rl_dest, rl_src[0], true, false);
+ // kPrimLong and kPrimDouble share the same entrypoints.
+ GenIGet(mir, opt_flags, k64, Primitive::kPrimLong, rl_dest, rl_src[0]);
break;
case Instruction::IGET:
- GenIGet(mir, opt_flags, k32, rl_dest, rl_src[0], false, false);
+ GenIGet(mir, opt_flags, k32, Primitive::kPrimInt, rl_dest, rl_src[0]);
break;
case Instruction::IGET_CHAR:
- GenIGet(mir, opt_flags, kUnsignedHalf, rl_dest, rl_src[0], false, false);
+ GenIGet(mir, opt_flags, kUnsignedHalf, Primitive::kPrimChar, rl_dest, rl_src[0]);
break;
case Instruction::IGET_SHORT:
- GenIGet(mir, opt_flags, kSignedHalf, rl_dest, rl_src[0], false, false);
+ GenIGet(mir, opt_flags, kSignedHalf, Primitive::kPrimShort, rl_dest, rl_src[0]);
break;
case Instruction::IGET_BOOLEAN:
+ GenIGet(mir, opt_flags, kUnsignedByte, Primitive::kPrimBoolean, rl_dest, rl_src[0]);
+ break;
+
case Instruction::IGET_BYTE:
- GenIGet(mir, opt_flags, kUnsignedByte, rl_dest, rl_src[0], false, false);
+ GenIGet(mir, opt_flags, kSignedByte, Primitive::kPrimByte, rl_dest, rl_src[0]);
break;
case Instruction::IPUT_WIDE:
- GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1], true, false);
+ GenIPut(mir, opt_flags, k64, rl_src[0], rl_src[1]);
break;
case Instruction::IPUT_OBJECT:
- GenIPut(mir, opt_flags, kReference, rl_src[0], rl_src[1], false, true);
+ GenIPut(mir, opt_flags, kReference, rl_src[0], rl_src[1]);
break;
case Instruction::IPUT:
- GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1], false, false);
+ GenIPut(mir, opt_flags, k32, rl_src[0], rl_src[1]);
break;
- case Instruction::IPUT_BOOLEAN:
case Instruction::IPUT_BYTE:
- GenIPut(mir, opt_flags, kUnsignedByte, rl_src[0], rl_src[1], false, false);
+ case Instruction::IPUT_BOOLEAN:
+ GenIPut(mir, opt_flags, kUnsignedByte, rl_src[0], rl_src[1]);
break;
case Instruction::IPUT_CHAR:
- GenIPut(mir, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1], false, false);
+ GenIPut(mir, opt_flags, kUnsignedHalf, rl_src[0], rl_src[1]);
break;
case Instruction::IPUT_SHORT:
- GenIPut(mir, opt_flags, kSignedHalf, rl_src[0], rl_src[1], false, false);
+ GenIPut(mir, opt_flags, kSignedHalf, rl_src[0], rl_src[1]);
break;
case Instruction::SGET_OBJECT:
- GenSget(mir, rl_dest, false, true);
+ GenSget(mir, rl_dest, kReference, Primitive::kPrimNot);
break;
+
case Instruction::SGET:
- case Instruction::SGET_BOOLEAN:
- case Instruction::SGET_BYTE:
+ GenSget(mir, rl_dest, k32, Primitive::kPrimInt);
+ break;
+
case Instruction::SGET_CHAR:
+ GenSget(mir, rl_dest, kUnsignedHalf, Primitive::kPrimChar);
+ break;
+
case Instruction::SGET_SHORT:
- GenSget(mir, rl_dest, false, false);
+ GenSget(mir, rl_dest, kSignedHalf, Primitive::kPrimShort);
+ break;
+
+ case Instruction::SGET_BOOLEAN:
+ GenSget(mir, rl_dest, kUnsignedByte, Primitive::kPrimBoolean);
+ break;
+
+ case Instruction::SGET_BYTE:
+ GenSget(mir, rl_dest, kSignedByte, Primitive::kPrimByte);
break;
case Instruction::SGET_WIDE:
- GenSget(mir, rl_dest, true, false);
+ // kPrimLong and kPrimDouble share the same entrypoints.
+ GenSget(mir, rl_dest, k64, Primitive::kPrimLong);
break;
case Instruction::SPUT_OBJECT:
- GenSput(mir, rl_src[0], false, true);
+ GenSput(mir, rl_src[0], kReference);
break;
case Instruction::SPUT:
- case Instruction::SPUT_BOOLEAN:
- case Instruction::SPUT_BYTE:
- case Instruction::SPUT_CHAR:
- case Instruction::SPUT_SHORT:
- GenSput(mir, rl_src[0], false, false);
+ GenSput(mir, rl_src[0], k32);
break;
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_BOOLEAN:
+ GenSput(mir, rl_src[0], kUnsignedByte);
+ break;
+
+ case Instruction::SPUT_CHAR:
+ GenSput(mir, rl_src[0], kUnsignedHalf);
+ break;
+
+ case Instruction::SPUT_SHORT:
+ GenSput(mir, rl_src[0], kSignedHalf);
+ break;
+
+
case Instruction::SPUT_WIDE:
- GenSput(mir, rl_src[0], true, false);
+ GenSput(mir, rl_src[0], k64);
break;
case Instruction::INVOKE_STATIC_RANGE:
@@ -1077,9 +1142,17 @@
case kMirOpSelect:
GenSelect(bb, mir);
break;
+ case kMirOpNullCheck: {
+ RegLocation rl_obj = mir_graph_->GetSrc(mir, 0);
+ rl_obj = LoadValue(rl_obj, kRefReg);
+ // An explicit check is done because it is not expected that when this is used,
+ // that it will actually trip up the implicit checks (since an invalid access
+ // is needed on the null object).
+ GenExplicitNullCheck(rl_obj.reg, mir->optimization_flags);
+ break;
+ }
case kMirOpPhi:
case kMirOpNop:
- case kMirOpNullCheck:
case kMirOpRangeCheck:
case kMirOpDivZeroCheck:
case kMirOpCheck:
@@ -1127,9 +1200,8 @@
if (bb->block_type == kEntryBlock) {
ResetRegPool();
- int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
- GenEntrySequence(&mir_graph_->reg_location_[start_vreg],
- mir_graph_->reg_location_[mir_graph_->GetMethodSReg()]);
+ int start_vreg = mir_graph_->GetFirstInVR();
+ GenEntrySequence(&mir_graph_->reg_location_[start_vreg], mir_graph_->GetMethodLoc());
} else if (bb->block_type == kExitBlock) {
ResetRegPool();
GenExitSequence();
@@ -1196,13 +1268,12 @@
bool Mir2Lir::SpecialMIR2LIR(const InlineMethod& special) {
cu_->NewTimingSplit("SpecialMIR2LIR");
// Find the first DalvikByteCode block.
- int num_reachable_blocks = mir_graph_->GetNumReachableBlocks();
+ DCHECK_EQ(mir_graph_->GetNumReachableBlocks(), mir_graph_->GetDfsOrder().size());
BasicBlock*bb = NULL;
- for (int idx = 0; idx < num_reachable_blocks; idx++) {
- // TODO: no direct access of growable lists.
- int dfs_index = mir_graph_->GetDfsOrder()->Get(idx);
- bb = mir_graph_->GetBasicBlock(dfs_index);
- if (bb->block_type == kDalvikByteCode) {
+ for (BasicBlockId dfs_id : mir_graph_->GetDfsOrder()) {
+ BasicBlock* candidate = mir_graph_->GetBasicBlock(dfs_id);
+ if (candidate->block_type == kDalvikByteCode) {
+ bb = candidate;
break;
}
}
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 3dc111f..67a8c0f 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -33,19 +33,10 @@
#include "utils/array_ref.h"
#include "utils/arena_allocator.h"
#include "utils/arena_containers.h"
-#include "utils/growable_array.h"
#include "utils/stack_checks.h"
namespace art {
-/*
- * TODO: refactoring pass to move these (and other) typdefs towards usage style of runtime to
- * add type safety (see runtime/offsets.h).
- */
-typedef uint32_t DexOffset; // Dex offset in code units.
-typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff.
-typedef uint32_t CodeOffset; // Native code offset in bytes.
-
// Set to 1 to measure cost of suspend check.
#define NO_SUSPEND 0
@@ -147,6 +138,7 @@
struct RegisterInfo;
class DexFileMethodInliner;
class MIRGraph;
+class MirMethodLoweringInfo;
class Mir2Lir;
typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
@@ -187,16 +179,6 @@
int32_t operands[5]; // [0..4] = [dest, src1, src2, extra, extra2].
};
-// Target-specific initialization.
-Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
- ArenaAllocator* const arena);
-Mir2Lir* Arm64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
- ArenaAllocator* const arena);
-Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
- ArenaAllocator* const arena);
-Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
- ArenaAllocator* const arena);
-
// Utility macros to traverse the LIR list.
#define NEXT_LIR(lir) (lir->next)
#define PREV_LIR(lir) (lir->prev)
@@ -455,20 +437,21 @@
static void* operator new(size_t size, ArenaAllocator* arena) {
return arena->Alloc(size, kArenaAllocRegAlloc);
}
+ static void operator delete(void* ptr) { UNUSED(ptr); }
void ResetNextTemp() {
next_core_reg_ = 0;
next_sp_reg_ = 0;
next_dp_reg_ = 0;
}
- GrowableArray<RegisterInfo*> core_regs_;
+ ArenaVector<RegisterInfo*> core_regs_;
int next_core_reg_;
- GrowableArray<RegisterInfo*> core64_regs_;
+ ArenaVector<RegisterInfo*> core64_regs_;
int next_core64_reg_;
- GrowableArray<RegisterInfo*> sp_regs_; // Single precision float.
+ ArenaVector<RegisterInfo*> sp_regs_; // Single precision float.
int next_sp_reg_;
- GrowableArray<RegisterInfo*> dp_regs_; // Double precision float.
+ ArenaVector<RegisterInfo*> dp_regs_; // Double precision float.
int next_dp_reg_;
- GrowableArray<RegisterInfo*>* ref_regs_; // Points to core_regs_ or core64_regs_
+ ArenaVector<RegisterInfo*>* ref_regs_; // Points to core_regs_ or core64_regs_
int* next_ref_reg_;
private:
@@ -615,13 +598,13 @@
* may be worth conditionally-compiling a set of identity functions here.
*/
uint32_t WrapPointer(void* pointer) {
- uint32_t res = pointer_storage_.Size();
- pointer_storage_.Insert(pointer);
+ uint32_t res = pointer_storage_.size();
+ pointer_storage_.push_back(pointer);
return res;
}
void* UnwrapPointer(size_t index) {
- return pointer_storage_.Get(index);
+ return pointer_storage_[index];
}
// strdup(), but allocates from the arena.
@@ -685,6 +668,7 @@
LIR* ScanLiteralPool(LIR* data_target, int value, unsigned int delta);
LIR* ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi);
LIR* ScanLiteralPoolMethod(LIR* data_target, const MethodReference& method);
+ LIR* ScanLiteralPoolClass(LIR* data_target, const DexFile& dex_file, uint32_t type_idx);
LIR* AddWordData(LIR* *constant_list_p, int value);
LIR* AddWideData(LIR* *constant_list_p, int val_lo, int val_hi);
void ProcessSwitchTables();
@@ -730,7 +714,7 @@
void SimpleRegAlloc();
void ResetRegPool();
void CompilerInitPool(RegisterInfo* info, RegStorage* regs, int num);
- void DumpRegPool(GrowableArray<RegisterInfo*>* regs);
+ void DumpRegPool(ArenaVector<RegisterInfo*>* regs);
void DumpCoreRegPool();
void DumpFpRegPool();
void DumpRegPools();
@@ -745,7 +729,7 @@
RegStorage AllocPreservedFpReg(int s_reg);
virtual RegStorage AllocPreservedSingle(int s_reg);
virtual RegStorage AllocPreservedDouble(int s_reg);
- RegStorage AllocTempBody(GrowableArray<RegisterInfo*> ®s, int* next_temp, bool required);
+ RegStorage AllocTempBody(ArenaVector<RegisterInfo*>& regs, int* next_temp, bool required);
virtual RegStorage AllocTemp(bool required = true);
virtual RegStorage AllocTempWide(bool required = true);
virtual RegStorage AllocTempRef(bool required = true);
@@ -756,7 +740,7 @@
void FlushReg(RegStorage reg);
void FlushRegWide(RegStorage reg);
RegStorage AllocLiveReg(int s_reg, int reg_class, bool wide);
- RegStorage FindLiveReg(GrowableArray<RegisterInfo*> ®s, int s_reg);
+ RegStorage FindLiveReg(ArenaVector<RegisterInfo*>& regs, int s_reg);
virtual void FreeTemp(RegStorage reg);
virtual void FreeRegLocTemps(RegLocation rl_keep, RegLocation rl_free);
virtual bool IsLive(RegStorage reg);
@@ -848,14 +832,14 @@
void GenNewArray(uint32_t type_idx, RegLocation rl_dest,
RegLocation rl_src);
void GenFilledNewArray(CallInfo* info);
- void GenSput(MIR* mir, RegLocation rl_src,
- bool is_long_or_double, bool is_object);
- void GenSget(MIR* mir, RegLocation rl_dest,
- bool is_long_or_double, bool is_object);
- void GenIGet(MIR* mir, int opt_flags, OpSize size,
- RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+ void GenSput(MIR* mir, RegLocation rl_src, OpSize size);
+ // Get entrypoints are specific for types, size alone is not sufficient to safely infer
+ // entrypoint.
+ void GenSget(MIR* mir, RegLocation rl_dest, OpSize size, Primitive::Type type);
+ void GenIGet(MIR* mir, int opt_flags, OpSize size, Primitive::Type type,
+ RegLocation rl_dest, RegLocation rl_obj);
void GenIPut(MIR* mir, int opt_flags, OpSize size,
- RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+ RegLocation rl_src, RegLocation rl_obj);
void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
RegLocation rl_src);
@@ -926,6 +910,15 @@
bool safepoint_pc);
void GenInvoke(CallInfo* info);
void GenInvokeNoInline(CallInfo* info);
+ virtual NextCallInsn GetNextSDCallInsn();
+
+ /*
+ * @brief Generate the actual call insn based on the method info.
+ * @param method_info the lowering info for the method call.
+ * @returns Call instruction
+ */
+ virtual LIR* GenCallInsn(const MirMethodLoweringInfo& method_info);
+
virtual void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
virtual int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
NextCallInsn next_call_insn,
@@ -963,7 +956,7 @@
bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty);
virtual bool GenInlinedReverseBits(CallInfo* info, OpSize size);
bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
- bool GenInlinedAbsInt(CallInfo* info);
+ virtual bool GenInlinedAbsInt(CallInfo* info);
virtual bool GenInlinedAbsLong(CallInfo* info);
virtual bool GenInlinedAbsFloat(CallInfo* info) = 0;
virtual bool GenInlinedAbsDouble(CallInfo* info) = 0;
@@ -995,6 +988,10 @@
virtual LIR* LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
return LoadBaseDisp(r_base, displacement, r_dest, kWord, kNotVolatile);
}
+ // Load 8 bits, regardless of target.
+ virtual LIR* Load8Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
+ return LoadBaseDisp(r_base, displacement, r_dest, kSignedByte, kNotVolatile);
+ }
// Load 32 bits, regardless of target.
virtual LIR* Load32Disp(RegStorage r_base, int displacement, RegStorage r_dest) {
return LoadBaseDisp(r_base, displacement, r_dest, k32, kNotVolatile);
@@ -1113,11 +1110,13 @@
/*
* @brief Load the Class* of a Dex Class type into the register.
+ * @param dex DexFile that contains the class type.
* @param type How the method will be invoked.
* @param register that will contain the code address.
* @note register will be passed to TargetReg to get physical register.
*/
- virtual void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg);
+ virtual void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+ SpecialTargetRegister symbolic_reg);
// Routines that work for the generic case, but may be overriden by target.
/*
@@ -1164,6 +1163,14 @@
(info1->StorageMask() & info2->StorageMask()) != 0);
}
+ static constexpr bool IsWide(OpSize size) {
+ return size == k64 || size == kDouble;
+ }
+
+ static constexpr bool IsRef(OpSize size) {
+ return size == kReference;
+ }
+
/**
* @brief Portable way of getting special registers from the backend.
* @param reg Enumeration describing the purpose of the register.
@@ -1316,7 +1323,7 @@
virtual void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) = 0;
virtual void GenExitSequence() = 0;
- virtual void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) = 0;
+ virtual void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) = 0;
virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) = 0;
virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0;
@@ -1498,10 +1505,6 @@
*/
virtual RegLocation ForceTempWide(RegLocation loc);
- static constexpr OpSize LoadStoreOpSize(bool wide, bool ref) {
- return wide ? k64 : ref ? kReference : k32;
- }
-
virtual void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx,
RegLocation rl_dest, RegLocation rl_src);
@@ -1525,10 +1528,10 @@
uint32_t type_idx, RegLocation rl_dest,
RegLocation rl_src);
/*
- * @brief Generate the debug_frame FDE information if possible.
- * @returns pointer to vector containg CFE information, or NULL.
+ * @brief Generate the eh_frame FDE information if possible.
+ * @returns pointer to vector containg FDE information, or NULL.
*/
- virtual std::vector<uint8_t>* ReturnCallFrameInformation();
+ virtual std::vector<uint8_t>* ReturnFrameDescriptionEntry();
/**
* @brief Used to insert marker that can be used to associate MIR with LIR.
@@ -1683,11 +1686,11 @@
protected:
CompilationUnit* const cu_;
MIRGraph* const mir_graph_;
- GrowableArray<SwitchTable*> switch_tables_;
- GrowableArray<FillArrayData*> fill_array_data_;
- GrowableArray<RegisterInfo*> tempreg_info_;
- GrowableArray<RegisterInfo*> reginfo_map_;
- GrowableArray<void*> pointer_storage_;
+ ArenaVector<SwitchTable*> switch_tables_;
+ ArenaVector<FillArrayData*> fill_array_data_;
+ ArenaVector<RegisterInfo*> tempreg_info_;
+ ArenaVector<RegisterInfo*> reginfo_map_;
+ ArenaVector<void*> pointer_storage_;
CodeOffset current_code_offset_; // Working byte offset of machine instructons.
CodeOffset data_offset_; // starting offset of literal pool.
size_t total_size_; // header + code size.
@@ -1704,7 +1707,7 @@
*/
DexOffset current_dalvik_offset_;
size_t estimated_native_code_size_; // Just an estimate; used to reserve code_buffer_ size.
- RegisterPool* reg_pool_;
+ std::unique_ptr<RegisterPool> reg_pool_;
/*
* Sanity checking for the register temp tracking. The same ssa
* name should never be associated with one temp register per
@@ -1712,11 +1715,14 @@
*/
int live_sreg_;
CodeBuffer code_buffer_;
+ // The source mapping table data (pc -> dex). More entries than in encoded_mapping_table_
+ SrcMap src_mapping_table_;
// The encoding mapping table data (dex -> pc offset and pc offset -> dex) with a size prefix.
std::vector<uint8_t> encoded_mapping_table_;
ArenaVector<uint32_t> core_vmap_table_;
ArenaVector<uint32_t> fp_vmap_table_;
std::vector<uint8_t> native_gc_map_;
+ ArenaVector<LinkerPatch> patches_;
int num_core_spills_;
int num_fp_spills_;
int frame_size_;
@@ -1725,7 +1731,7 @@
LIR* first_lir_insn_;
LIR* last_lir_insn_;
- GrowableArray<LIRSlowPath*> slow_paths_;
+ ArenaVector<LIRSlowPath*> slow_paths_;
// The memory reference type for new LIRs.
// NOTE: Passing this as an explicit parameter by all functions that directly or indirectly
@@ -1737,6 +1743,9 @@
// (i.e. 8 bytes on 32-bit arch, 16 bytes on 64-bit arch) and we use ResourceMaskCache
// to deduplicate the masks.
ResourceMaskCache mask_cache_;
+
+ private:
+ static bool SizeMatchesTypeForEntrypoint(OpSize size, Primitive::Type type);
}; // Class Mir2Lir
} // namespace art
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
new file mode 100644
index 0000000..6f2a647
--- /dev/null
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -0,0 +1,650 @@
+/*
+ * 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.
+ */
+
+#include "quick_compiler.h"
+
+#include <cstdint>
+
+#include "compiler.h"
+#include "dex/frontend.h"
+#include "dex/mir_graph.h"
+#include "dex/quick/mir_to_lir.h"
+#include "driver/compiler_driver.h"
+#include "elf_writer_quick.h"
+#include "jni/quick/jni_compiler.h"
+#include "mirror/art_method-inl.h"
+#include "base/logging.h"
+
+// Specific compiler backends.
+#include "dex/quick/arm/backend_arm.h"
+#include "dex/quick/arm64/backend_arm64.h"
+#include "dex/quick/mips/backend_mips.h"
+#include "dex/quick/x86/backend_x86.h"
+
+namespace art {
+
+class QuickCompiler : public Compiler {
+ public:
+ explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
+
+ void Init() const OVERRIDE;
+
+ void UnInit() const OVERRIDE;
+
+ bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+ OVERRIDE;
+
+ CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const OVERRIDE;
+
+ CompiledMethod* JniCompile(uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file) const OVERRIDE;
+
+ uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool WriteElf(art::File* file,
+ OatWriter* oat_writer,
+ const std::vector<const art::DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host) const
+ OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
+
+ void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
+};
+
+COMPILE_ASSERT(0U == static_cast<size_t>(kNone), kNone_not_0);
+COMPILE_ASSERT(1U == static_cast<size_t>(kArm), kArm_not_1);
+COMPILE_ASSERT(2U == static_cast<size_t>(kArm64), kArm64_not_2);
+COMPILE_ASSERT(3U == static_cast<size_t>(kThumb2), kThumb2_not_3);
+COMPILE_ASSERT(4U == static_cast<size_t>(kX86), kX86_not_4);
+COMPILE_ASSERT(5U == static_cast<size_t>(kX86_64), kX86_64_not_5);
+COMPILE_ASSERT(6U == static_cast<size_t>(kMips), kMips_not_6);
+COMPILE_ASSERT(7U == static_cast<size_t>(kMips64), kMips64_not_7);
+
+// Additional disabled optimizations (over generally disabled) per instruction set.
+static constexpr uint32_t kDisabledOptimizationsPerISA[] = {
+ // 0 = kNone.
+ ~0U,
+ // 1 = kArm, unused (will use kThumb2).
+ ~0U,
+ // 2 = kArm64.
+ 0,
+ // 3 = kThumb2.
+ 0,
+ // 4 = kX86.
+ (1 << kLoadStoreElimination) |
+ 0,
+ // 5 = kX86_64.
+ (1 << kLoadStoreElimination) |
+ 0,
+ // 6 = kMips.
+ (1 << kLoadStoreElimination) |
+ (1 << kLoadHoisting) |
+ (1 << kSuppressLoads) |
+ (1 << kNullCheckElimination) |
+ (1 << kPromoteRegs) |
+ (1 << kTrackLiveTemps) |
+ (1 << kSafeOptimizations) |
+ (1 << kBBOpt) |
+ (1 << kMatch) |
+ (1 << kPromoteCompilerTemps) |
+ 0,
+ // 7 = kMips64.
+ ~0U
+};
+COMPILE_ASSERT(sizeof(kDisabledOptimizationsPerISA) == 8 * sizeof(uint32_t), kDisabledOpts_unexp);
+
+// Supported shorty types per instruction set. nullptr means that all are available.
+// Z : boolean
+// B : byte
+// S : short
+// C : char
+// I : int
+// J : long
+// F : float
+// D : double
+// L : reference(object, array)
+// V : void
+static const char* kSupportedTypes[] = {
+ // 0 = kNone.
+ "",
+ // 1 = kArm, unused (will use kThumb2).
+ "",
+ // 2 = kArm64.
+ nullptr,
+ // 3 = kThumb2.
+ nullptr,
+ // 4 = kX86.
+ nullptr,
+ // 5 = kX86_64.
+ nullptr,
+ // 6 = kMips.
+ nullptr,
+ // 7 = kMips64.
+ ""
+};
+COMPILE_ASSERT(sizeof(kSupportedTypes) == 8 * sizeof(char*), kSupportedTypes_unexp);
+
+static int kAllOpcodes[] = {
+ Instruction::NOP,
+ Instruction::MOVE,
+ Instruction::MOVE_FROM16,
+ Instruction::MOVE_16,
+ Instruction::MOVE_WIDE,
+ Instruction::MOVE_WIDE_FROM16,
+ Instruction::MOVE_WIDE_16,
+ Instruction::MOVE_OBJECT,
+ Instruction::MOVE_OBJECT_FROM16,
+ Instruction::MOVE_OBJECT_16,
+ Instruction::MOVE_RESULT,
+ Instruction::MOVE_RESULT_WIDE,
+ Instruction::MOVE_RESULT_OBJECT,
+ Instruction::MOVE_EXCEPTION,
+ Instruction::RETURN_VOID,
+ Instruction::RETURN,
+ Instruction::RETURN_WIDE,
+ Instruction::RETURN_OBJECT,
+ Instruction::CONST_4,
+ Instruction::CONST_16,
+ Instruction::CONST,
+ Instruction::CONST_HIGH16,
+ Instruction::CONST_WIDE_16,
+ Instruction::CONST_WIDE_32,
+ Instruction::CONST_WIDE,
+ Instruction::CONST_WIDE_HIGH16,
+ Instruction::CONST_STRING,
+ Instruction::CONST_STRING_JUMBO,
+ Instruction::CONST_CLASS,
+ Instruction::MONITOR_ENTER,
+ Instruction::MONITOR_EXIT,
+ Instruction::CHECK_CAST,
+ Instruction::INSTANCE_OF,
+ Instruction::ARRAY_LENGTH,
+ Instruction::NEW_INSTANCE,
+ Instruction::NEW_ARRAY,
+ Instruction::FILLED_NEW_ARRAY,
+ Instruction::FILLED_NEW_ARRAY_RANGE,
+ Instruction::FILL_ARRAY_DATA,
+ Instruction::THROW,
+ Instruction::GOTO,
+ Instruction::GOTO_16,
+ Instruction::GOTO_32,
+ Instruction::PACKED_SWITCH,
+ Instruction::SPARSE_SWITCH,
+ Instruction::CMPL_FLOAT,
+ Instruction::CMPG_FLOAT,
+ Instruction::CMPL_DOUBLE,
+ Instruction::CMPG_DOUBLE,
+ Instruction::CMP_LONG,
+ Instruction::IF_EQ,
+ Instruction::IF_NE,
+ Instruction::IF_LT,
+ Instruction::IF_GE,
+ Instruction::IF_GT,
+ Instruction::IF_LE,
+ Instruction::IF_EQZ,
+ Instruction::IF_NEZ,
+ Instruction::IF_LTZ,
+ Instruction::IF_GEZ,
+ Instruction::IF_GTZ,
+ Instruction::IF_LEZ,
+ Instruction::UNUSED_3E,
+ Instruction::UNUSED_3F,
+ Instruction::UNUSED_40,
+ Instruction::UNUSED_41,
+ Instruction::UNUSED_42,
+ Instruction::UNUSED_43,
+ Instruction::AGET,
+ Instruction::AGET_WIDE,
+ Instruction::AGET_OBJECT,
+ Instruction::AGET_BOOLEAN,
+ Instruction::AGET_BYTE,
+ Instruction::AGET_CHAR,
+ Instruction::AGET_SHORT,
+ Instruction::APUT,
+ Instruction::APUT_WIDE,
+ Instruction::APUT_OBJECT,
+ Instruction::APUT_BOOLEAN,
+ Instruction::APUT_BYTE,
+ Instruction::APUT_CHAR,
+ Instruction::APUT_SHORT,
+ Instruction::IGET,
+ Instruction::IGET_WIDE,
+ Instruction::IGET_OBJECT,
+ Instruction::IGET_BOOLEAN,
+ Instruction::IGET_BYTE,
+ Instruction::IGET_CHAR,
+ Instruction::IGET_SHORT,
+ Instruction::IPUT,
+ Instruction::IPUT_WIDE,
+ Instruction::IPUT_OBJECT,
+ Instruction::IPUT_BOOLEAN,
+ Instruction::IPUT_BYTE,
+ Instruction::IPUT_CHAR,
+ Instruction::IPUT_SHORT,
+ Instruction::SGET,
+ Instruction::SGET_WIDE,
+ Instruction::SGET_OBJECT,
+ Instruction::SGET_BOOLEAN,
+ Instruction::SGET_BYTE,
+ Instruction::SGET_CHAR,
+ Instruction::SGET_SHORT,
+ Instruction::SPUT,
+ Instruction::SPUT_WIDE,
+ Instruction::SPUT_OBJECT,
+ Instruction::SPUT_BOOLEAN,
+ Instruction::SPUT_BYTE,
+ Instruction::SPUT_CHAR,
+ Instruction::SPUT_SHORT,
+ Instruction::INVOKE_VIRTUAL,
+ Instruction::INVOKE_SUPER,
+ Instruction::INVOKE_DIRECT,
+ Instruction::INVOKE_STATIC,
+ Instruction::INVOKE_INTERFACE,
+ Instruction::RETURN_VOID_BARRIER,
+ Instruction::INVOKE_VIRTUAL_RANGE,
+ Instruction::INVOKE_SUPER_RANGE,
+ Instruction::INVOKE_DIRECT_RANGE,
+ Instruction::INVOKE_STATIC_RANGE,
+ Instruction::INVOKE_INTERFACE_RANGE,
+ Instruction::UNUSED_79,
+ Instruction::UNUSED_7A,
+ Instruction::NEG_INT,
+ Instruction::NOT_INT,
+ Instruction::NEG_LONG,
+ Instruction::NOT_LONG,
+ Instruction::NEG_FLOAT,
+ Instruction::NEG_DOUBLE,
+ Instruction::INT_TO_LONG,
+ Instruction::INT_TO_FLOAT,
+ Instruction::INT_TO_DOUBLE,
+ Instruction::LONG_TO_INT,
+ Instruction::LONG_TO_FLOAT,
+ Instruction::LONG_TO_DOUBLE,
+ Instruction::FLOAT_TO_INT,
+ Instruction::FLOAT_TO_LONG,
+ Instruction::FLOAT_TO_DOUBLE,
+ Instruction::DOUBLE_TO_INT,
+ Instruction::DOUBLE_TO_LONG,
+ Instruction::DOUBLE_TO_FLOAT,
+ Instruction::INT_TO_BYTE,
+ Instruction::INT_TO_CHAR,
+ Instruction::INT_TO_SHORT,
+ Instruction::ADD_INT,
+ Instruction::SUB_INT,
+ Instruction::MUL_INT,
+ Instruction::DIV_INT,
+ Instruction::REM_INT,
+ Instruction::AND_INT,
+ Instruction::OR_INT,
+ Instruction::XOR_INT,
+ Instruction::SHL_INT,
+ Instruction::SHR_INT,
+ Instruction::USHR_INT,
+ Instruction::ADD_LONG,
+ Instruction::SUB_LONG,
+ Instruction::MUL_LONG,
+ Instruction::DIV_LONG,
+ Instruction::REM_LONG,
+ Instruction::AND_LONG,
+ Instruction::OR_LONG,
+ Instruction::XOR_LONG,
+ Instruction::SHL_LONG,
+ Instruction::SHR_LONG,
+ Instruction::USHR_LONG,
+ Instruction::ADD_FLOAT,
+ Instruction::SUB_FLOAT,
+ Instruction::MUL_FLOAT,
+ Instruction::DIV_FLOAT,
+ Instruction::REM_FLOAT,
+ Instruction::ADD_DOUBLE,
+ Instruction::SUB_DOUBLE,
+ Instruction::MUL_DOUBLE,
+ Instruction::DIV_DOUBLE,
+ Instruction::REM_DOUBLE,
+ Instruction::ADD_INT_2ADDR,
+ Instruction::SUB_INT_2ADDR,
+ Instruction::MUL_INT_2ADDR,
+ Instruction::DIV_INT_2ADDR,
+ Instruction::REM_INT_2ADDR,
+ Instruction::AND_INT_2ADDR,
+ Instruction::OR_INT_2ADDR,
+ Instruction::XOR_INT_2ADDR,
+ Instruction::SHL_INT_2ADDR,
+ Instruction::SHR_INT_2ADDR,
+ Instruction::USHR_INT_2ADDR,
+ Instruction::ADD_LONG_2ADDR,
+ Instruction::SUB_LONG_2ADDR,
+ Instruction::MUL_LONG_2ADDR,
+ Instruction::DIV_LONG_2ADDR,
+ Instruction::REM_LONG_2ADDR,
+ Instruction::AND_LONG_2ADDR,
+ Instruction::OR_LONG_2ADDR,
+ Instruction::XOR_LONG_2ADDR,
+ Instruction::SHL_LONG_2ADDR,
+ Instruction::SHR_LONG_2ADDR,
+ Instruction::USHR_LONG_2ADDR,
+ Instruction::ADD_FLOAT_2ADDR,
+ Instruction::SUB_FLOAT_2ADDR,
+ Instruction::MUL_FLOAT_2ADDR,
+ Instruction::DIV_FLOAT_2ADDR,
+ Instruction::REM_FLOAT_2ADDR,
+ Instruction::ADD_DOUBLE_2ADDR,
+ Instruction::SUB_DOUBLE_2ADDR,
+ Instruction::MUL_DOUBLE_2ADDR,
+ Instruction::DIV_DOUBLE_2ADDR,
+ Instruction::REM_DOUBLE_2ADDR,
+ Instruction::ADD_INT_LIT16,
+ Instruction::RSUB_INT,
+ Instruction::MUL_INT_LIT16,
+ Instruction::DIV_INT_LIT16,
+ Instruction::REM_INT_LIT16,
+ Instruction::AND_INT_LIT16,
+ Instruction::OR_INT_LIT16,
+ Instruction::XOR_INT_LIT16,
+ Instruction::ADD_INT_LIT8,
+ Instruction::RSUB_INT_LIT8,
+ Instruction::MUL_INT_LIT8,
+ Instruction::DIV_INT_LIT8,
+ Instruction::REM_INT_LIT8,
+ Instruction::AND_INT_LIT8,
+ Instruction::OR_INT_LIT8,
+ Instruction::XOR_INT_LIT8,
+ Instruction::SHL_INT_LIT8,
+ Instruction::SHR_INT_LIT8,
+ Instruction::USHR_INT_LIT8,
+ Instruction::IGET_QUICK,
+ Instruction::IGET_WIDE_QUICK,
+ Instruction::IGET_OBJECT_QUICK,
+ Instruction::IPUT_QUICK,
+ Instruction::IPUT_WIDE_QUICK,
+ Instruction::IPUT_OBJECT_QUICK,
+ Instruction::INVOKE_VIRTUAL_QUICK,
+ Instruction::INVOKE_VIRTUAL_RANGE_QUICK,
+ Instruction::IPUT_BOOLEAN_QUICK,
+ Instruction::IPUT_BYTE_QUICK,
+ Instruction::IPUT_CHAR_QUICK,
+ Instruction::IPUT_SHORT_QUICK,
+ Instruction::UNUSED_EF,
+ Instruction::UNUSED_F0,
+ Instruction::UNUSED_F1,
+ Instruction::UNUSED_F2,
+ Instruction::UNUSED_F3,
+ Instruction::UNUSED_F4,
+ Instruction::UNUSED_F5,
+ Instruction::UNUSED_F6,
+ Instruction::UNUSED_F7,
+ Instruction::UNUSED_F8,
+ Instruction::UNUSED_F9,
+ Instruction::UNUSED_FA,
+ Instruction::UNUSED_FB,
+ Instruction::UNUSED_FC,
+ Instruction::UNUSED_FD,
+ Instruction::UNUSED_FE,
+ Instruction::UNUSED_FF,
+ // ----- ExtendedMIROpcode -----
+ kMirOpPhi,
+ kMirOpCopy,
+ kMirOpFusedCmplFloat,
+ kMirOpFusedCmpgFloat,
+ kMirOpFusedCmplDouble,
+ kMirOpFusedCmpgDouble,
+ kMirOpFusedCmpLong,
+ kMirOpNop,
+ kMirOpNullCheck,
+ kMirOpRangeCheck,
+ kMirOpDivZeroCheck,
+ kMirOpCheck,
+ kMirOpCheckPart2,
+ kMirOpSelect,
+};
+
+// Unsupported opcodes. nullptr can be used when everything is supported. Size of the lists is
+// recorded below.
+static const int* kUnsupportedOpcodes[] = {
+ // 0 = kNone.
+ kAllOpcodes,
+ // 1 = kArm, unused (will use kThumb2).
+ kAllOpcodes,
+ // 2 = kArm64.
+ nullptr,
+ // 3 = kThumb2.
+ nullptr,
+ // 4 = kX86.
+ nullptr,
+ // 5 = kX86_64.
+ nullptr,
+ // 6 = kMips.
+ nullptr,
+ // 7 = kMips64.
+ kAllOpcodes
+};
+COMPILE_ASSERT(sizeof(kUnsupportedOpcodes) == 8 * sizeof(int*), kUnsupportedOpcodes_unexp);
+
+// Size of the arrays stored above.
+static const size_t kUnsupportedOpcodesSize[] = {
+ // 0 = kNone.
+ arraysize(kAllOpcodes),
+ // 1 = kArm, unused (will use kThumb2).
+ arraysize(kAllOpcodes),
+ // 2 = kArm64.
+ 0,
+ // 3 = kThumb2.
+ 0,
+ // 4 = kX86.
+ 0,
+ // 5 = kX86_64.
+ 0,
+ // 6 = kMips.
+ 0,
+ // 7 = kMips64.
+ arraysize(kAllOpcodes),
+};
+COMPILE_ASSERT(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t),
+ kUnsupportedOpcodesSize_unexp);
+
+// The maximum amount of Dalvik register in a method for which we will start compiling. Tries to
+// avoid an abort when we need to manage more SSA registers than we can.
+static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2;
+
+static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) {
+ const char* supported_types = kSupportedTypes[instruction_set];
+ if (supported_types == nullptr) {
+ // Everything available.
+ return true;
+ }
+
+ uint32_t shorty_size = strlen(shorty);
+ CHECK_GE(shorty_size, 1u);
+
+ for (uint32_t i = 0; i < shorty_size; i++) {
+ if (strchr(supported_types, shorty[i]) == nullptr) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Skip the method that we do not support currently.
+bool QuickCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
+ CompilationUnit* cu) const {
+ // This is a limitation in mir_graph. See MirGraph::SetNumSSARegs.
+ if (cu->mir_graph->GetNumOfCodeAndTempVRs() > kMaxAllowedDalvikRegisters) {
+ VLOG(compiler) << "Too many dalvik registers : " << cu->mir_graph->GetNumOfCodeAndTempVRs();
+ return false;
+ }
+
+ // Check whether we do have limitations at all.
+ if (kSupportedTypes[cu->instruction_set] == nullptr &&
+ kUnsupportedOpcodesSize[cu->instruction_set] == 0U) {
+ return true;
+ }
+
+ // Check if we can compile the prototype.
+ const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
+ if (!CanCompileShorty(shorty, cu->instruction_set)) {
+ VLOG(compiler) << "Unsupported shorty : " << shorty;
+ return false;
+ }
+
+ const int *unsupport_list = kUnsupportedOpcodes[cu->instruction_set];
+ int unsupport_list_size = kUnsupportedOpcodesSize[cu->instruction_set];
+
+ for (unsigned int idx = 0; idx < cu->mir_graph->GetNumBlocks(); idx++) {
+ BasicBlock* bb = cu->mir_graph->GetBasicBlock(idx);
+ if (bb == NULL) continue;
+ if (bb->block_type == kDead) continue;
+ for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+ int opcode = mir->dalvikInsn.opcode;
+ // Check if we support the byte code.
+ if (std::find(unsupport_list, unsupport_list + unsupport_list_size,
+ opcode) != unsupport_list + unsupport_list_size) {
+ if (!MIR::DecodedInstruction::IsPseudoMirOp(opcode)) {
+ VLOG(compiler) << "Unsupported dalvik byte code : "
+ << mir->dalvikInsn.opcode;
+ } else {
+ VLOG(compiler) << "Unsupported extended MIR opcode : "
+ << MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst];
+ }
+ return false;
+ }
+ // Check if it invokes a prototype that we cannot support.
+ if (Instruction::INVOKE_VIRTUAL == opcode ||
+ Instruction::INVOKE_SUPER == opcode ||
+ Instruction::INVOKE_DIRECT == opcode ||
+ Instruction::INVOKE_STATIC == opcode ||
+ Instruction::INVOKE_INTERFACE == opcode) {
+ uint32_t invoke_method_idx = mir->dalvikInsn.vB;
+ const char* invoke_method_shorty = dex_file.GetMethodShorty(
+ dex_file.GetMethodId(invoke_method_idx));
+ if (!CanCompileShorty(invoke_method_shorty, cu->instruction_set)) {
+ VLOG(compiler) << "Unsupported to invoke '"
+ << PrettyMethod(invoke_method_idx, dex_file)
+ << "' with shorty : " << invoke_method_shorty;
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void QuickCompiler::InitCompilationUnit(CompilationUnit& cu) const {
+ // Disable optimizations according to instruction set.
+ cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
+}
+
+void QuickCompiler::Init() const {
+ CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
+}
+
+void QuickCompiler::UnInit() const {
+ CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
+}
+
+CompiledMethod* QuickCompiler::Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const {
+ CompiledMethod* method = TryCompileWithSeaIR(code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file);
+ if (method != nullptr) {
+ return method;
+ }
+
+ // TODO: check method fingerprint here to determine appropriate backend type. Until then, use
+ // build default.
+ CompilerDriver* driver = GetCompilerDriver();
+ return CompileOneMethod(driver, this, code_item, access_flags, invoke_type, class_def_idx,
+ method_idx, class_loader, dex_file, nullptr /* use thread llvm_info */);
+}
+
+CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file) const {
+ return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
+}
+
+uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
+ return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
+}
+
+bool QuickCompiler::WriteElf(art::File* file,
+ OatWriter* oat_writer,
+ const std::vector<const art::DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host) const {
+ return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
+ *GetCompilerDriver());
+}
+
+Backend* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+ Mir2Lir* mir_to_lir = nullptr;
+ switch (cu->instruction_set) {
+ case kThumb2:
+ mir_to_lir = ArmCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+ break;
+ case kArm64:
+ mir_to_lir = Arm64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+ break;
+ case kMips:
+ mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+ break;
+ case kX86:
+ // Fall-through.
+ case kX86_64:
+ mir_to_lir = X86CodeGenerator(cu, cu->mir_graph.get(), &cu->arena);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected instruction set: " << cu->instruction_set;
+ }
+
+ /* The number of compiler temporaries depends on backend so set it up now if possible */
+ if (mir_to_lir) {
+ size_t max_temps = mir_to_lir->GetMaxPossibleCompilerTemps();
+ bool set_max = cu->mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps);
+ CHECK(set_max);
+ }
+ return mir_to_lir;
+}
+
+
+Compiler* CreateQuickCompiler(CompilerDriver* driver) {
+ return new QuickCompiler(driver);
+}
+
+} // namespace art
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
new file mode 100644
index 0000000..10de5fb
--- /dev/null
+++ b/compiler/dex/quick/quick_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * 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_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
+#define ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
+
+namespace art {
+
+class Compiler;
+class CompilerDriver;
+
+Compiler* CreateQuickCompiler(CompilerDriver* driver);
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_QUICK_QUICK_COMPILER_H_
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index bed86d8..6305b22 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -28,8 +28,7 @@
* live until it is either explicitly killed or reallocated.
*/
void Mir2Lir::ResetRegPool() {
- GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
- for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ for (RegisterInfo* info : tempreg_info_) {
info->MarkFree();
}
// Reset temp tracking sanity check.
@@ -66,41 +65,38 @@
const ArrayRef<const RegStorage>& core64_temps,
const ArrayRef<const RegStorage>& sp_temps,
const ArrayRef<const RegStorage>& dp_temps) :
- core_regs_(arena, core_regs.size()), next_core_reg_(0),
- core64_regs_(arena, core64_regs.size()), next_core64_reg_(0),
- sp_regs_(arena, sp_regs.size()), next_sp_reg_(0),
- dp_regs_(arena, dp_regs.size()), next_dp_reg_(0), m2l_(m2l) {
+ core_regs_(arena->Adapter()), next_core_reg_(0),
+ core64_regs_(arena->Adapter()), next_core64_reg_(0),
+ sp_regs_(arena->Adapter()), next_sp_reg_(0),
+ dp_regs_(arena->Adapter()), next_dp_reg_(0), m2l_(m2l) {
// Initialize the fast lookup map.
- m2l_->reginfo_map_.Reset();
- if (kIsDebugBuild) {
- m2l_->reginfo_map_.Resize(RegStorage::kMaxRegs);
- for (unsigned i = 0; i < RegStorage::kMaxRegs; i++) {
- m2l_->reginfo_map_.Insert(nullptr);
- }
- } else {
- m2l_->reginfo_map_.SetSize(RegStorage::kMaxRegs);
- }
+ m2l_->reginfo_map_.clear();
+ m2l_->reginfo_map_.resize(RegStorage::kMaxRegs, nullptr);
// Construct the register pool.
+ core_regs_.reserve(core_regs.size());
for (const RegStorage& reg : core_regs) {
RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
- m2l_->reginfo_map_.Put(reg.GetReg(), info);
- core_regs_.Insert(info);
+ m2l_->reginfo_map_[reg.GetReg()] = info;
+ core_regs_.push_back(info);
}
+ core64_regs_.reserve(core64_regs.size());
for (const RegStorage& reg : core64_regs) {
RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
- m2l_->reginfo_map_.Put(reg.GetReg(), info);
- core64_regs_.Insert(info);
+ m2l_->reginfo_map_[reg.GetReg()] = info;
+ core64_regs_.push_back(info);
}
+ sp_regs_.reserve(sp_regs.size());
for (const RegStorage& reg : sp_regs) {
RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
- m2l_->reginfo_map_.Put(reg.GetReg(), info);
- sp_regs_.Insert(info);
+ m2l_->reginfo_map_[reg.GetReg()] = info;
+ sp_regs_.push_back(info);
}
+ dp_regs_.reserve(dp_regs.size());
for (const RegStorage& reg : dp_regs) {
RegisterInfo* info = new (arena) RegisterInfo(reg, m2l_->GetRegMaskCommon(reg));
- m2l_->reginfo_map_.Put(reg.GetReg(), info);
- dp_regs_.Insert(info);
+ m2l_->reginfo_map_[reg.GetReg()] = info;
+ dp_regs_.push_back(info);
}
// Keep special registers from being allocated.
@@ -127,10 +123,10 @@
// Add an entry for InvalidReg with zero'd mask.
RegisterInfo* invalid_reg = new (arena) RegisterInfo(RegStorage::InvalidReg(), kEncodeNone);
- m2l_->reginfo_map_.Put(RegStorage::InvalidReg().GetReg(), invalid_reg);
+ m2l_->reginfo_map_[RegStorage::InvalidReg().GetReg()] = invalid_reg;
// Existence of core64 registers implies wide references.
- if (core64_regs_.Size() != 0) {
+ if (core64_regs_.size() != 0) {
ref_regs_ = &core64_regs_;
next_ref_reg_ = &next_core64_reg_;
} else {
@@ -139,10 +135,9 @@
}
}
-void Mir2Lir::DumpRegPool(GrowableArray<RegisterInfo*>* regs) {
+void Mir2Lir::DumpRegPool(ArenaVector<RegisterInfo*>* regs) {
LOG(INFO) << "================================================";
- GrowableArray<RegisterInfo*>::Iterator it(regs);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : *regs) {
LOG(INFO) << StringPrintf(
"R[%d:%d:%c]: T:%d, U:%d, W:%d, p:%d, LV:%d, D:%d, SR:%d, DEF:%d",
info->GetReg().GetReg(), info->GetReg().GetRegNum(), info->GetReg().IsFloat() ? 'f' : 'c',
@@ -222,8 +217,7 @@
if (kIsDebugBuild && s_reg == live_sreg_) {
live_sreg_ = INVALID_SREG;
}
- GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
- for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ for (RegisterInfo* info : tempreg_info_) {
if (info->SReg() == s_reg) {
if (info->GetReg().NotExactlyEquals(info->Partner())) {
// Dealing with a pair - clobber the other half.
@@ -252,20 +246,7 @@
DCHECK_LT(s_reg, mir_graph_->GetNumSSARegs());
DCHECK_GE(s_reg, 0);
int v_reg = mir_graph_->SRegToVReg(s_reg);
- if (v_reg >= 0) {
- DCHECK_LT(v_reg, cu_->num_dalvik_registers);
- return v_reg;
- } else {
- /*
- * It must be the case that the v_reg for temporary is less than or equal to the
- * base reg for temps. For that reason, "position" must be zero or positive.
- */
- unsigned int position = std::abs(v_reg) - std::abs(static_cast<int>(kVRegTempBaseReg));
-
- // The temporaries are placed after dalvik registers in the promotion map
- DCHECK_LT(position, mir_graph_->GetNumUsedCompilerTemps());
- return cu_->num_dalvik_registers + position;
- }
+ return v_reg;
}
// TODO: refactor following Alloc/Record routines - much commonality.
@@ -291,8 +272,7 @@
* happens from the single or double pool. This entire section of code could stand
* a good refactoring.
*/
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->core_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->core_regs_) {
if (!info->IsTemp() && !info->InUse()) {
res = info->GetReg();
RecordCorePromotion(res, s_reg);
@@ -324,8 +304,7 @@
*/
DCHECK_NE(cu_->instruction_set, kThumb2);
RegStorage res;
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->sp_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->sp_regs_) {
if (!info->IsTemp() && !info->InUse()) {
res = info->GetReg();
RecordFpPromotion(res, s_reg);
@@ -350,13 +329,14 @@
}
-RegStorage Mir2Lir::AllocTempBody(GrowableArray<RegisterInfo*> ®s, int* next_temp, bool required) {
- int num_regs = regs.Size();
+RegStorage Mir2Lir::AllocTempBody(ArenaVector<RegisterInfo*>& regs, int* next_temp, bool required) {
+ int num_regs = regs.size();
int next = *next_temp;
for (int i = 0; i< num_regs; i++) {
- if (next >= num_regs)
+ if (next >= num_regs) {
next = 0;
- RegisterInfo* info = regs.Get(next);
+ }
+ RegisterInfo* info = regs[next];
// Try to allocate a register that doesn't hold a live value.
if (info->IsTemp() && !info->InUse() && info->IsDead()) {
// If it's wide, split it up.
@@ -380,9 +360,10 @@
next = *next_temp;
// No free non-live regs. Anything we can kill?
for (int i = 0; i< num_regs; i++) {
- if (next >= num_regs)
+ if (next >= num_regs) {
next = 0;
- RegisterInfo* info = regs.Get(next);
+ }
+ RegisterInfo* info = regs[next];
if (info->IsTemp() && !info->InUse()) {
// Got one. Kill it.
ClobberSReg(info->SReg());
@@ -414,7 +395,7 @@
RegStorage Mir2Lir::AllocTempWide(bool required) {
RegStorage res;
- if (reg_pool_->core64_regs_.Size() != 0) {
+ if (reg_pool_->core64_regs_.size() != 0) {
res = AllocTempBody(reg_pool_->core64_regs_, ®_pool_->next_core64_reg_, required);
} else {
RegStorage low_reg = AllocTemp();
@@ -471,10 +452,9 @@
return AllocTemp(required);
}
-RegStorage Mir2Lir::FindLiveReg(GrowableArray<RegisterInfo*> ®s, int s_reg) {
+RegStorage Mir2Lir::FindLiveReg(ArenaVector<RegisterInfo*>& regs, int s_reg) {
RegStorage res;
- GrowableArray<RegisterInfo*>::Iterator it(®s);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : regs) {
if ((info->SReg() == s_reg) && info->IsLive()) {
res = info->GetReg();
break;
@@ -727,15 +707,13 @@
}
void Mir2Lir::ResetDefTracking() {
- GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
- for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ for (RegisterInfo* info : tempreg_info_) {
info->ResetDefBody();
}
}
void Mir2Lir::ClobberAllTemps() {
- GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
- for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ for (RegisterInfo* info : tempreg_info_) {
ClobberBody(info);
}
}
@@ -793,8 +771,7 @@
}
void Mir2Lir::FlushAllRegs() {
- GrowableArray<RegisterInfo*>::Iterator it(&tempreg_info_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : tempreg_info_) {
if (info->IsDirty() && info->IsLive()) {
FlushSpecificReg(info);
}
@@ -866,14 +843,16 @@
void Mir2Lir::MarkTemp(RegStorage reg) {
DCHECK(!reg.IsPair());
RegisterInfo* info = GetRegInfo(reg);
- tempreg_info_.Insert(info);
+ tempreg_info_.push_back(info);
info->SetIsTemp(true);
}
void Mir2Lir::UnmarkTemp(RegStorage reg) {
DCHECK(!reg.IsPair());
RegisterInfo* info = GetRegInfo(reg);
- tempreg_info_.Delete(info);
+ auto pos = std::find(tempreg_info_.begin(), tempreg_info_.end(), info);
+ DCHECK(pos != tempreg_info_.end());
+ tempreg_info_.erase(pos);
info->SetIsTemp(false);
}
@@ -945,8 +924,7 @@
}
bool Mir2Lir::CheckCorePoolSanity() {
- GrowableArray<RegisterInfo*>::Iterator it(&tempreg_info_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : tempreg_info_) {
int my_sreg = info->SReg();
if (info->IsTemp() && info->IsLive() && info->IsWide() && my_sreg != INVALID_SREG) {
RegStorage my_reg = info->GetReg();
@@ -1207,8 +1185,7 @@
* optimization is disabled.
*/
void Mir2Lir::DoPromotion() {
- int dalvik_regs = cu_->num_dalvik_registers;
- int num_regs = dalvik_regs + mir_graph_->GetNumUsedCompilerTemps();
+ int num_regs = mir_graph_->GetNumOfCodeAndTempVRs();
const int promotion_threshold = 1;
// Allocate the promotion map - one entry for each Dalvik vReg or compiler temp
promotion_map_ = static_cast<PromotionMap*>
@@ -1237,17 +1214,10 @@
static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * fp_reg_count_size,
kArenaAllocRegAlloc));
// Set ssa names for original Dalvik registers
- for (int i = 0; i < dalvik_regs; i++) {
+ for (int i = 0; i < num_regs; i++) {
core_regs[i].s_reg = fp_regs[i].s_reg = i;
}
- // Set ssa names for compiler temporaries
- for (unsigned int ct_idx = 0; ct_idx < mir_graph_->GetNumUsedCompilerTemps(); ct_idx++) {
- CompilerTemp* ct = mir_graph_->GetCompilerTemp(ct_idx);
- core_regs[dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
- fp_regs[dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
- }
-
// Duplicate in upper half to represent possible wide starting sregs.
for (size_t i = num_regs; i < fp_reg_count_size; i++) {
fp_regs[i].s_reg = fp_regs[i - num_regs].s_reg | STARTING_WIDE_SREG;
@@ -1353,7 +1323,8 @@
/* Returns sp-relative offset in bytes for a VReg */
int Mir2Lir::VRegOffset(int v_reg) {
- return StackVisitor::GetVRegOffset(cu_->code_item, core_spill_mask_,
+ const DexFile::CodeItem* code_item = mir_graph_->GetCurrentDexCompilationUnit()->GetCodeItem();
+ return StackVisitor::GetVRegOffset(code_item, core_spill_mask_,
fp_spill_mask_, frame_size_, v_reg,
cu_->instruction_set);
}
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index 8ebe55c..dce2b73 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -16,6 +16,7 @@
#include "codegen_x86.h"
#include "dex/quick/mir_to_lir-inl.h"
+#include "oat.h"
#include "x86_lir.h"
namespace art {
@@ -188,8 +189,10 @@
{ kX86Mov32MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0, 0, 0x89, 0, 0, 0, 0, 0, false }, "Mov32MR", "[!0r+!1d],!2r" },
{ kX86Mov32AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0, 0, 0x89, 0, 0, 0, 0, 0, false }, "Mov32AR", "[!0r+!1r<<!2d+!3d],!4r" },
+ { kX86Movnti32MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0, 0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti32MR", "[!0r+!1d],!2r" },
+ { kX86Movnti32AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0, 0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti32AR", "[!0r+!1r<<!2d+!3d],!4r" },
{ kX86Mov32TR, kThreadReg, IS_STORE | IS_BINARY_OP | REG_USE1, { THREAD_PREFIX, 0, 0x89, 0, 0, 0, 0, 0, false }, "Mov32TR", "fs:[!0d],!1r" },
- { kX86Mov32RR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RR", "!0r,!1r" },
+ { kX86Mov32RR, kRegReg, IS_MOVE | IS_BINARY_OP | REG_DEF0_USE1, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RR", "!0r,!1r" },
{ kX86Mov32RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_DEF0_USE1, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RM", "!0r,[!1r+!2d]" },
{ kX86Mov32RA, kRegArray, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
{ kX86Mov32RT, kRegThread, IS_LOAD | IS_BINARY_OP | REG_DEF0, { THREAD_PREFIX, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov32RT", "!0r,fs:[!1d]" },
@@ -198,13 +201,15 @@
{ kX86Mov32AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0, 0, 0xC7, 0, 0, 0, 0, 4, false }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" },
{ kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4, false }, "Mov32TI", "fs:[!0d],!1d" },
- { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE1, { 0, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RM", "!0r,[!1r+!2d]" },
- { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+ { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | REG_DEF0_USE1, { 0, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RM", "!0r,[!1r+!2d]" },
+ { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" },
{ kX86Mov64MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { REX_W, 0, 0x89, 0, 0, 0, 0, 0, false }, "Mov64MR", "[!0r+!1d],!2r" },
{ kX86Mov64AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { REX_W, 0, 0x89, 0, 0, 0, 0, 0, false }, "Mov64AR", "[!0r+!1r<<!2d+!3d],!4r" },
+ { kX86Movnti64MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { REX_W, 0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti64MR", "[!0r+!1d],!2r" },
+ { kX86Movnti64AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { REX_W, 0, 0x0F, 0xC3, 0, 0, 0, 0, false }, "Movnti64AR", "[!0r+!1r<<!2d+!3d],!4r" },
{ kX86Mov64TR, kThreadReg, IS_STORE | IS_BINARY_OP | REG_USE1, { THREAD_PREFIX, REX_W, 0x89, 0, 0, 0, 0, 0, false }, "Mov64TR", "fs:[!0d],!1r" },
- { kX86Mov64RR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { REX_W, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RR", "!0r,!1r" },
+ { kX86Mov64RR, kRegReg, IS_MOVE | IS_BINARY_OP | REG_DEF0_USE1, { REX_W, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RR", "!0r,!1r" },
{ kX86Mov64RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_DEF0_USE1, { REX_W, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RM", "!0r,[!1r+!2d]" },
{ kX86Mov64RA, kRegArray, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { REX_W, 0, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RA", "!0r,[!1r+!2r<<!3d+!4d]" },
{ kX86Mov64RT, kRegThread, IS_LOAD | IS_BINARY_OP | REG_DEF0, { THREAD_PREFIX, REX_W, 0x8B, 0, 0, 0, 0, 0, false }, "Mov64RT", "!0r,fs:[!1d]" },
@@ -214,8 +219,8 @@
{ kX86Mov64AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { REX_W, 0, 0xC7, 0, 0, 0, 0, 4, false }, "Mov64AI", "[!0r+!1r<<!2d+!3d],!4d" },
{ kX86Mov64TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, REX_W, 0xC7, 0, 0, 0, 0, 4, false }, "Mov64TI", "fs:[!0d],!1d" },
- { kX86Lea64RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE1, { REX_W, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RM", "!0r,[!1r+!2d]" },
- { kX86Lea64RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { REX_W, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RA", "!0r,[!1r+!2r<<!3d+!4d]" },
+ { kX86Lea64RM, kRegMem, IS_TERTIARY_OP | REG_DEF0_USE1, { REX_W, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RM", "!0r,[!1r+!2d]" },
+ { kX86Lea64RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { REX_W, 0, 0x8D, 0, 0, 0, 0, 0, false }, "Lea64RA", "!0r,[!1r+!2r<<!3d+!4d]" },
{ kX86Cmov32RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, { 0, 0, 0x0F, 0x40, 0, 0, 0, 0, false }, "Cmovcc32RR", "!2c !0r,!1r" },
{ kX86Cmov64RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, { REX_W, 0, 0x0F, 0x40, 0, 0, 0, 0, false }, "Cmovcc64RR", "!2c !0r,!1r" },
@@ -263,8 +268,10 @@
{ kX86Cmc, kNullary, NO_OPERAND, { 0, 0, 0xF5, 0, 0, 0, 0, 0, false }, "Cmc", "" },
{ kX86Shld32RRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld32RRI", "!0r,!1r,!2d" },
+ { kX86Shld32RRC, kShiftRegRegCl, IS_TERTIARY_OP | REG_DEF0_USE01 | REG_USEC | SETS_CCODES, { 0, 0, 0x0F, 0xA5, 0, 0, 0, 0, false }, "Shld32RRC", "!0r,!1r,cl" },
{ kX86Shld32MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { 0, 0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld32MRI", "[!0r+!1d],!2r,!3d" },
{ kX86Shrd32RRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd32RRI", "!0r,!1r,!2d" },
+ { kX86Shrd32RRC, kShiftRegRegCl, IS_TERTIARY_OP | REG_DEF0_USE01 | REG_USEC | SETS_CCODES, { 0, 0, 0x0F, 0xAD, 0, 0, 0, 0, false }, "Shrd32RRC", "!0r,!1r,cl" },
{ kX86Shrd32MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { 0, 0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd32MRI", "[!0r+!1d],!2r,!3d" },
{ kX86Shld64RRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { REX_W, 0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld64RRI", "!0r,!1r,!2d" },
{ kX86Shld64MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { REX_W, 0, 0x0F, 0xA4, 0, 0, 0, 1, false }, "Shld64MRI", "[!0r+!1d],!2r,!3d" },
@@ -383,20 +390,27 @@
EXT_0F_ENCODING_MAP(Subss, 0xF3, 0x5C, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Divsd, 0xF2, 0x5E, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Divss, 0xF3, 0x5E, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Punpcklbw, 0x66, 0x60, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Punpcklwd, 0x66, 0x61, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Punpckldq, 0x66, 0x62, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Punpcklqdq, 0x66, 0x6C, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Sqrtsd, 0xF2, 0x51, REG_DEF0_USE0),
EXT_0F_ENCODING2_MAP(Pmulld, 0x66, 0x38, 0x40, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Pmullw, 0x66, 0xD5, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Pmuludq, 0x66, 0xF4, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Mulps, 0x00, 0x59, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Mulpd, 0x66, 0x59, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Paddb, 0x66, 0xFC, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Paddw, 0x66, 0xFD, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Paddd, 0x66, 0xFE, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Paddq, 0x66, 0xD4, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Psadbw, 0x66, 0xF6, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Addps, 0x00, 0x58, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Addpd, 0xF2, 0x58, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Psubb, 0x66, 0xF8, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Psubw, 0x66, 0xF9, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Psubd, 0x66, 0xFA, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Psubq, 0x66, 0xFB, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Subps, 0x00, 0x5C, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Subpd, 0x66, 0x5C, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Pand, 0x66, 0xDB, REG_DEF0_USE0),
@@ -425,25 +439,26 @@
{ kX86PsrlwRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x71, 0, 2, 0, 1, false }, "PsrlwRI", "!0r,!1d" },
{ kX86PsrldRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x72, 0, 2, 0, 1, false }, "PsrldRI", "!0r,!1d" },
{ kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1, false }, "PsrlqRI", "!0r,!1d" },
+ { kX86PsrldqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 3, 0, 1, false }, "PsrldqRI", "!0r,!1d" },
{ kX86PsllwRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x71, 0, 6, 0, 1, false }, "PsllwRI", "!0r,!1d" },
{ kX86PslldRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x72, 0, 6, 0, 1, false }, "PslldRI", "!0r,!1d" },
{ kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1, false }, "PsllqRI", "!0r,!1d" },
- { kX86Fild32M, kMem, IS_LOAD | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDB, 0x00, 0, 0, 0, 0, false }, "Fild32M", "[!0r,!1d]" },
- { kX86Fild64M, kMem, IS_LOAD | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDF, 0x00, 0, 5, 0, 0, false }, "Fild64M", "[!0r,!1d]" },
- { kX86Fld32M, kMem, IS_LOAD | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 0, 0, 0, false }, "Fld32M", "[!0r,!1d]" },
- { kX86Fld64M, kMem, IS_LOAD | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 0, 0, 0, false }, "Fld64M", "[!0r,!1d]" },
- { kX86Fstp32M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 3, 0, 0, false }, "Fstps32M", "[!0r,!1d]" },
- { kX86Fstp64M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0, false }, "Fstpd64M", "[!0r,!1d]" },
- { kX86Fst32M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 2, 0, 0, false }, "Fsts32M", "[!0r,!1d]" },
- { kX86Fst64M, kMem, IS_STORE | IS_UNARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 2, 0, 0, false }, "Fstd64M", "[!0r,!1d]" },
+ { kX86Fild32M, kMem, IS_LOAD | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDB, 0x00, 0, 0, 0, 0, false }, "Fild32M", "[!0r,!1d]" },
+ { kX86Fild64M, kMem, IS_LOAD | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDF, 0x00, 0, 5, 0, 0, false }, "Fild64M", "[!0r,!1d]" },
+ { kX86Fld32M, kMem, IS_LOAD | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 0, 0, 0, false }, "Fld32M", "[!0r,!1d]" },
+ { kX86Fld64M, kMem, IS_LOAD | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 0, 0, 0, false }, "Fld64M", "[!0r,!1d]" },
+ { kX86Fstp32M, kMem, IS_STORE | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 3, 0, 0, false }, "Fstps32M", "[!0r,!1d]" },
+ { kX86Fstp64M, kMem, IS_STORE | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0, false }, "Fstpd64M", "[!0r,!1d]" },
+ { kX86Fst32M, kMem, IS_STORE | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xD9, 0x00, 0, 2, 0, 0, false }, "Fsts32M", "[!0r,!1d]" },
+ { kX86Fst64M, kMem, IS_STORE | IS_BINARY_OP | REG_USE0 | USE_FP_STACK, { 0x0, 0, 0xDD, 0x00, 0, 2, 0, 0, false }, "Fstd64M", "[!0r,!1d]" },
{ kX86Fprem, kNullary, NO_OPERAND | USE_FP_STACK, { 0xD9, 0, 0xF8, 0, 0, 0, 0, 0, false }, "Fprem64", "" },
{ kX86Fucompp, kNullary, NO_OPERAND | USE_FP_STACK, { 0xDA, 0, 0xE9, 0, 0, 0, 0, 0, false }, "Fucompp", "" },
{ kX86Fstsw16R, kNullary, NO_OPERAND | REG_DEFA | USE_FP_STACK, { 0x9B, 0xDF, 0xE0, 0, 0, 0, 0, 0, false }, "Fstsw16R", "ax" },
- EXT_0F_ENCODING_MAP(Mova128, 0x66, 0x6F, REG_DEF0),
- { kX86Mova128MR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "Mova128MR", "[!0r+!1d],!2r" },
- { kX86Mova128AR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "Mova128AR", "[!0r+!1r<<!2d+!3d],!4r" },
+ EXT_0F_ENCODING_MAP(Movdqa, 0x66, 0x6F, REG_DEF0),
+ { kX86MovdqaMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "MovdqaMR", "[!0r+!1d],!2r" },
+ { kX86MovdqaAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0x66, 0, 0x0F, 0x6F, 0, 0, 0, 0, false }, "MovdqaAR", "[!0r+!1r<<!2d+!3d],!4r" },
EXT_0F_ENCODING_MAP(Movups, 0x0, 0x10, REG_DEF0),
@@ -484,7 +499,9 @@
// TODO: load/store?
// Encode the modrm opcode as an extra opcode byte to avoid computation during assembly.
+ { kX86Lfence, kReg, NO_OPERAND, { 0, 0, 0x0F, 0xAE, 0, 5, 0, 0, false }, "Lfence", "" },
{ kX86Mfence, kReg, NO_OPERAND, { 0, 0, 0x0F, 0xAE, 0, 6, 0, 0, false }, "Mfence", "" },
+ { kX86Sfence, kReg, NO_OPERAND, { 0, 0, 0x0F, 0xAE, 0, 7, 0, 0, false }, "Sfence", "" },
EXT_0F_ENCODING_MAP(Imul16, 0x66, 0xAF, REG_USE0 | REG_DEF0 | SETS_CCODES),
EXT_0F_ENCODING_MAP(Imul32, 0x00, 0xAF, REG_USE0 | REG_DEF0 | SETS_CCODES),
@@ -526,7 +543,7 @@
{ kX86StartOfMethod, kMacro, IS_UNARY_OP | SETS_CCODES, { 0, 0, 0, 0, 0, 0, 0, 0, false }, "StartOfMethod", "!0r" },
{ kX86PcRelLoadRA, kPcRel, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0, false }, "PcRelLoadRA", "!0r,[!1r+!2r<<!3d+!4p]" },
- { kX86PcRelAdr, kPcRel, IS_LOAD | IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4, false }, "PcRelAdr", "!0r,!1d" },
+ { kX86PcRelAdr, kPcRel, IS_LOAD | IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4, false }, "PcRelAdr", "!0r,!1p" },
{ kX86RepneScasw, kNullary, NO_OPERAND | REG_USEA | REG_USEC | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0, false }, "RepNE ScasW", "" },
};
@@ -563,7 +580,7 @@
case kX86CallA: return true;
default: return false;
}
- case kPcRel: return true;
+ case kPcRel:
switch (entry->opcode) {
case kX86PcRelLoadRA: return true;
default: return false;
@@ -591,6 +608,7 @@
case kShiftRegCl: return true;
case kRegCond: return true;
case kRegRegCond: return true;
+ case kShiftRegRegCl: return true;
case kJmp:
switch (entry->opcode) {
case kX86JmpR: return true;
@@ -662,7 +680,8 @@
}
if (displacement != 0 || LowRegisterBits(raw_base) == rs_rBP.GetRegNum()) {
// BP requires an explicit displacement, even when it's 0.
- if (entry->opcode != kX86Lea32RA && entry->opcode != kX86Lea64RA) {
+ if (entry->opcode != kX86Lea32RA && entry->opcode != kX86Lea64RA &&
+ entry->opcode != kX86Lea32RM && entry->opcode != kX86Lea64RM) {
DCHECK_NE(entry->flags & (IS_LOAD | IS_STORE), UINT64_C(0)) << entry->name;
}
size += IS_SIMM8(displacement) ? 1 : 4;
@@ -768,6 +787,9 @@
DCHECK_EQ(rs_rCX.GetRegNum(), RegStorage::RegNum(lir->operands[4]));
return ComputeSize(entry, lir->operands[4], lir->operands[1], lir->operands[0],
lir->operands[3]);
+ case kShiftRegRegCl: // lir operands - 0: reg1, 1: reg2, 2: cl
+ DCHECK_EQ(rs_rCX.GetRegNum(), RegStorage::RegNum(lir->operands[2]));
+ return ComputeSize(entry, lir->operands[0], NO_REG, lir->operands[1], 0);
case kRegCond: // lir operands - 0: reg, 1: cond
return ComputeSize(entry, NO_REG, NO_REG, lir->operands[0], 0);
case kMemCond: // lir operands - 0: base, 1: disp, 2: cond
@@ -895,22 +917,22 @@
if (r8_form) {
// Do we need an empty REX prefix to normalize byte register addressing?
if (RegStorage::RegNum(raw_reg_r) >= 4 && !IsByteSecondOperand(entry)) {
- rex |= 0x40; // REX.0000
+ rex |= REX; // REX.0000
} else if (modrm_is_reg_reg && RegStorage::RegNum(raw_reg_b) >= 4) {
- rex |= 0x40; // REX.0000
+ rex |= REX; // REX.0000
}
}
if (w) {
- rex |= 0x48; // REX.W000
+ rex |= REX_W; // REX.W000
}
if (r) {
- rex |= 0x44; // REX.0R00
+ rex |= REX_R; // REX.0R00
}
if (x) {
- rex |= 0x42; // REX.00X0
+ rex |= REX_X; // REX.00X0
}
if (b) {
- rex |= 0x41; // REX.000B
+ rex |= REX_B; // REX.000B
}
if (entry->skeleton.prefix1 != 0) {
if (cu_->target64 && entry->skeleton.prefix1 == THREAD_PREFIX) {
@@ -1336,6 +1358,19 @@
DCHECK_EQ(0, entry->skeleton.immediate_bytes);
}
+void X86Mir2Lir::EmitShiftRegRegCl(const X86EncodingMap* entry, int32_t raw_reg1, int32_t raw_reg2, int32_t raw_cl) {
+ DCHECK_EQ(false, entry->skeleton.r8_form);
+ DCHECK_EQ(rs_rCX.GetRegNum(), RegStorage::RegNum(raw_cl));
+ EmitPrefixAndOpcode(entry, raw_reg1, NO_REG, raw_reg2);
+ uint8_t low_reg1 = LowRegisterBits(raw_reg1);
+ uint8_t low_reg2 = LowRegisterBits(raw_reg2);
+ uint8_t modrm = (3 << 6) | (low_reg1 << 3) | low_reg2;
+ code_buffer_.push_back(modrm);
+ DCHECK_EQ(0, entry->skeleton.modrm_opcode);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
void X86Mir2Lir::EmitShiftMemImm(const X86EncodingMap* entry, int32_t raw_base, int32_t disp,
int32_t imm) {
DCHECK_EQ(false, entry->skeleton.r8_form);
@@ -1829,6 +1864,9 @@
case kShiftMemCl: // lir operands - 0: base, 1:displacement, 2: cl
EmitShiftMemCl(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
break;
+ case kShiftRegRegCl: // lir operands - 0: reg1, 1: reg2, 2: cl
+ EmitShiftRegRegCl(entry, lir->operands[1], lir->operands[0], lir->operands[2]);
+ break;
case kRegCond: // lir operands - 0: reg, 1: condition
EmitRegCond(entry, lir->operands[0], lir->operands[1]);
break;
@@ -1928,17 +1966,12 @@
int offset = AssignInsnOffsets();
if (const_vectors_ != nullptr) {
- /* assign offsets to vector literals */
-
- // First, get offset to 12 mod 16 to align to 16 byte boundary.
- // This will ensure that the vector is 16 byte aligned, as the procedure is
- // always aligned at at 4 mod 16.
- int align_size = (16-4) - (offset & 0xF);
- if (align_size < 0) {
- align_size += 16;
- }
-
- offset += align_size;
+ // Vector literals must be 16-byte aligned. The header that is placed
+ // in the code section causes misalignment so we take it into account.
+ // Otherwise, we are sure that for x86 method is aligned to 16.
+ DCHECK_EQ(GetInstructionSetAlignment(cu_->instruction_set), 16u);
+ uint32_t bytes_to_fill = (0x10 - ((offset + sizeof(OatQuickMethodHeader)) & 0xF)) & 0xF;
+ offset += bytes_to_fill;
// Now assign each literal the right offset.
for (LIR *p = const_vectors_; p != nullptr; p = p->next) {
diff --git a/compiler/dex/quick/x86/backend_x86.h b/compiler/dex/quick/x86/backend_x86.h
new file mode 100644
index 0000000..f73db94
--- /dev/null
+++ b/compiler/dex/quick/x86/backend_x86.h
@@ -0,0 +1,32 @@
+/*
+ * 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_COMPILER_DEX_QUICK_X86_BACKEND_X86_H_
+#define ART_COMPILER_DEX_QUICK_X86_BACKEND_X86_H_
+
+namespace art {
+
+struct CompilationUnit;
+class Mir2Lir;
+class MIRGraph;
+class ArenaAllocator;
+
+Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
+ ArenaAllocator* const arena);
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_QUICK_X86_BACKEND_X86_H_
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 996689a..441ec9e 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -19,6 +19,8 @@
#include "codegen_x86.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "gc/accounting/card_table.h"
+#include "mirror/art_method.h"
+#include "mirror/object_array-inl.h"
#include "x86_lir.h"
namespace art {
@@ -28,7 +30,7 @@
* pairs.
*/
void X86Mir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
@@ -61,7 +63,7 @@
* done:
*/
void X86Mir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
@@ -73,7 +75,7 @@
int size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
kArenaAllocLIR));
- switch_tables_.Insert(tab_rec);
+ switch_tables_.push_back(tab_rec);
// Get the switch value
rl_src = LoadValue(rl_src, kCoreReg);
@@ -134,8 +136,8 @@
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
- const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
+void X86Mir2Lir::GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
+ const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
// Add the table to the list - we'll process it later
FillArrayData* tab_rec =
static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), kArenaAllocData));
@@ -145,7 +147,7 @@
uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
tab_rec->size = (size * width) + 8;
- fill_array_data_.Insert(tab_rec);
+ fill_array_data_.push_back(tab_rec);
// Making a call - use explicit registers
FlushAllRegs(); /* Everything to home location */
@@ -330,4 +332,58 @@
MarkPossibleNullPointerException(opt_flags);
}
+/*
+ * Bit of a hack here - in the absence of a real scheduling pass,
+ * emit the next instruction in static & direct invoke sequences.
+ */
+static int X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
+ int state, const MethodReference& target_method,
+ uint32_t unused,
+ uintptr_t direct_code, uintptr_t direct_method,
+ InvokeType type) {
+ Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
+ if (direct_method != 0) {
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ if (direct_method != static_cast<uintptr_t>(-1)) {
+ cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
+ } else {
+ cg->LoadMethodAddress(target_method, type, kArg0);
+ }
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
+ switch (state) {
+ case 0: // Get the current Method* [sets kArg0]
+ // TUNING: we can save a reg copy if Method* has been promoted.
+ cg->LoadCurrMethodDirect(arg0_ref);
+ break;
+ case 1: // Get method->dex_cache_resolved_methods_
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ break;
+ case 2: // Grab target method*
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ cg->LoadRefDisp(arg0_ref,
+ mirror::ObjectArray<mirror::Object>::OffsetOfElement(
+ target_method.dex_method_index).Int32Value(),
+ arg0_ref,
+ kNotVolatile);
+ break;
+ default:
+ return -1;
+ }
+ }
+ return state + 1;
+}
+
+NextCallInsn X86Mir2Lir::GetNextSDCallInsn() {
+ return X86NextSDCallInsn;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index d74caae..8edfc01 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -18,9 +18,11 @@
#define ART_COMPILER_DEX_QUICK_X86_CODEGEN_X86_H_
#include "dex/compiler_internals.h"
+#include "dex/quick/mir_to_lir.h"
#include "x86_lir.h"
#include <map>
+#include <vector>
namespace art {
@@ -60,6 +62,15 @@
bool initialized_;
};
+ class ExplicitTempRegisterLock {
+ public:
+ ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir, int n_regs, ...);
+ ~ExplicitTempRegisterLock();
+ protected:
+ std::vector<RegStorage> temp_regs_;
+ X86Mir2Lir* const mir_to_lir_;
+ };
+
public:
X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena);
@@ -124,7 +135,7 @@
void CompilerInitializeRegAlloc() OVERRIDE;
int VectorRegisterSize() OVERRIDE;
- int NumReservableVectorRegisters(bool fp_used) OVERRIDE;
+ int NumReservableVectorRegisters(bool long_or_fp) OVERRIDE;
// Required for target - miscellaneous.
void AssembleLIR() OVERRIDE;
@@ -159,6 +170,7 @@
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object) OVERRIDE;
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) OVERRIDE;
bool GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) OVERRIDE;
+ bool GenInlinedReverseBits(CallInfo* info, OpSize size) OVERRIDE;
bool GenInlinedSqrt(CallInfo* info) OVERRIDE;
bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
@@ -233,7 +245,7 @@
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
void GenExitSequence() OVERRIDE;
void GenSpecialExitSequence() OVERRIDE;
- void GenFillArrayData(DexOffset table_offset, RegLocation rl_src) OVERRIDE;
+ void GenFillArrayData(MIR* mir, DexOffset table_offset, RegLocation rl_src) OVERRIDE;
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) OVERRIDE;
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) OVERRIDE;
void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
@@ -319,14 +331,17 @@
/*
* @brief Load the Class* of a Dex Class type into the register.
+ * @param dex DexFile that contains the class type.
* @param type How the method will be invoked.
* @param register that will contain the code address.
* @note register will be passed to TargetReg to get physical register.
*/
- void LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) OVERRIDE;
+ void LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+ SpecialTargetRegister symbolic_reg) OVERRIDE;
void FlushIns(RegLocation* ArgLocs, RegLocation rl_method) OVERRIDE;
+ NextCallInsn GetNextSDCallInsn() OVERRIDE;
int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
NextCallInsn next_call_insn,
const MethodReference& target_method,
@@ -347,7 +362,14 @@
* @param type How the method will be invoked.
* @returns Call instruction
*/
- virtual LIR * CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+ LIR* CallWithLinkerFixup(const MethodReference& target_method, InvokeType type);
+
+ /*
+ * @brief Generate the actual call insn based on the method info.
+ * @param method_info the lowering info for the method call.
+ * @returns Call instruction
+ */
+ LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE;
/*
* @brief Handle x86 specific literals
@@ -355,16 +377,10 @@
void InstallLiteralPools() OVERRIDE;
/*
- * @brief Generate the debug_frame CFI information.
- * @returns pointer to vector containing CFE information
- */
- static std::vector<uint8_t>* ReturnCommonCallFrameInformation(bool is_x86_64);
-
- /*
* @brief Generate the debug_frame FDE information.
* @returns pointer to vector containing CFE information
*/
- std::vector<uint8_t>* ReturnCallFrameInformation() OVERRIDE;
+ std::vector<uint8_t>* ReturnFrameDescriptionEntry() OVERRIDE;
LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE;
@@ -410,7 +426,7 @@
LIR* LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement,
RegStorage r_dest, OpSize size);
LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement,
- RegStorage r_src, OpSize size);
+ RegStorage r_src, OpSize size, int opt_flags = 0);
RegStorage GetCoreArgMappingToPhysicalReg(int core_arg_num);
@@ -460,6 +476,8 @@
void EmitShiftRegImm(const X86EncodingMap* entry, int32_t raw_reg, int32_t imm);
void EmitShiftRegCl(const X86EncodingMap* entry, int32_t raw_reg, int32_t raw_cl);
void EmitShiftMemCl(const X86EncodingMap* entry, int32_t raw_base, int32_t disp, int32_t raw_cl);
+ void EmitShiftRegRegCl(const X86EncodingMap* entry, int32_t raw_reg1, int32_t raw_reg2,
+ int32_t raw_cl);
void EmitShiftMemImm(const X86EncodingMap* entry, int32_t raw_base, int32_t disp, int32_t imm);
void EmitRegCond(const X86EncodingMap* entry, int32_t raw_reg, int32_t cc);
void EmitMemCond(const X86EncodingMap* entry, int32_t raw_base, int32_t disp, int32_t cc);
@@ -479,11 +497,16 @@
void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
int64_t val, ConditionCode ccode);
void GenConstWide(RegLocation rl_dest, int64_t value);
- void GenMultiplyVectorSignedByte(BasicBlock *bb, MIR *mir);
+ void GenMultiplyVectorSignedByte(RegStorage rs_dest_src1, RegStorage rs_src2);
+ void GenMultiplyVectorLong(RegStorage rs_dest_src1, RegStorage rs_src2);
void GenShiftByteVector(BasicBlock *bb, MIR *mir);
- void AndMaskVectorRegister(RegStorage rs_src1, uint32_t m1, uint32_t m2, uint32_t m3, uint32_t m4);
- void MaskVectorRegister(X86OpCode opcode, RegStorage rs_src1, uint32_t m1, uint32_t m2, uint32_t m3, uint32_t m4);
+ void AndMaskVectorRegister(RegStorage rs_src1, uint32_t m1, uint32_t m2, uint32_t m3,
+ uint32_t m4);
+ void MaskVectorRegister(X86OpCode opcode, RegStorage rs_src1, uint32_t m1, uint32_t m2,
+ uint32_t m3, uint32_t m4);
void AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir);
+ virtual void LoadVectorRegister(RegStorage rs_dest, RegStorage rs_src, OpSize opsize,
+ int op_mov);
static bool ProvidesFullMemoryBarrier(X86OpCode opcode);
@@ -519,20 +542,18 @@
bool GenInlinedIndexOf(CallInfo* info, bool zero_based);
/**
- * @brief Reserve a fixed number of vector registers from the register pool
- * @details The mir->dalvikInsn.vA specifies an N such that vector registers
- * [0..N-1] are removed from the temporary pool. The caller must call
- * ReturnVectorRegisters before calling ReserveVectorRegisters again.
- * Also sets the num_reserved_vector_regs_ to the specified value
- * @param mir whose vA specifies the number of registers to reserve
+ * @brief Used to reserve a range of vector registers.
+ * @see kMirOpReserveVectorRegisters
+ * @param mir The extended MIR for reservation.
*/
void ReserveVectorRegisters(MIR* mir);
/**
- * @brief Return all the reserved vector registers to the temp pool
- * @details Returns [0..num_reserved_vector_regs_]
+ * @brief Used to return a range of vector registers.
+ * @see kMirOpReturnVectorRegisters
+ * @param mir The extended MIR for returning vector regs.
*/
- void ReturnVectorRegisters();
+ void ReturnVectorRegisters(MIR* mir);
/*
* @brief Load 128 bit constant into vector register.
@@ -554,7 +575,8 @@
void GenMoveVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed multiply of units in two vector registers: vB = vB .* @note vC using vA to know the type of the vector.
+ * @brief Packed multiply of units in two vector registers: vB = vB .* @note vC using vA to know
+ * the type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -564,7 +586,8 @@
void GenMultiplyVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed addition of units in two vector registers: vB = vB .+ vC using vA to know the type of the vector.
+ * @brief Packed addition of units in two vector registers: vB = vB .+ vC using vA to know the
+ * type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -574,7 +597,8 @@
void GenAddVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed subtraction of units in two vector registers: vB = vB .- vC using vA to know the type of the vector.
+ * @brief Packed subtraction of units in two vector registers: vB = vB .- vC using vA to know the
+ * type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -584,7 +608,8 @@
void GenSubtractVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed shift left of units in two vector registers: vB = vB .<< vC using vA to know the type of the vector.
+ * @brief Packed shift left of units in two vector registers: vB = vB .<< vC using vA to know the
+ * type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -594,7 +619,8 @@
void GenShiftLeftVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed signed shift right of units in two vector registers: vB = vB .>> vC using vA to know the type of the vector.
+ * @brief Packed signed shift right of units in two vector registers: vB = vB .>> vC using vA to
+ * know the type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -604,7 +630,8 @@
void GenSignedShiftRightVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed unsigned shift right of units in two vector registers: vB = vB .>>> vC using vA to know the type of the vector.
+ * @brief Packed unsigned shift right of units in two vector registers: vB = vB .>>> vC using vA
+ * to know the type of the vector.
* @param bb The basic block in which the MIR is from..
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -614,7 +641,8 @@
void GenUnsignedShiftRightVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed bitwise and of units in two vector registers: vB = vB .& vC using vA to know the type of the vector.
+ * @brief Packed bitwise and of units in two vector registers: vB = vB .& vC using vA to know the
+ * type of the vector.
* @note vA: TypeSize
* @note vB: destination and source
* @note vC: source
@@ -622,7 +650,8 @@
void GenAndVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed bitwise or of units in two vector registers: vB = vB .| vC using vA to know the type of the vector.
+ * @brief Packed bitwise or of units in two vector registers: vB = vB .| vC using vA to know the
+ * type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -632,7 +661,8 @@
void GenOrVector(BasicBlock *bb, MIR *mir);
/*
- * @brief Packed bitwise xor of units in two vector registers: vB = vB .^ vC using vA to know the type of the vector.
+ * @brief Packed bitwise xor of units in two vector registers: vB = vB .^ vC using vA to know the
+ * type of the vector.
* @param bb The basic block in which the MIR is from.
* @param mir The MIR whose opcode is kMirConstVector.
* @note vA: TypeSize
@@ -673,6 +703,20 @@
*/
void GenSetVector(BasicBlock *bb, MIR *mir);
+ /**
+ * @brief Used to generate code for kMirOpPackedArrayGet.
+ * @param bb The basic block of MIR.
+ * @param mir The mir whose opcode is kMirOpPackedArrayGet.
+ */
+ void GenPackedArrayGet(BasicBlock *bb, MIR *mir);
+
+ /**
+ * @brief Used to generate code for kMirOpPackedArrayPut.
+ * @param bb The basic block of MIR.
+ * @param mir The mir whose opcode is kMirOpPackedArrayPut.
+ */
+ void GenPackedArrayPut(BasicBlock *bb, MIR *mir);
+
/*
* @brief Generate code for a vector opcode.
* @param bb The basic block in which the MIR is from.
@@ -880,7 +924,7 @@
* @param bb Basic block containing instruction.
* @param mir Instruction to analyze.
*/
- void AnalyzeFPInstruction(int opcode, BasicBlock * bb, MIR *mir);
+ virtual void AnalyzeFPInstruction(int opcode, BasicBlock * bb, MIR *mir);
/*
* @brief Analyze one use of a double operand.
@@ -911,13 +955,13 @@
LIR* setup_method_address_[2];
// Instructions needing patching with Method* values.
- GrowableArray<LIR*> method_address_insns_;
+ ArenaVector<LIR*> method_address_insns_;
// Instructions needing patching with Class Type* values.
- GrowableArray<LIR*> class_type_address_insns_;
+ ArenaVector<LIR*> class_type_address_insns_;
// Instructions needing patching with PC relative code addresses.
- GrowableArray<LIR*> call_method_insns_;
+ ArenaVector<LIR*> call_method_insns_;
// Prologue decrement of stack pointer.
LIR* stack_decrement_;
@@ -926,20 +970,20 @@
LIR* stack_increment_;
// The list of const vector literals.
- LIR *const_vectors_;
+ LIR* const_vectors_;
/*
* @brief Search for a matching vector literal
- * @param mir A kMirOpConst128b MIR instruction to match.
+ * @param constants An array of size 4 which contains all of 32-bit constants.
* @returns pointer to matching LIR constant, or nullptr if not found.
*/
- LIR *ScanVectorLiteral(MIR *mir);
+ LIR* ScanVectorLiteral(int32_t* constants);
/*
* @brief Add a constant vector literal
- * @param mir A kMirOpConst128b MIR instruction to match.
+ * @param constants An array of size 4 which contains all of 32-bit constants.
*/
- LIR *AddVectorLiteral(MIR *mir);
+ LIR* AddVectorLiteral(int32_t* constants);
InToRegStorageMapping in_to_reg_storage_mapping_;
@@ -959,8 +1003,8 @@
static const X86EncodingMap EncodingMap[kX86Last];
private:
- // The number of vector registers [0..N] reserved by a call to ReserveVectorRegisters
- int num_reserved_vector_regs_;
+ void SwapBits(RegStorage result_reg, int shift, int32_t value);
+ void SwapBits64(RegStorage result_reg, int shift, int64_t value);
};
} // namespace art
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index 2920fb6..21d1a5c 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -730,6 +730,25 @@
// Handle NaN.
branch_nan->target = NewLIR0(kPseudoTargetLabel);
LoadConstantWide(rl_result.reg, INT64_C(0x7ff8000000000000));
+
+ // The base_of_code_ compiler temp is non-null when it is reserved
+ // for being able to do data accesses relative to method start.
+ if (base_of_code_ != nullptr) {
+ // Loading from the constant pool may have used base of code register.
+ // However, the code here generates logic in diamond shape and not all
+ // paths load base of code register. Therefore, we ensure it is clobbered so
+ // that the temp caching system does not believe it is live at merge point.
+ RegLocation rl_method = mir_graph_->GetRegLocation(base_of_code_->s_reg_low);
+ if (rl_method.wide) {
+ rl_method = UpdateLocWide(rl_method);
+ } else {
+ rl_method = UpdateLoc(rl_method);
+ }
+ if (rl_method.location == kLocPhysReg) {
+ Clobber(rl_method.reg);
+ }
+ }
+
LIR* branch_exit_nan = NewLIR1(kX86Jmp8, 0);
// Handle Min/Max. Copy greater/lesser value from src2.
branch_cond1->target = NewLIR0(kPseudoTargetLabel);
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 00a2621..4357657 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -49,8 +49,8 @@
return;
}
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage
+ // Prepare for explicit register usage
+ ExplicitTempRegisterLock(this, 4, &rs_r0, &rs_r1, &rs_r2, &rs_r3);
RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1);
RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3);
LoadValueDirectWideFixed(rl_src1, r_tmp1);
@@ -407,8 +407,8 @@
return;
}
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage
+ // Prepare for explicit register usage
+ ExplicitTempRegisterLock(this, 4, &rs_r0, &rs_r1, &rs_r2, &rs_r3);
RegStorage r_tmp1 = RegStorage::MakeRegPair(rs_r0, rs_r1);
RegStorage r_tmp2 = RegStorage::MakeRegPair(rs_r2, rs_r3);
LoadValueDirectWideFixed(rl_src1, r_tmp1);
@@ -616,6 +616,12 @@
rl_result = EvalLoc(rl_dest, kCoreReg, true);
if (is_div) {
LoadValueDirectFixed(rl_src, rl_result.reg);
+
+ // Check if numerator is 0
+ OpRegImm(kOpCmp, rl_result.reg, 0);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
+
+ // handle 0x80000000 / -1
OpRegImm(kOpCmp, rl_result.reg, 0x80000000);
LIR *minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
@@ -624,6 +630,7 @@
// EAX already contains the right value (0x80000000),
minint_branch->target = NewLIR0(kPseudoTargetLabel);
+ branch->target = NewLIR0(kPseudoTargetLabel);
} else {
// x % -1 == 0.
LoadConstantNoClobber(rl_result.reg, 0);
@@ -636,6 +643,14 @@
RegStorage rs_temp = AllocTypedTemp(false, kCoreReg);
rl_result.reg.SetReg(rs_temp.GetReg());
}
+
+ // Check if numerator is 0
+ OpRegImm(kOpCmp, rl_src.reg, 0);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LoadConstantNoClobber(rl_result.reg, 0);
+ LIR* done = NewLIR1(kX86Jmp8, 0);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+
NewLIR3(kX86Lea32RM, rl_result.reg.GetReg(), rl_src.reg.GetReg(), std::abs(imm) - 1);
NewLIR2(kX86Test32RR, rl_src.reg.GetReg(), rl_src.reg.GetReg());
OpCondRegReg(kOpCmov, kCondPl, rl_result.reg, rl_src.reg);
@@ -644,6 +659,7 @@
if (imm < 0) {
OpReg(kOpNeg, rl_result.reg);
}
+ done->target = NewLIR0(kPseudoTargetLabel);
} else {
CHECK(imm <= -2 || imm >= 2);
@@ -676,26 +692,27 @@
Clobber(rs_r2);
LockTemp(rs_r2);
- // Assume that the result will be in EDX.
- rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, rs_r2, INVALID_SREG, INVALID_SREG};
+ // Assume that the result will be in EDX for divide, and EAX for remainder.
+ rl_result = {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, is_div ? rs_r2 : rs_r0,
+ INVALID_SREG, INVALID_SREG};
- // Numerator into EAX.
- RegStorage numerator_reg;
- if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
- // We will need the value later.
- rl_src = LoadValue(rl_src, kCoreReg);
- numerator_reg = rl_src.reg;
- OpRegCopy(rs_r0, numerator_reg);
- } else {
- // Only need this once. Just put it into EAX.
- LoadValueDirectFixed(rl_src, rs_r0);
- }
+ // We need the value at least twice. Load into a temp.
+ rl_src = LoadValue(rl_src, kCoreReg);
+ RegStorage numerator_reg = rl_src.reg;
- // EDX = magic.
- LoadConstantNoClobber(rs_r2, magic);
+ // Check if numerator is 0.
+ OpRegImm(kOpCmp, numerator_reg, 0);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ // Return result 0 if numerator was 0.
+ LoadConstantNoClobber(rl_result.reg, 0);
+ LIR* done = NewLIR1(kX86Jmp8, 0);
+ branch->target = NewLIR0(kPseudoTargetLabel);
- // EDX:EAX = magic & dividend.
- NewLIR1(kX86Imul32DaR, rs_r2.GetReg());
+ // EAX = magic.
+ LoadConstant(rs_r0, magic);
+
+ // EDX:EAX = magic * numerator.
+ NewLIR1(kX86Imul32DaR, numerator_reg.GetReg());
if (imm > 0 && magic < 0) {
// Add numerator to EDX.
@@ -733,12 +750,12 @@
// EAX = numerator * imm.
OpRegRegImm(kOpMul, rs_r2, rs_r2, imm);
- // EDX -= EAX.
+ // EAX -= EDX.
NewLIR2(kX86Sub32RR, rs_r0.GetReg(), rs_r2.GetReg());
// For this case, return the result in EAX.
- rl_result.reg.SetReg(r0);
}
+ done->target = NewLIR0(kPseudoTargetLabel);
}
return rl_result;
@@ -753,8 +770,9 @@
RegLocation X86Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2, bool is_div, bool check_zero) {
// We have to use fixed registers, so flush all the temps.
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
// Load LHS into EAX.
LoadValueDirectFixed(rl_src1, rs_r0);
@@ -770,13 +788,19 @@
GenDivZeroCheck(rs_r1);
}
+ // Check if numerator is 0
+ OpRegImm(kOpCmp, rs_r0, 0);
+ LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
+
// Have to catch 0x80000000/-1 case, or we will get an exception!
OpRegImm(kOpCmp, rs_r1, -1);
- LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LIR* minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
// RHS is -1.
OpRegImm(kOpCmp, rs_r0, 0x80000000);
- LIR * minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LIR* minint_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+
+ branch->target = NewLIR0(kPseudoTargetLabel);
// In 0x80000000/-1 case.
if (!is_div) {
@@ -802,8 +826,115 @@
bool X86Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) {
DCHECK(cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64);
- if (is_long && cu_->instruction_set == kX86) {
- return false;
+ if (is_long && !cu_->target64) {
+ /*
+ * We want to implement the following algorithm
+ * mov eax, low part of arg1
+ * mov edx, high part of arg1
+ * mov ebx, low part of arg2
+ * mov ecx, high part of arg2
+ * mov edi, eax
+ * sub edi, ebx
+ * mov edi, edx
+ * sbb edi, ecx
+ * is_min ? "cmovgel eax, ebx" : "cmovll eax, ebx"
+ * is_min ? "cmovgel edx, ecx" : "cmovll edx, ecx"
+ *
+ * The algorithm above needs 5 registers: a pair for the first operand
+ * (which later will be used as result), a pair for the second operand
+ * and a temp register (e.g. 'edi') for intermediate calculations.
+ * Ideally we have 6 GP caller-save registers in 32-bit mode. They are:
+ * 'eax', 'ebx', 'ecx', 'edx', 'esi' and 'edi'. So there should be
+ * always enough registers to operate on. Practically, there is a pair
+ * of registers 'edi' and 'esi' which holds promoted values and
+ * sometimes should be treated as 'callee save'. If one of the operands
+ * is in the promoted registers then we have enough register to
+ * operate on. Otherwise there is lack of resources and we have to
+ * save 'edi' before calculations and restore after.
+ */
+
+ RegLocation rl_src1 = info->args[0];
+ RegLocation rl_src2 = info->args[2];
+ RegLocation rl_dest = InlineTargetWide(info);
+ int res_vreg, src1_vreg, src2_vreg;
+
+ /*
+ * If the result register is the same as the second element, then we
+ * need to be careful. The reason is that the first copy will
+ * inadvertently clobber the second element with the first one thus
+ * yielding the wrong result. Thus we do a swap in that case.
+ */
+ res_vreg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
+ src2_vreg = mir_graph_->SRegToVReg(rl_src2.s_reg_low);
+ if (res_vreg == src2_vreg) {
+ std::swap(rl_src1, rl_src2);
+ }
+
+ rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+ // Pick the first integer as min/max.
+ OpRegCopyWide(rl_result.reg, rl_src1.reg);
+
+ /*
+ * If the integers are both in the same register, then there is
+ * nothing else to do because they are equal and we have already
+ * moved one into the result.
+ */
+ src1_vreg = mir_graph_->SRegToVReg(rl_src1.s_reg_low);
+ src2_vreg = mir_graph_->SRegToVReg(rl_src2.s_reg_low);
+ if (src1_vreg == src2_vreg) {
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+ }
+
+ // Free registers to make some room for the second operand.
+ // But don't try to free ourselves or promoted registers.
+ if (res_vreg != src1_vreg &&
+ IsTemp(rl_src1.reg.GetLow()) && IsTemp(rl_src1.reg.GetHigh())) {
+ FreeTemp(rl_src1.reg);
+ }
+ rl_src2 = LoadValueWide(rl_src2, kCoreReg);
+
+ // Do we have a free register for intermediate calculations?
+ RegStorage tmp = AllocTemp(false);
+ if (tmp == RegStorage::InvalidReg()) {
+ /*
+ * No, will use 'edi'.
+ *
+ * As mentioned above we have 4 temporary and 2 promotable
+ * caller-save registers. Therefore, we assume that a free
+ * register can be allocated only if 'esi' and 'edi' are
+ * already used as operands. If number of promotable registers
+ * increases from 2 to 4 then our assumption fails and operand
+ * data is corrupted.
+ * Let's DCHECK it.
+ */
+ DCHECK(IsTemp(rl_src2.reg.GetLow()) &&
+ IsTemp(rl_src2.reg.GetHigh()) &&
+ IsTemp(rl_result.reg.GetLow()) &&
+ IsTemp(rl_result.reg.GetHigh()));
+ tmp = rs_rDI;
+ NewLIR1(kX86Push32R, tmp.GetReg());
+ }
+
+ // Now we are ready to do calculations.
+ OpRegReg(kOpMov, tmp, rl_result.reg.GetLow());
+ OpRegReg(kOpSub, tmp, rl_src2.reg.GetLow());
+ OpRegReg(kOpMov, tmp, rl_result.reg.GetHigh());
+ OpRegReg(kOpSbc, tmp, rl_src2.reg.GetHigh());
+
+ // Let's put pop 'edi' here to break a bit the dependency chain.
+ if (tmp == rs_rDI) {
+ NewLIR1(kX86Pop32R, tmp.GetReg());
+ }
+
+ // Conditionally move the other integer into the destination register.
+ ConditionCode cc = is_min ? kCondGe : kCondLt;
+ OpCondRegReg(kOpCmov, cc, rl_result.reg.GetLow(), rl_src2.reg.GetLow());
+ OpCondRegReg(kOpCmov, cc, rl_result.reg.GetHigh(), rl_src2.reg.GetHigh());
+ StoreValueWide(rl_dest, rl_result);
+ return true;
}
// Get the two arguments to the invoke and place them in GP registers.
@@ -1070,6 +1201,83 @@
return true;
}
+void X86Mir2Lir::SwapBits(RegStorage result_reg, int shift, int32_t value) {
+ RegStorage r_temp = AllocTemp();
+ OpRegCopy(r_temp, result_reg);
+ OpRegImm(kOpLsr, result_reg, shift);
+ OpRegImm(kOpAnd, r_temp, value);
+ OpRegImm(kOpAnd, result_reg, value);
+ OpRegImm(kOpLsl, r_temp, shift);
+ OpRegReg(kOpOr, result_reg, r_temp);
+ FreeTemp(r_temp);
+}
+
+void X86Mir2Lir::SwapBits64(RegStorage result_reg, int shift, int64_t value) {
+ RegStorage r_temp = AllocTempWide();
+ OpRegCopy(r_temp, result_reg);
+ OpRegImm(kOpLsr, result_reg, shift);
+ RegStorage r_value = AllocTempWide();
+ LoadConstantWide(r_value, value);
+ OpRegReg(kOpAnd, r_temp, r_value);
+ OpRegReg(kOpAnd, result_reg, r_value);
+ OpRegImm(kOpLsl, r_temp, shift);
+ OpRegReg(kOpOr, result_reg, r_temp);
+ FreeTemp(r_temp);
+ FreeTemp(r_value);
+}
+
+bool X86Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
+ RegLocation rl_src_i = info->args[0];
+ RegLocation rl_i = (size == k64) ? LoadValueWide(rl_src_i, kCoreReg)
+ : LoadValue(rl_src_i, kCoreReg);
+ RegLocation rl_dest = (size == k64) ? InlineTargetWide(info) : InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == k64) {
+ if (cu_->instruction_set == kX86_64) {
+ /* Use one bswap instruction to reverse byte order first and then use 3 rounds of
+ swapping bits to reverse bits in a long number x. Using bswap to save instructions
+ compared to generic luni implementation which has 5 rounds of swapping bits.
+ x = bswap x
+ x = (x & 0x5555555555555555) << 1 | (x >> 1) & 0x5555555555555555;
+ x = (x & 0x3333333333333333) << 2 | (x >> 2) & 0x3333333333333333;
+ x = (x & 0x0F0F0F0F0F0F0F0F) << 4 | (x >> 4) & 0x0F0F0F0F0F0F0F0F;
+ */
+ OpRegReg(kOpRev, rl_result.reg, rl_i.reg);
+ SwapBits64(rl_result.reg, 1, 0x5555555555555555);
+ SwapBits64(rl_result.reg, 2, 0x3333333333333333);
+ SwapBits64(rl_result.reg, 4, 0x0f0f0f0f0f0f0f0f);
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+ }
+ RegStorage r_i_low = rl_i.reg.GetLow();
+ if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+ // First REV shall clobber rl_result.reg.GetLowReg(), save the value in a temp for the second
+ // REV.
+ r_i_low = AllocTemp();
+ OpRegCopy(r_i_low, rl_i.reg);
+ }
+ OpRegReg(kOpRev, rl_result.reg.GetLow(), rl_i.reg.GetHigh());
+ OpRegReg(kOpRev, rl_result.reg.GetHigh(), r_i_low);
+ if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+ FreeTemp(r_i_low);
+ }
+ SwapBits(rl_result.reg.GetLow(), 1, 0x55555555);
+ SwapBits(rl_result.reg.GetLow(), 2, 0x33333333);
+ SwapBits(rl_result.reg.GetLow(), 4, 0x0f0f0f0f);
+ SwapBits(rl_result.reg.GetHigh(), 1, 0x55555555);
+ SwapBits(rl_result.reg.GetHigh(), 2, 0x33333333);
+ SwapBits(rl_result.reg.GetHigh(), 4, 0x0f0f0f0f);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ OpRegReg(kOpRev, rl_result.reg, rl_i.reg);
+ SwapBits(rl_result.reg, 1, 0x55555555);
+ SwapBits(rl_result.reg, 2, 0x33333333);
+ SwapBits(rl_result.reg, 4, 0x0f0f0f0f);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
LIR* X86Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) {
CHECK(base_of_code_ != nullptr);
@@ -1401,8 +1609,8 @@
if (!cu_->target64) {
int32_t val_lo = Low32Bits(val);
int32_t val_hi = High32Bits(val);
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
bool src1_in_reg = rl_src1.location == kLocPhysReg;
int displacement = SRegOffset(rl_src1.s_reg_low);
@@ -1485,8 +1693,8 @@
bool is_square = mir_graph_->SRegToVReg(rl_src1.s_reg_low) ==
mir_graph_->SRegToVReg(rl_src2.s_reg_low);
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 3, &rs_r0, &rs_r1, &rs_r2);
rl_src1 = UpdateLocWideTyped(rl_src1, kCoreReg);
rl_src2 = UpdateLocWideTyped(rl_src2, kCoreReg);
@@ -1509,7 +1717,7 @@
NewLIR2(kX86Imul32RR, rs_r1.GetReg(), rl_src2.reg.GetLowReg());
} else {
int displacement = SRegOffset(rl_src2.s_reg_low);
- LIR *m = NewLIR3(kX86Imul32RM, rs_r1.GetReg(), rs_rX86_SP.GetReg(),
+ LIR* m = NewLIR3(kX86Imul32RM, rs_r1.GetReg(), rs_rX86_SP.GetReg(),
displacement + LOWORD_OFFSET);
AnnotateDalvikRegAccess(m, (displacement + LOWORD_OFFSET) >> 2,
true /* is_load */, true /* is_64bit */);
@@ -1830,7 +2038,8 @@
Clobber(rs_r2q);
LockTemp(rs_r2q);
- RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, rs_r2q, INVALID_SREG, INVALID_SREG};
+ RegLocation rl_result = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1,
+ is_div ? rs_r2q : rs_r0q, INVALID_SREG, INVALID_SREG};
// Use H.S.Warren's Hacker's Delight Chapter 10 and
// T,Grablund, P.L.Montogomery's Division by invariant integers using multiplication.
@@ -1854,24 +2063,35 @@
* 5. Thus, RDX is the quotient
*/
- // Numerator into RAX.
+ // RAX = magic.
+ LoadConstantWide(rs_r0q, magic);
+
+ // Multiply by numerator.
RegStorage numerator_reg;
if (!is_div || (imm > 0 && magic < 0) || (imm < 0 && magic > 0)) {
// We will need the value later.
rl_src = LoadValueWide(rl_src, kCoreReg);
numerator_reg = rl_src.reg;
- OpRegCopyWide(rs_r0q, numerator_reg);
+
+ // RDX:RAX = magic * numerator.
+ NewLIR1(kX86Imul64DaR, numerator_reg.GetReg());
} else {
- // Only need this once. Just put it into RAX.
- LoadValueDirectWideFixed(rl_src, rs_r0q);
+ // Only need this once. Multiply directly from the value.
+ rl_src = UpdateLocWideTyped(rl_src, kCoreReg);
+ if (rl_src.location != kLocPhysReg) {
+ // Okay, we can do this from memory.
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+ int displacement = SRegOffset(rl_src.s_reg_low);
+ // RDX:RAX = magic * numerator.
+ LIR *m = NewLIR2(kX86Imul64DaM, rs_rX86_SP.GetReg(), displacement);
+ AnnotateDalvikRegAccess(m, displacement >> 2,
+ true /* is_load */, true /* is_64bit */);
+ } else {
+ // RDX:RAX = magic * numerator.
+ NewLIR1(kX86Imul64DaR, rl_src.reg.GetReg());
+ }
}
- // RDX = magic.
- LoadConstantWide(rs_r2q, magic);
-
- // RDX:RAX = magic & dividend.
- NewLIR1(kX86Imul64DaR, rs_r2q.GetReg());
-
if (imm > 0 && magic < 0) {
// Add numerator to RDX.
DCHECK(numerator_reg.Valid());
@@ -1919,14 +2139,12 @@
NewLIR3(kX86Imul64RRI, rs_r2q.GetReg(), rs_r2q.GetReg(), short_imm);
}
- // RDX -= RAX.
+ // RAX -= RDX.
OpRegReg(kOpSub, rs_r0q, rs_r2q);
- // Store result.
- OpRegCopyWide(rl_result.reg, rs_r0q);
+ // Result in RAX.
} else {
- // Store result.
- OpRegCopyWide(rl_result.reg, rs_r2q);
+ // Result in RDX.
}
StoreValueWide(rl_dest, rl_result);
FreeTemp(rs_r0q);
@@ -1949,8 +2167,8 @@
}
// We have to use fixed registers, so flush all the temps.
- FlushAllRegs();
- LockCallTemps(); // Prepare for explicit register usage.
+ // Prepare for explicit register usage.
+ ExplicitTempRegisterLock(this, 4, &rs_r0q, &rs_r1q, &rs_r2q, &rs_r6q);
// Load LHS into RAX.
LoadValueDirectWideFixed(rl_src1, rs_r0q);
@@ -1966,7 +2184,7 @@
// Have to catch 0x8000000000000000/-1 case, or we will get an exception!
NewLIR2(kX86Cmp64RI8, rs_r1q.GetReg(), -1);
- LIR *minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
+ LIR* minus_one_branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
// RHS is -1.
LoadConstantWide(rs_r6q, 0x8000000000000000);
@@ -2149,9 +2367,9 @@
if ((size == kSignedByte || size == kUnsignedByte) && !IsByteRegister(rl_src.reg)) {
RegStorage temp = AllocTemp();
OpRegCopy(temp, rl_src.reg);
- StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, temp, size);
+ StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, temp, size, opt_flags);
} else {
- StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, rl_src.reg, size);
+ StoreBaseIndexedDisp(rl_array.reg, rl_index.reg, scale, data_offset, rl_src.reg, size, opt_flags);
}
if (card_mark) {
// Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
@@ -2813,7 +3031,6 @@
LoadValueDirectFixed(rl_rhs, t_reg);
if (is_two_addr) {
// Can we do this directly into memory?
- rl_rhs = LoadValue(rl_rhs, kCoreReg);
rl_result = UpdateLocTyped(rl_dest, kCoreReg);
if (rl_result.location != kLocPhysReg) {
// Okay, we can do this into memory
@@ -2965,7 +3182,53 @@
void X86Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_shift) {
if (!cu_->target64) {
- Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift);
+ // Long shift operations in 32-bit. Use shld or shrd to create a 32-bit register filled from
+ // the other half, shift the other half, if the shift amount is less than 32 we're done,
+ // otherwise move one register to the other and place zero or sign bits in the other.
+ LIR* branch;
+ FlushAllRegs();
+ LockCallTemps();
+ LoadValueDirectFixed(rl_shift, rs_rCX);
+ RegStorage r_tmp = RegStorage::MakeRegPair(rs_rAX, rs_rDX);
+ LoadValueDirectWideFixed(rl_src1, r_tmp);
+ switch (opcode) {
+ case Instruction::SHL_LONG:
+ case Instruction::SHL_LONG_2ADDR:
+ NewLIR3(kX86Shld32RRC, r_tmp.GetHighReg(), r_tmp.GetLowReg(), rs_rCX.GetReg());
+ NewLIR2(kX86Sal32RC, r_tmp.GetLowReg(), rs_rCX.GetReg());
+ NewLIR2(kX86Test8RI, rs_rCX.GetReg(), 32);
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondZ);
+ OpRegCopy(r_tmp.GetHigh(), r_tmp.GetLow());
+ LoadConstant(r_tmp.GetLow(), 0);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ break;
+ case Instruction::SHR_LONG:
+ case Instruction::SHR_LONG_2ADDR:
+ NewLIR3(kX86Shrd32RRC, r_tmp.GetLowReg(), r_tmp.GetHighReg(), rs_rCX.GetReg());
+ NewLIR2(kX86Sar32RC, r_tmp.GetHighReg(), rs_rCX.GetReg());
+ NewLIR2(kX86Test8RI, rs_rCX.GetReg(), 32);
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondZ);
+ OpRegCopy(r_tmp.GetLow(), r_tmp.GetHigh());
+ NewLIR2(kX86Sar32RI, r_tmp.GetHighReg(), 31);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ break;
+ case Instruction::USHR_LONG:
+ case Instruction::USHR_LONG_2ADDR:
+ NewLIR3(kX86Shrd32RRC, r_tmp.GetLowReg(), r_tmp.GetHighReg(),
+ rs_rCX.GetReg());
+ NewLIR2(kX86Shr32RC, r_tmp.GetHighReg(), rs_rCX.GetReg());
+ NewLIR2(kX86Test8RI, rs_rCX.GetReg(), 32);
+ branch = NewLIR2(kX86Jcc8, 0, kX86CondZ);
+ OpRegCopy(r_tmp.GetLow(), r_tmp.GetHigh());
+ LoadConstant(r_tmp.GetHigh(), 0);
+ branch->target = NewLIR0(kPseudoTargetLabel);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected case: " << opcode;
+ return;
+ }
+ RegLocation rl_result = LocCReturnWide();
+ StoreValueWide(rl_dest, rl_result);
return;
}
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index f7cb820..9aeaf60 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -14,16 +14,21 @@
* limitations under the License.
*/
-#include <string>
+#include <cstdarg>
#include <inttypes.h>
+#include <string>
+#include "backend_x86.h"
#include "codegen_x86.h"
#include "dex/compiler_internals.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "dex/reg_storage_eq.h"
#include "mirror/array.h"
+#include "mirror/art_method.h"
#include "mirror/string.h"
+#include "oat.h"
#include "x86_lir.h"
+#include "utils/dwarf_cfi.h"
namespace art {
@@ -362,6 +367,7 @@
int64_t value = static_cast<int64_t>(static_cast<int64_t>(operand) << 32 |
static_cast<uint32_t>(lir->operands[operand_number+1]));
buf +=StringPrintf("%" PRId64, value);
+ break;
}
case 'p': {
EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(operand));
@@ -451,7 +457,7 @@
}
RegStorage X86Mir2Lir::Get128BitRegister(RegStorage reg) {
- return GetRegInfo(reg)->FindMatchingView(RegisterInfo::k128SoloStorageMask)->GetReg();
+ return GetRegInfo(reg)->Master()->GetReg();
}
bool X86Mir2Lir::IsByteRegister(RegStorage reg) {
@@ -594,6 +600,9 @@
mem_barrier = NewLIR0(kX86Mfence);
ret = true;
}
+ } else if (barrier_kind == kNTStoreStore) {
+ mem_barrier = NewLIR0(kX86Sfence);
+ ret = true;
}
// Now ensure that a scheduling barrier is in place.
@@ -612,13 +621,15 @@
void X86Mir2Lir::CompilerInitializeRegAlloc() {
if (cu_->target64) {
- reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs_64, core_regs_64q, sp_regs_64,
- dp_regs_64, reserved_regs_64, reserved_regs_64q,
- core_temps_64, core_temps_64q, sp_temps_64, dp_temps_64);
+ reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs_64, core_regs_64q, sp_regs_64,
+ dp_regs_64, reserved_regs_64, reserved_regs_64q,
+ core_temps_64, core_temps_64q,
+ sp_temps_64, dp_temps_64));
} else {
- reg_pool_ = new (arena_) RegisterPool(this, arena_, core_regs_32, empty_pool, sp_regs_32,
- dp_regs_32, reserved_regs_32, empty_pool,
- core_temps_32, empty_pool, sp_temps_32, dp_temps_32);
+ reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs_32, empty_pool, sp_regs_32,
+ dp_regs_32, reserved_regs_32, empty_pool,
+ core_temps_32, empty_pool,
+ sp_temps_32, dp_temps_32));
}
// Target-specific adjustments.
@@ -627,7 +638,7 @@
const ArrayRef<const RegStorage> *xp_regs = cu_->target64 ? &xp_regs_64 : &xp_regs_32;
for (RegStorage reg : *xp_regs) {
RegisterInfo* info = new (arena_) RegisterInfo(reg, GetRegMaskCommon(reg));
- reginfo_map_.Put(reg.GetReg(), info);
+ reginfo_map_[reg.GetReg()] = info;
}
const ArrayRef<const RegStorage> *xp_temps = cu_->target64 ? &xp_temps_64 : &xp_temps_32;
for (RegStorage reg : *xp_temps) {
@@ -637,8 +648,7 @@
// Alias single precision xmm to double xmms.
// TODO: as needed, add larger vector sizes - alias all to the largest.
- GrowableArray<RegisterInfo*>::Iterator it(®_pool_->sp_regs_);
- for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) {
+ for (RegisterInfo* info : reg_pool_->sp_regs_) {
int sp_reg_num = info->GetReg().GetRegNum();
RegStorage xp_reg = RegStorage::Solo128(sp_reg_num);
RegisterInfo* xp_reg_info = GetRegInfo(xp_reg);
@@ -658,8 +668,7 @@
if (cu_->target64) {
// Alias 32bit W registers to corresponding 64bit X registers.
- GrowableArray<RegisterInfo*>::Iterator w_it(®_pool_->core_regs_);
- for (RegisterInfo* info = w_it.Next(); info != nullptr; info = w_it.Next()) {
+ for (RegisterInfo* info : reg_pool_->core_regs_) {
int x_reg_num = info->GetReg().GetRegNum();
RegStorage x_reg = RegStorage::Solo64(x_reg_num);
RegisterInfo* x_reg_info = GetRegInfo(x_reg);
@@ -683,8 +692,11 @@
return 128;
}
-int X86Mir2Lir::NumReservableVectorRegisters(bool fp_used) {
- return fp_used ? 5 : 7;
+int X86Mir2Lir::NumReservableVectorRegisters(bool long_or_fp) {
+ int num_vector_temps = cu_->target64 ? xp_temps_64.size() : xp_temps_32.size();
+
+ // Leave a few temps for use by backend as scratch.
+ return long_or_fp ? num_vector_temps - 2 : num_vector_temps - 1;
}
void X86Mir2Lir::SpillCoreRegs() {
@@ -758,10 +770,7 @@
RegisterClass X86Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) {
// X86_64 can handle any size.
if (cu_->target64) {
- if (size == kReference) {
- return kRefReg;
- }
- return kCoreReg;
+ return RegClassBySize(size);
}
if (UNLIKELY(is_volatile)) {
@@ -777,11 +786,14 @@
X86Mir2Lir::X86Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
: Mir2Lir(cu, mir_graph, arena),
base_of_code_(nullptr), store_method_addr_(false), store_method_addr_used_(false),
- method_address_insns_(arena, 100, kGrowableArrayMisc),
- class_type_address_insns_(arena, 100, kGrowableArrayMisc),
- call_method_insns_(arena, 100, kGrowableArrayMisc),
+ method_address_insns_(arena->Adapter()),
+ class_type_address_insns_(arena->Adapter()),
+ call_method_insns_(arena->Adapter()),
stack_decrement_(nullptr), stack_increment_(nullptr),
const_vectors_(nullptr) {
+ method_address_insns_.reserve(100);
+ class_type_address_insns_.reserve(100);
+ call_method_insns_.reserve(100);
store_method_addr_used_ = false;
if (kIsDebugBuild) {
for (int i = 0; i < kX86Last; i++) {
@@ -861,9 +873,6 @@
rX86_RET1 = rDX;
rX86_INVOKE_TGT = rAX;
rX86_COUNT = rCX;
-
- // Initialize the number of reserved vector registers
- num_reserved_vector_regs_ = -1;
}
Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
@@ -972,56 +981,85 @@
static_cast<int>(target_method_id_ptr), target_method_idx,
WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
AppendLIR(move);
- method_address_insns_.Insert(move);
+ method_address_insns_.push_back(move);
}
-void X86Mir2Lir::LoadClassType(uint32_t type_idx, SpecialTargetRegister symbolic_reg) {
+void X86Mir2Lir::LoadClassType(const DexFile& dex_file, uint32_t type_idx,
+ SpecialTargetRegister symbolic_reg) {
/*
* For x86, just generate a 32 bit move immediate instruction, that will be filled
* in at 'link time'. For now, put a unique value based on target to ensure that
* code deduplication works.
*/
- const DexFile::TypeId& id = cu_->dex_file->GetTypeId(type_idx);
+ const DexFile::TypeId& id = dex_file.GetTypeId(type_idx);
uintptr_t ptr = reinterpret_cast<uintptr_t>(&id);
// Generate the move instruction with the unique pointer and save index and type.
LIR *move = RawLIR(current_dalvik_offset_, kX86Mov32RI,
TargetReg(symbolic_reg, kNotWide).GetReg(),
- static_cast<int>(ptr), type_idx);
+ static_cast<int>(ptr), type_idx,
+ WrapPointer(const_cast<DexFile*>(&dex_file)));
AppendLIR(move);
- class_type_address_insns_.Insert(move);
+ class_type_address_insns_.push_back(move);
}
-LIR *X86Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
+LIR* X86Mir2Lir::CallWithLinkerFixup(const MethodReference& target_method, InvokeType type) {
/*
* For x86, just generate a 32 bit call relative instruction, that will be filled
- * in at 'link time'. For now, put a unique value based on target to ensure that
- * code deduplication works.
+ * in at 'link time'.
*/
int target_method_idx = target_method.dex_method_index;
const DexFile* target_dex_file = target_method.dex_file;
- const DexFile::MethodId& target_method_id = target_dex_file->GetMethodId(target_method_idx);
- uintptr_t target_method_id_ptr = reinterpret_cast<uintptr_t>(&target_method_id);
// Generate the call instruction with the unique pointer and save index, dex_file, and type.
- LIR *call = RawLIR(current_dalvik_offset_, kX86CallI, static_cast<int>(target_method_id_ptr),
+ // NOTE: Method deduplication takes linker patches into account, so we can just pass 0
+ // as a placeholder for the offset.
+ LIR* call = RawLIR(current_dalvik_offset_, kX86CallI, 0,
target_method_idx, WrapPointer(const_cast<DexFile*>(target_dex_file)), type);
AppendLIR(call);
- call_method_insns_.Insert(call);
+ call_method_insns_.push_back(call);
return call;
}
-/*
- * @brief Enter a 32 bit quantity into a buffer
- * @param buf buffer.
- * @param data Data value.
- */
+static LIR* GenInvokeNoInlineCall(Mir2Lir* mir_to_lir, InvokeType type) {
+ QuickEntrypointEnum trampoline;
+ switch (type) {
+ case kInterface:
+ trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
+ break;
+ case kDirect:
+ trampoline = kQuickInvokeDirectTrampolineWithAccessCheck;
+ break;
+ case kStatic:
+ trampoline = kQuickInvokeStaticTrampolineWithAccessCheck;
+ break;
+ case kSuper:
+ trampoline = kQuickInvokeSuperTrampolineWithAccessCheck;
+ break;
+ case kVirtual:
+ trampoline = kQuickInvokeVirtualTrampolineWithAccessCheck;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke type";
+ trampoline = kQuickInvokeInterfaceTrampolineWithAccessCheck;
+ }
+ return mir_to_lir->InvokeTrampoline(kOpBlx, RegStorage::InvalidReg(), trampoline);
+}
-static void PushWord(std::vector<uint8_t>&buf, int32_t data) {
- buf.push_back(data & 0xff);
- buf.push_back((data >> 8) & 0xff);
- buf.push_back((data >> 16) & 0xff);
- buf.push_back((data >> 24) & 0xff);
+LIR* X86Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) {
+ LIR* call_insn;
+ if (method_info.FastPath()) {
+ if (method_info.DirectCode() == static_cast<uintptr_t>(-1)) {
+ // We can have the linker fixup a call relative.
+ call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType());
+ } else {
+ call_insn = OpMem(kOpBlx, TargetReg(kArg0, kRef),
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+ }
+ } else {
+ call_insn = GenInvokeNoInlineCall(this, method_info.GetSharpType());
+ }
+ return call_insn;
}
void X86Mir2Lir::InstallLiteralPools() {
@@ -1030,30 +1068,28 @@
DCHECK(method_literal_list_ == nullptr);
DCHECK(class_literal_list_ == nullptr);
- // Align to 16 byte boundary. We have implicit knowledge that the start of the method is
- // on a 4 byte boundary. How can I check this if it changes (other than aligned loads
- // will fail at runtime)?
+
if (const_vectors_ != nullptr) {
- int align_size = (16-4) - (code_buffer_.size() & 0xF);
- if (align_size < 0) {
- align_size += 16;
+ // Vector literals must be 16-byte aligned. The header that is placed
+ // in the code section causes misalignment so we take it into account.
+ // Otherwise, we are sure that for x86 method is aligned to 16.
+ DCHECK_EQ(GetInstructionSetAlignment(cu_->instruction_set), 16u);
+ uint32_t bytes_to_fill = (0x10 - ((code_buffer_.size() + sizeof(OatQuickMethodHeader)) & 0xF)) & 0xF;
+ while (bytes_to_fill > 0) {
+ code_buffer_.push_back(0);
+ bytes_to_fill--;
}
- while (align_size > 0) {
- code_buffer_.push_back(0);
- align_size--;
- }
for (LIR *p = const_vectors_; p != nullptr; p = p->next) {
- PushWord(code_buffer_, p->operands[0]);
- PushWord(code_buffer_, p->operands[1]);
- PushWord(code_buffer_, p->operands[2]);
- PushWord(code_buffer_, p->operands[3]);
+ PushWord(&code_buffer_, p->operands[0]);
+ PushWord(&code_buffer_, p->operands[1]);
+ PushWord(&code_buffer_, p->operands[2]);
+ PushWord(&code_buffer_, p->operands[3]);
}
}
// Handle the fixups for methods.
- for (uint32_t i = 0; i < method_address_insns_.Size(); i++) {
- LIR* p = method_address_insns_.Get(i);
+ for (LIR* p : method_address_insns_) {
DCHECK_EQ(p->opcode, kX86Mov32RI);
uint32_t target_method_idx = p->operands[2];
const DexFile* target_dex_file =
@@ -1061,28 +1097,27 @@
// The offset to patch is the last 4 bytes of the instruction.
int patch_offset = p->offset + p->flags.size - 4;
- cu_->compiler_driver->AddMethodPatch(cu_->dex_file, cu_->class_def_idx,
- cu_->method_idx, cu_->invoke_type,
- target_method_idx, target_dex_file,
- static_cast<InvokeType>(p->operands[4]),
- patch_offset);
+ patches_.push_back(LinkerPatch::MethodPatch(patch_offset,
+ target_dex_file, target_method_idx));
}
// Handle the fixups for class types.
- for (uint32_t i = 0; i < class_type_address_insns_.Size(); i++) {
- LIR* p = class_type_address_insns_.Get(i);
+ for (LIR* p : class_type_address_insns_) {
DCHECK_EQ(p->opcode, kX86Mov32RI);
- uint32_t target_method_idx = p->operands[2];
+
+ const DexFile* class_dex_file =
+ reinterpret_cast<const DexFile*>(UnwrapPointer(p->operands[3]));
+ uint32_t target_type_idx = p->operands[2];
// The offset to patch is the last 4 bytes of the instruction.
int patch_offset = p->offset + p->flags.size - 4;
- cu_->compiler_driver->AddClassPatch(cu_->dex_file, cu_->class_def_idx,
- cu_->method_idx, target_method_idx, patch_offset);
+ patches_.push_back(LinkerPatch::TypePatch(patch_offset,
+ class_dex_file, target_type_idx));
}
// And now the PC-relative calls to methods.
- for (uint32_t i = 0; i < call_method_insns_.Size(); i++) {
- LIR* p = call_method_insns_.Get(i);
+ patches_.reserve(call_method_insns_.size());
+ for (LIR* p : call_method_insns_) {
DCHECK_EQ(p->opcode, kX86CallI);
uint32_t target_method_idx = p->operands[1];
const DexFile* target_dex_file =
@@ -1090,11 +1125,8 @@
// The offset to patch is the last 4 bytes of the instruction.
int patch_offset = p->offset + p->flags.size - 4;
- cu_->compiler_driver->AddRelativeCodePatch(cu_->dex_file, cu_->class_def_idx,
- cu_->method_idx, cu_->invoke_type,
- target_method_idx, target_dex_file,
- static_cast<InvokeType>(p->operands[3]),
- patch_offset, -4 /* offset */);
+ patches_.push_back(LinkerPatch::RelativeCodePatch(patch_offset,
+ target_dex_file, target_method_idx));
}
// And do the normal processing.
@@ -1240,7 +1272,7 @@
RegLocation rl_obj = info->args[0];
RegLocation rl_char = info->args[1];
RegLocation rl_start; // Note: only present in III flavor or IndexOf.
- // RBX is callee-save register in 64-bit mode.
+ // RBX is promotable in 64-bit mode.
RegStorage rs_tmp = cu_->target64 ? rs_r11 : rs_rBX;
int start_value = -1;
@@ -1260,23 +1292,7 @@
// EBX or R11: temporary during execution (depending on mode).
// REP SCASW: search instruction.
- FlushReg(rs_rAX);
- Clobber(rs_rAX);
- LockTemp(rs_rAX);
- FlushReg(rs_rCX);
- Clobber(rs_rCX);
- LockTemp(rs_rCX);
- FlushReg(rs_rDX);
- Clobber(rs_rDX);
- LockTemp(rs_rDX);
- FlushReg(rs_tmp);
- Clobber(rs_tmp);
- LockTemp(rs_tmp);
- if (cu_->target64) {
- FlushReg(rs_rDI);
- Clobber(rs_rDI);
- LockTemp(rs_rDI);
- }
+ FlushAllRegs();
RegLocation rl_return = GetReturn(kCoreReg);
RegLocation rl_dest = InlineTarget(info);
@@ -1318,7 +1334,7 @@
MarkPossibleNullPointerException(0);
if (!cu_->target64) {
- // EDI is callee-save register in 32-bit mode.
+ // EDI is promotable in 32-bit mode.
NewLIR1(kX86Push32R, rs_rDI.GetReg());
}
@@ -1426,142 +1442,9 @@
}
StoreValue(rl_dest, rl_return);
-
- FreeTemp(rs_rAX);
- FreeTemp(rs_rCX);
- FreeTemp(rs_rDX);
- FreeTemp(rs_tmp);
- if (cu_->target64) {
- FreeTemp(rs_rDI);
- }
-
return true;
}
-/*
- * @brief Enter an 'advance LOC' into the FDE buffer
- * @param buf FDE buffer.
- * @param increment Amount by which to increase the current location.
- */
-static void AdvanceLoc(std::vector<uint8_t>&buf, uint32_t increment) {
- if (increment < 64) {
- // Encoding in opcode.
- buf.push_back(0x1 << 6 | increment);
- } else if (increment < 256) {
- // Single byte delta.
- buf.push_back(0x02);
- buf.push_back(increment);
- } else if (increment < 256 * 256) {
- // Two byte delta.
- buf.push_back(0x03);
- buf.push_back(increment & 0xff);
- buf.push_back((increment >> 8) & 0xff);
- } else {
- // Four byte delta.
- buf.push_back(0x04);
- PushWord(buf, increment);
- }
-}
-
-
-std::vector<uint8_t>* X86CFIInitialization(bool is_x86_64) {
- return X86Mir2Lir::ReturnCommonCallFrameInformation(is_x86_64);
-}
-
-static void EncodeUnsignedLeb128(std::vector<uint8_t>& buf, uint32_t value) {
- uint8_t buffer[12];
- uint8_t *ptr = EncodeUnsignedLeb128(buffer, value);
- for (uint8_t *p = buffer; p < ptr; p++) {
- buf.push_back(*p);
- }
-}
-
-static void EncodeSignedLeb128(std::vector<uint8_t>& buf, int32_t value) {
- uint8_t buffer[12];
- uint8_t *ptr = EncodeSignedLeb128(buffer, value);
- for (uint8_t *p = buffer; p < ptr; p++) {
- buf.push_back(*p);
- }
-}
-
-std::vector<uint8_t>* X86Mir2Lir::ReturnCommonCallFrameInformation(bool is_x86_64) {
- std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
-
- // Length (will be filled in later in this routine).
- PushWord(*cfi_info, 0);
-
- // CIE id: always 0.
- PushWord(*cfi_info, 0);
-
- // Version: always 1.
- cfi_info->push_back(0x01);
-
- // Augmentation: 'zR\0'
- cfi_info->push_back(0x7a);
- cfi_info->push_back(0x52);
- cfi_info->push_back(0x0);
-
- // Code alignment: 1.
- EncodeUnsignedLeb128(*cfi_info, 1);
-
- // Data alignment.
- if (is_x86_64) {
- EncodeSignedLeb128(*cfi_info, -8);
- } else {
- EncodeSignedLeb128(*cfi_info, -4);
- }
-
- // Return address register.
- if (is_x86_64) {
- // R16(RIP)
- cfi_info->push_back(0x10);
- } else {
- // R8(EIP)
- cfi_info->push_back(0x08);
- }
-
- // Augmentation length: 1.
- cfi_info->push_back(1);
-
- // Augmentation data: 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
- cfi_info->push_back(0x03);
-
- // Initial instructions.
- if (is_x86_64) {
- // DW_CFA_def_cfa R7(RSP) 8.
- cfi_info->push_back(0x0c);
- cfi_info->push_back(0x07);
- cfi_info->push_back(0x08);
-
- // DW_CFA_offset R16(RIP) 1 (* -8).
- cfi_info->push_back(0x90);
- cfi_info->push_back(0x01);
- } else {
- // DW_CFA_def_cfa R4(ESP) 4.
- cfi_info->push_back(0x0c);
- cfi_info->push_back(0x04);
- cfi_info->push_back(0x04);
-
- // DW_CFA_offset R8(EIP) 1 (* -4).
- cfi_info->push_back(0x88);
- cfi_info->push_back(0x01);
- }
-
- // Padding to a multiple of 4
- while ((cfi_info->size() & 3) != 0) {
- // DW_CFA_nop is encoded as 0.
- cfi_info->push_back(0);
- }
-
- // Set the length of the CIE inside the generated bytes.
- uint32_t length = cfi_info->size() - 4;
- (*cfi_info)[0] = length;
- (*cfi_info)[1] = length >> 8;
- (*cfi_info)[2] = length >> 16;
- (*cfi_info)[3] = length >> 24;
- return cfi_info;
-}
-
static bool ARTRegIDToDWARFRegID(bool is_x86_64, int art_reg_id, int* dwarf_reg_id) {
if (is_x86_64) {
switch (art_reg_id) {
@@ -1584,36 +1467,23 @@
}
}
-std::vector<uint8_t>* X86Mir2Lir::ReturnCallFrameInformation() {
- std::vector<uint8_t>*cfi_info = new std::vector<uint8_t>;
+std::vector<uint8_t>* X86Mir2Lir::ReturnFrameDescriptionEntry() {
+ std::vector<uint8_t>* cfi_info = new std::vector<uint8_t>;
// Generate the FDE for the method.
DCHECK_NE(data_offset_, 0U);
- // Length (will be filled in later in this routine).
- PushWord(*cfi_info, 0);
-
- // 'CIE_pointer' (filled in by linker).
- PushWord(*cfi_info, 0);
-
- // 'initial_location' (filled in by linker).
- PushWord(*cfi_info, 0);
-
- // 'address_range' (number of bytes in the method).
- PushWord(*cfi_info, data_offset_);
-
- // Augmentation length: 0
- cfi_info->push_back(0);
+ WriteFDEHeader(cfi_info, cu_->target64);
+ WriteFDEAddressRange(cfi_info, data_offset_, cu_->target64);
// The instructions in the FDE.
if (stack_decrement_ != nullptr) {
// Advance LOC to just past the stack decrement.
uint32_t pc = NEXT_LIR(stack_decrement_)->offset;
- AdvanceLoc(*cfi_info, pc);
+ DW_CFA_advance_loc(cfi_info, pc);
// Now update the offset to the call frame: DW_CFA_def_cfa_offset frame_size.
- cfi_info->push_back(0x0e);
- EncodeUnsignedLeb128(*cfi_info, frame_size_);
+ DW_CFA_def_cfa_offset(cfi_info, frame_size_);
// Handle register spills
const uint32_t kSpillInstLen = (cu_->target64) ? 5 : 4;
@@ -1625,14 +1495,12 @@
pc += kSpillInstLen;
// Advance LOC to pass this instruction
- AdvanceLoc(*cfi_info, kSpillInstLen);
+ DW_CFA_advance_loc(cfi_info, kSpillInstLen);
int dwarf_reg_id;
if (ARTRegIDToDWARFRegID(cu_->target64, reg, &dwarf_reg_id)) {
- // DW_CFA_offset_extended_sf reg_no offset
- cfi_info->push_back(0x11);
- EncodeUnsignedLeb128(*cfi_info, dwarf_reg_id);
- EncodeSignedLeb128(*cfi_info, offset / kDataAlignmentFactor);
+ // DW_CFA_offset_extended_sf reg offset
+ DW_CFA_offset_extended_sf(cfi_info, dwarf_reg_id, offset / kDataAlignmentFactor);
}
offset += GetInstructionSetPointerSize(cu_->instruction_set);
@@ -1642,16 +1510,15 @@
// We continue with that stack until the epilogue.
if (stack_increment_ != nullptr) {
uint32_t new_pc = NEXT_LIR(stack_increment_)->offset;
- AdvanceLoc(*cfi_info, new_pc - pc);
+ DW_CFA_advance_loc(cfi_info, new_pc - pc);
// We probably have code snippets after the epilogue, so save the
// current state: DW_CFA_remember_state.
- cfi_info->push_back(0x0a);
+ DW_CFA_remember_state(cfi_info);
// We have now popped the stack: DW_CFA_def_cfa_offset 4/8.
// There is only the return PC on the stack now.
- cfi_info->push_back(0x0e);
- EncodeUnsignedLeb128(*cfi_info, GetInstructionSetPointerSize(cu_->instruction_set));
+ DW_CFA_def_cfa_offset(cfi_info, GetInstructionSetPointerSize(cu_->instruction_set));
// Everything after that is the same as before the epilogue.
// Stack bump was followed by RET instruction.
@@ -1659,25 +1526,16 @@
if (post_ret_insn != nullptr) {
pc = new_pc;
new_pc = post_ret_insn->offset;
- AdvanceLoc(*cfi_info, new_pc - pc);
+ DW_CFA_advance_loc(cfi_info, new_pc - pc);
// Restore the state: DW_CFA_restore_state.
- cfi_info->push_back(0x0b);
+ DW_CFA_restore_state(cfi_info);
}
}
}
- // Padding to a multiple of 4
- while ((cfi_info->size() & 3) != 0) {
- // DW_CFA_nop is encoded as 0.
- cfi_info->push_back(0);
- }
+ PadCFI(cfi_info);
+ WriteCFILength(cfi_info, cu_->target64);
- // Set the length of the FDE inside the generated bytes.
- uint32_t length = cfi_info->size() - 4;
- (*cfi_info)[0] = length;
- (*cfi_info)[1] = length >> 8;
- (*cfi_info)[2] = length >> 16;
- (*cfi_info)[3] = length >> 24;
return cfi_info;
}
@@ -1687,7 +1545,7 @@
ReserveVectorRegisters(mir);
break;
case kMirOpReturnVectorRegisters:
- ReturnVectorRegisters();
+ ReturnVectorRegisters(mir);
break;
case kMirOpConstVector:
GenConst128(bb, mir);
@@ -1731,17 +1589,22 @@
case kMirOpPackedSet:
GenSetVector(bb, mir);
break;
+ case kMirOpMemBarrier:
+ GenMemBarrier(static_cast<MemBarrierKind>(mir->dalvikInsn.vA));
+ break;
+ case kMirOpPackedArrayGet:
+ GenPackedArrayGet(bb, mir);
+ break;
+ case kMirOpPackedArrayPut:
+ GenPackedArrayPut(bb, mir);
+ break;
default:
break;
}
}
void X86Mir2Lir::ReserveVectorRegisters(MIR* mir) {
- // We should not try to reserve twice without returning the registers
- DCHECK_NE(num_reserved_vector_regs_, -1);
-
- int num_vector_reg = mir->dalvikInsn.vA;
- for (int i = 0; i < num_vector_reg; i++) {
+ for (uint32_t i = mir->dalvikInsn.vA; i <= mir->dalvikInsn.vB; i++) {
RegStorage xp_reg = RegStorage::Solo128(i);
RegisterInfo *xp_reg_info = GetRegInfo(xp_reg);
Clobber(xp_reg);
@@ -1749,20 +1612,17 @@
for (RegisterInfo *info = xp_reg_info->GetAliasChain();
info != nullptr;
info = info->GetAliasChain()) {
- if (info->GetReg().IsSingle()) {
- reg_pool_->sp_regs_.Delete(info);
- } else {
- reg_pool_->dp_regs_.Delete(info);
- }
+ ArenaVector<RegisterInfo*>* regs =
+ info->GetReg().IsSingle() ? ®_pool_->sp_regs_ : ®_pool_->dp_regs_;
+ auto it = std::find(regs->begin(), regs->end(), info);
+ DCHECK(it != regs->end());
+ regs->erase(it);
}
}
-
- num_reserved_vector_regs_ = num_vector_reg;
}
-void X86Mir2Lir::ReturnVectorRegisters() {
- // Return all the reserved registers
- for (int i = 0; i < num_reserved_vector_regs_; i++) {
+void X86Mir2Lir::ReturnVectorRegisters(MIR* mir) {
+ for (uint32_t i = mir->dalvikInsn.vA; i <= mir->dalvikInsn.vB; i++) {
RegStorage xp_reg = RegStorage::Solo128(i);
RegisterInfo *xp_reg_info = GetRegInfo(xp_reg);
@@ -1770,23 +1630,18 @@
info != nullptr;
info = info->GetAliasChain()) {
if (info->GetReg().IsSingle()) {
- reg_pool_->sp_regs_.Insert(info);
+ reg_pool_->sp_regs_.push_back(info);
} else {
- reg_pool_->dp_regs_.Insert(info);
+ reg_pool_->dp_regs_.push_back(info);
}
}
}
-
- // We don't have anymore reserved vector registers
- num_reserved_vector_regs_ = -1;
}
void X86Mir2Lir::GenConst128(BasicBlock* bb, MIR* mir) {
- store_method_addr_used_ = true;
- int type_size = mir->dalvikInsn.vB;
- // We support 128 bit vectors.
- DCHECK_EQ(type_size & 0xFFFF, 128);
RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest);
+
uint32_t *args = mir->dalvikInsn.arg;
int reg = rs_dest.GetReg();
// Check for all 0 case.
@@ -1796,14 +1651,24 @@
}
// Append the mov const vector to reg opcode.
- AppendOpcodeWithConst(kX86MovupsRM, reg, mir);
+ AppendOpcodeWithConst(kX86MovdqaRM, reg, mir);
}
void X86Mir2Lir::AppendOpcodeWithConst(X86OpCode opcode, int reg, MIR* mir) {
- // Okay, load it from the constant vector area.
- LIR *data_target = ScanVectorLiteral(mir);
+ // The literal pool needs position independent logic.
+ store_method_addr_used_ = true;
+
+ // To deal with correct memory ordering, reverse order of constants.
+ int32_t constants[4];
+ constants[3] = mir->dalvikInsn.arg[0];
+ constants[2] = mir->dalvikInsn.arg[1];
+ constants[1] = mir->dalvikInsn.arg[2];
+ constants[0] = mir->dalvikInsn.arg[3];
+
+ // Search if there is already a constant in pool with this value.
+ LIR *data_target = ScanVectorLiteral(constants);
if (data_target == nullptr) {
- data_target = AddVectorLiteral(mir);
+ data_target = AddVectorLiteral(constants);
}
// Address the start of the method.
@@ -1819,7 +1684,7 @@
// 4 byte offset. We will fix this up in the assembler later to have the right
// value.
ScopedMemRefType mem_ref_type(this, ResourceMask::kLiteral);
- LIR *load = NewLIR2(opcode, reg, rl_method.reg.GetReg());
+ LIR *load = NewLIR3(opcode, reg, rl_method.reg.GetReg(), 256 /* bogus */);
load->flags.fixup = kFixupLoad;
load->target = data_target;
}
@@ -1828,16 +1693,12 @@
// We only support 128 bit registers.
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest);
RegStorage rs_src = RegStorage::Solo128(mir->dalvikInsn.vB);
- NewLIR2(kX86Mova128RR, rs_dest.GetReg(), rs_src.GetReg());
+ NewLIR2(kX86MovdqaRR, rs_dest.GetReg(), rs_src.GetReg());
}
-void X86Mir2Lir::GenMultiplyVectorSignedByte(BasicBlock *bb, MIR *mir) {
- const int BYTE_SIZE = 8;
- RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
- RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
- RegStorage rs_src1_high_tmp = Get128BitRegister(AllocTempWide());
-
+void X86Mir2Lir::GenMultiplyVectorSignedByte(RegStorage rs_dest_src1, RegStorage rs_src2) {
/*
* Emulate the behavior of a kSignedByte by separating out the 16 values in the two XMM
* and multiplying 8 at a time before recombining back into one XMM register.
@@ -1855,29 +1716,100 @@
*/
// Copy xmm1.
- NewLIR2(kX86Mova128RR, rs_src1_high_tmp.GetReg(), rs_dest_src1.GetReg());
+ RegStorage rs_src1_high_tmp = Get128BitRegister(AllocTempDouble());
+ RegStorage rs_dest_high_tmp = Get128BitRegister(AllocTempDouble());
+ NewLIR2(kX86MovdqaRR, rs_src1_high_tmp.GetReg(), rs_src2.GetReg());
+ NewLIR2(kX86MovdqaRR, rs_dest_high_tmp.GetReg(), rs_dest_src1.GetReg());
// Multiply low bits.
+ // x7 *= x3
NewLIR2(kX86PmullwRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
// xmm1 now has low bits.
AndMaskVectorRegister(rs_dest_src1, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF);
// Prepare high bits for multiplication.
- NewLIR2(kX86PsrlwRI, rs_src1_high_tmp.GetReg(), BYTE_SIZE);
- AndMaskVectorRegister(rs_src2, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00);
+ NewLIR2(kX86PsrlwRI, rs_src1_high_tmp.GetReg(), 0x8);
+ AndMaskVectorRegister(rs_dest_high_tmp, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00);
// Multiply high bits and xmm2 now has high bits.
- NewLIR2(kX86PmullwRR, rs_src2.GetReg(), rs_src1_high_tmp.GetReg());
+ NewLIR2(kX86PmullwRR, rs_src1_high_tmp.GetReg(), rs_dest_high_tmp.GetReg());
// Combine back into dest XMM register.
- NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
+ NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_src1_high_tmp.GetReg());
+}
+
+void X86Mir2Lir::GenMultiplyVectorLong(RegStorage rs_dest_src1, RegStorage rs_src2) {
+ /*
+ * We need to emulate the packed long multiply.
+ * For kMirOpPackedMultiply xmm1, xmm0:
+ * - xmm1 is src/dest
+ * - xmm0 is src
+ * - Get xmm2 and xmm3 as temp
+ * - Idea is to multiply the lower 32 of each operand with the higher 32 of the other.
+ * - Then add the two results.
+ * - Move it to the upper 32 of the destination
+ * - Then multiply the lower 32-bits of the operands and add the result to the destination.
+ *
+ * (op dest src )
+ * movdqa %xmm2, %xmm1
+ * movdqa %xmm3, %xmm0
+ * psrlq %xmm3, $0x20
+ * pmuludq %xmm3, %xmm2
+ * psrlq %xmm1, $0x20
+ * pmuludq %xmm1, %xmm0
+ * paddq %xmm1, %xmm3
+ * psllq %xmm1, $0x20
+ * pmuludq %xmm2, %xmm0
+ * paddq %xmm1, %xmm2
+ *
+ * When both the operands are the same, then we need to calculate the lower-32 * higher-32
+ * calculation only once. Thus we don't need the xmm3 temp above. That sequence becomes:
+ *
+ * (op dest src )
+ * movdqa %xmm2, %xmm1
+ * psrlq %xmm1, $0x20
+ * pmuludq %xmm1, %xmm0
+ * paddq %xmm1, %xmm1
+ * psllq %xmm1, $0x20
+ * pmuludq %xmm2, %xmm0
+ * paddq %xmm1, %xmm2
+ *
+ */
+
+ bool both_operands_same = (rs_dest_src1.GetReg() == rs_src2.GetReg());
+
+ RegStorage rs_tmp_vector_1;
+ RegStorage rs_tmp_vector_2;
+ rs_tmp_vector_1 = Get128BitRegister(AllocTempDouble());
+ NewLIR2(kX86MovdqaRR, rs_tmp_vector_1.GetReg(), rs_dest_src1.GetReg());
+
+ if (both_operands_same == false) {
+ rs_tmp_vector_2 = Get128BitRegister(AllocTempDouble());
+ NewLIR2(kX86MovdqaRR, rs_tmp_vector_2.GetReg(), rs_src2.GetReg());
+ NewLIR2(kX86PsrlqRI, rs_tmp_vector_2.GetReg(), 0x20);
+ NewLIR2(kX86PmuludqRR, rs_tmp_vector_2.GetReg(), rs_tmp_vector_1.GetReg());
+ }
+
+ NewLIR2(kX86PsrlqRI, rs_dest_src1.GetReg(), 0x20);
+ NewLIR2(kX86PmuludqRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
+
+ if (both_operands_same == false) {
+ NewLIR2(kX86PaddqRR, rs_dest_src1.GetReg(), rs_tmp_vector_2.GetReg());
+ } else {
+ NewLIR2(kX86PaddqRR, rs_dest_src1.GetReg(), rs_dest_src1.GetReg());
+ }
+
+ NewLIR2(kX86PsllqRI, rs_dest_src1.GetReg(), 0x20);
+ NewLIR2(kX86PmuludqRR, rs_tmp_vector_1.GetReg(), rs_src2.GetReg());
+ NewLIR2(kX86PaddqRR, rs_dest_src1.GetReg(), rs_tmp_vector_1.GetReg());
}
void X86Mir2Lir::GenMultiplyVector(BasicBlock *bb, MIR *mir) {
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
int opcode = 0;
switch (opsize) {
@@ -1895,7 +1827,10 @@
break;
case kSignedByte:
// HW doesn't support 16x16 byte multiplication so emulate it.
- GenMultiplyVectorSignedByte(bb, mir);
+ GenMultiplyVectorSignedByte(rs_dest_src1, rs_src2);
+ return;
+ case k64:
+ GenMultiplyVectorLong(rs_dest_src1, rs_src2);
return;
default:
LOG(FATAL) << "Unsupported vector multiply " << opsize;
@@ -1908,12 +1843,16 @@
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
int opcode = 0;
switch (opsize) {
case k32:
opcode = kX86PadddRR;
break;
+ case k64:
+ opcode = kX86PaddqRR;
+ break;
case kSignedHalf:
case kUnsignedHalf:
opcode = kX86PaddwRR;
@@ -1939,12 +1878,16 @@
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
int opcode = 0;
switch (opsize) {
case k32:
opcode = kX86PsubdRR;
break;
+ case k64:
+ opcode = kX86PsubqRR;
+ break;
case kSignedHalf:
case kUnsignedHalf:
opcode = kX86PsubwRR;
@@ -1967,58 +1910,54 @@
}
void X86Mir2Lir::GenShiftByteVector(BasicBlock *bb, MIR *mir) {
+ // Destination does not need clobbered because it has already been as part
+ // of the general packed shift handler (caller of this method).
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
- RegStorage rs_tmp = Get128BitRegister(AllocTempWide());
int opcode = 0;
- int imm = mir->dalvikInsn.vB;
-
switch (static_cast<ExtendedMIROpcode>(mir->dalvikInsn.opcode)) {
case kMirOpPackedShiftLeft:
opcode = kX86PsllwRI;
break;
case kMirOpPackedSignedShiftRight:
- opcode = kX86PsrawRI;
- break;
case kMirOpPackedUnsignedShiftRight:
- opcode = kX86PsrlwRI;
- break;
+ // TODO Add support for emulated byte shifts.
default:
LOG(FATAL) << "Unsupported shift operation on byte vector " << opcode;
break;
}
- /*
- * xmm1 will have low bits
- * xmm2 will have high bits
- *
- * xmm2 = xmm1
- * xmm1 = xmm1 .<< N
- * xmm2 = xmm2 && 0xFF00FF00FF00FF00FF00FF00FF00FF00
- * xmm2 = xmm2 .<< N
- * xmm1 = xmm1 | xmm2
- */
-
- // Copy xmm1.
- NewLIR2(kX86Mova128RR, rs_tmp.GetReg(), rs_dest_src1.GetReg());
+ // Clear xmm register and return if shift more than byte length.
+ int imm = mir->dalvikInsn.vB;
+ if (imm >= 8) {
+ NewLIR2(kX86PxorRR, rs_dest_src1.GetReg(), rs_dest_src1.GetReg());
+ return;
+ }
// Shift lower values.
NewLIR2(opcode, rs_dest_src1.GetReg(), imm);
- // Mask bottom bits.
- AndMaskVectorRegister(rs_tmp, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00);
+ /*
+ * The above shift will shift the whole word, but that means
+ * both the bytes will shift as well. To emulate a byte level
+ * shift, we can just throw away the lower (8 - N) bits of the
+ * upper byte, and we are done.
+ */
+ uint8_t byte_mask = 0xFF << imm;
+ uint32_t int_mask = byte_mask;
+ int_mask = int_mask << 8 | byte_mask;
+ int_mask = int_mask << 8 | byte_mask;
+ int_mask = int_mask << 8 | byte_mask;
- // Shift higher values.
- NewLIR2(opcode, rs_tmp.GetReg(), imm);
-
- // Combine back into dest XMM register.
- NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_tmp.GetReg());
+ // And the destination with the mask
+ AndMaskVectorRegister(rs_dest_src1, int_mask, int_mask, int_mask, int_mask);
}
void X86Mir2Lir::GenShiftLeftVector(BasicBlock *bb, MIR *mir) {
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
int imm = mir->dalvikInsn.vB;
int opcode = 0;
switch (opsize) {
@@ -2047,6 +1986,7 @@
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
int imm = mir->dalvikInsn.vB;
int opcode = 0;
switch (opsize) {
@@ -2061,6 +2001,8 @@
case kUnsignedByte:
GenShiftByteVector(bb, mir);
return;
+ case k64:
+ // TODO Implement emulated shift algorithm.
default:
LOG(FATAL) << "Unsupported vector signed shift right " << opsize;
break;
@@ -2072,6 +2014,7 @@
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
int imm = mir->dalvikInsn.vB;
int opcode = 0;
switch (opsize) {
@@ -2100,6 +2043,7 @@
// We only support 128 bit registers.
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
NewLIR2(kX86PandRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
}
@@ -2108,6 +2052,7 @@
// We only support 128 bit registers.
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
NewLIR2(kX86PorRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
}
@@ -2116,6 +2061,7 @@
// We only support 128 bit registers.
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
RegStorage rs_dest_src1 = RegStorage::Solo128(mir->dalvikInsn.vA);
+ Clobber(rs_dest_src1);
RegStorage rs_src2 = RegStorage::Solo128(mir->dalvikInsn.vB);
NewLIR2(kX86PxorRR, rs_dest_src1.GetReg(), rs_src2.GetReg());
}
@@ -2140,134 +2086,248 @@
void X86Mir2Lir::GenAddReduceVector(BasicBlock *bb, MIR *mir) {
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
- RegStorage rs_src1 = RegStorage::Solo128(mir->dalvikInsn.vB);
- RegLocation rl_dest = mir_graph_->GetDest(mir);
- RegStorage rs_tmp;
+ RegStorage vector_src = RegStorage::Solo128(mir->dalvikInsn.vB);
+ bool is_wide = opsize == k64 || opsize == kDouble;
- int vec_bytes = (mir->dalvikInsn.vC & 0xFFFF) / 8;
- int vec_unit_size = 0;
- int opcode = 0;
- int extr_opcode = 0;
- RegLocation rl_result;
-
- switch (opsize) {
- case k32:
- extr_opcode = kX86PextrdRRI;
- opcode = kX86PhadddRR;
- vec_unit_size = 4;
- break;
- case kSignedByte:
- case kUnsignedByte:
- extr_opcode = kX86PextrbRRI;
- opcode = kX86PhaddwRR;
- vec_unit_size = 2;
- break;
- case kSignedHalf:
- case kUnsignedHalf:
- extr_opcode = kX86PextrwRRI;
- opcode = kX86PhaddwRR;
- vec_unit_size = 2;
- break;
- case kSingle:
- rl_result = EvalLoc(rl_dest, kFPReg, true);
- vec_unit_size = 4;
- for (int i = 0; i < 3; i++) {
- NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), rs_src1.GetReg());
- NewLIR3(kX86ShufpsRRI, rs_src1.GetReg(), rs_src1.GetReg(), 0x39);
- }
- NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), rs_src1.GetReg());
- StoreValue(rl_dest, rl_result);
-
- // For single-precision floats, we are done here
- return;
- default:
- LOG(FATAL) << "Unsupported vector add reduce " << opsize;
- break;
- }
-
- int elems = vec_bytes / vec_unit_size;
-
- // Emulate horizontal add instruction by reducing 2 vectors with 8 values before adding them again
- // TODO is overflow handled correctly?
- if (opsize == kSignedByte || opsize == kUnsignedByte) {
- rs_tmp = Get128BitRegister(AllocTempWide());
-
- // tmp = xmm1 .>> 8.
- NewLIR2(kX86Mova128RR, rs_tmp.GetReg(), rs_src1.GetReg());
- NewLIR2(kX86PsrlwRI, rs_tmp.GetReg(), 8);
-
- // Zero extend low bits in xmm1.
- AndMaskVectorRegister(rs_src1, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF);
- }
-
- while (elems > 1) {
- if (opsize == kSignedByte || opsize == kUnsignedByte) {
- NewLIR2(opcode, rs_tmp.GetReg(), rs_tmp.GetReg());
- }
- NewLIR2(opcode, rs_src1.GetReg(), rs_src1.GetReg());
- elems >>= 1;
- }
-
- // Combine the results if we separated them.
- if (opsize == kSignedByte || opsize == kUnsignedByte) {
- NewLIR2(kX86PaddbRR, rs_src1.GetReg(), rs_tmp.GetReg());
- }
-
- // We need to extract to a GPR.
- RegStorage temp = AllocTemp();
- NewLIR3(extr_opcode, temp.GetReg(), rs_src1.GetReg(), 0);
-
- // Can we do this directly into memory?
- rl_result = UpdateLocTyped(rl_dest, kCoreReg);
- if (rl_result.location == kLocPhysReg) {
- // Ensure res is in a core reg
- rl_result = EvalLoc(rl_dest, kCoreReg, true);
- OpRegReg(kOpAdd, rl_result.reg, temp);
- StoreFinalValue(rl_dest, rl_result);
+ // Get the location of the virtual register. Since this bytecode is overloaded
+ // for different types (and sizes), we need different logic for each path.
+ // The design of bytecode uses same VR for source and destination.
+ RegLocation rl_src, rl_dest, rl_result;
+ if (is_wide) {
+ rl_src = mir_graph_->GetSrcWide(mir, 0);
+ rl_dest = mir_graph_->GetDestWide(mir);
} else {
- OpMemReg(kOpAdd, rl_result, temp.GetReg());
+ rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_dest = mir_graph_->GetDest(mir);
}
- FreeTemp(temp);
+ // We need a temp for byte and short values
+ RegStorage temp;
+
+ // There is a different path depending on type and size.
+ if (opsize == kSingle) {
+ // Handle float case.
+ // TODO Add support for fast math (not value safe) and do horizontal add in that case.
+
+ rl_src = LoadValue(rl_src, kFPReg);
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+
+ // Since we are doing an add-reduce, we move the reg holding the VR
+ // into the result so we include it in result.
+ OpRegCopy(rl_result.reg, rl_src.reg);
+ NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+
+ // Since FP must keep order of operation for value safety, we shift to low
+ // 32-bits and add to result.
+ for (int i = 0; i < 3; i++) {
+ NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), 0x39);
+ NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+ }
+
+ StoreValue(rl_dest, rl_result);
+ } else if (opsize == kDouble) {
+ // Handle double case.
+ rl_src = LoadValueWide(rl_src, kFPReg);
+ rl_result = EvalLocWide(rl_dest, kFPReg, true);
+ LOG(FATAL) << "Unsupported vector add reduce for double.";
+ } else if (opsize == k64) {
+ /*
+ * Handle long case:
+ * 1) Reduce the vector register to lower half (with addition).
+ * 1-1) Get an xmm temp and fill it with vector register.
+ * 1-2) Shift the xmm temp by 8-bytes.
+ * 1-3) Add the xmm temp to vector register that is being reduced.
+ * 2) Allocate temp GP / GP pair.
+ * 2-1) In 64-bit case, use movq to move result to a 64-bit GP.
+ * 2-2) In 32-bit case, use movd twice to move to 32-bit GP pair.
+ * 3) Finish the add reduction by doing what add-long/2addr does,
+ * but instead of having a VR as one of the sources, we have our temp GP.
+ */
+ RegStorage rs_tmp_vector = Get128BitRegister(AllocTempDouble());
+ NewLIR2(kX86MovdqaRR, rs_tmp_vector.GetReg(), vector_src.GetReg());
+ NewLIR2(kX86PsrldqRI, rs_tmp_vector.GetReg(), 8);
+ NewLIR2(kX86PaddqRR, vector_src.GetReg(), rs_tmp_vector.GetReg());
+ FreeTemp(rs_tmp_vector);
+
+ // We would like to be able to reuse the add-long implementation, so set up a fake
+ // register location to pass it.
+ RegLocation temp_loc = mir_graph_->GetBadLoc();
+ temp_loc.core = 1;
+ temp_loc.wide = 1;
+ temp_loc.location = kLocPhysReg;
+ temp_loc.reg = AllocTempWide();
+
+ if (cu_->target64) {
+ DCHECK(!temp_loc.reg.IsPair());
+ NewLIR2(kX86MovqrxRR, temp_loc.reg.GetReg(), vector_src.GetReg());
+ } else {
+ NewLIR2(kX86MovdrxRR, temp_loc.reg.GetLowReg(), vector_src.GetReg());
+ NewLIR2(kX86PsrlqRI, vector_src.GetReg(), 0x20);
+ NewLIR2(kX86MovdrxRR, temp_loc.reg.GetHighReg(), vector_src.GetReg());
+ }
+
+ GenArithOpLong(Instruction::ADD_LONG_2ADDR, rl_dest, temp_loc, temp_loc);
+ } else if (opsize == kSignedByte || opsize == kUnsignedByte) {
+ RegStorage rs_tmp = Get128BitRegister(AllocTempDouble());
+ NewLIR2(kX86PxorRR, rs_tmp.GetReg(), rs_tmp.GetReg());
+ NewLIR2(kX86PsadbwRR, vector_src.GetReg(), rs_tmp.GetReg());
+ NewLIR3(kX86PshufdRRI, rs_tmp.GetReg(), vector_src.GetReg(), 0x4e);
+ NewLIR2(kX86PaddbRR, vector_src.GetReg(), rs_tmp.GetReg());
+ // Move to a GPR
+ temp = AllocTemp();
+ NewLIR2(kX86MovdrxRR, temp.GetReg(), vector_src.GetReg());
+ } else {
+ // Handle and the int and short cases together
+
+ // Initialize as if we were handling int case. Below we update
+ // the opcode if handling byte or short.
+ int vec_bytes = (mir->dalvikInsn.vC & 0xFFFF) / 8;
+ int vec_unit_size;
+ int horizontal_add_opcode;
+ int extract_opcode;
+
+ if (opsize == kSignedHalf || opsize == kUnsignedHalf) {
+ extract_opcode = kX86PextrwRRI;
+ horizontal_add_opcode = kX86PhaddwRR;
+ vec_unit_size = 2;
+ } else if (opsize == k32) {
+ vec_unit_size = 4;
+ horizontal_add_opcode = kX86PhadddRR;
+ extract_opcode = kX86PextrdRRI;
+ } else {
+ LOG(FATAL) << "Unsupported vector add reduce " << opsize;
+ return;
+ }
+
+ int elems = vec_bytes / vec_unit_size;
+
+ while (elems > 1) {
+ NewLIR2(horizontal_add_opcode, vector_src.GetReg(), vector_src.GetReg());
+ elems >>= 1;
+ }
+
+ // Handle this as arithmetic unary case.
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
+
+ // Extract to a GP register because this is integral typed.
+ temp = AllocTemp();
+ NewLIR3(extract_opcode, temp.GetReg(), vector_src.GetReg(), 0);
+ }
+
+ if (opsize != k64 && opsize != kSingle && opsize != kDouble) {
+ // The logic below looks very similar to the handling of ADD_INT_2ADDR
+ // except the rhs is not a VR but a physical register allocated above.
+ // No load of source VR is done because it assumes that rl_result will
+ // share physical register / memory location.
+ rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+ if (rl_result.location == kLocPhysReg) {
+ // Ensure res is in a core reg.
+ rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ OpRegReg(kOpAdd, rl_result.reg, temp);
+ StoreFinalValue(rl_dest, rl_result);
+ } else {
+ // Do the addition directly to memory.
+ OpMemReg(kOpAdd, rl_result, temp.GetReg());
+ }
+ }
}
void X86Mir2Lir::GenReduceVector(BasicBlock *bb, MIR *mir) {
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegLocation rl_dest = mir_graph_->GetDest(mir);
- RegStorage rs_src1 = RegStorage::Solo128(mir->dalvikInsn.vB);
- int extract_index = mir->dalvikInsn.arg[0];
- int extr_opcode = 0;
+ RegStorage vector_src = RegStorage::Solo128(mir->dalvikInsn.vB);
RegLocation rl_result;
bool is_wide = false;
- switch (opsize) {
- case k32:
- rl_result = UpdateLocTyped(rl_dest, kCoreReg);
- extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrdMRI : kX86PextrdRRI;
- break;
- case kSignedHalf:
- case kUnsignedHalf:
- rl_result= UpdateLocTyped(rl_dest, kCoreReg);
- extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrwMRI : kX86PextrwRRI;
- break;
- default:
- LOG(FATAL) << "Unsupported vector add reduce " << opsize;
- return;
- break;
- }
+ // There is a different path depending on type and size.
+ if (opsize == kSingle) {
+ // Handle float case.
+ // TODO Add support for fast math (not value safe) and do horizontal add in that case.
- if (rl_result.location == kLocPhysReg) {
- NewLIR3(extr_opcode, rl_result.reg.GetReg(), rs_src1.GetReg(), extract_index);
- if (is_wide == true) {
+ rl_result = EvalLoc(rl_dest, kFPReg, true);
+ NewLIR2(kX86PxorRR, rl_result.reg.GetReg(), rl_result.reg.GetReg());
+ NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+
+ // Since FP must keep order of operation for value safety, we shift to low
+ // 32-bits and add to result.
+ for (int i = 0; i < 3; i++) {
+ NewLIR3(kX86ShufpsRRI, vector_src.GetReg(), vector_src.GetReg(), 0x39);
+ NewLIR2(kX86AddssRR, rl_result.reg.GetReg(), vector_src.GetReg());
+ }
+
+ StoreValue(rl_dest, rl_result);
+ } else if (opsize == kDouble) {
+ // TODO Handle double case.
+ LOG(FATAL) << "Unsupported add reduce for double.";
+ } else if (opsize == k64) {
+ /*
+ * Handle long case:
+ * 1) Reduce the vector register to lower half (with addition).
+ * 1-1) Get an xmm temp and fill it with vector register.
+ * 1-2) Shift the xmm temp by 8-bytes.
+ * 1-3) Add the xmm temp to vector register that is being reduced.
+ * 2) Evaluate destination to a GP / GP pair.
+ * 2-1) In 64-bit case, use movq to move result to a 64-bit GP.
+ * 2-2) In 32-bit case, use movd twice to move to 32-bit GP pair.
+ * 3) Store the result to the final destination.
+ */
+ NewLIR2(kX86PsrldqRI, vector_src.GetReg(), 8);
+ rl_result = EvalLocWide(rl_dest, kCoreReg, true);
+ if (cu_->target64) {
+ DCHECK(!rl_result.reg.IsPair());
+ NewLIR2(kX86MovqrxRR, rl_result.reg.GetReg(), vector_src.GetReg());
+ } else {
+ NewLIR2(kX86MovdrxRR, rl_result.reg.GetLowReg(), vector_src.GetReg());
+ NewLIR2(kX86PsrlqRI, vector_src.GetReg(), 0x20);
+ NewLIR2(kX86MovdrxRR, rl_result.reg.GetHighReg(), vector_src.GetReg());
+ }
+
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ int extract_index = mir->dalvikInsn.arg[0];
+ int extr_opcode = 0;
+ rl_result = UpdateLocTyped(rl_dest, kCoreReg);
+
+ // Handle the rest of integral types now.
+ switch (opsize) {
+ case k32:
+ extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrdRRI : kX86PextrdMRI;
+ break;
+ case kSignedHalf:
+ case kUnsignedHalf:
+ extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrwRRI : kX86PextrwMRI;
+ break;
+ case kSignedByte:
+ extr_opcode = (rl_result.location == kLocPhysReg) ? kX86PextrbRRI : kX86PextrbMRI;
+ break;
+ default:
+ LOG(FATAL) << "Unsupported vector reduce " << opsize;
+ return;
+ }
+
+ if (rl_result.location == kLocPhysReg) {
+ NewLIR3(extr_opcode, rl_result.reg.GetReg(), vector_src.GetReg(), extract_index);
StoreFinalValue(rl_dest, rl_result);
} else {
- StoreFinalValueWide(rl_dest, rl_result);
+ int displacement = SRegOffset(rl_result.s_reg_low);
+ LIR *l = NewLIR3(extr_opcode, rs_rX86_SP.GetReg(), displacement, vector_src.GetReg());
+ AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
+ AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
}
+ }
+}
+
+void X86Mir2Lir::LoadVectorRegister(RegStorage rs_dest, RegStorage rs_src,
+ OpSize opsize, int op_mov) {
+ if (!cu_->target64 && opsize == k64) {
+ // Logic assumes that longs are loaded in GP register pairs.
+ NewLIR2(kX86MovdxrRR, rs_dest.GetReg(), rs_src.GetLowReg());
+ RegStorage r_tmp = AllocTempDouble();
+ NewLIR2(kX86MovdxrRR, r_tmp.GetReg(), rs_src.GetHighReg());
+ NewLIR2(kX86PunpckldqRR, rs_dest.GetReg(), r_tmp.GetReg());
+ FreeTemp(r_tmp);
} else {
- int displacement = SRegOffset(rl_result.s_reg_low);
- LIR *l = NewLIR3(extr_opcode, rs_rX86_SP.GetReg(), displacement, rs_src1.GetReg());
- AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
- AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
+ NewLIR2(op_mov, rs_dest.GetReg(), rs_src.GetReg());
}
}
@@ -2275,96 +2335,104 @@
DCHECK_EQ(mir->dalvikInsn.vC & 0xFFFF, 128U);
OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
RegStorage rs_dest = RegStorage::Solo128(mir->dalvikInsn.vA);
- int op_low = 0, op_high = 0, imm = 0, op_mov = kX86MovdxrRR;
+ Clobber(rs_dest);
+ int op_shuffle = 0, op_shuffle_high = 0, op_mov = kX86MovdxrRR;
RegisterClass reg_type = kCoreReg;
+ bool is_wide = false;
switch (opsize) {
case k32:
- op_low = kX86PshufdRRI;
+ op_shuffle = kX86PshufdRRI;
break;
case kSingle:
- op_low = kX86PshufdRRI;
- op_mov = kX86Mova128RR;
+ op_shuffle = kX86PshufdRRI;
+ op_mov = kX86MovdqaRR;
reg_type = kFPReg;
break;
case k64:
- op_low = kX86PshufdRRI;
- imm = 0x44;
- break;
- case kDouble:
- op_low = kX86PshufdRRI;
- op_mov = kX86Mova128RR;
- reg_type = kFPReg;
- imm = 0x44;
+ op_shuffle = kX86PunpcklqdqRR;
+ op_mov = kX86MovqxrRR;
+ is_wide = true;
break;
case kSignedByte:
case kUnsignedByte:
- // Shuffle 8 bit value into 16 bit word.
- // We set val = val + (val << 8) below and use 16 bit shuffle.
+ // We will have the source loaded up in a
+ // double-word before we use this shuffle
+ op_shuffle = kX86PshufdRRI;
+ break;
case kSignedHalf:
case kUnsignedHalf:
// Handles low quadword.
- op_low = kX86PshuflwRRI;
+ op_shuffle = kX86PshuflwRRI;
// Handles upper quadword.
- op_high = kX86PshufdRRI;
+ op_shuffle_high = kX86PshufdRRI;
break;
default:
LOG(FATAL) << "Unsupported vector set " << opsize;
break;
}
- RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
-
- // Load the value from the VR into the reg.
- if (rl_src.wide == 0) {
+ // Load the value from the VR into a physical register.
+ RegLocation rl_src;
+ if (!is_wide) {
+ rl_src = mir_graph_->GetSrc(mir, 0);
rl_src = LoadValue(rl_src, reg_type);
} else {
+ rl_src = mir_graph_->GetSrcWide(mir, 0);
rl_src = LoadValueWide(rl_src, reg_type);
}
-
- // If opsize is 8 bits wide then double value and use 16 bit shuffle instead.
- if (opsize == kSignedByte || opsize == kUnsignedByte) {
- RegStorage temp = AllocTemp();
- // val = val + (val << 8).
- NewLIR2(kX86Mov32RR, temp.GetReg(), rl_src.reg.GetReg());
- NewLIR2(kX86Sal32RI, temp.GetReg(), 8);
- NewLIR2(kX86Or32RR, rl_src.reg.GetReg(), temp.GetReg());
- FreeTemp(temp);
- }
+ RegStorage reg_to_shuffle = rl_src.reg;
// Load the value into the XMM register.
- NewLIR2(op_mov, rs_dest.GetReg(), rl_src.reg.GetReg());
+ LoadVectorRegister(rs_dest, reg_to_shuffle, opsize, op_mov);
+
+ if (opsize == kSignedByte || opsize == kUnsignedByte) {
+ // In the byte case, first duplicate it to be a word
+ // Then duplicate it to be a double-word
+ NewLIR2(kX86PunpcklbwRR, rs_dest.GetReg(), rs_dest.GetReg());
+ NewLIR2(kX86PunpcklwdRR, rs_dest.GetReg(), rs_dest.GetReg());
+ }
// Now shuffle the value across the destination.
- NewLIR3(op_low, rs_dest.GetReg(), rs_dest.GetReg(), imm);
+ if (op_shuffle == kX86PunpcklqdqRR) {
+ NewLIR2(op_shuffle, rs_dest.GetReg(), rs_dest.GetReg());
+ } else {
+ NewLIR3(op_shuffle, rs_dest.GetReg(), rs_dest.GetReg(), 0);
+ }
// And then repeat as needed.
- if (op_high != 0) {
- NewLIR3(op_high, rs_dest.GetReg(), rs_dest.GetReg(), imm);
+ if (op_shuffle_high != 0) {
+ NewLIR3(op_shuffle_high, rs_dest.GetReg(), rs_dest.GetReg(), 0);
}
}
-LIR *X86Mir2Lir::ScanVectorLiteral(MIR *mir) {
- int *args = reinterpret_cast<int*>(mir->dalvikInsn.arg);
+void X86Mir2Lir::GenPackedArrayGet(BasicBlock *bb, MIR *mir) {
+ UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayGet not supported.";
+}
+
+void X86Mir2Lir::GenPackedArrayPut(BasicBlock *bb, MIR *mir) {
+ UNIMPLEMENTED(FATAL) << "Extended opcode kMirOpPackedArrayPut not supported.";
+}
+
+LIR* X86Mir2Lir::ScanVectorLiteral(int32_t* constants) {
for (LIR *p = const_vectors_; p != nullptr; p = p->next) {
- if (args[0] == p->operands[0] && args[1] == p->operands[1] &&
- args[2] == p->operands[2] && args[3] == p->operands[3]) {
+ if (constants[0] == p->operands[0] && constants[1] == p->operands[1] &&
+ constants[2] == p->operands[2] && constants[3] == p->operands[3]) {
return p;
}
}
return nullptr;
}
-LIR *X86Mir2Lir::AddVectorLiteral(MIR *mir) {
+LIR* X86Mir2Lir::AddVectorLiteral(int32_t* constants) {
LIR* new_value = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), kArenaAllocData));
- int *args = reinterpret_cast<int*>(mir->dalvikInsn.arg);
- new_value->operands[0] = args[0];
- new_value->operands[1] = args[1];
- new_value->operands[2] = args[2];
- new_value->operands[3] = args[3];
+ new_value->operands[0] = constants[0];
+ new_value->operands[1] = constants[1];
+ new_value->operands[2] = constants[2];
+ new_value->operands[3] = constants[3];
new_value->next = const_vectors_;
if (const_vectors_ == nullptr) {
- estimated_native_code_size_ += 12; // Amount needed to align to 16 byte boundary.
+ estimated_native_code_size_ += 12; // Maximum needed to align to 16 byte boundary.
}
estimated_native_code_size_ += 16; // Space for one vector.
const_vectors_ = new_value;
@@ -2429,11 +2497,11 @@
}
if (!in_to_reg_storage_mapping_.IsInitialized()) {
- int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+ int start_vreg = cu_->mir_graph->GetFirstInVR();
RegLocation* arg_locs = &mir_graph_->reg_location_[start_vreg];
InToRegStorageX86_64Mapper mapper(this);
- in_to_reg_storage_mapping_.Initialize(arg_locs, cu_->num_ins, &mapper);
+ in_to_reg_storage_mapping_.Initialize(arg_locs, mir_graph_->GetNumOfInVRs(), &mapper);
}
return in_to_reg_storage_mapping_.Get(arg_num);
}
@@ -2482,11 +2550,11 @@
StoreRefDisp(rs_rX86_SP, 0, As32BitReg(TargetReg(kArg0, kRef)), kNotVolatile);
}
- if (cu_->num_ins == 0) {
+ if (mir_graph_->GetNumOfInVRs() == 0) {
return;
}
- int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
+ int start_vreg = cu_->mir_graph->GetFirstInVR();
/*
* Copy incoming arguments to their proper home locations.
* NOTE: an older version of dx had an issue in which
@@ -2500,7 +2568,7 @@
* half to memory as well.
*/
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- for (int i = 0; i < cu_->num_ins; i++) {
+ for (uint32_t i = 0; i < mir_graph_->GetNumOfInVRs(); i++) {
// get reg corresponding to input
RegStorage reg = GetArgMappingToPhysicalReg(i);
@@ -2632,9 +2700,6 @@
}
}
- // Logic below assumes that Method pointer is at offset zero from SP.
- DCHECK_EQ(VRegOffset(static_cast<int>(kVRegMethodPtrBaseReg)), 0);
-
// The rest can be copied together
int start_offset = SRegOffset(info->args[last_mapped_in + size_of_the_last_mapped].s_reg_low);
int outs_offset = StackVisitor::GetOutVROffset(last_mapped_in + size_of_the_last_mapped,
@@ -2909,4 +2974,46 @@
return true;
}
+/**
+ * Lock temp registers for explicit usage. Registers will be freed in destructor.
+ */
+X86Mir2Lir::ExplicitTempRegisterLock::ExplicitTempRegisterLock(X86Mir2Lir* mir_to_lir,
+ int n_regs, ...) :
+ temp_regs_(n_regs),
+ mir_to_lir_(mir_to_lir) {
+ va_list regs;
+ va_start(regs, n_regs);
+ for (int i = 0; i < n_regs; i++) {
+ RegStorage reg = *(va_arg(regs, RegStorage*));
+ RegisterInfo* info = mir_to_lir_->GetRegInfo(reg);
+
+ // Make sure we don't have promoted register here.
+ DCHECK(info->IsTemp());
+
+ temp_regs_.push_back(reg);
+ mir_to_lir_->FlushReg(reg);
+
+ if (reg.IsPair()) {
+ RegStorage partner = info->Partner();
+ temp_regs_.push_back(partner);
+ mir_to_lir_->FlushReg(partner);
+ }
+
+ mir_to_lir_->Clobber(reg);
+ mir_to_lir_->LockTemp(reg);
+ }
+
+ va_end(regs);
+}
+
+/*
+ * Free all locked registers.
+ */
+X86Mir2Lir::ExplicitTempRegisterLock::~ExplicitTempRegisterLock() {
+ // Free all locked temps.
+ for (auto it : temp_regs_) {
+ mir_to_lir_->FreeTemp(it);
+ }
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index a48613f..30384ec 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -592,7 +592,6 @@
kDouble, kNotVolatile);
res->target = data_target;
res->flags.fixup = kFixupLoad;
- Clobber(rl_method.reg);
store_method_addr_used_ = true;
} else {
if (r_dest.IsPair()) {
@@ -779,15 +778,20 @@
}
LIR* X86Mir2Lir::StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale,
- int displacement, RegStorage r_src, OpSize size) {
+ int displacement, RegStorage r_src, OpSize size,
+ int opt_flags) {
LIR *store = NULL;
LIR *store2 = NULL;
bool is_array = r_index.Valid();
bool pair = r_src.IsPair();
bool is64bit = (size == k64) || (size == kDouble);
+ bool consider_non_temporal = false;
+
X86OpCode opcode = kX86Nop;
switch (size) {
case k64:
+ consider_non_temporal = true;
+ // Fall through!
case kDouble:
if (r_src.IsFloat()) {
opcode = is_array ? kX86MovsdAR : kX86MovsdMR;
@@ -804,6 +808,7 @@
opcode = is_array ? kX86Mov64AR : kX86Mov64MR;
CHECK_EQ(is_array, false);
CHECK_EQ(r_src.IsFloat(), false);
+ consider_non_temporal = true;
break;
} // else fall-through to k32 case
case k32:
@@ -815,6 +820,7 @@
DCHECK(r_src.IsSingle());
}
DCHECK_EQ((displacement & 0x3), 0);
+ consider_non_temporal = true;
break;
case kUnsignedHalf:
case kSignedHalf:
@@ -829,6 +835,28 @@
LOG(FATAL) << "Bad case in StoreBaseIndexedDispBody";
}
+ // Handle non temporal hint here.
+ if (consider_non_temporal && ((opt_flags & MIR_STORE_NON_TEMPORAL) != 0)) {
+ switch (opcode) {
+ // We currently only handle 32/64 bit moves here.
+ case kX86Mov64AR:
+ opcode = kX86Movnti64AR;
+ break;
+ case kX86Mov64MR:
+ opcode = kX86Movnti64MR;
+ break;
+ case kX86Mov32AR:
+ opcode = kX86Movnti32AR;
+ break;
+ case kX86Mov32MR:
+ opcode = kX86Movnti32MR;
+ break;
+ default:
+ // Do nothing here.
+ break;
+ }
+ }
+
if (!is_array) {
if (!pair) {
store = NewLIR3(opcode, r_base.GetReg(), displacement + LOWORD_OFFSET, r_src.GetReg());
@@ -875,6 +903,17 @@
// StoreBaseDisp() will emit correct insn for atomic store on x86
// assuming r_dest is correctly prepared using RegClassForFieldLoadStore().
+ // x86 only allows registers EAX-EDX to be used as byte registers, if the input src is not
+ // valid, allocate a temp.
+ bool allocated_temp = false;
+ if (size == kUnsignedByte || size == kSignedByte) {
+ if (!cu_->target64 && !r_src.Low4()) {
+ RegStorage r_input = r_src;
+ r_src = AllocateByteRegister();
+ OpRegCopy(r_src, r_input);
+ allocated_temp = true;
+ }
+ }
LIR* store = StoreBaseIndexedDisp(r_base, RegStorage::InvalidReg(), 0, displacement, r_src, size);
@@ -884,6 +923,10 @@
GenMemBarrier(kAnyAny);
}
+ if (allocated_temp) {
+ FreeTemp(r_src);
+ }
+
return store;
}
@@ -913,7 +956,8 @@
// Did we need a pointer to the method code?
if (store_method_addr_) {
- base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempVR, cu_->target64 == true);
+ base_of_code_ = mir_graph_->GetNewCompilerTemp(kCompilerTempBackend, cu_->target64 == true);
+ DCHECK(base_of_code_ != nullptr);
} else {
base_of_code_ = nullptr;
}
@@ -946,6 +990,17 @@
case kMirOpConstVector:
store_method_addr_ = true;
break;
+ case kMirOpPackedMultiply:
+ case kMirOpPackedShiftLeft:
+ case kMirOpPackedSignedShiftRight:
+ case kMirOpPackedUnsignedShiftRight: {
+ // Byte emulation requires constants from the literal pool.
+ OpSize opsize = static_cast<OpSize>(mir->dalvikInsn.vC >> 16);
+ if (opsize == kSignedByte || opsize == kUnsignedByte) {
+ store_method_addr_ = true;
+ }
+ break;
+ }
default:
// Ignore the rest.
break;
@@ -980,6 +1035,7 @@
store_method_addr_ = true;
break;
case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
AnalyzeInvokeStatic(opcode, bb, mir);
break;
default:
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 500c6b8..22a2f30 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -75,33 +75,36 @@
* ST1 .. ST7: caller save
*
* Stack frame diagram (stack grows down, higher addresses at top):
+ * For a more detailed view of each region see stack.h.
*
- * +------------------------+
- * | IN[ins-1] | {Note: resides in caller's frame}
- * | . |
- * | IN[0] |
- * | caller's Method* |
- * +========================+ {Note: start of callee's frame}
- * | return address | {pushed by call}
- * | spill region | {variable sized}
- * +------------------------+
- * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
- * +------------------------+
- * | V[locals-1] |
- * | V[locals-2] |
- * | . |
- * | . |
- * | V[1] |
- * | V[0] |
- * +------------------------+
- * | 0 to 3 words padding |
- * +------------------------+
- * | OUT[outs-1] |
- * | OUT[outs-2] |
- * | . |
- * | OUT[0] |
- * | cur_method* | <<== sp w/ 16-byte alignment
- * +========================+
+ * +---------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's Method* |
+ * +===========================+ {Note: start of callee's frame}
+ * | return address | {pushed by call}
+ * | spill region | {variable sized}
+ * +---------------------------+
+ * | ...filler 4-bytes... | {Note: used as 2nd word of V[locals-1] if long]
+ * +---------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . |
+ * | V[1] |
+ * | V[0] |
+ * +---------------------------+
+ * | 0 to 12-bytes padding |
+ * +---------------------------+
+ * | compiler temp region |
+ * +---------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | StackReference<ArtMethod> | <<== sp w/ 16-byte alignment
+ * +===========================+
*/
enum X86ResourceEncodingPos {
@@ -440,12 +443,12 @@
kX86Mov16MR, kX86Mov16AR, kX86Mov16TR,
kX86Mov16RR, kX86Mov16RM, kX86Mov16RA, kX86Mov16RT,
kX86Mov16RI, kX86Mov16MI, kX86Mov16AI, kX86Mov16TI,
- kX86Mov32MR, kX86Mov32AR, kX86Mov32TR,
+ kX86Mov32MR, kX86Mov32AR, kX86Movnti32MR, kX86Movnti32AR, kX86Mov32TR,
kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT,
kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI,
kX86Lea32RM,
kX86Lea32RA,
- kX86Mov64MR, kX86Mov64AR, kX86Mov64TR,
+ kX86Mov64MR, kX86Mov64AR, kX86Movnti64MR, kX86Movnti64AR, kX86Mov64TR,
kX86Mov64RR, kX86Mov64RM, kX86Mov64RA, kX86Mov64RT,
kX86Mov64RI32, kX86Mov64RI64, kX86Mov64MI, kX86Mov64AI, kX86Mov64TI,
kX86Lea64RM,
@@ -484,8 +487,10 @@
#undef BinaryShiftOpcode
kX86Cmc,
kX86Shld32RRI,
+ kX86Shld32RRC,
kX86Shld32MRI,
kX86Shrd32RRI,
+ kX86Shrd32RRC,
kX86Shrd32MRI,
kX86Shld64RRI,
kX86Shld64MRI,
@@ -550,20 +555,27 @@
Binary0fOpCode(kX86Subss), // float subtract
Binary0fOpCode(kX86Divsd), // double divide
Binary0fOpCode(kX86Divss), // float divide
- Binary0fOpCode(kX86Punpckldq), // Interleave low-order double words
+ Binary0fOpCode(kX86Punpcklbw), // Interleave low-order bytes
+ Binary0fOpCode(kX86Punpcklwd), // Interleave low-order single words (16-bits)
+ Binary0fOpCode(kX86Punpckldq), // Interleave low-order double words (32-bit)
+ Binary0fOpCode(kX86Punpcklqdq), // Interleave low-order quad word
Binary0fOpCode(kX86Sqrtsd), // square root
Binary0fOpCode(kX86Pmulld), // parallel integer multiply 32 bits x 4
Binary0fOpCode(kX86Pmullw), // parallel integer multiply 16 bits x 8
+ Binary0fOpCode(kX86Pmuludq), // parallel unsigned 32 integer and stores result as 64
Binary0fOpCode(kX86Mulps), // parallel FP multiply 32 bits x 4
Binary0fOpCode(kX86Mulpd), // parallel FP multiply 64 bits x 2
Binary0fOpCode(kX86Paddb), // parallel integer addition 8 bits x 16
Binary0fOpCode(kX86Paddw), // parallel integer addition 16 bits x 8
Binary0fOpCode(kX86Paddd), // parallel integer addition 32 bits x 4
+ Binary0fOpCode(kX86Paddq), // parallel integer addition 64 bits x 2
+ Binary0fOpCode(kX86Psadbw), // computes sum of absolute differences for unsigned byte integers
Binary0fOpCode(kX86Addps), // parallel FP addition 32 bits x 4
Binary0fOpCode(kX86Addpd), // parallel FP addition 64 bits x 2
Binary0fOpCode(kX86Psubb), // parallel integer subtraction 8 bits x 16
Binary0fOpCode(kX86Psubw), // parallel integer subtraction 16 bits x 8
Binary0fOpCode(kX86Psubd), // parallel integer subtraction 32 bits x 4
+ Binary0fOpCode(kX86Psubq), // parallel integer subtraction 32 bits x 4
Binary0fOpCode(kX86Subps), // parallel FP subtraction 32 bits x 4
Binary0fOpCode(kX86Subpd), // parallel FP subtraction 64 bits x 2
Binary0fOpCode(kX86Pand), // parallel AND 128 bits x 1
@@ -588,6 +600,7 @@
kX86PsrlwRI, // logical right shift of floating point registers 16 bits x 8
kX86PsrldRI, // logical right shift of floating point registers 32 bits x 4
kX86PsrlqRI, // logical right shift of floating point registers 64 bits x 2
+ kX86PsrldqRI, // logical shift of 128-bit vector register, immediate in bytes
kX86PsllwRI, // left shift of floating point registers 16 bits x 8
kX86PslldRI, // left shift of floating point registers 32 bits x 4
kX86PsllqRI, // left shift of floating point registers 64 bits x 2
@@ -602,8 +615,8 @@
kX86Fprem, // remainder from dividing of two floating point values
kX86Fucompp, // compare floating point values and pop x87 fp stack twice
kX86Fstsw16R, // store FPU status word
- Binary0fOpCode(kX86Mova128), // move 128 bits aligned
- kX86Mova128MR, kX86Mova128AR, // store 128 bit aligned from xmm1 to m128
+ Binary0fOpCode(kX86Movdqa), // move 128 bits aligned
+ kX86MovdqaMR, kX86MovdqaAR, // store 128 bit aligned from xmm1 to m128
Binary0fOpCode(kX86Movups), // load unaligned packed single FP values from xmm2/m128 to xmm1
kX86MovupsMR, kX86MovupsAR, // store unaligned packed single FP values from xmm1 to m128
Binary0fOpCode(kX86Movaps), // load aligned packed single FP values from xmm2/m128 to xmm1
@@ -618,7 +631,12 @@
kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR, // move into reg from xmm
kX86MovsxdRR, kX86MovsxdRM, kX86MovsxdRA, // move 32 bit to 64 bit with sign extension
kX86Set8R, kX86Set8M, kX86Set8A, // set byte depending on condition operand
- kX86Mfence, // memory barrier
+ kX86Lfence, // memory barrier to serialize all previous
+ // load-from-memory instructions
+ kX86Mfence, // memory barrier to serialize all previous
+ // load-from-memory and store-to-memory instructions
+ kX86Sfence, // memory barrier to serialize all previous
+ // store-to-memory instructions
Binary0fOpCode(kX86Imul16), // 16bit multiply
Binary0fOpCode(kX86Imul32), // 32bit multiply
Binary0fOpCode(kX86Imul64), // 64bit multiply
@@ -675,6 +693,7 @@
kMemRegImm, // MRI instruction kinds.
kShiftRegImm, kShiftMemImm, kShiftArrayImm, // Shift opcode with immediate.
kShiftRegCl, kShiftMemCl, kShiftArrayCl, // Shift opcode with register CL.
+ kShiftRegRegCl,
// kRegRegReg, kRegRegMem, kRegRegArray, // RRR, RRM, RRA instruction kinds.
kRegCond, kMemCond, kArrayCond, // R, M, A instruction kinds following by a condition.
kRegRegCond, // RR instruction kind followed by a condition.
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index e26745a..3cc573b 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/bit_vector-inl.h"
#include "compiler_internals.h"
#include "dataflow_iterator-inl.h"
#include "utils/scoped_arena_containers.h"
@@ -44,12 +45,7 @@
res = NeedsVisit(GetBasicBlock(bb->taken));
if (res == NULL) {
if (bb->successor_block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
- while (true) {
- SuccessorBlockInfo *sbi = iterator.Next();
- if (sbi == NULL) {
- break;
- }
+ for (SuccessorBlockInfo* sbi : bb->successor_blocks) {
res = NeedsVisit(GetBasicBlock(sbi->block));
if (res != NULL) {
break;
@@ -65,7 +61,7 @@
block->visited = true;
/* Enqueue the pre_order block id */
if (block->id != NullBasicBlockId) {
- dfs_order_->Insert(block->id);
+ dfs_order_.push_back(block->id);
}
}
@@ -82,9 +78,9 @@
succ.push_back(next_successor);
continue;
}
- curr->dfs_id = dfs_post_order_->Size();
+ curr->dfs_id = dfs_post_order_.size();
if (curr->id != NullBasicBlockId) {
- dfs_post_order_->Insert(curr->id);
+ dfs_post_order_.push_back(curr->id);
}
succ.pop_back();
}
@@ -92,23 +88,11 @@
/* Sort the blocks by the Depth-First-Search */
void MIRGraph::ComputeDFSOrders() {
- /* Initialize or reset the DFS pre_order list */
- if (dfs_order_ == NULL) {
- dfs_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
- kGrowableArrayDfsOrder);
- } else {
- /* Just reset the used length on the counter */
- dfs_order_->Reset();
- }
-
- /* Initialize or reset the DFS post_order list */
- if (dfs_post_order_ == NULL) {
- dfs_post_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
- kGrowableArrayDfsPostOrder);
- } else {
- /* Just reset the used length on the counter */
- dfs_post_order_->Reset();
- }
+ /* Clear the DFS pre-order and post-order lists. */
+ dfs_order_.clear();
+ dfs_order_.reserve(GetNumBlocks());
+ dfs_post_order_.clear();
+ dfs_post_order_.reserve(GetNumBlocks());
// Reset visited flags from all nodes
ClearAllVisitedFlags();
@@ -116,7 +100,7 @@
// Record dfs orders
RecordDFSOrders(GetEntryBlock());
- num_reachable_blocks_ = dfs_order_->Size();
+ num_reachable_blocks_ = dfs_order_.size();
if (num_reachable_blocks_ != num_blocks_) {
// Hide all unreachable blocks.
@@ -146,8 +130,8 @@
}
void MIRGraph::ComputeDefBlockMatrix() {
- int num_registers = cu_->num_dalvik_registers;
- /* Allocate num_dalvik_registers bit vector pointers */
+ int num_registers = GetNumOfCodeAndTempVRs();
+ /* Allocate num_registers bit vector pointers */
def_block_matrix_ = static_cast<ArenaBitVector**>
(arena_->Alloc(sizeof(ArenaBitVector *) * num_registers,
kArenaAllocDFInfo));
@@ -158,6 +142,7 @@
def_block_matrix_[i] =
new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix);
}
+
AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
FindLocalLiveIn(bb);
@@ -171,22 +156,18 @@
* Also set the incoming parameters as defs in the entry block.
* Only need to handle the parameters for the outer method.
*/
- int num_regs = cu_->num_dalvik_registers;
- int in_reg = num_regs - cu_->num_ins;
+ int num_regs = GetNumOfCodeVRs();
+ int in_reg = GetFirstInVR();
for (; in_reg < num_regs; in_reg++) {
def_block_matrix_[in_reg]->SetBit(GetEntryBlock()->id);
}
}
void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) {
- if (dom_post_order_traversal_ == NULL || max_num_reachable_blocks_ < num_reachable_blocks_) {
- // First time or too small - create the array.
- dom_post_order_traversal_ =
- new (arena_) GrowableArray<BasicBlockId>(arena_, num_reachable_blocks_,
- kGrowableArrayDomPostOrderTraversal);
- } else {
- dom_post_order_traversal_->Reset();
- }
+ // Clear the dominator post-order list.
+ dom_post_order_traversal_.clear();
+ dom_post_order_traversal_.reserve(num_reachable_blocks_);
+
ClearAllVisitedFlags();
DCHECK(temp_scoped_alloc_.get() != nullptr);
ScopedArenaVector<std::pair<BasicBlock*, ArenaBitVector::IndexIterator>> work_stack(
@@ -209,7 +190,7 @@
} else {
// no successor/next
if (curr_bb->id != NullBasicBlockId) {
- dom_post_order_traversal_->Insert(curr_bb->id);
+ dom_post_order_traversal_.push_back(curr_bb->id);
}
work_stack.pop_back();
@@ -245,15 +226,10 @@
CheckForDominanceFrontier(bb, GetBasicBlock(bb->fall_through));
}
if (bb->successor_block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
- while (true) {
- SuccessorBlockInfo *successor_block_info = iterator.Next();
- if (successor_block_info == NULL) {
- break;
- }
- BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
- CheckForDominanceFrontier(bb, succ_bb);
- }
+ for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
+ CheckForDominanceFrontier(bb, succ_bb);
+ }
}
/* Calculate DF_up */
@@ -274,11 +250,11 @@
if (bb->dominators == NULL) {
bb->dominators = new (arena_) ArenaBitVector(arena_, num_total_blocks,
- false /* expandable */, kBitMapDominators);
+ true /* expandable */, kBitMapDominators);
bb->i_dominated = new (arena_) ArenaBitVector(arena_, num_total_blocks,
- false /* expandable */, kBitMapIDominated);
+ true /* expandable */, kBitMapIDominated);
bb->dom_frontier = new (arena_) ArenaBitVector(arena_, num_total_blocks,
- false /* expandable */, kBitMapDomFrontier);
+ true /* expandable */, kBitMapDomFrontier);
} else {
bb->dominators->ClearAllBits();
bb->i_dominated->ClearAllBits();
@@ -317,13 +293,14 @@
}
/* Iterate through the predecessors */
- GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
+ auto it = bb->predecessors.begin(), end = bb->predecessors.end();
/* Find the first processed predecessor */
int idom = -1;
- while (true) {
- BasicBlock* pred_bb = GetBasicBlock(iter.Next());
- CHECK(pred_bb != NULL);
+ for ( ; ; ++it) {
+ CHECK(it != end);
+ BasicBlock* pred_bb = GetBasicBlock(*it);
+ DCHECK(pred_bb != nullptr);
if (i_dom_list_[pred_bb->dfs_id] != NOTVISITED) {
idom = pred_bb->dfs_id;
break;
@@ -331,11 +308,9 @@
}
/* Scan the rest of the predecessors */
- while (true) {
- BasicBlock* pred_bb = GetBasicBlock(iter.Next());
- if (!pred_bb) {
- break;
- }
+ for ( ; it != end; ++it) {
+ BasicBlock* pred_bb = GetBasicBlock(*it);
+ DCHECK(pred_bb != nullptr);
if (i_dom_list_[pred_bb->dfs_id] == NOTVISITED) {
continue;
} else {
@@ -368,7 +343,7 @@
if (bb != GetEntryBlock()) {
int idom_dfs_idx = i_dom_list_[bb->dfs_id];
DCHECK_NE(idom_dfs_idx, NOTVISITED);
- int i_dom_idx = dfs_post_order_->Get(idom_dfs_idx);
+ int i_dom_idx = dfs_post_order_[idom_dfs_idx];
BasicBlock* i_dom = GetBasicBlock(i_dom_idx);
bb->i_dom = i_dom->id;
/* Add bb to the i_dominated set of the immediate dominator block */
@@ -456,7 +431,7 @@
* insert a phi node if the variable is live-in to the block.
*/
bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb) {
- DCHECK_EQ(temp_bit_vector_size_, cu_->num_dalvik_registers);
+ DCHECK_EQ(temp_bit_vector_size_, cu_->mir_graph.get()->GetNumOfCodeAndTempVRs());
ArenaBitVector* temp_dalvik_register_v = temp_bit_vector_;
if (bb->data_flow_info == NULL) {
@@ -472,12 +447,7 @@
ComputeSuccLineIn(temp_dalvik_register_v, bb_fall_through->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
if (bb->successor_block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
- while (true) {
- SuccessorBlockInfo *successor_block_info = iterator.Next();
- if (successor_block_info == NULL) {
- break;
- }
+ for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) {
BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
if (succ_bb->data_flow_info) {
ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v,
@@ -507,7 +477,7 @@
}
/* Iterate through each Dalvik register */
- for (dalvik_reg = cu_->num_dalvik_registers - 1; dalvik_reg >= 0; dalvik_reg--) {
+ for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) {
input_blocks->Copy(def_block_matrix_[dalvik_reg]);
phi_blocks->ClearAllBits();
do {
@@ -554,8 +524,7 @@
int v_reg = SRegToVReg(ssa_reg);
/* Iterate through the predecessors */
- GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
- size_t num_uses = bb->predecessors->Size();
+ size_t num_uses = bb->predecessors.size();
AllocateSSAUseData(mir, num_uses);
int* uses = mir->ssa_rep->uses;
BasicBlockId* incoming =
@@ -563,14 +532,12 @@
kArenaAllocDFInfo));
mir->meta.phi_incoming = incoming;
int idx = 0;
- while (true) {
- BasicBlock* pred_bb = GetBasicBlock(iter.Next());
- if (!pred_bb) {
- break;
- }
+ for (BasicBlockId pred_id : bb->predecessors) {
+ BasicBlock* pred_bb = GetBasicBlock(pred_id);
+ DCHECK(pred_bb != nullptr);
int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map_exit[v_reg];
uses[idx] = ssa_reg;
- incoming[idx] = pred_bb->id;
+ incoming[idx] = pred_id;
idx++;
}
}
@@ -586,7 +553,7 @@
/* Process this block */
DoSSAConversion(block);
- int map_size = sizeof(int) * cu_->num_dalvik_registers;
+ int map_size = sizeof(int) * GetNumOfCodeAndTempVRs();
/* Save SSA map snapshot */
ScopedArenaAllocator allocator(&cu_->arena_stack);
@@ -605,12 +572,7 @@
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
}
if (block->successor_block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(block->successor_blocks);
- while (true) {
- SuccessorBlockInfo *successor_block_info = iterator.Next();
- if (successor_block_info == NULL) {
- break;
- }
+ for (SuccessorBlockInfo* successor_block_info : block->successor_blocks) {
BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
DoDFSPreOrderSSARename(succ_bb);
/* Restore SSA map snapshot */
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 1b3f2a1..9f0a696 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -38,7 +38,7 @@
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
#include "verifier/method_verifier-inl.h"
-#include "verifier/register_line.h"
+#include "verifier/reg_type-inl.h"
#include "verifier/register_line-inl.h"
namespace art {
@@ -127,7 +127,7 @@
dex_gc_map_.push_back((i >> 8) & 0xFF);
}
verifier::RegisterLine* line = method_verifier->GetRegLine(i);
- line->WriteReferenceBitMap(dex_gc_map_, ref_bitmap_bytes);
+ line->WriteReferenceBitMap(method_verifier, &dex_gc_map_, ref_bitmap_bytes);
}
}
DCHECK_EQ(dex_gc_map_.size(), table_size);
@@ -151,7 +151,7 @@
map_index++;
verifier::RegisterLine* line = method_verifier->GetRegLine(i);
for (size_t j = 0; j < code_item->registers_size_; j++) {
- if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
+ if (line->GetRegisterType(method_verifier, j).IsNonZeroReferenceTypes()) {
DCHECK_LT(j / 8, map.RegWidth());
DCHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
} else if ((j / 8) < map.RegWidth()) {
@@ -178,7 +178,7 @@
local_gc_points++;
max_insn = i;
verifier::RegisterLine* line = method_verifier->GetRegLine(i);
- max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
+ max_ref_reg = line->GetMaxNonZeroReferenceReg(method_verifier, max_ref_reg);
}
}
*gc_points = local_gc_points;
@@ -216,8 +216,9 @@
verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
(inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
- verifier::RegType&
- reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
+ const verifier::RegType&
+ reg_type(line->GetRegisterType(method_verifier,
+ is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
if (!reg_type.HasClass()) {
// We will compute devirtualization information only when we know the Class of the reg type.
@@ -284,18 +285,21 @@
const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
bool is_safe_cast = false;
if (code == Instruction::CHECK_CAST) {
- verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
- verifier::RegType& cast_type =
+ const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
+ inst->VRegA_21c()));
+ const verifier::RegType& cast_type =
method_verifier->ResolveCheckedClass(inst->VRegB_21c());
is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
} else {
- verifier::RegType& array_type(line->GetRegisterType(inst->VRegB_23x()));
+ const verifier::RegType& array_type(line->GetRegisterType(method_verifier,
+ inst->VRegB_23x()));
// We only know its safe to assign to an array if the array type is precise. For example,
// an Object[] can have any type of object stored in it, but it may also be assigned a
// String[] in which case the stores need to be of Strings.
if (array_type.IsPreciseReference()) {
- verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
- verifier::RegType& component_type = method_verifier->GetRegTypeCache()
+ const verifier::RegType& value_type(line->GetRegisterType(method_verifier,
+ inst->VRegA_23x()));
+ const verifier::RegType& component_type = method_verifier->GetRegTypeCache()
->GetComponentType(array_type, method_verifier->GetClassLoader());
is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
}
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index 4a3e071..bdfab13 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -252,7 +252,7 @@
// Special-case handling for format 35c/3rc invokes
Instruction::Code opcode = mir->dalvikInsn.opcode;
int flags = MIR::DecodedInstruction::IsPseudoMirOp(opcode) ?
- 0 : Instruction::FlagsOf(mir->dalvikInsn.opcode);
+ 0 : mir->dalvikInsn.FlagsOf();
if ((flags & Instruction::kInvoke) &&
(attrs & (DF_FORMAT_35C | DF_FORMAT_3RC))) {
DCHECK_EQ(next, 0);
@@ -401,33 +401,18 @@
return changed;
}
-static const char* storage_name[] = {" Frame ", "PhysReg", " Spill "};
+static const char* storage_name[] = {" Frame ", "PhysReg", " CompilerTemp "};
void MIRGraph::DumpRegLocTable(RegLocation* table, int count) {
- // FIXME: Quick-specific. Move to Quick (and make a generic version for MIRGraph?
- Mir2Lir* cg = static_cast<Mir2Lir*>(cu_->cg.get());
- if (cg != NULL) {
- for (int i = 0; i < count; i++) {
- LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c 0x%04x S%d",
- table[i].orig_sreg, storage_name[table[i].location],
- table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
- table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
- table[i].is_const ? 'c' : 'n',
- table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
- table[i].reg.GetRawBits(),
- table[i].s_reg_low);
- }
- } else {
- // Either pre-regalloc or Portable.
- for (int i = 0; i < count; i++) {
- LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c S%d",
- table[i].orig_sreg, storage_name[table[i].location],
- table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
- table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
- table[i].is_const ? 'c' : 'n',
- table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
- table[i].s_reg_low);
- }
+ for (int i = 0; i < count; i++) {
+ LOG(INFO) << StringPrintf("Loc[%02d] : %s, %c %c %c %c %c %c 0x%04x S%d",
+ table[i].orig_sreg, storage_name[table[i].location],
+ table[i].wide ? 'W' : 'N', table[i].defined ? 'D' : 'U',
+ table[i].fp ? 'F' : table[i].ref ? 'R' :'C',
+ table[i].is_const ? 'c' : 'n',
+ table[i].high_word ? 'H' : 'L', table[i].home ? 'h' : 't',
+ table[i].reg.GetRawBits(),
+ table[i].s_reg_low);
}
}
@@ -436,7 +421,9 @@
RegStorage(), INVALID_SREG, INVALID_SREG};
void MIRGraph::InitRegLocations() {
- /* Allocate the location map */
+ // Allocate the location map. We also include the maximum possible temps because
+ // the temp allocation initializes reg location as well (in order to deal with
+ // case when it will be called after this pass).
int max_regs = GetNumSSARegs() + GetMaxPossibleCompilerTemps();
RegLocation* loc = static_cast<RegLocation*>(arena_->Alloc(max_regs * sizeof(*loc),
kArenaAllocRegAlloc));
@@ -447,22 +434,18 @@
loc[i].wide = false;
}
- /* Patch up the locations for the compiler temps */
- GrowableArray<CompilerTemp*>::Iterator iter(&compiler_temps_);
- for (CompilerTemp* ct = iter.Next(); ct != NULL; ct = iter.Next()) {
- loc[ct->s_reg_low].location = kLocCompilerTemp;
- loc[ct->s_reg_low].defined = true;
- }
-
/* Treat Method* as a normal reference */
- loc[GetMethodSReg()].ref = true;
+ int method_sreg = GetMethodSReg();
+ loc[method_sreg].ref = true;
+ loc[method_sreg].location = kLocCompilerTemp;
+ loc[method_sreg].defined = true;
reg_location_ = loc;
- int num_regs = cu_->num_dalvik_registers;
+ int num_regs = GetNumOfCodeVRs();
/* Add types of incoming arguments based on signature */
- int num_ins = cu_->num_ins;
+ int num_ins = GetNumOfInVRs();
if (num_ins > 0) {
int s_reg = num_regs - num_ins;
if ((cu_->access_flags & kAccStatic) == 0) {
@@ -517,11 +500,9 @@
*/
void MIRGraph::RemapRegLocations() {
for (int i = 0; i < GetNumSSARegs(); i++) {
- if (reg_location_[i].location != kLocCompilerTemp) {
- int orig_sreg = reg_location_[i].s_reg_low;
- reg_location_[i].orig_sreg = orig_sreg;
- reg_location_[i].s_reg_low = SRegToVReg(orig_sreg);
- }
+ int orig_sreg = reg_location_[i].s_reg_low;
+ reg_location_[i].orig_sreg = orig_sreg;
+ reg_location_[i].s_reg_low = SRegToVReg(orig_sreg);
}
}
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 022ec6b..4d5d253 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -20,6 +20,7 @@
#include "compiler_driver.h"
#include "dex/compiler_ir.h"
+#include "dex_compilation_unit.h"
#include "field_helper.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index fa2a560..cdb816d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -342,6 +342,8 @@
freezing_constructor_lock_("freezing constructor lock"),
compiled_classes_lock_("compiled classes lock"),
compiled_methods_lock_("compiled method lock"),
+ compiled_methods_(),
+ non_relative_linker_patch_count_(0u),
image_(image),
image_classes_(image_classes),
thread_count_(thread_count),
@@ -355,8 +357,8 @@
compiler_enable_auto_elf_loading_(nullptr),
compiler_get_method_code_addr_(nullptr),
support_boot_image_fixup_(instruction_set != kMips),
- cfi_info_(nullptr),
dedupe_code_("dedupe code"),
+ dedupe_src_mapping_table_("dedupe source mapping table"),
dedupe_mapping_table_("dedupe mapping table"),
dedupe_vmap_table_("dedupe vmap table"),
dedupe_gc_map_("dedupe gc map"),
@@ -378,11 +380,6 @@
CHECK(image_classes_.get() == nullptr);
}
- // Are we generating CFI information?
- if (compiler_options->GetGenerateGDBInformation()) {
- cfi_info_.reset(compiler_->GetCallFrameInformationInitialization(*this));
- }
-
// Read the profile file if one is provided.
if (!profile_file.empty()) {
profile_present_ = profile_file_.LoadFile(profile_file);
@@ -398,6 +395,10 @@
return dedupe_code_.Add(Thread::Current(), code);
}
+SrcMap* CompilerDriver::DeduplicateSrcMappingTable(const SrcMap& src_map) {
+ return dedupe_src_mapping_table_.Add(Thread::Current(), src_map);
+}
+
std::vector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const std::vector<uint8_t>& code) {
return dedupe_mapping_table_.Add(Thread::Current(), code);
}
@@ -427,18 +428,6 @@
MutexLock mu(self, compiled_methods_lock_);
STLDeleteValues(&compiled_methods_);
}
- {
- MutexLock mu(self, compiled_methods_lock_);
- STLDeleteElements(&code_to_patch_);
- }
- {
- MutexLock mu(self, compiled_methods_lock_);
- STLDeleteElements(&methods_to_patch_);
- }
- {
- MutexLock mu(self, compiled_methods_lock_);
- STLDeleteElements(&classes_to_patch_);
- }
CHECK_PTHREAD_CALL(pthread_key_delete, (tls_key_), "delete tls key");
compiler_->UnInit();
}
@@ -447,7 +436,7 @@
// Lazily create thread-local storage
CompilerTls* res = static_cast<CompilerTls*>(pthread_getspecific(tls_key_));
if (res == nullptr) {
- res = new CompilerTls();
+ res = compiler_->CreateNewCompilerTls();
CHECK_PTHREAD_CALL(pthread_setspecific, (tls_key_, res), "compiler tls");
}
return res;
@@ -628,7 +617,7 @@
}
}
-static void ResolveExceptionsForMethod(MethodHelper* mh,
+static void ResolveExceptionsForMethod(MutableMethodHelper* mh,
std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile::CodeItem* code_item = mh->GetMethod()->GetCodeItem();
@@ -671,7 +660,7 @@
std::set<std::pair<uint16_t, const DexFile*>>* exceptions_to_resolve =
reinterpret_cast<std::set<std::pair<uint16_t, const DexFile*>>*>(arg);
StackHandleScope<1> hs(Thread::Current());
- MethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
for (size_t i = 0; i < c->NumVirtualMethods(); ++i) {
mh.ChangeMethod(c->GetVirtualMethod(i));
ResolveExceptionsForMethod(&mh, *exceptions_to_resolve);
@@ -762,7 +751,7 @@
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
// Make a copy of the handle so that we don't clobber it doing Assign.
- Handle<mirror::Class> klass(hs.NewHandle(c.Get()));
+ MutableHandle<mirror::Class> klass(hs.NewHandle(c.Get()));
std::string temp;
while (!klass->IsObjectClass()) {
const char* descriptor = klass->GetDescriptor(&temp);
@@ -796,14 +785,11 @@
if (IsImage()) {
TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
// Update image_classes_ with classes for objects created by <clinit> methods.
- Thread* self = Thread::Current();
- const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
gc::Heap* heap = Runtime::Current()->GetHeap();
// TODO: Image spaces only?
ScopedObjectAccess soa(Thread::Current());
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ WriterMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
heap->VisitObjects(FindClinitImageClassesCallback, this);
- self->EndAssertNoThreadSuspension(old_cause);
}
}
@@ -972,6 +958,43 @@
}
}
+bool CompilerDriver::CanEmbedReferenceTypeInCode(ClassReference* ref,
+ bool* use_direct_ptr,
+ uintptr_t* direct_type_ptr) {
+ CHECK(ref != nullptr);
+ CHECK(use_direct_ptr != nullptr);
+ CHECK(direct_type_ptr != nullptr);
+
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference();
+ bool is_initialized = false;
+ bool unused_finalizable;
+ // Make sure we have a finished Reference class object before attempting to use it.
+ if (!CanEmbedTypeInCode(*reference_class->GetDexCache()->GetDexFile(),
+ reference_class->GetDexTypeIndex(), &is_initialized,
+ use_direct_ptr, direct_type_ptr, &unused_finalizable) ||
+ !is_initialized) {
+ return false;
+ }
+ ref->first = &reference_class->GetDexFile();
+ ref->second = reference_class->GetDexClassDefIndex();
+ return true;
+}
+
+uint32_t CompilerDriver::GetReferenceSlowFlagOffset() const {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* klass = mirror::Reference::GetJavaLangRefReference();
+ DCHECK(klass->IsInitialized());
+ return klass->GetSlowPathFlagOffset().Uint32Value();
+}
+
+uint32_t CompilerDriver::GetReferenceDisableFlagOffset() const {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* klass = mirror::Reference::GetJavaLangRefReference();
+ DCHECK(klass->IsInitialized());
+ return klass->GetDisableIntrinsicFlagOffset().Uint32Value();
+}
+
void CompilerDriver::ProcessedInstanceField(bool resolved) {
if (!resolved) {
stats_->UnresolvedInstanceField();
@@ -1287,75 +1310,6 @@
return result;
}
-void CompilerDriver::AddCodePatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset) {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- code_to_patch_.push_back(new CallPatchInformation(dex_file,
- referrer_class_def_idx,
- referrer_method_idx,
- referrer_invoke_type,
- target_method_idx,
- target_dex_file,
- target_invoke_type,
- literal_offset));
-}
-void CompilerDriver::AddRelativeCodePatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset,
- int32_t pc_relative_offset) {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- code_to_patch_.push_back(new RelativeCallPatchInformation(dex_file,
- referrer_class_def_idx,
- referrer_method_idx,
- referrer_invoke_type,
- target_method_idx,
- target_dex_file,
- target_invoke_type,
- literal_offset,
- pc_relative_offset));
-}
-void CompilerDriver::AddMethodPatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset) {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- methods_to_patch_.push_back(new CallPatchInformation(dex_file,
- referrer_class_def_idx,
- referrer_method_idx,
- referrer_invoke_type,
- target_method_idx,
- target_dex_file,
- target_invoke_type,
- literal_offset));
-}
-void CompilerDriver::AddClassPatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- uint32_t target_type_idx,
- size_t literal_offset) {
- MutexLock mu(Thread::Current(), compiled_methods_lock_);
- classes_to_patch_.push_back(new TypePatchInformation(dex_file,
- referrer_class_def_idx,
- referrer_method_idx,
- target_type_idx,
- literal_offset));
-}
-
class ParallelCompilationManager {
public:
typedef void Callback(const ParallelCompilationManager* manager, size_t index);
@@ -1699,15 +1653,15 @@
*/
Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
std::string error_msg;
- if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache, class_loader, &class_def, true,
- &error_msg) ==
+ if (verifier::MethodVerifier::VerifyClass(soa.Self(), &dex_file, dex_cache, class_loader,
+ &class_def, true, &error_msg) ==
verifier::MethodVerifier::kHardFailure) {
LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
<< " because: " << error_msg;
}
} else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
CHECK(klass->IsResolved()) << PrettyClass(klass.Get());
- class_linker->VerifyClass(klass);
+ class_linker->VerifyClass(soa.Self(), klass);
if (klass->IsErroneous()) {
// ClassLinker::VerifyClass throws, which isn't useful in the compiler.
@@ -1796,7 +1750,7 @@
if (klass->IsVerified()) {
// Attempt to initialize the class but bail if we either need to initialize the super-class
// or static fields.
- manager->GetClassLinker()->EnsureInitialized(klass, false, false);
+ manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false);
if (!klass->IsInitialized()) {
// We don't want non-trivial class initialization occurring on multiple threads due to
// deadlock problems. For example, a parent class is initialized (holding its lock) that
@@ -1810,7 +1764,7 @@
ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
// Attempt to initialize allowing initialization of parent classes but still not static
// fields.
- manager->GetClassLinker()->EnsureInitialized(klass, false, true);
+ manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
if (!klass->IsInitialized()) {
// We need to initialize static fields, we only do this for image classes that aren't
// marked with the $NoPreloadHolder (which implies this should not be initialized early).
@@ -1829,11 +1783,13 @@
// Run the class initializer in transaction mode.
runtime->EnterTransactionMode(&transaction);
const mirror::Class::Status old_status = klass->GetStatus();
- bool success = manager->GetClassLinker()->EnsureInitialized(klass, true, true);
+ bool success = manager->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
+ true);
// TODO we detach transaction from runtime to indicate we quit the transactional
// mode which prevents the GC from visiting objects modified during the transaction.
// Ensure GC is not run so don't access freed objects when aborting transaction.
- const char* old_casue = soa.Self()->StartAssertNoThreadSuspension("Transaction end");
+
+ ScopedAssertNoThreadSuspension ants(soa.Self(), "Transaction end");
runtime->ExitTransactionMode();
if (!success) {
@@ -1846,7 +1802,6 @@
transaction.Abort();
CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
}
- soa.Self()->EndAssertNoThreadSuspension(old_casue);
}
}
soa.Self()->AssertNoPendingException();
@@ -2040,11 +1995,19 @@
Thread* self = Thread::Current();
if (compiled_method != nullptr) {
+ // Count non-relative linker patches.
+ size_t non_relative_linker_patch_count = 0u;
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() != kLinkerPatchCallRelative) {
+ ++non_relative_linker_patch_count;
+ }
+ }
MethodReference ref(&dex_file, method_idx);
DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file);
{
MutexLock mu(self, compiled_methods_lock_);
compiled_methods_.Put(ref, compiled_method);
+ non_relative_linker_patch_count_ += non_relative_linker_patch_count;
}
DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file);
}
@@ -2102,6 +2065,11 @@
return it->second;
}
+size_t CompilerDriver::GetNonRelativeLinkerPatchCount() const {
+ MutexLock mu(Thread::Current(), compiled_methods_lock_);
+ return non_relative_linker_patch_count_;
+}
+
void CompilerDriver::AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
uint16_t class_def_index) {
WriterMutexLock mu(self, freezing_constructor_lock_);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a165901..c445683 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -76,20 +76,6 @@
kOptimize // Perform required transformation and peep-hole optimizations.
};
-// Thread-local storage compiler worker threads
-class CompilerTls {
- public:
- CompilerTls() : llvm_info_(nullptr) {}
- ~CompilerTls() {}
-
- void* GetLLVMInfo() { return llvm_info_; }
-
- void SetLLVMInfo(void* llvm_info) { llvm_info_ = llvm_info; }
-
- private:
- void* llvm_info_;
-};
-
class CompilerDriver {
public:
// Create a compiler targeting the requested "instruction_set".
@@ -183,6 +169,8 @@
CompiledMethod* GetCompiledMethod(MethodReference ref) const
LOCKS_EXCLUDED(compiled_methods_lock_);
+ size_t GetNonRelativeLinkerPatchCount() const
+ LOCKS_EXCLUDED(compiled_methods_lock_);
void AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
uint16_t class_def_index);
@@ -211,6 +199,12 @@
bool* is_type_initialized, bool* use_direct_type_ptr,
uintptr_t* direct_type_ptr, bool* out_is_finalizable);
+ // Query methods for the java.lang.ref.Reference class.
+ bool CanEmbedReferenceTypeInCode(ClassReference* ref,
+ bool* use_direct_type_ptr, uintptr_t* direct_type_ptr);
+ uint32_t GetReferenceSlowFlagOffset() const;
+ uint32_t GetReferenceDisableFlagOffset() const;
+
// Get the DexCache for the
mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -268,7 +262,7 @@
uint16_t* declaring_class_idx, uint16_t* declaring_method_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Get declaration location of a resolved field.
+ // Get the index in the vtable of the method.
uint16_t GetResolvedMethodVTableIndex(
mirror::ArtMethod* resolved_method, InvokeType type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -321,42 +315,6 @@
const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
- // Record patch information for later fix up.
- void AddCodePatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset)
- LOCKS_EXCLUDED(compiled_methods_lock_);
- void AddRelativeCodePatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset,
- int32_t pc_relative_offset)
- LOCKS_EXCLUDED(compiled_methods_lock_);
- void AddMethodPatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset)
- LOCKS_EXCLUDED(compiled_methods_lock_);
- void AddClassPatch(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- uint32_t target_method_idx,
- size_t literal_offset)
- LOCKS_EXCLUDED(compiled_methods_lock_);
-
bool GetSupportBootImageFixup() const {
return support_boot_image_fixup_;
}
@@ -393,198 +351,14 @@
return thread_count_;
}
- class CallPatchInformation;
- class TypePatchInformation;
-
bool GetDumpPasses() const {
return dump_passes_;
}
- bool DidIncludeDebugSymbols() const {
- return compiler_options_->GetIncludeDebugSymbols();
- }
-
CumulativeLogger* GetTimingsLogger() const {
return timings_logger_;
}
- class PatchInformation {
- public:
- const DexFile& GetDexFile() const {
- return *dex_file_;
- }
- uint16_t GetReferrerClassDefIdx() const {
- return referrer_class_def_idx_;
- }
- uint32_t GetReferrerMethodIdx() const {
- return referrer_method_idx_;
- }
- size_t GetLiteralOffset() const {
- return literal_offset_;
- }
-
- virtual bool IsCall() const {
- return false;
- }
- virtual bool IsType() const {
- return false;
- }
- virtual const CallPatchInformation* AsCall() const {
- LOG(FATAL) << "Unreachable";
- return nullptr;
- }
- virtual const TypePatchInformation* AsType() const {
- LOG(FATAL) << "Unreachable";
- return nullptr;
- }
-
- protected:
- PatchInformation(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- size_t literal_offset)
- : dex_file_(dex_file),
- referrer_class_def_idx_(referrer_class_def_idx),
- referrer_method_idx_(referrer_method_idx),
- literal_offset_(literal_offset) {
- CHECK(dex_file_ != nullptr);
- }
- virtual ~PatchInformation() {}
-
- const DexFile* const dex_file_;
- const uint16_t referrer_class_def_idx_;
- const uint32_t referrer_method_idx_;
- const size_t literal_offset_;
-
- friend class CompilerDriver;
- };
-
- class CallPatchInformation : public PatchInformation {
- public:
- InvokeType GetReferrerInvokeType() const {
- return referrer_invoke_type_;
- }
- uint32_t GetTargetMethodIdx() const {
- return target_method_idx_;
- }
- const DexFile* GetTargetDexFile() const {
- return target_dex_file_;
- }
- InvokeType GetTargetInvokeType() const {
- return target_invoke_type_;
- }
-
- const CallPatchInformation* AsCall() const {
- return this;
- }
- bool IsCall() const {
- return true;
- }
- virtual bool IsRelative() const {
- return false;
- }
- virtual int RelativeOffset() const {
- return 0;
- }
-
- protected:
- CallPatchInformation(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset)
- : PatchInformation(dex_file, referrer_class_def_idx,
- referrer_method_idx, literal_offset),
- referrer_invoke_type_(referrer_invoke_type),
- target_method_idx_(target_method_idx),
- target_dex_file_(target_dex_file),
- target_invoke_type_(target_invoke_type) {
- }
-
- private:
- const InvokeType referrer_invoke_type_;
- const uint32_t target_method_idx_;
- const DexFile* target_dex_file_;
- const InvokeType target_invoke_type_;
-
- friend class CompilerDriver;
- DISALLOW_COPY_AND_ASSIGN(CallPatchInformation);
- };
-
- class RelativeCallPatchInformation : public CallPatchInformation {
- public:
- bool IsRelative() const {
- return true;
- }
- int RelativeOffset() const {
- return offset_;
- }
-
- private:
- RelativeCallPatchInformation(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- InvokeType referrer_invoke_type,
- uint32_t target_method_idx,
- const DexFile* target_dex_file,
- InvokeType target_invoke_type,
- size_t literal_offset,
- int32_t pc_relative_offset)
- : CallPatchInformation(dex_file, referrer_class_def_idx,
- referrer_method_idx, referrer_invoke_type, target_method_idx,
- target_dex_file, target_invoke_type, literal_offset),
- offset_(pc_relative_offset) {
- }
-
- const int offset_;
-
- friend class CompilerDriver;
- DISALLOW_COPY_AND_ASSIGN(RelativeCallPatchInformation);
- };
-
- class TypePatchInformation : public PatchInformation {
- public:
- uint32_t GetTargetTypeIdx() const {
- return target_type_idx_;
- }
-
- bool IsType() const {
- return true;
- }
- const TypePatchInformation* AsType() const {
- return this;
- }
-
- private:
- TypePatchInformation(const DexFile* dex_file,
- uint16_t referrer_class_def_idx,
- uint32_t referrer_method_idx,
- uint32_t target_type_idx,
- size_t literal_offset)
- : PatchInformation(dex_file, referrer_class_def_idx,
- referrer_method_idx, literal_offset),
- target_type_idx_(target_type_idx) {
- }
-
- const uint32_t target_type_idx_;
-
- friend class CompilerDriver;
- DISALLOW_COPY_AND_ASSIGN(TypePatchInformation);
- };
-
- const std::vector<const CallPatchInformation*>& GetCodeToPatch() const {
- return code_to_patch_;
- }
- const std::vector<const CallPatchInformation*>& GetMethodsToPatch() const {
- return methods_to_patch_;
- }
- const std::vector<const TypePatchInformation*>& GetClassesToPatch() const {
- return classes_to_patch_;
- }
-
// Checks if class specified by type_idx is one of the image_classes_
bool IsImageClass(const char* descriptor) const;
@@ -592,19 +366,12 @@
LOCKS_EXCLUDED(compiled_classes_lock_);
std::vector<uint8_t>* DeduplicateCode(const std::vector<uint8_t>& code);
+ SrcMap* DeduplicateSrcMappingTable(const SrcMap& src_map);
std::vector<uint8_t>* DeduplicateMappingTable(const std::vector<uint8_t>& code);
std::vector<uint8_t>* DeduplicateVMapTable(const std::vector<uint8_t>& code);
std::vector<uint8_t>* DeduplicateGCMap(const std::vector<uint8_t>& code);
std::vector<uint8_t>* DeduplicateCFIInfo(const std::vector<uint8_t>* cfi_info);
- /*
- * @brief return the pointer to the Call Frame Information.
- * @return pointer to call frame information for this compilation.
- */
- std::vector<uint8_t>* GetCallFrameInformation() const {
- return cfi_info_.get();
- }
-
ProfileFile profile_file_;
bool profile_present_;
@@ -701,10 +468,6 @@
static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- std::vector<const CallPatchInformation*> code_to_patch_;
- std::vector<const CallPatchInformation*> methods_to_patch_;
- std::vector<const TypePatchInformation*> classes_to_patch_;
-
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -727,6 +490,9 @@
// All method references that this compiler has compiled.
mutable Mutex compiled_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
MethodTable compiled_methods_ GUARDED_BY(compiled_methods_lock_);
+ // Number of non-relative patches in all compiled methods. These patches need space
+ // in the .oat_patches ELF section if requested in the compiler options.
+ size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_);
const bool image_;
@@ -775,19 +541,17 @@
bool support_boot_image_fixup_;
- // Call Frame Information, which might be generated to help stack tracebacks.
- std::unique_ptr<std::vector<uint8_t>> cfi_info_;
-
// DeDuplication data structures, these own the corresponding byte arrays.
+ template <typename ByteArray>
class DedupeHashFunc {
public:
- size_t operator()(const std::vector<uint8_t>& array) const {
+ size_t operator()(const ByteArray& array) const {
// For small arrays compute a hash using every byte.
static const size_t kSmallArrayThreshold = 16;
size_t hash = 0x811c9dc5;
if (array.size() <= kSmallArrayThreshold) {
- for (uint8_t b : array) {
- hash = (hash * 16777619) ^ b;
+ for (auto b : array) {
+ hash = (hash * 16777619) ^ static_cast<uint8_t>(b);
}
} else {
// For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
@@ -795,12 +559,12 @@
// values at random.
static const size_t kRandomHashCount = 16;
for (size_t i = 0; i < 2; ++i) {
- uint8_t b = array[i + 6];
+ uint8_t b = static_cast<uint8_t>(array[i + 6]);
hash = (hash * 16777619) ^ b;
}
for (size_t i = 2; i < kRandomHashCount; ++i) {
size_t r = i * 1103515245 + 12345;
- uint8_t b = array[r % array.size()];
+ uint8_t b = static_cast<uint8_t>(array[r % array.size()]);
hash = (hash * 16777619) ^ b;
}
}
@@ -812,11 +576,13 @@
return hash;
}
};
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_cfi_info_;
+
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_code_;
+ DedupeSet<SrcMap, size_t, DedupeHashFunc<SrcMap>, 4> dedupe_src_mapping_table_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_mapping_table_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_vmap_table_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_gc_map_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc<std::vector<uint8_t>>, 4> dedupe_cfi_info_;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 9ba6645..9ae9bd4 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -129,12 +129,10 @@
<< " "
<< dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
<< " " << dex->GetMethodName(dex->GetMethodId(i));
-#if defined(ART_USE_PORTABLE_COMPILER)
EXPECT_TRUE(method->GetEntryPointFromPortableCompiledCode() != NULL) << "method_idx=" << i
<< " "
<< dex->GetMethodDeclaringClassDescriptor(dex->GetMethodId(i))
<< " " << dex->GetMethodName(dex->GetMethodId(i));
-#endif
}
EXPECT_EQ(dex->NumFieldIds(), dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index c0f91d1..eb3de97 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -27,7 +27,8 @@
kSpace, // Maximize space savings.
kBalanced, // Try to get the best performance return on compilation investment.
kSpeed, // Maximize runtime performance.
- kEverything // Force compilation (Note: excludes compilaton of class initializers).
+ kEverything, // Force compilation (Note: excludes compilation of class initializers).
+ kTime // Compile methods, but minimize compilation time.
};
// Guide heuristics to determine whether to compile method if profile data not available.
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 840b0ad..986fc71 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -23,18 +23,6 @@
namespace art {
-DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu)
- : cu_(cu),
- class_loader_(cu->class_loader),
- class_linker_(cu->class_linker),
- dex_file_(cu->dex_file),
- code_item_(cu->code_item),
- class_def_idx_(cu->class_def_idx),
- dex_method_idx_(cu->method_idx),
- access_flags_(cu->access_flags),
- verified_method_(cu_->compiler_driver->GetVerifiedMethod(cu->dex_file, cu->method_idx)) {
-}
-
DexCompilationUnit::DexCompilationUnit(CompilationUnit* cu,
jobject class_loader,
ClassLinker* class_linker,
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
new file mode 100644
index 0000000..3be2478
--- /dev/null
+++ b/compiler/elf_builder.h
@@ -0,0 +1,1170 @@
+/*
+ * 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_COMPILER_ELF_BUILDER_H_
+#define ART_COMPILER_ELF_BUILDER_H_
+
+#include "base/stl_util.h"
+#include "buffered_output_stream.h"
+#include "elf_utils.h"
+#include "file_output_stream.h"
+#include "instruction_set.h"
+
+namespace art {
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfSectionBuilder {
+ public:
+ ElfSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link, Elf_Word info,
+ Elf_Word align, Elf_Word entsize) : name_(sec_name), link_(link) {
+ memset(§ion_, 0, sizeof(section_));
+ section_.sh_type = type;
+ section_.sh_flags = flags;
+ section_.sh_info = info;
+ section_.sh_addralign = align;
+ section_.sh_entsize = entsize;
+ }
+
+ virtual ~ElfSectionBuilder() {}
+
+ Elf_Shdr section_;
+ Elf_Word section_index_ = 0;
+
+ Elf_Word GetLink() {
+ return (link_) ? link_->section_index_ : 0;
+ }
+
+ const std::string name_;
+
+ protected:
+ const ElfSectionBuilder* link_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Dyn, typename Elf_Shdr>
+class ElfDynamicBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ void AddDynamicTag(Elf_Sword tag, Elf_Word d_un) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({nullptr, tag, d_un});
+ }
+
+ void AddDynamicTag(Elf_Sword tag, Elf_Word d_un,
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section) {
+ if (tag == DT_NULL) {
+ return;
+ }
+ dynamics_.push_back({section, tag, d_un});
+ }
+
+ ElfDynamicBuilder(const std::string& sec_name,
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *link)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC,
+ link, 0, kPageSize, sizeof(Elf_Dyn)) {}
+ ~ElfDynamicBuilder() {}
+
+ Elf_Word GetSize() {
+ // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
+ // these must be added when we actually put the file together because
+ // their values are very dependent on state.
+ return dynamics_.size() + 3;
+ }
+
+ // Create the actual dynamic vector. strsz should be the size of the .dynstr
+ // table and soname_off should be the offset of the soname in .dynstr.
+ // Since niether can be found prior to final layout we will wait until here
+ // to add them.
+ std::vector<Elf_Dyn> GetDynamics(Elf_Word strsz, Elf_Word soname) {
+ std::vector<Elf_Dyn> ret;
+ for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
+ if (it->section_) {
+ // We are adding an address relative to a section.
+ ret.push_back(
+ {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
+ } else {
+ ret.push_back({it->tag_, {it->off_}});
+ }
+ }
+ ret.push_back({DT_STRSZ, {strsz}});
+ ret.push_back({DT_SONAME, {soname}});
+ ret.push_back({DT_NULL, {0}});
+ return ret;
+ }
+
+ protected:
+ struct ElfDynamicState {
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+ Elf_Sword tag_;
+ Elf_Word off_;
+ };
+ std::vector<ElfDynamicState> dynamics_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfRawSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ ElfRawSectionBuilder(const std::string& sec_name, Elf_Word type, Elf_Word flags,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* link, Elf_Word info,
+ Elf_Word align, Elf_Word entsize)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, link, info, align,
+ entsize) {}
+ ~ElfRawSectionBuilder() {}
+ std::vector<uint8_t>* GetBuffer() { return &buf_; }
+ void SetBuffer(std::vector<uint8_t>&& buf) { buf_ = buf; }
+
+ protected:
+ std::vector<uint8_t> buf_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Shdr>
+class ElfOatSectionBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ ElfOatSectionBuilder(const std::string& sec_name, Elf_Word size, Elf_Word offset,
+ Elf_Word type, Elf_Word flags)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, flags, nullptr, 0, kPageSize,
+ 0), offset_(offset), size_(size) {}
+ ~ElfOatSectionBuilder() {}
+
+ Elf_Word GetOffset() {
+ return offset_;
+ }
+
+ Elf_Word GetSize() {
+ return size_;
+ }
+
+ protected:
+ // Offset of the content within the file.
+ Elf_Word offset_;
+ // Size of the content within the file.
+ Elf_Word size_;
+};
+
+static inline constexpr uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
+ return ((binding) << 4) + ((type) & 0xf);
+}
+
+// from bionic
+static inline unsigned elfhash(const char *_name) {
+ const unsigned char *name = (const unsigned char *) _name;
+ unsigned h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+ return h;
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Sym,
+ typename Elf_Shdr>
+class ElfSymtabBuilder : public ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> {
+ public:
+ // Add a symbol with given name to this symtab. The symbol refers to
+ // 'relative_addr' within the given section and has the given attributes.
+ void AddSymbol(const std::string& name,
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section,
+ Elf_Addr addr,
+ bool is_relative,
+ Elf_Word size,
+ uint8_t binding,
+ uint8_t type,
+ uint8_t other = 0) {
+ CHECK(section);
+ ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
+ MakeStInfo(binding, type), other, 0};
+ symbols_.push_back(state);
+ }
+
+ ElfSymtabBuilder(const std::string& sec_name, Elf_Word type,
+ const std::string& str_name, Elf_Word str_type, bool alloc)
+ : ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>(sec_name, type, ((alloc) ? SHF_ALLOC : 0U),
+ &strtab_, 0, sizeof(Elf_Word),
+ sizeof(Elf_Sym)), str_name_(str_name),
+ str_type_(str_type),
+ strtab_(str_name,
+ str_type,
+ ((alloc) ? SHF_ALLOC : 0U),
+ nullptr, 0, 1, 1) {}
+ ~ElfSymtabBuilder() {}
+
+ std::vector<Elf_Word> GenerateHashContents() {
+ // Here is how The ELF hash table works.
+ // There are 3 arrays to worry about.
+ // * The symbol table where the symbol information is.
+ // * The bucket array which is an array of indexes into the symtab and chain.
+ // * The chain array which is also an array of indexes into the symtab and chain.
+ //
+ // Lets say the state is something like this.
+ // +--------+ +--------+ +-----------+
+ // | symtab | | bucket | | chain |
+ // | null | | 1 | | STN_UNDEF |
+ // | <sym1> | | 4 | | 2 |
+ // | <sym2> | | | | 5 |
+ // | <sym3> | | | | STN_UNDEF |
+ // | <sym4> | | | | 3 |
+ // | <sym5> | | | | STN_UNDEF |
+ // +--------+ +--------+ +-----------+
+ //
+ // The lookup process (in python psudocode) is
+ //
+ // def GetSym(name):
+ // # NB STN_UNDEF == 0
+ // indx = bucket[elfhash(name) % num_buckets]
+ // while indx != STN_UNDEF:
+ // if GetSymbolName(symtab[indx]) == name:
+ // return symtab[indx]
+ // indx = chain[indx]
+ // return SYMBOL_NOT_FOUND
+ //
+ // Between bucket and chain arrays every symtab index must be present exactly
+ // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
+
+ // Select number of buckets.
+ // This is essentially arbitrary.
+ Elf_Word nbuckets;
+ Elf_Word chain_size = GetSize();
+ if (symbols_.size() < 8) {
+ nbuckets = 2;
+ } else if (symbols_.size() < 32) {
+ nbuckets = 4;
+ } else if (symbols_.size() < 256) {
+ nbuckets = 16;
+ } else {
+ // Have about 32 ids per bucket.
+ nbuckets = RoundUp(symbols_.size()/32, 2);
+ }
+ std::vector<Elf_Word> hash;
+ hash.push_back(nbuckets);
+ hash.push_back(chain_size);
+ uint32_t bucket_offset = hash.size();
+ uint32_t chain_offset = bucket_offset + nbuckets;
+ hash.resize(hash.size() + nbuckets + chain_size, 0);
+
+ Elf_Word* buckets = hash.data() + bucket_offset;
+ Elf_Word* chain = hash.data() + chain_offset;
+
+ // Set up the actual hash table.
+ for (Elf_Word i = 0; i < symbols_.size(); i++) {
+ // Add 1 since we need to have the null symbol that is not in the symbols
+ // list.
+ Elf_Word index = i + 1;
+ Elf_Word hash_val = static_cast<Elf_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
+ if (buckets[hash_val] == 0) {
+ buckets[hash_val] = index;
+ } else {
+ hash_val = buckets[hash_val];
+ CHECK_LT(hash_val, chain_size);
+ while (chain[hash_val] != 0) {
+ hash_val = chain[hash_val];
+ CHECK_LT(hash_val, chain_size);
+ }
+ chain[hash_val] = index;
+ // Check for loops. Works because if this is non-empty then there must be
+ // another cell which already contains the same symbol index as this one,
+ // which means some symbol has more then one name, which isn't allowed.
+ CHECK_EQ(chain[index], static_cast<Elf_Word>(0));
+ }
+ }
+
+ return hash;
+ }
+
+ std::string GenerateStrtab() {
+ std::string tab;
+ tab += '\0';
+ for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
+ it->name_idx_ = tab.size();
+ tab += it->name_;
+ tab += '\0';
+ }
+ strtab_.section_.sh_size = tab.size();
+ return tab;
+ }
+
+ std::vector<Elf_Sym> GenerateSymtab() {
+ std::vector<Elf_Sym> ret;
+ Elf_Sym undef_sym;
+ memset(&undef_sym, 0, sizeof(undef_sym));
+ undef_sym.st_shndx = SHN_UNDEF;
+ ret.push_back(undef_sym);
+
+ for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
+ Elf_Sym sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_name = it->name_idx_;
+ if (it->is_relative_) {
+ sym.st_value = it->addr_ + it->section_->section_.sh_offset;
+ } else {
+ sym.st_value = it->addr_;
+ }
+ sym.st_size = it->size_;
+ sym.st_other = it->other_;
+ sym.st_shndx = it->section_->section_index_;
+ sym.st_info = it->info_;
+
+ ret.push_back(sym);
+ }
+ return ret;
+ }
+
+ Elf_Word GetSize() {
+ // 1 is for the implicit NULL symbol.
+ return symbols_.size() + 1;
+ }
+
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* GetStrTab() {
+ return &strtab_;
+ }
+
+ protected:
+ struct ElfSymbolState {
+ const std::string name_;
+ const ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>* section_;
+ Elf_Addr addr_;
+ Elf_Word size_;
+ bool is_relative_;
+ uint8_t info_;
+ uint8_t other_;
+ // Used during Write() to temporarially hold name index in the strtab.
+ Elf_Word name_idx_;
+ };
+
+ // Information for the strsym for dynstr sections.
+ const std::string str_name_;
+ Elf_Word str_type_;
+ // The symbols in the same order they will be in the symbol table.
+ std::vector<ElfSymbolState> symbols_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> strtab_;
+};
+
+template <typename Elf_Word>
+class ElfFilePiece {
+ public:
+ virtual ~ElfFilePiece() {}
+
+ virtual bool Write(File* elf_file) {
+ if (static_cast<off_t>(offset_) != lseek(elf_file->Fd(), offset_, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to " << GetDescription() << " offset " << offset_ << " for "
+ << elf_file->GetPath();
+ return false;
+ }
+
+ return DoActualWrite(elf_file);
+ }
+
+ static bool Compare(ElfFilePiece* a, ElfFilePiece* b) {
+ return a->offset_ < b->offset_;
+ }
+
+ protected:
+ explicit ElfFilePiece(Elf_Word offset) : offset_(offset) {}
+
+ virtual std::string GetDescription() = 0;
+ virtual bool DoActualWrite(File* elf_file) = 0;
+
+ Elf_Word offset_;
+};
+
+template <typename Elf_Word>
+class ElfFileMemoryPiece : public ElfFilePiece<Elf_Word> {
+ public:
+ ElfFileMemoryPiece(const std::string& name, Elf_Word offset, const void* data, Elf_Word size)
+ : ElfFilePiece<Elf_Word>(offset), dbg_name_(name), data_(data), size_(size) {}
+
+ bool DoActualWrite(File* elf_file) OVERRIDE {
+ DCHECK(data_ != nullptr || size_ == 0U) << dbg_name_ << " " << size_;
+
+ if (!elf_file->WriteFully(data_, size_)) {
+ PLOG(ERROR) << "Failed to write " << dbg_name_ << " for " << elf_file->GetPath();
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string GetDescription() OVERRIDE {
+ return dbg_name_;
+ }
+
+ private:
+ const std::string& dbg_name_;
+ const void *data_;
+ Elf_Word size_;
+};
+
+class CodeOutput {
+ public:
+ virtual void SetCodeOffset(size_t offset) = 0;
+ virtual bool Write(OutputStream* out) = 0;
+ virtual ~CodeOutput() {}
+};
+
+template <typename Elf_Word>
+class ElfFileRodataPiece : public ElfFilePiece<Elf_Word> {
+ public:
+ ElfFileRodataPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+ output_(output) {}
+
+ bool DoActualWrite(File* elf_file) OVERRIDE {
+ output_->SetCodeOffset(this->offset_);
+ std::unique_ptr<BufferedOutputStream> output_stream(
+ new BufferedOutputStream(new FileOutputStream(elf_file)));
+ if (!output_->Write(output_stream.get())) {
+ PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file->GetPath();
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string GetDescription() OVERRIDE {
+ return ".rodata";
+ }
+
+ private:
+ CodeOutput* output_;
+};
+
+template <typename Elf_Word>
+class ElfFileOatTextPiece : public ElfFilePiece<Elf_Word> {
+ public:
+ ElfFileOatTextPiece(Elf_Word offset, CodeOutput* output) : ElfFilePiece<Elf_Word>(offset),
+ output_(output) {}
+
+ bool DoActualWrite(File* elf_file) OVERRIDE {
+ // All data is written by the ElfFileRodataPiece right now, as the oat writer writes in one
+ // piece. This is for future flexibility.
+ UNUSED(output_);
+ return true;
+ }
+
+ std::string GetDescription() OVERRIDE {
+ return ".text";
+ }
+
+ private:
+ CodeOutput* output_;
+};
+
+template <typename Elf_Word>
+static bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces, File* elf_file) {
+ // TODO It would be nice if this checked for overlap.
+ for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+ if (!(*it)->Write(elf_file)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename Elf_Word, typename Elf_Shdr>
+static inline constexpr Elf_Word NextOffset(const Elf_Shdr& cur, const Elf_Shdr& prev) {
+ return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+}
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn,
+ typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr>
+class ElfBuilder FINAL {
+ public:
+ ElfBuilder(CodeOutput* oat_writer,
+ File* elf_file,
+ InstructionSet isa,
+ Elf_Word rodata_relative_offset,
+ Elf_Word rodata_size,
+ Elf_Word text_relative_offset,
+ Elf_Word text_size,
+ const bool add_symbols,
+ bool debug = false)
+ : oat_writer_(oat_writer),
+ elf_file_(elf_file),
+ add_symbols_(add_symbols),
+ debug_logging_(debug),
+ text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
+ SHF_ALLOC | SHF_EXECINSTR),
+ rodata_builder_(".rodata", rodata_size, rodata_relative_offset, SHT_PROGBITS, SHF_ALLOC),
+ dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
+ symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
+ hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0, sizeof(Elf_Word),
+ sizeof(Elf_Word)),
+ dynamic_builder_(".dynamic", &dynsym_builder_),
+ shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
+ SetupEhdr();
+ SetupDynamic();
+ SetupRequiredSymbols();
+ SetISA(isa);
+ }
+ ~ElfBuilder() {}
+
+ bool Init() {
+ // The basic layout of the elf file. Order may be different in final output.
+ // +-------------------------+
+ // | Elf_Ehdr |
+ // +-------------------------+
+ // | Elf_Phdr PHDR |
+ // | Elf_Phdr LOAD R | .dynsym .dynstr .hash .rodata
+ // | Elf_Phdr LOAD R X | .text
+ // | Elf_Phdr LOAD RW | .dynamic
+ // | Elf_Phdr DYNAMIC | .dynamic
+ // +-------------------------+
+ // | .dynsym |
+ // | Elf_Sym STN_UNDEF |
+ // | Elf_Sym oatdata |
+ // | Elf_Sym oatexec |
+ // | Elf_Sym oatlastword |
+ // +-------------------------+
+ // | .dynstr |
+ // | \0 |
+ // | oatdata\0 |
+ // | oatexec\0 |
+ // | oatlastword\0 |
+ // | boot.oat\0 |
+ // +-------------------------+
+ // | .hash |
+ // | Elf_Word nbucket = b |
+ // | Elf_Word nchain = c |
+ // | Elf_Word bucket[0] |
+ // | ... |
+ // | Elf_Word bucket[b - 1] |
+ // | Elf_Word chain[0] |
+ // | ... |
+ // | Elf_Word chain[c - 1] |
+ // +-------------------------+
+ // | .rodata |
+ // | oatdata..oatexec-4 |
+ // +-------------------------+
+ // | .text |
+ // | oatexec..oatlastword |
+ // +-------------------------+
+ // | .dynamic |
+ // | Elf_Dyn DT_SONAME |
+ // | Elf_Dyn DT_HASH |
+ // | Elf_Dyn DT_SYMTAB |
+ // | Elf_Dyn DT_SYMENT |
+ // | Elf_Dyn DT_STRTAB |
+ // | Elf_Dyn DT_STRSZ |
+ // | Elf_Dyn DT_NULL |
+ // +-------------------------+ (Optional)
+ // | .strtab | (Optional)
+ // | program symbol names | (Optional)
+ // +-------------------------+ (Optional)
+ // | .symtab | (Optional)
+ // | program symbols | (Optional)
+ // +-------------------------+
+ // | .shstrtab |
+ // | \0 |
+ // | .dynamic\0 |
+ // | .dynsym\0 |
+ // | .dynstr\0 |
+ // | .hash\0 |
+ // | .rodata\0 |
+ // | .text\0 |
+ // | .shstrtab\0 |
+ // | .symtab\0 | (Optional)
+ // | .strtab\0 | (Optional)
+ // | .debug_str\0 | (Optional)
+ // | .debug_info\0 | (Optional)
+ // | .eh_frame\0 | (Optional)
+ // | .debug_line\0 | (Optional)
+ // | .debug_abbrev\0 | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_info | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_abbrev | (Optional)
+ // +-------------------------+ (Optional)
+ // | .eh_frame | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_line | (Optional)
+ // +-------------------------+ (Optional)
+ // | .debug_str | (Optional)
+ // +-------------------------+ (Optional)
+ // | Elf_Shdr NULL |
+ // | Elf_Shdr .dynsym |
+ // | Elf_Shdr .dynstr |
+ // | Elf_Shdr .hash |
+ // | Elf_Shdr .text |
+ // | Elf_Shdr .rodata |
+ // | Elf_Shdr .dynamic |
+ // | Elf_Shdr .shstrtab |
+ // | Elf_Shdr .debug_info | (Optional)
+ // | Elf_Shdr .debug_abbrev | (Optional)
+ // | Elf_Shdr .eh_frame | (Optional)
+ // | Elf_Shdr .debug_line | (Optional)
+ // | Elf_Shdr .debug_str | (Optional)
+ // +-------------------------+
+
+ if (fatal_error_) {
+ return false;
+ }
+ // Step 1. Figure out all the offsets.
+
+ if (debug_logging_) {
+ LOG(INFO) << "phdr_offset=" << PHDR_OFFSET << std::hex << " " << PHDR_OFFSET;
+ LOG(INFO) << "phdr_size=" << PHDR_SIZE << std::hex << " " << PHDR_SIZE;
+ }
+
+ memset(&program_headers_, 0, sizeof(program_headers_));
+ program_headers_[PH_PHDR].p_type = PT_PHDR;
+ program_headers_[PH_PHDR].p_offset = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_vaddr = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_paddr = PHDR_OFFSET;
+ program_headers_[PH_PHDR].p_filesz = sizeof(program_headers_);
+ program_headers_[PH_PHDR].p_memsz = sizeof(program_headers_);
+ program_headers_[PH_PHDR].p_flags = PF_R;
+ program_headers_[PH_PHDR].p_align = sizeof(Elf_Word);
+
+ program_headers_[PH_LOAD_R__].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_R__].p_offset = 0;
+ program_headers_[PH_LOAD_R__].p_vaddr = 0;
+ program_headers_[PH_LOAD_R__].p_paddr = 0;
+ program_headers_[PH_LOAD_R__].p_flags = PF_R;
+
+ program_headers_[PH_LOAD_R_X].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_R_X].p_flags = PF_R | PF_X;
+
+ program_headers_[PH_LOAD_RW_].p_type = PT_LOAD;
+ program_headers_[PH_LOAD_RW_].p_flags = PF_R | PF_W;
+
+ program_headers_[PH_DYNAMIC].p_type = PT_DYNAMIC;
+ program_headers_[PH_DYNAMIC].p_flags = PF_R | PF_W;
+
+ // Get the dynstr string.
+ dynstr_ = dynsym_builder_.GenerateStrtab();
+
+ // Add the SONAME to the dynstr.
+ dynstr_soname_offset_ = dynstr_.size();
+ std::string file_name(elf_file_->GetPath());
+ size_t directory_separator_pos = file_name.rfind('/');
+ if (directory_separator_pos != std::string::npos) {
+ file_name = file_name.substr(directory_separator_pos + 1);
+ }
+ dynstr_ += file_name;
+ dynstr_ += '\0';
+ if (debug_logging_) {
+ LOG(INFO) << "dynstr size (bytes) =" << dynstr_.size()
+ << std::hex << " " << dynstr_.size();
+ LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
+ << std::hex << " " << dynsym_builder_.GetSize();
+ }
+
+ // Get the section header string table.
+ shstrtab_ += '\0';
+
+ // Setup sym_undef
+ memset(&null_hdr_, 0, sizeof(null_hdr_));
+ null_hdr_.sh_type = SHT_NULL;
+ null_hdr_.sh_link = SHN_UNDEF;
+ section_ptrs_.push_back(&null_hdr_);
+
+ section_index_ = 1;
+
+ // setup .dynsym
+ section_ptrs_.push_back(&dynsym_builder_.section_);
+ AssignSectionStr(&dynsym_builder_, &shstrtab_);
+ dynsym_builder_.section_index_ = section_index_++;
+
+ // Setup .dynstr
+ section_ptrs_.push_back(&dynsym_builder_.GetStrTab()->section_);
+ AssignSectionStr(dynsym_builder_.GetStrTab(), &shstrtab_);
+ dynsym_builder_.GetStrTab()->section_index_ = section_index_++;
+
+ // Setup .hash
+ section_ptrs_.push_back(&hash_builder_.section_);
+ AssignSectionStr(&hash_builder_, &shstrtab_);
+ hash_builder_.section_index_ = section_index_++;
+
+ // Setup .rodata
+ section_ptrs_.push_back(&rodata_builder_.section_);
+ AssignSectionStr(&rodata_builder_, &shstrtab_);
+ rodata_builder_.section_index_ = section_index_++;
+
+ // Setup .text
+ section_ptrs_.push_back(&text_builder_.section_);
+ AssignSectionStr(&text_builder_, &shstrtab_);
+ text_builder_.section_index_ = section_index_++;
+
+ // Setup .dynamic
+ section_ptrs_.push_back(&dynamic_builder_.section_);
+ AssignSectionStr(&dynamic_builder_, &shstrtab_);
+ dynamic_builder_.section_index_ = section_index_++;
+
+ // Fill in the hash section.
+ hash_ = dynsym_builder_.GenerateHashContents();
+
+ if (debug_logging_) {
+ LOG(INFO) << ".hash size (bytes)=" << hash_.size() * sizeof(Elf_Word)
+ << std::hex << " " << hash_.size() * sizeof(Elf_Word);
+ }
+
+ Elf_Word base_offset = sizeof(Elf_Ehdr) + sizeof(program_headers_);
+
+ // Get the layout in the sections.
+ //
+ // Get the layout of the dynsym section.
+ dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
+ dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
+ dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf_Sym);
+ dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ dynsym_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (dynsym_builder_.GetStrTab()->section_,
+ dynsym_builder_.section_);
+ dynsym_builder_.GetStrTab()->section_.sh_addr = dynsym_builder_.GetStrTab()->section_.sh_offset;
+ dynsym_builder_.GetStrTab()->section_.sh_size = dynstr_.size();
+ dynsym_builder_.GetStrTab()->section_.sh_link = dynsym_builder_.GetStrTab()->GetLink();
+
+ // Get the layout of the hash section
+ hash_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (hash_builder_.section_,
+ dynsym_builder_.GetStrTab()->section_);
+ hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
+ hash_builder_.section_.sh_size = hash_.size() * sizeof(Elf_Word);
+ hash_builder_.section_.sh_link = hash_builder_.GetLink();
+
+ // Get the layout of the rodata section.
+ rodata_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (rodata_builder_.section_,
+ hash_builder_.section_);
+ rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
+ rodata_builder_.section_.sh_size = rodata_builder_.GetSize();
+ rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
+
+ // Get the layout of the text section.
+ text_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (text_builder_.section_, rodata_builder_.section_);
+ text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
+ text_builder_.section_.sh_size = text_builder_.GetSize();
+ text_builder_.section_.sh_link = text_builder_.GetLink();
+ CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
+
+ // Get the layout of the dynamic section.
+ dynamic_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (dynamic_builder_.section_,
+ text_builder_.section_);
+ dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
+ dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf_Dyn);
+ dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
+
+ if (debug_logging_) {
+ LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
+ << " dynsym size=" << dynsym_builder_.section_.sh_size;
+ LOG(INFO) << "dynstr off=" << dynsym_builder_.GetStrTab()->section_.sh_offset
+ << " dynstr size=" << dynsym_builder_.GetStrTab()->section_.sh_size;
+ LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
+ << " hash size=" << hash_builder_.section_.sh_size;
+ LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
+ << " rodata size=" << rodata_builder_.section_.sh_size;
+ LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
+ << " text size=" << text_builder_.section_.sh_size;
+ LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
+ << " dynamic size=" << dynamic_builder_.section_.sh_size;
+ }
+
+ return true;
+ }
+
+ bool Write() {
+ std::vector<ElfFilePiece<Elf_Word>*> pieces;
+ Elf_Shdr prev = dynamic_builder_.section_;
+ std::string strtab;
+
+ if (IncludingDebugSymbols()) {
+ // Setup .symtab
+ section_ptrs_.push_back(&symtab_builder_.section_);
+ AssignSectionStr(&symtab_builder_, &shstrtab_);
+ symtab_builder_.section_index_ = section_index_++;
+
+ // Setup .strtab
+ section_ptrs_.push_back(&symtab_builder_.GetStrTab()->section_);
+ AssignSectionStr(symtab_builder_.GetStrTab(), &shstrtab_);
+ symtab_builder_.GetStrTab()->section_index_ = section_index_++;
+
+ strtab = symtab_builder_.GenerateStrtab();
+ if (debug_logging_) {
+ LOG(INFO) << "strtab size (bytes) =" << strtab.size()
+ << std::hex << " " << strtab.size();
+ LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
+ << std::hex << " " << symtab_builder_.GetSize();
+ }
+ }
+
+ // Setup all the other sections.
+ for (ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder = other_builders_.data(),
+ *end = builder + other_builders_.size();
+ builder != end; ++builder) {
+ section_ptrs_.push_back(&builder->section_);
+ AssignSectionStr(builder, &shstrtab_);
+ builder->section_index_ = section_index_++;
+ }
+
+ // Setup shstrtab
+ section_ptrs_.push_back(&shstrtab_builder_.section_);
+ AssignSectionStr(&shstrtab_builder_, &shstrtab_);
+ shstrtab_builder_.section_index_ = section_index_++;
+
+ if (debug_logging_) {
+ LOG(INFO) << ".shstrtab size (bytes) =" << shstrtab_.size()
+ << std::hex << " " << shstrtab_.size();
+ LOG(INFO) << "section list size (elements)=" << section_ptrs_.size()
+ << std::hex << " " << section_ptrs_.size();
+ }
+
+ if (IncludingDebugSymbols()) {
+ // Get the layout of the symtab section.
+ symtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (symtab_builder_.section_,
+ dynamic_builder_.section_);
+ symtab_builder_.section_.sh_addr = 0;
+ // Add to leave space for the null symbol.
+ symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf_Sym);
+ symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
+
+ // Get the layout of the dynstr section.
+ symtab_builder_.GetStrTab()->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (symtab_builder_.GetStrTab()->section_,
+ symtab_builder_.section_);
+ symtab_builder_.GetStrTab()->section_.sh_addr = 0;
+ symtab_builder_.GetStrTab()->section_.sh_size = strtab.size();
+ symtab_builder_.GetStrTab()->section_.sh_link = symtab_builder_.GetStrTab()->GetLink();
+
+ prev = symtab_builder_.GetStrTab()->section_;
+ if (debug_logging_) {
+ LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
+ << " symtab size=" << symtab_builder_.section_.sh_size;
+ LOG(INFO) << "strtab off=" << symtab_builder_.GetStrTab()->section_.sh_offset
+ << " strtab size=" << symtab_builder_.GetStrTab()->section_.sh_size;
+ }
+ }
+
+ // Get the layout of the extra sections. (This will deal with the debug
+ // sections if they are there)
+ for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+ it->section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>(it->section_, prev);
+ it->section_.sh_addr = 0;
+ it->section_.sh_size = it->GetBuffer()->size();
+ it->section_.sh_link = it->GetLink();
+
+ // We postpone adding an ElfFilePiece to keep the order in "pieces."
+
+ prev = it->section_;
+ if (debug_logging_) {
+ LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
+ << " size=" << it->section_.sh_size;
+ }
+ }
+
+ // Get the layout of the shstrtab section
+ shstrtab_builder_.section_.sh_offset = NextOffset<Elf_Word, Elf_Shdr>
+ (shstrtab_builder_.section_, prev);
+ shstrtab_builder_.section_.sh_addr = 0;
+ shstrtab_builder_.section_.sh_size = shstrtab_.size();
+ shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
+ if (debug_logging_) {
+ LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
+ << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
+ }
+
+ // The section list comes after come after.
+ Elf_Word sections_offset = RoundUp(
+ shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
+ sizeof(Elf_Word));
+
+ // Setup the actual symbol arrays.
+ std::vector<Elf_Sym> dynsym = dynsym_builder_.GenerateSymtab();
+ CHECK_EQ(dynsym.size() * sizeof(Elf_Sym), dynsym_builder_.section_.sh_size);
+ std::vector<Elf_Sym> symtab;
+ if (IncludingDebugSymbols()) {
+ symtab = symtab_builder_.GenerateSymtab();
+ CHECK_EQ(symtab.size() * sizeof(Elf_Sym), symtab_builder_.section_.sh_size);
+ }
+
+ // Setup the dynamic section.
+ // This will add the 2 values we cannot know until now time, namely the size
+ // and the soname_offset.
+ std::vector<Elf_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr_.size(),
+ dynstr_soname_offset_);
+ CHECK_EQ(dynamic.size() * sizeof(Elf_Dyn), dynamic_builder_.section_.sh_size);
+
+ // Finish setup of the program headers now that we know the layout of the
+ // whole file.
+ Elf_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_R__].p_filesz = load_r_size;
+ program_headers_[PH_LOAD_R__].p_memsz = load_r_size;
+ program_headers_[PH_LOAD_R__].p_align = rodata_builder_.section_.sh_addralign;
+
+ Elf_Word load_rx_size = text_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_vaddr = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_paddr = text_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_R_X].p_filesz = load_rx_size;
+ program_headers_[PH_LOAD_R_X].p_memsz = load_rx_size;
+ program_headers_[PH_LOAD_R_X].p_align = text_builder_.section_.sh_addralign;
+
+ program_headers_[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_vaddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_paddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_RW_].p_memsz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_LOAD_RW_].p_align = dynamic_builder_.section_.sh_addralign;
+
+ program_headers_[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_vaddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_paddr = dynamic_builder_.section_.sh_offset;
+ program_headers_[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_DYNAMIC].p_memsz = dynamic_builder_.section_.sh_size;
+ program_headers_[PH_DYNAMIC].p_align = dynamic_builder_.section_.sh_addralign;
+
+ // Finish setup of the Ehdr values.
+ elf_header_.e_phoff = PHDR_OFFSET;
+ elf_header_.e_shoff = sections_offset;
+ elf_header_.e_phnum = PH_NUM;
+ elf_header_.e_shnum = section_ptrs_.size();
+ elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
+
+ // Add the rest of the pieces to the list.
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Elf Header", 0, &elf_header_,
+ sizeof(elf_header_)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("Program headers", PHDR_OFFSET,
+ &program_headers_, sizeof(program_headers_)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynamic",
+ dynamic_builder_.section_.sh_offset,
+ dynamic.data(),
+ dynamic_builder_.section_.sh_size));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynsym", dynsym_builder_.section_.sh_offset,
+ dynsym.data(),
+ dynsym.size() * sizeof(Elf_Sym)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".dynstr",
+ dynsym_builder_.GetStrTab()->section_.sh_offset,
+ dynstr_.c_str(), dynstr_.size()));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".hash", hash_builder_.section_.sh_offset,
+ hash_.data(),
+ hash_.size() * sizeof(Elf_Word)));
+ pieces.push_back(new ElfFileRodataPiece<Elf_Word>(rodata_builder_.section_.sh_offset,
+ oat_writer_));
+ pieces.push_back(new ElfFileOatTextPiece<Elf_Word>(text_builder_.section_.sh_offset,
+ oat_writer_));
+ if (IncludingDebugSymbols()) {
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".symtab",
+ symtab_builder_.section_.sh_offset,
+ symtab.data(),
+ symtab.size() * sizeof(Elf_Sym)));
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".strtab",
+ symtab_builder_.GetStrTab()->section_.sh_offset,
+ strtab.c_str(), strtab.size()));
+ }
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(".shstrtab",
+ shstrtab_builder_.section_.sh_offset,
+ &shstrtab_[0], shstrtab_.size()));
+ for (uint32_t i = 0; i < section_ptrs_.size(); ++i) {
+ // Just add all the sections in induvidually since they are all over the
+ // place on the heap/stack.
+ Elf_Word cur_off = sections_offset + i * sizeof(Elf_Shdr);
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>("section table piece", cur_off,
+ section_ptrs_[i], sizeof(Elf_Shdr)));
+ }
+
+ // Postponed debug info.
+ for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
+ pieces.push_back(new ElfFileMemoryPiece<Elf_Word>(it->name_, it->section_.sh_offset,
+ it->GetBuffer()->data(),
+ it->GetBuffer()->size()));
+ }
+
+ if (!WriteOutFile(pieces)) {
+ LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
+
+ STLDeleteElements(&pieces); // Have to manually clean pieces.
+ return false;
+ }
+
+ STLDeleteElements(&pieces); // Have to manually clean pieces.
+ return true;
+ }
+
+ // Adds the given raw section to the builder. This will copy it. The caller
+ // is responsible for deallocating their copy.
+ void RegisterRawSection(ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> bld) {
+ other_builders_.push_back(bld);
+ }
+
+ private:
+ CodeOutput* oat_writer_;
+ File* elf_file_;
+ const bool add_symbols_;
+ const bool debug_logging_;
+
+ bool fatal_error_ = false;
+
+ // What phdr is.
+ static const uint32_t PHDR_OFFSET = sizeof(Elf_Ehdr);
+ enum : uint8_t {
+ PH_PHDR = 0,
+ PH_LOAD_R__ = 1,
+ PH_LOAD_R_X = 2,
+ PH_LOAD_RW_ = 3,
+ PH_DYNAMIC = 4,
+ PH_NUM = 5,
+ };
+ static const uint32_t PHDR_SIZE = sizeof(Elf_Phdr) * PH_NUM;
+ Elf_Phdr program_headers_[PH_NUM];
+
+ Elf_Ehdr elf_header_;
+
+ Elf_Shdr null_hdr_;
+ std::string shstrtab_;
+ uint32_t section_index_;
+ std::string dynstr_;
+ uint32_t dynstr_soname_offset_;
+ std::vector<Elf_Shdr*> section_ptrs_;
+ std::vector<Elf_Word> hash_;
+
+ public:
+ ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> text_builder_;
+ ElfOatSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> rodata_builder_;
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> dynsym_builder_;
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Sym, Elf_Shdr> symtab_builder_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> hash_builder_;
+ ElfDynamicBuilder<Elf_Word, Elf_Sword, Elf_Dyn, Elf_Shdr> dynamic_builder_;
+ ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> shstrtab_builder_;
+ std::vector<ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr>> other_builders_;
+
+ private:
+ void SetISA(InstructionSet isa) {
+ switch (isa) {
+ case kArm:
+ // Fall through.
+ case kThumb2: {
+ elf_header_.e_machine = EM_ARM;
+ elf_header_.e_flags = EF_ARM_EABI_VER5;
+ break;
+ }
+ case kArm64: {
+ elf_header_.e_machine = EM_AARCH64;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kX86: {
+ elf_header_.e_machine = EM_386;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kX86_64: {
+ elf_header_.e_machine = EM_X86_64;
+ elf_header_.e_flags = 0;
+ break;
+ }
+ case kMips: {
+ elf_header_.e_machine = EM_MIPS;
+ elf_header_.e_flags = (EF_MIPS_NOREORDER |
+ EF_MIPS_PIC |
+ EF_MIPS_CPIC |
+ EF_MIPS_ABI_O32 |
+ EF_MIPS_ARCH_32R2);
+ break;
+ }
+ default: {
+ fatal_error_ = true;
+ LOG(FATAL) << "Unknown instruction set: " << isa;
+ break;
+ }
+ }
+ }
+
+ void SetupEhdr() {
+ memset(&elf_header_, 0, sizeof(elf_header_));
+ elf_header_.e_ident[EI_MAG0] = ELFMAG0;
+ elf_header_.e_ident[EI_MAG1] = ELFMAG1;
+ elf_header_.e_ident[EI_MAG2] = ELFMAG2;
+ elf_header_.e_ident[EI_MAG3] = ELFMAG3;
+ elf_header_.e_ident[EI_CLASS] = ELFCLASS32;
+ elf_header_.e_ident[EI_DATA] = ELFDATA2LSB;
+ elf_header_.e_ident[EI_VERSION] = EV_CURRENT;
+ elf_header_.e_ident[EI_OSABI] = ELFOSABI_LINUX;
+ elf_header_.e_ident[EI_ABIVERSION] = 0;
+ elf_header_.e_type = ET_DYN;
+ elf_header_.e_version = 1;
+ elf_header_.e_entry = 0;
+ elf_header_.e_ehsize = sizeof(Elf_Ehdr);
+ elf_header_.e_phentsize = sizeof(Elf_Phdr);
+ elf_header_.e_shentsize = sizeof(Elf_Shdr);
+ elf_header_.e_phoff = sizeof(Elf_Ehdr);
+ }
+
+ // Sets up a bunch of the required Dynamic Section entries.
+ // Namely it will initialize all the mandatory ones that it can.
+ // Specifically:
+ // DT_HASH
+ // DT_STRTAB
+ // DT_SYMTAB
+ // DT_SYMENT
+ //
+ // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
+ void SetupDynamic() {
+ dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
+ dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, dynsym_builder_.GetStrTab());
+ dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
+ dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf_Sym));
+ }
+
+ // Sets up the basic dynamic symbols that are needed, namely all those we
+ // can know already.
+ //
+ // Specifically adds:
+ // oatdata
+ // oatexec
+ // oatlastword
+ void SetupRequiredSymbols() {
+ dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
+ rodata_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
+ text_builder_.GetSize(), STB_GLOBAL, STT_OBJECT);
+ dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.GetSize() - 4,
+ true, 4, STB_GLOBAL, STT_OBJECT);
+ }
+
+ void AssignSectionStr(ElfSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> *builder,
+ std::string* strtab) {
+ builder->section_.sh_name = strtab->size();
+ *strtab += builder->name_;
+ *strtab += '\0';
+ if (debug_logging_) {
+ LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
+ << "to shstrtab at offset " << builder->section_.sh_name;
+ }
+ }
+
+
+ // Write each of the pieces out to the file.
+ bool WriteOutFile(const std::vector<ElfFilePiece<Elf_Word>*>& pieces) {
+ for (auto it = pieces.begin(); it != pieces.end(); ++it) {
+ if (!(*it)->Write(elf_file_)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc
index 0155c82..0d34879 100644
--- a/compiler/elf_fixup.cc
+++ b/compiler/elf_fixup.cc
@@ -38,28 +38,32 @@
Elf32_Off base_address = oat_data_begin - oatdata_address;
if (!FixupDynamic(*elf_file.get(), base_address)) {
- LOG(WARNING) << "Failed fo fixup .dynamic in " << file->GetPath();
- return false;
+ LOG(WARNING) << "Failed to fixup .dynamic in " << file->GetPath();
+ return false;
}
if (!FixupSectionHeaders(*elf_file.get(), base_address)) {
- LOG(WARNING) << "Failed fo fixup section headers in " << file->GetPath();
- return false;
+ LOG(WARNING) << "Failed to fixup section headers in " << file->GetPath();
+ return false;
}
if (!FixupProgramHeaders(*elf_file.get(), base_address)) {
- LOG(WARNING) << "Failed fo fixup program headers in " << file->GetPath();
- return false;
+ LOG(WARNING) << "Failed to fixup program headers in " << file->GetPath();
+ return false;
}
if (!FixupSymbols(*elf_file.get(), base_address, true)) {
- LOG(WARNING) << "Failed fo fixup .dynsym in " << file->GetPath();
- return false;
+ LOG(WARNING) << "Failed to fixup .dynsym in " << file->GetPath();
+ return false;
}
if (!FixupSymbols(*elf_file.get(), base_address, false)) {
- LOG(WARNING) << "Failed fo fixup .symtab in " << file->GetPath();
- return false;
+ LOG(WARNING) << "Failed to fixup .symtab in " << file->GetPath();
+ return false;
}
if (!FixupRelocations(*elf_file.get(), base_address)) {
- LOG(WARNING) << "Failed fo fixup .rel.dyn in " << file->GetPath();
- return false;
+ LOG(WARNING) << "Failed to fixup .rel.dyn in " << file->GetPath();
+ return false;
+ }
+ if (!elf_file->FixupDebugSections(base_address)) {
+ LOG(WARNING) << "Failed to fixup debug sections in " << file->GetPath();
+ return false;
}
return true;
}
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
deleted file mode 100644
index e4c957a..0000000
--- a/compiler/elf_patcher.cc
+++ /dev/null
@@ -1,295 +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.
- */
-
-#include "elf_patcher.h"
-
-#include <vector>
-#include <set>
-
-#include "elf_file.h"
-#include "elf_utils.h"
-#include "mirror/art_field-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/array-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/class_loader.h"
-#include "mirror/dex_cache-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/string-inl.h"
-#include "oat.h"
-#include "os.h"
-#include "utils.h"
-
-namespace art {
-
-bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf_file,
- const std::string& oat_location,
- ImageAddressCallback cb, void* cb_data,
- std::string* error_msg) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
- if (oat_file == nullptr) {
- CHECK(Runtime::Current()->IsCompiler());
- oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
- if (oat_file == nullptr) {
- *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
- error_msg->c_str());
- return false;
- }
- CHECK_EQ(class_linker->RegisterOatFile(oat_file), oat_file);
- }
- return ElfPatcher::Patch(driver, elf_file, oat_file,
- reinterpret_cast<uintptr_t>(oat_file->Begin()), cb, cb_data, error_msg);
-}
-
-bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf, const OatFile* oat_file,
- uintptr_t oat_data_start, ImageAddressCallback cb, void* cb_data,
- std::string* error_msg) {
- Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
- if (data_sec == nullptr) {
- *error_msg = "Unable to find .rodata section and oat header";
- return false;
- }
- OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
- if (!oat_header->IsValid()) {
- *error_msg = "Oat header was not valid";
- return false;
- }
-
- ElfPatcher p(driver, elf, oat_file, oat_header, oat_data_start, cb, cb_data, error_msg);
- return p.PatchElf();
-}
-
-mirror::ArtMethod* ElfPatcher::GetTargetMethod(const CompilerDriver::CallPatchInformation* patch) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
- mirror::ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
- patch->GetTargetMethodIdx(),
- dex_cache,
- NullHandle<mirror::ClassLoader>(),
- NullHandle<mirror::ArtMethod>(),
- patch->GetTargetInvokeType());
- CHECK(method != NULL)
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
- CHECK(!method->IsRuntimeMethod())
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
- CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
- << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
- << PrettyMethod(method);
- return method;
-}
-
-mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
- mirror::Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
- dex_cache, NullHandle<mirror::ClassLoader>());
- CHECK(klass != NULL)
- << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
- CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
- << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
- << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
- << PrettyClass(klass);
- return klass;
-}
-
-void ElfPatcher::AddPatch(uintptr_t p) {
- if (write_patches_ && patches_set_.find(p) == patches_set_.end()) {
- patches_set_.insert(p);
- patches_.push_back(p);
- }
-}
-
-uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) {
- CHECK_GE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()));
- CHECK_LE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->End()));
- uintptr_t off = patch_ptr - reinterpret_cast<uintptr_t>(oat_file_->Begin());
- uintptr_t ret = reinterpret_cast<uintptr_t>(oat_header_) + off;
-
- CHECK_GE(ret, reinterpret_cast<uintptr_t>(elf_file_->Begin()));
- CHECK_LT(ret, reinterpret_cast<uintptr_t>(elf_file_->End()));
- return reinterpret_cast<uint32_t*>(ret);
-}
-
-void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
- patch->GetReferrerClassDefIdx(),
- patch->GetReferrerMethodIdx());
- // TODO: make this Thumb2 specific
- uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
- uintptr_t patch_ptr = reinterpret_cast<uintptr_t>(base + patch->GetLiteralOffset());
- uint32_t* patch_location = GetPatchLocation(patch_ptr);
- if (kIsDebugBuild) {
- if (patch->IsCall()) {
- const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
- const DexFile::MethodId& id =
- cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
- uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
- uint32_t actual = *patch_location;
- CHECK(actual == expected || actual == value) << "Patching call failed: " << std::hex
- << " actual=" << actual
- << " expected=" << expected
- << " value=" << value;
- }
- if (patch->IsType()) {
- const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
- const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
- uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
- uint32_t actual = *patch_location;
- CHECK(actual == expected || actual == value) << "Patching type failed: " << std::hex
- << " actual=" << actual
- << " expected=" << expected
- << " value=" << value;
- }
- }
- *patch_location = value;
- oat_header_->UpdateChecksum(patch_location, sizeof(value));
-
- if (patch->IsCall() && patch->AsCall()->IsRelative()) {
- // We never record relative patches.
- return;
- }
- uintptr_t loc = patch_ptr - (reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
- oat_header_->GetExecutableOffset());
- CHECK_GT(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
- oat_header_->GetExecutableOffset());
- CHECK_LT(loc, oat_file_->Size() - oat_header_->GetExecutableOffset());
- AddPatch(loc);
-}
-
-bool ElfPatcher::PatchElf() {
- // TODO if we are adding patches the resulting ELF file might have a
- // potentially rather large amount of free space where patches might have been
- // placed. We should adjust the ELF file to get rid of this excess space.
- if (write_patches_) {
- patches_.reserve(compiler_driver_->GetCodeToPatch().size() +
- compiler_driver_->GetMethodsToPatch().size() +
- compiler_driver_->GetClassesToPatch().size());
- }
- Thread* self = Thread::Current();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const char* old_cause = self->StartAssertNoThreadSuspension("ElfPatcher");
-
- typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
- const CallPatches& code_to_patch = compiler_driver_->GetCodeToPatch();
- for (size_t i = 0; i < code_to_patch.size(); i++) {
- const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
-
- mirror::ArtMethod* target = GetTargetMethod(patch);
- uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
- DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
- const OatFile* target_oat =
- class_linker->FindOpenedOatDexFileForDexFile(*patch->GetTargetDexFile())->GetOatFile();
- // Get where the data actually starts. if target is this oat_file_ it is oat_data_start_,
- // otherwise it is wherever target_oat is loaded.
- uintptr_t oat_data_addr = GetBaseAddressFor(target_oat);
- uintptr_t code_base = reinterpret_cast<uintptr_t>(target_oat->Begin());
- uintptr_t code_offset = quick_code - code_base;
- bool is_quick_offset = false;
- if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
- is_quick_offset = true;
- code_offset = oat_header_->GetQuickToInterpreterBridgeOffset();
- } else if (quick_code ==
- reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
- CHECK(target->IsNative());
- is_quick_offset = true;
- code_offset = oat_header_->GetQuickGenericJniTrampolineOffset();
- }
- uintptr_t value;
- if (patch->IsRelative()) {
- // value to patch is relative to the location being patched
- const void* quick_oat_code =
- class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
- patch->GetReferrerClassDefIdx(),
- patch->GetReferrerMethodIdx());
- if (is_quick_offset) {
- // If its a quick offset it means that we are doing a relative patch from the class linker
- // oat_file to the elf_patcher oat_file so we need to adjust the quick oat code to be the
- // one in the output oat_file (ie where it is actually going to be loaded).
- quick_code = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
- quick_oat_code =
- reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
- oat_data_addr - code_base);
- }
- uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
- uintptr_t patch_location = base + patch->GetLiteralOffset();
- value = quick_code - patch_location + patch->RelativeOffset();
- } else if (code_offset != 0) {
- value = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
- } else {
- value = 0;
- }
- SetPatchLocation(patch, value);
- }
-
- const CallPatches& methods_to_patch = compiler_driver_->GetMethodsToPatch();
- for (size_t i = 0; i < methods_to_patch.size(); i++) {
- const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
- mirror::ArtMethod* target = GetTargetMethod(patch);
- SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
- }
-
- const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
- compiler_driver_->GetClassesToPatch();
- for (size_t i = 0; i < classes_to_patch.size(); i++) {
- const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
- mirror::Class* target = GetTargetType(patch);
- SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
- }
-
- self->EndAssertNoThreadSuspension(old_cause);
-
- if (write_patches_) {
- return WriteOutPatchData();
- }
- return true;
-}
-
-bool ElfPatcher::WriteOutPatchData() {
- Elf32_Shdr* shdr = elf_file_->FindSectionByName(".oat_patches");
- if (shdr != nullptr) {
- CHECK_EQ(shdr, elf_file_->FindSectionByType(SHT_OAT_PATCH))
- << "Incorrect type for .oat_patches section";
- CHECK_LE(patches_.size() * sizeof(uintptr_t), shdr->sh_size)
- << "We got more patches than anticipated";
- CHECK_LE(reinterpret_cast<uintptr_t>(elf_file_->Begin()) + shdr->sh_offset + shdr->sh_size,
- reinterpret_cast<uintptr_t>(elf_file_->End())) << "section is too large";
- CHECK(shdr == elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) ||
- shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
- << "Section overlaps onto next section";
- // It's mmap'd so we can just memcpy.
- memcpy(elf_file_->Begin() + shdr->sh_offset, patches_.data(),
- patches_.size() * sizeof(uintptr_t));
- // TODO We should fill in the newly empty space between the last patch and
- // the start of the next section by moving the following sections down if
- // possible.
- shdr->sh_size = patches_.size() * sizeof(uintptr_t);
- return true;
- } else {
- LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
- *error_msg_ = "Unable to find section to write patch information to in ";
- *error_msg_ += elf_file_->GetFile().GetPath();
- return false;
- }
-}
-
-} // namespace art
diff --git a/compiler/elf_patcher.h b/compiler/elf_patcher.h
deleted file mode 100644
index 0a9f0a01..0000000
--- a/compiler/elf_patcher.h
+++ /dev/null
@@ -1,132 +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_COMPILER_ELF_PATCHER_H_
-#define ART_COMPILER_ELF_PATCHER_H_
-
-#include "base/mutex.h"
-#include "driver/compiler_driver.h"
-#include "elf_file.h"
-#include "mirror/art_method.h"
-#include "mirror/class.h"
-#include "mirror/object.h"
-#include "oat_file.h"
-#include "oat.h"
-#include "os.h"
-
-namespace art {
-
-class ElfPatcher {
- public:
- typedef void* (*ImageAddressCallback)(void* data, mirror::Object* obj);
-
- static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
- const std::string& oat_location,
- ImageAddressCallback cb, void* cb_data,
- std::string* error_msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
- const OatFile* oat_file, uintptr_t oat_data_begin,
- ImageAddressCallback cb, void* cb_data,
- std::string* error_msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
- const std::string& oat_location,
- std::string* error_msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return ElfPatcher::Patch(driver, elf_file, oat_location,
- DefaultImageAddressCallback, nullptr, error_msg);
- }
-
- static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
- const OatFile* oat_file, uintptr_t oat_data_begin,
- std::string* error_msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return ElfPatcher::Patch(driver, elf_file, oat_file, oat_data_begin,
- DefaultImageAddressCallback, nullptr, error_msg);
- }
-
- private:
- ElfPatcher(const CompilerDriver* driver, ElfFile* elf_file, const OatFile* oat_file,
- OatHeader* oat_header, uintptr_t oat_data_begin,
- ImageAddressCallback cb, void* cb_data, std::string* error_msg)
- : compiler_driver_(driver), elf_file_(elf_file), oat_file_(oat_file),
- oat_header_(oat_header), oat_data_begin_(oat_data_begin), get_image_address_(cb),
- cb_data_(cb_data), error_msg_(error_msg),
- write_patches_(compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {}
- ~ElfPatcher() {}
-
- static void* DefaultImageAddressCallback(void* data_unused, mirror::Object* obj) {
- return static_cast<void*>(obj);
- }
-
- bool PatchElf()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- mirror::ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- mirror::Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void AddPatch(uintptr_t off);
-
- void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // Takes the pointer into the oat_file_ and get the pointer in to the ElfFile.
- uint32_t* GetPatchLocation(uintptr_t patch_ptr);
-
- bool WriteOutPatchData();
-
- uintptr_t GetBaseAddressFor(const OatFile* f) {
- if (f == oat_file_) {
- return oat_data_begin_;
- } else {
- return reinterpret_cast<uintptr_t>(f->Begin());
- }
- }
-
- const CompilerDriver* compiler_driver_;
-
- // The elf_file containing the oat_data we are patching up
- ElfFile* elf_file_;
-
- // The oat_file that is actually loaded.
- const OatFile* oat_file_;
-
- // The oat_header_ within the elf_file_
- OatHeader* oat_header_;
-
- // Where the elf_file will be loaded during normal runs.
- uintptr_t oat_data_begin_;
-
- // Callback to get image addresses.
- ImageAddressCallback get_image_address_;
- void* cb_data_;
-
- std::string* error_msg_;
- std::vector<uintptr_t> patches_;
- std::set<uintptr_t> patches_set_;
- bool write_patches_;
-
- DISALLOW_COPY_AND_ASSIGN(ElfPatcher);
-};
-
-} // namespace art
-#endif // ART_COMPILER_ELF_PATCHER_H_
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
index 3dba426..f017641 100644
--- a/compiler/elf_writer_mclinker.cc
+++ b/compiler/elf_writer_mclinker.cc
@@ -67,19 +67,40 @@
bool is_host) {
std::vector<uint8_t> oat_contents;
oat_contents.reserve(oat_writer->GetSize());
- VectorOutputStream output_stream("oat contents", oat_contents);
- CHECK(oat_writer->Write(&output_stream));
- CHECK_EQ(oat_writer->GetSize(), oat_contents.size());
Init();
- AddOatInput(oat_contents);
+ mcld::LDSection* oat_section = AddOatInput(oat_writer, &oat_contents);
if (kUsePortableCompiler) {
AddMethodInputs(dex_files);
AddRuntimeInputs(android_root, is_host);
}
- if (!Link()) {
+
+ // link inputs
+ if (!linker_->link(*module_.get(), *ir_builder_.get())) {
+ LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
return false;
}
+
+ // Fill oat_contents.
+ VectorOutputStream output_stream("oat contents", oat_contents);
+ oat_writer->SetOatDataOffset(oat_section->offset());
+ CHECK(oat_writer->Write(&output_stream));
+ CHECK_EQ(oat_writer->GetSize(), oat_contents.size());
+
+ // emit linked output
+ // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
+ int fd = dup(elf_file_->Fd());
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
+ return false;
+ }
+ if (!linker_->emit(*module_.get(), fd)) {
+ LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
+ return false;
+ }
+ mcld::Finalize();
+ LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
+
oat_contents.clear();
if (kUsePortableCompiler) {
FixupOatMethodOffsets(dex_files);
@@ -156,16 +177,13 @@
linker_->emulate(*linker_script_.get(), *linker_config_.get());
}
-void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) {
- // Add an artificial memory input. Based on LinkerTest.
- std::string error_msg;
- std::unique_ptr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath(), &error_msg));
- CHECK(oat_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
-
- const char* oat_data_start = reinterpret_cast<const char*>(&oat_file->GetOatHeader());
- const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset();
+mcld::LDSection* ElfWriterMclinker::AddOatInput(OatWriter* oat_writer,
+ std::vector<uint8_t>* oat_contents) {
+ // NOTE: oat_contents has sufficient reserved space but it doesn't contain the data yet.
+ const char* oat_data_start = reinterpret_cast<const char*>(&(*oat_contents)[0]);
+ const size_t oat_data_length = oat_writer->GetOatHeader().GetExecutableOffset();
const char* oat_code_start = oat_data_start + oat_data_length;
- const size_t oat_code_length = oat_file->Size() - oat_data_length;
+ const size_t oat_code_length = oat_writer->GetSize() - oat_data_length;
// TODO: ownership of oat_input?
oat_input_ = ir_builder_->CreateInput("oat contents",
@@ -205,7 +223,7 @@
// TODO: why does IRBuilder::CreateRegion take a non-const pointer?
mcld::Fragment* text_fragment = ir_builder_->CreateRegion(const_cast<char*>(oat_data_start),
- oat_file->Size());
+ oat_writer->GetSize());
CHECK(text_fragment != NULL);
ir_builder_->AppendFragment(*text_fragment, *text_sectiondata);
@@ -236,6 +254,8 @@
// subtract a word so symbol is within section
(oat_data_length + oat_code_length) - sizeof(uint32_t), // offset
text_section);
+
+ return text_section;
}
void ElfWriterMclinker::AddMethodInputs(const std::vector<const DexFile*>& dex_files) {
@@ -322,29 +342,6 @@
CHECK(libm_lib_input_input != NULL);
}
-bool ElfWriterMclinker::Link() {
- // link inputs
- if (!linker_->link(*module_.get(), *ir_builder_.get())) {
- LOG(ERROR) << "Failed to link " << elf_file_->GetPath();
- return false;
- }
-
- // emit linked output
- // TODO: avoid dup of fd by fixing Linker::emit to not close the argument fd.
- int fd = dup(elf_file_->Fd());
- if (fd == -1) {
- PLOG(ERROR) << "Failed to dup file descriptor for " << elf_file_->GetPath();
- return false;
- }
- if (!linker_->emit(*module_.get(), fd)) {
- LOG(ERROR) << "Failed to emit " << elf_file_->GetPath();
- return false;
- }
- mcld::Finalize();
- LOG(INFO) << "ELF file written successfully: " << elf_file_->GetPath();
- return true;
-}
-
void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
std::string error_msg;
std::unique_ptr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg));
diff --git a/compiler/elf_writer_mclinker.h b/compiler/elf_writer_mclinker.h
index 955e5d2..489fefb 100644
--- a/compiler/elf_writer_mclinker.h
+++ b/compiler/elf_writer_mclinker.h
@@ -61,11 +61,10 @@
~ElfWriterMclinker();
void Init();
- void AddOatInput(std::vector<uint8_t>& oat_contents);
+ mcld::LDSection* AddOatInput(OatWriter* oat_writer, std::vector<uint8_t>* oat_contents);
void AddMethodInputs(const std::vector<const DexFile*>& dex_files);
void AddCompiledCodeInput(const CompiledCode& compiled_code);
void AddRuntimeInputs(const std::string& android_root, bool is_host);
- bool Link();
void FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint32_t FixupCompiledCodeOffset(ElfFile& elf_file,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 703e63f..e661324 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -16,869 +16,66 @@
#include "elf_writer_quick.h"
+#include <unordered_map>
+
#include "base/logging.h"
-#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "buffered_output_stream.h"
#include "driver/compiler_driver.h"
#include "dwarf.h"
+#include "elf_builder.h"
+#include "elf_file.h"
#include "elf_utils.h"
#include "file_output_stream.h"
#include "globals.h"
+#include "leb128.h"
#include "oat.h"
#include "oat_writer.h"
#include "utils.h"
namespace art {
-static constexpr Elf32_Word NextOffset(const Elf32_Shdr& cur, const Elf32_Shdr& prev) {
- return RoundUp(prev.sh_size + prev.sh_offset, cur.sh_addralign);
+static void PushByte(std::vector<uint8_t>* buf, int data) {
+ buf->push_back(data & 0xff);
}
-static uint8_t MakeStInfo(uint8_t binding, uint8_t type) {
- return ((binding) << 4) + ((type) & 0xf);
+static uint32_t PushStr(std::vector<uint8_t>* buf, const char* str, const char* def = nullptr) {
+ if (str == nullptr) {
+ str = def;
+ }
+
+ uint32_t offset = buf->size();
+ for (size_t i = 0; str[i] != '\0'; ++i) {
+ buf->push_back(str[i]);
+ }
+ buf->push_back('\0');
+ return offset;
}
-class ElfFilePiece {
- public:
- virtual ~ElfFilePiece() {}
-
- virtual bool Write(File* elf_file) {
- if (static_cast<off_t>(offset_) != lseek(elf_file->Fd(), offset_, SEEK_SET)) {
- PLOG(ERROR) << "Failed to seek to " << GetDescription() << " offset " << offset_ << " for "
- << elf_file->GetPath();
- return false;
- }
-
- return DoActualWrite(elf_file);
- }
-
- static bool Compare(ElfFilePiece* a, ElfFilePiece* b) {
- return a->offset_ < b->offset_;
- }
-
- protected:
- explicit ElfFilePiece(Elf32_Word offset) : offset_(offset) {}
-
- virtual std::string GetDescription() = 0;
- virtual bool DoActualWrite(File* elf_file) = 0;
-
- Elf32_Word offset_;
-};
-
-class ElfFileMemoryPiece : public ElfFilePiece {
- public:
- ElfFileMemoryPiece(const std::string& name, Elf32_Word offset, const void* data, Elf32_Word size)
- : ElfFilePiece(offset), dbg_name_(name), data_(data), size_(size) {}
-
- bool DoActualWrite(File* elf_file) OVERRIDE {
- DCHECK(data_ != nullptr || size_ == 0U) << dbg_name_ << " " << size_;
-
- if (!elf_file->WriteFully(data_, size_)) {
- PLOG(ERROR) << "Failed to write " << dbg_name_ << " for " << elf_file->GetPath();
- return false;
- }
-
- return true;
- }
-
- std::string GetDescription() OVERRIDE {
- return dbg_name_;
- }
-
- private:
- const std::string& dbg_name_;
- const void *data_;
- Elf32_Word size_;
-};
-
-class ElfFileRodataPiece : public ElfFilePiece {
- public:
- ElfFileRodataPiece(Elf32_Word offset, OatWriter* oat_writer) : ElfFilePiece(offset),
- oat_writer_(oat_writer) {}
-
- bool DoActualWrite(File* elf_file) OVERRIDE {
- std::unique_ptr<BufferedOutputStream> output_stream(
- new BufferedOutputStream(new FileOutputStream(elf_file)));
- if (!oat_writer_->Write(output_stream.get())) {
- PLOG(ERROR) << "Failed to write .rodata and .text for " << elf_file->GetPath();
- return false;
- }
-
- return true;
- }
-
- std::string GetDescription() OVERRIDE {
- return ".rodata";
- }
-
- private:
- OatWriter* oat_writer_;
-};
-
-class ElfFileOatTextPiece : public ElfFilePiece {
- public:
- ElfFileOatTextPiece(Elf32_Word offset, OatWriter* oat_writer) : ElfFilePiece(offset),
- oat_writer_(oat_writer) {}
-
- bool DoActualWrite(File* elf_file) OVERRIDE {
- // All data is written by the ElfFileRodataPiece right now, as the oat writer writes in one
- // piece. This is for future flexibility.
- UNUSED(oat_writer_);
- return true;
- }
-
- std::string GetDescription() OVERRIDE {
- return ".text";
- }
-
- private:
- OatWriter* oat_writer_;
-};
-
-static bool WriteOutFile(const std::vector<ElfFilePiece*>& pieces, File* elf_file) {
- // TODO It would be nice if this checked for overlap.
- for (auto it = pieces.begin(); it != pieces.end(); ++it) {
- if (!(*it)->Write(elf_file)) {
- return false;
- }
- }
- return true;
+static uint32_t PushStr(std::vector<uint8_t>* buf, const std::string &str) {
+ uint32_t offset = buf->size();
+ buf->insert(buf->end(), str.begin(), str.end());
+ buf->push_back('\0');
+ return offset;
}
-bool ElfWriterQuick::ElfBuilder::Write() {
- // The basic layout of the elf file. Order may be different in final output.
- // +-------------------------+
- // | Elf32_Ehdr |
- // +-------------------------+
- // | Elf32_Phdr PHDR |
- // | Elf32_Phdr LOAD R | .dynsym .dynstr .hash .rodata
- // | Elf32_Phdr LOAD R X | .text
- // | Elf32_Phdr LOAD RW | .dynamic
- // | Elf32_Phdr DYNAMIC | .dynamic
- // +-------------------------+
- // | .dynsym |
- // | Elf32_Sym STN_UNDEF |
- // | Elf32_Sym oatdata |
- // | Elf32_Sym oatexec |
- // | Elf32_Sym oatlastword |
- // +-------------------------+
- // | .dynstr |
- // | \0 |
- // | oatdata\0 |
- // | oatexec\0 |
- // | oatlastword\0 |
- // | boot.oat\0 |
- // +-------------------------+
- // | .hash |
- // | Elf32_Word nbucket = b |
- // | Elf32_Word nchain = c |
- // | Elf32_Word bucket[0] |
- // | ... |
- // | Elf32_Word bucket[b - 1]|
- // | Elf32_Word chain[0] |
- // | ... |
- // | Elf32_Word chain[c - 1] |
- // +-------------------------+
- // | .rodata |
- // | oatdata..oatexec-4 |
- // +-------------------------+
- // | .text |
- // | oatexec..oatlastword |
- // +-------------------------+
- // | .dynamic |
- // | Elf32_Dyn DT_SONAME |
- // | Elf32_Dyn DT_HASH |
- // | Elf32_Dyn DT_SYMTAB |
- // | Elf32_Dyn DT_SYMENT |
- // | Elf32_Dyn DT_STRTAB |
- // | Elf32_Dyn DT_STRSZ |
- // | Elf32_Dyn DT_NULL |
- // +-------------------------+ (Optional)
- // | .strtab | (Optional)
- // | program symbol names | (Optional)
- // +-------------------------+ (Optional)
- // | .symtab | (Optional)
- // | program symbols | (Optional)
- // +-------------------------+
- // | .shstrtab |
- // | \0 |
- // | .dynamic\0 |
- // | .dynsym\0 |
- // | .dynstr\0 |
- // | .hash\0 |
- // | .rodata\0 |
- // | .text\0 |
- // | .shstrtab\0 |
- // | .symtab\0 | (Optional)
- // | .strtab\0 | (Optional)
- // | .debug_str\0 | (Optional)
- // | .debug_info\0 | (Optional)
- // | .eh_frame\0 | (Optional)
- // | .debug_abbrev\0 | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_str | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_info | (Optional)
- // +-------------------------+ (Optional)
- // | .eh_frame | (Optional)
- // +-------------------------+ (Optional)
- // | .debug_abbrev | (Optional)
- // +-------------------------+
- // | Elf32_Shdr NULL |
- // | Elf32_Shdr .dynsym |
- // | Elf32_Shdr .dynstr |
- // | Elf32_Shdr .hash |
- // | Elf32_Shdr .text |
- // | Elf32_Shdr .rodata |
- // | Elf32_Shdr .dynamic |
- // | Elf32_Shdr .shstrtab |
- // | Elf32_Shdr .debug_str | (Optional)
- // | Elf32_Shdr .debug_info | (Optional)
- // | Elf32_Shdr .eh_frame | (Optional)
- // | Elf32_Shdr .debug_abbrev| (Optional)
- // +-------------------------+
-
-
- if (fatal_error_) {
- return false;
- }
- // Step 1. Figure out all the offsets.
-
- // What phdr is.
- uint32_t phdr_offset = sizeof(Elf32_Ehdr);
- const uint8_t PH_PHDR = 0;
- const uint8_t PH_LOAD_R__ = 1;
- const uint8_t PH_LOAD_R_X = 2;
- const uint8_t PH_LOAD_RW_ = 3;
- const uint8_t PH_DYNAMIC = 4;
- const uint8_t PH_NUM = 5;
- uint32_t phdr_size = sizeof(Elf32_Phdr) * PH_NUM;
- if (debug_logging_) {
- LOG(INFO) << "phdr_offset=" << phdr_offset << std::hex << " " << phdr_offset;
- LOG(INFO) << "phdr_size=" << phdr_size << std::hex << " " << phdr_size;
- }
- Elf32_Phdr program_headers[PH_NUM];
- memset(&program_headers, 0, sizeof(program_headers));
- program_headers[PH_PHDR].p_type = PT_PHDR;
- program_headers[PH_PHDR].p_offset = phdr_offset;
- program_headers[PH_PHDR].p_vaddr = phdr_offset;
- program_headers[PH_PHDR].p_paddr = phdr_offset;
- program_headers[PH_PHDR].p_filesz = sizeof(program_headers);
- program_headers[PH_PHDR].p_memsz = sizeof(program_headers);
- program_headers[PH_PHDR].p_flags = PF_R;
- program_headers[PH_PHDR].p_align = sizeof(Elf32_Word);
-
- program_headers[PH_LOAD_R__].p_type = PT_LOAD;
- program_headers[PH_LOAD_R__].p_offset = 0;
- program_headers[PH_LOAD_R__].p_vaddr = 0;
- program_headers[PH_LOAD_R__].p_paddr = 0;
- program_headers[PH_LOAD_R__].p_flags = PF_R;
-
- program_headers[PH_LOAD_R_X].p_type = PT_LOAD;
- program_headers[PH_LOAD_R_X].p_flags = PF_R | PF_X;
-
- program_headers[PH_LOAD_RW_].p_type = PT_LOAD;
- program_headers[PH_LOAD_RW_].p_flags = PF_R | PF_W;
-
- program_headers[PH_DYNAMIC].p_type = PT_DYNAMIC;
- program_headers[PH_DYNAMIC].p_flags = PF_R | PF_W;
-
- // Get the dynstr string.
- std::string dynstr(dynsym_builder_.GenerateStrtab());
-
- // Add the SONAME to the dynstr.
- uint32_t dynstr_soname_offset = dynstr.size();
- std::string file_name(elf_file_->GetPath());
- size_t directory_separator_pos = file_name.rfind('/');
- if (directory_separator_pos != std::string::npos) {
- file_name = file_name.substr(directory_separator_pos + 1);
- }
- dynstr += file_name;
- dynstr += '\0';
- if (debug_logging_) {
- LOG(INFO) << "dynstr size (bytes) =" << dynstr.size()
- << std::hex << " " << dynstr.size();
- LOG(INFO) << "dynsym size (elements)=" << dynsym_builder_.GetSize()
- << std::hex << " " << dynsym_builder_.GetSize();
- }
-
- // get the strtab
- std::string strtab;
- if (IncludingDebugSymbols()) {
- strtab = symtab_builder_.GenerateStrtab();
- if (debug_logging_) {
- LOG(INFO) << "strtab size (bytes) =" << strtab.size()
- << std::hex << " " << strtab.size();
- LOG(INFO) << "symtab size (elements) =" << symtab_builder_.GetSize()
- << std::hex << " " << symtab_builder_.GetSize();
- }
- }
-
- // Get the section header string table.
- std::vector<Elf32_Shdr*> section_ptrs;
- std::string shstrtab;
- shstrtab += '\0';
-
- // Setup sym_undef
- Elf32_Shdr null_hdr;
- memset(&null_hdr, 0, sizeof(null_hdr));
- null_hdr.sh_type = SHT_NULL;
- null_hdr.sh_link = SHN_UNDEF;
- section_ptrs.push_back(&null_hdr);
-
- uint32_t section_index = 1;
-
- // setup .dynsym
- section_ptrs.push_back(&dynsym_builder_.section_);
- AssignSectionStr(&dynsym_builder_, &shstrtab);
- dynsym_builder_.section_index_ = section_index++;
-
- // Setup .dynstr
- section_ptrs.push_back(&dynsym_builder_.strtab_.section_);
- AssignSectionStr(&dynsym_builder_.strtab_, &shstrtab);
- dynsym_builder_.strtab_.section_index_ = section_index++;
-
- // Setup .hash
- section_ptrs.push_back(&hash_builder_.section_);
- AssignSectionStr(&hash_builder_, &shstrtab);
- hash_builder_.section_index_ = section_index++;
-
- // Setup .rodata
- section_ptrs.push_back(&rodata_builder_.section_);
- AssignSectionStr(&rodata_builder_, &shstrtab);
- rodata_builder_.section_index_ = section_index++;
-
- // Setup .text
- section_ptrs.push_back(&text_builder_.section_);
- AssignSectionStr(&text_builder_, &shstrtab);
- text_builder_.section_index_ = section_index++;
-
- // Setup .dynamic
- section_ptrs.push_back(&dynamic_builder_.section_);
- AssignSectionStr(&dynamic_builder_, &shstrtab);
- dynamic_builder_.section_index_ = section_index++;
-
- if (IncludingDebugSymbols()) {
- // Setup .symtab
- section_ptrs.push_back(&symtab_builder_.section_);
- AssignSectionStr(&symtab_builder_, &shstrtab);
- symtab_builder_.section_index_ = section_index++;
-
- // Setup .strtab
- section_ptrs.push_back(&symtab_builder_.strtab_.section_);
- AssignSectionStr(&symtab_builder_.strtab_, &shstrtab);
- symtab_builder_.strtab_.section_index_ = section_index++;
- }
- ElfRawSectionBuilder* it = other_builders_.data();
- for (uint32_t cnt = 0; cnt < other_builders_.size(); ++it, ++cnt) {
- // Setup all the other sections.
- section_ptrs.push_back(&it->section_);
- AssignSectionStr(it, &shstrtab);
- it->section_index_ = section_index++;
- }
-
- // Setup shstrtab
- section_ptrs.push_back(&shstrtab_builder_.section_);
- AssignSectionStr(&shstrtab_builder_, &shstrtab);
- shstrtab_builder_.section_index_ = section_index++;
-
- if (debug_logging_) {
- LOG(INFO) << ".shstrtab size (bytes) =" << shstrtab.size()
- << std::hex << " " << shstrtab.size();
- LOG(INFO) << "section list size (elements)=" << section_ptrs.size()
- << std::hex << " " << section_ptrs.size();
- }
-
- // Fill in the hash section.
- std::vector<Elf32_Word> hash = dynsym_builder_.GenerateHashContents();
-
- if (debug_logging_) {
- LOG(INFO) << ".hash size (bytes)=" << hash.size() * sizeof(Elf32_Word)
- << std::hex << " " << hash.size() * sizeof(Elf32_Word);
- }
-
- Elf32_Word base_offset = sizeof(Elf32_Ehdr) + sizeof(program_headers);
- std::vector<ElfFilePiece*> pieces;
-
- // Get the layout in the sections.
- //
- // Get the layout of the dynsym section.
- dynsym_builder_.section_.sh_offset = RoundUp(base_offset, dynsym_builder_.section_.sh_addralign);
- dynsym_builder_.section_.sh_addr = dynsym_builder_.section_.sh_offset;
- dynsym_builder_.section_.sh_size = dynsym_builder_.GetSize() * sizeof(Elf32_Sym);
- dynsym_builder_.section_.sh_link = dynsym_builder_.GetLink();
-
- // Get the layout of the dynstr section.
- dynsym_builder_.strtab_.section_.sh_offset = NextOffset(dynsym_builder_.strtab_.section_,
- dynsym_builder_.section_);
- dynsym_builder_.strtab_.section_.sh_addr = dynsym_builder_.strtab_.section_.sh_offset;
- dynsym_builder_.strtab_.section_.sh_size = dynstr.size();
- dynsym_builder_.strtab_.section_.sh_link = dynsym_builder_.strtab_.GetLink();
-
- // Get the layout of the hash section
- hash_builder_.section_.sh_offset = NextOffset(hash_builder_.section_,
- dynsym_builder_.strtab_.section_);
- hash_builder_.section_.sh_addr = hash_builder_.section_.sh_offset;
- hash_builder_.section_.sh_size = hash.size() * sizeof(Elf32_Word);
- hash_builder_.section_.sh_link = hash_builder_.GetLink();
-
- // Get the layout of the rodata section.
- rodata_builder_.section_.sh_offset = NextOffset(rodata_builder_.section_,
- hash_builder_.section_);
- rodata_builder_.section_.sh_addr = rodata_builder_.section_.sh_offset;
- rodata_builder_.section_.sh_size = rodata_builder_.size_;
- rodata_builder_.section_.sh_link = rodata_builder_.GetLink();
-
- // Get the layout of the text section.
- text_builder_.section_.sh_offset = NextOffset(text_builder_.section_, rodata_builder_.section_);
- text_builder_.section_.sh_addr = text_builder_.section_.sh_offset;
- text_builder_.section_.sh_size = text_builder_.size_;
- text_builder_.section_.sh_link = text_builder_.GetLink();
- CHECK_ALIGNED(rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size, kPageSize);
-
- // Get the layout of the dynamic section.
- dynamic_builder_.section_.sh_offset = NextOffset(dynamic_builder_.section_,
- text_builder_.section_);
- dynamic_builder_.section_.sh_addr = dynamic_builder_.section_.sh_offset;
- dynamic_builder_.section_.sh_size = dynamic_builder_.GetSize() * sizeof(Elf32_Dyn);
- dynamic_builder_.section_.sh_link = dynamic_builder_.GetLink();
-
- Elf32_Shdr prev = dynamic_builder_.section_;
- if (IncludingDebugSymbols()) {
- // Get the layout of the symtab section.
- symtab_builder_.section_.sh_offset = NextOffset(symtab_builder_.section_,
- dynamic_builder_.section_);
- symtab_builder_.section_.sh_addr = 0;
- // Add to leave space for the null symbol.
- symtab_builder_.section_.sh_size = symtab_builder_.GetSize() * sizeof(Elf32_Sym);
- symtab_builder_.section_.sh_link = symtab_builder_.GetLink();
-
- // Get the layout of the dynstr section.
- symtab_builder_.strtab_.section_.sh_offset = NextOffset(symtab_builder_.strtab_.section_,
- symtab_builder_.section_);
- symtab_builder_.strtab_.section_.sh_addr = 0;
- symtab_builder_.strtab_.section_.sh_size = strtab.size();
- symtab_builder_.strtab_.section_.sh_link = symtab_builder_.strtab_.GetLink();
-
- prev = symtab_builder_.strtab_.section_;
- }
- if (debug_logging_) {
- LOG(INFO) << "dynsym off=" << dynsym_builder_.section_.sh_offset
- << " dynsym size=" << dynsym_builder_.section_.sh_size;
- LOG(INFO) << "dynstr off=" << dynsym_builder_.strtab_.section_.sh_offset
- << " dynstr size=" << dynsym_builder_.strtab_.section_.sh_size;
- LOG(INFO) << "hash off=" << hash_builder_.section_.sh_offset
- << " hash size=" << hash_builder_.section_.sh_size;
- LOG(INFO) << "rodata off=" << rodata_builder_.section_.sh_offset
- << " rodata size=" << rodata_builder_.section_.sh_size;
- LOG(INFO) << "text off=" << text_builder_.section_.sh_offset
- << " text size=" << text_builder_.section_.sh_size;
- LOG(INFO) << "dynamic off=" << dynamic_builder_.section_.sh_offset
- << " dynamic size=" << dynamic_builder_.section_.sh_size;
- if (IncludingDebugSymbols()) {
- LOG(INFO) << "symtab off=" << symtab_builder_.section_.sh_offset
- << " symtab size=" << symtab_builder_.section_.sh_size;
- LOG(INFO) << "strtab off=" << symtab_builder_.strtab_.section_.sh_offset
- << " strtab size=" << symtab_builder_.strtab_.section_.sh_size;
- }
- }
- // Get the layout of the extra sections. (This will deal with the debug
- // sections if they are there)
- for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
- it->section_.sh_offset = NextOffset(it->section_, prev);
- it->section_.sh_addr = 0;
- it->section_.sh_size = it->GetBuffer()->size();
- it->section_.sh_link = it->GetLink();
-
- // We postpone adding an ElfFilePiece to keep the order in "pieces."
-
- prev = it->section_;
- if (debug_logging_) {
- LOG(INFO) << it->name_ << " off=" << it->section_.sh_offset
- << " " << it->name_ << " size=" << it->section_.sh_size;
- }
- }
- // Get the layout of the shstrtab section
- shstrtab_builder_.section_.sh_offset = NextOffset(shstrtab_builder_.section_, prev);
- shstrtab_builder_.section_.sh_addr = 0;
- shstrtab_builder_.section_.sh_size = shstrtab.size();
- shstrtab_builder_.section_.sh_link = shstrtab_builder_.GetLink();
- if (debug_logging_) {
- LOG(INFO) << "shstrtab off=" << shstrtab_builder_.section_.sh_offset
- << " shstrtab size=" << shstrtab_builder_.section_.sh_size;
- }
-
- // The section list comes after come after.
- Elf32_Word sections_offset = RoundUp(
- shstrtab_builder_.section_.sh_offset + shstrtab_builder_.section_.sh_size,
- sizeof(Elf32_Word));
-
- // Setup the actual symbol arrays.
- std::vector<Elf32_Sym> dynsym = dynsym_builder_.GenerateSymtab();
- CHECK_EQ(dynsym.size() * sizeof(Elf32_Sym), dynsym_builder_.section_.sh_size);
- std::vector<Elf32_Sym> symtab;
- if (IncludingDebugSymbols()) {
- symtab = symtab_builder_.GenerateSymtab();
- CHECK_EQ(symtab.size() * sizeof(Elf32_Sym), symtab_builder_.section_.sh_size);
- }
-
- // Setup the dynamic section.
- // This will add the 2 values we cannot know until now time, namely the size
- // and the soname_offset.
- std::vector<Elf32_Dyn> dynamic = dynamic_builder_.GetDynamics(dynstr.size(),
- dynstr_soname_offset);
- CHECK_EQ(dynamic.size() * sizeof(Elf32_Dyn), dynamic_builder_.section_.sh_size);
-
- // Finish setup of the program headers now that we know the layout of the
- // whole file.
- Elf32_Word load_r_size = rodata_builder_.section_.sh_offset + rodata_builder_.section_.sh_size;
- program_headers[PH_LOAD_R__].p_filesz = load_r_size;
- program_headers[PH_LOAD_R__].p_memsz = load_r_size;
- program_headers[PH_LOAD_R__].p_align = rodata_builder_.section_.sh_addralign;
-
- Elf32_Word load_rx_size = text_builder_.section_.sh_size;
- program_headers[PH_LOAD_R_X].p_offset = text_builder_.section_.sh_offset;
- program_headers[PH_LOAD_R_X].p_vaddr = text_builder_.section_.sh_offset;
- program_headers[PH_LOAD_R_X].p_paddr = text_builder_.section_.sh_offset;
- program_headers[PH_LOAD_R_X].p_filesz = load_rx_size;
- program_headers[PH_LOAD_R_X].p_memsz = load_rx_size;
- program_headers[PH_LOAD_R_X].p_align = text_builder_.section_.sh_addralign;
-
- program_headers[PH_LOAD_RW_].p_offset = dynamic_builder_.section_.sh_offset;
- program_headers[PH_LOAD_RW_].p_vaddr = dynamic_builder_.section_.sh_offset;
- program_headers[PH_LOAD_RW_].p_paddr = dynamic_builder_.section_.sh_offset;
- program_headers[PH_LOAD_RW_].p_filesz = dynamic_builder_.section_.sh_size;
- program_headers[PH_LOAD_RW_].p_memsz = dynamic_builder_.section_.sh_size;
- program_headers[PH_LOAD_RW_].p_align = dynamic_builder_.section_.sh_addralign;
-
- program_headers[PH_DYNAMIC].p_offset = dynamic_builder_.section_.sh_offset;
- program_headers[PH_DYNAMIC].p_vaddr = dynamic_builder_.section_.sh_offset;
- program_headers[PH_DYNAMIC].p_paddr = dynamic_builder_.section_.sh_offset;
- program_headers[PH_DYNAMIC].p_filesz = dynamic_builder_.section_.sh_size;
- program_headers[PH_DYNAMIC].p_memsz = dynamic_builder_.section_.sh_size;
- program_headers[PH_DYNAMIC].p_align = dynamic_builder_.section_.sh_addralign;
-
- // Finish setup of the Ehdr values.
- elf_header_.e_phoff = phdr_offset;
- elf_header_.e_shoff = sections_offset;
- elf_header_.e_phnum = PH_NUM;
- elf_header_.e_shnum = section_ptrs.size();
- elf_header_.e_shstrndx = shstrtab_builder_.section_index_;
-
- // Add the rest of the pieces to the list.
- pieces.push_back(new ElfFileMemoryPiece("Elf Header", 0, &elf_header_, sizeof(elf_header_)));
- pieces.push_back(new ElfFileMemoryPiece("Program headers", phdr_offset,
- &program_headers, sizeof(program_headers)));
- pieces.push_back(new ElfFileMemoryPiece(".dynamic", dynamic_builder_.section_.sh_offset,
- dynamic.data(), dynamic_builder_.section_.sh_size));
- pieces.push_back(new ElfFileMemoryPiece(".dynsym", dynsym_builder_.section_.sh_offset,
- dynsym.data(), dynsym.size() * sizeof(Elf32_Sym)));
- pieces.push_back(new ElfFileMemoryPiece(".dynstr", dynsym_builder_.strtab_.section_.sh_offset,
- dynstr.c_str(), dynstr.size()));
- pieces.push_back(new ElfFileMemoryPiece(".hash", hash_builder_.section_.sh_offset,
- hash.data(), hash.size() * sizeof(Elf32_Word)));
- pieces.push_back(new ElfFileRodataPiece(rodata_builder_.section_.sh_offset, oat_writer_));
- pieces.push_back(new ElfFileOatTextPiece(text_builder_.section_.sh_offset, oat_writer_));
- if (IncludingDebugSymbols()) {
- pieces.push_back(new ElfFileMemoryPiece(".symtab", symtab_builder_.section_.sh_offset,
- symtab.data(), symtab.size() * sizeof(Elf32_Sym)));
- pieces.push_back(new ElfFileMemoryPiece(".strtab", symtab_builder_.strtab_.section_.sh_offset,
- strtab.c_str(), strtab.size()));
- }
- pieces.push_back(new ElfFileMemoryPiece(".shstrtab", shstrtab_builder_.section_.sh_offset,
- &shstrtab[0], shstrtab.size()));
- for (uint32_t i = 0; i < section_ptrs.size(); ++i) {
- // Just add all the sections in individually since they are all over the
- // place on the heap/stack.
- Elf32_Word cur_off = sections_offset + i * sizeof(Elf32_Shdr);
- pieces.push_back(new ElfFileMemoryPiece("section table piece", cur_off,
- section_ptrs[i], sizeof(Elf32_Shdr)));
- }
-
- // Postponed debug info.
- for (auto it = other_builders_.begin(); it != other_builders_.end(); ++it) {
- pieces.push_back(new ElfFileMemoryPiece(it->name_, it->section_.sh_offset,
- it->GetBuffer()->data(), it->GetBuffer()->size()));
- }
-
- if (!WriteOutFile(pieces, elf_file_)) {
- LOG(ERROR) << "Unable to write to file " << elf_file_->GetPath();
-
- STLDeleteElements(&pieces); // Have to manually clean pieces.
- return false;
- }
-
- STLDeleteElements(&pieces); // Have to manually clean pieces.
- return true;
+static void UpdateWord(std::vector<uint8_t>* buf, int offset, int data) {
+ (*buf)[offset+0] = data;
+ (*buf)[offset+1] = data >> 8;
+ (*buf)[offset+2] = data >> 16;
+ (*buf)[offset+3] = data >> 24;
}
-void ElfWriterQuick::ElfBuilder::SetupDynamic() {
- dynamic_builder_.AddDynamicTag(DT_HASH, 0, &hash_builder_);
- dynamic_builder_.AddDynamicTag(DT_STRTAB, 0, &dynsym_builder_.strtab_);
- dynamic_builder_.AddDynamicTag(DT_SYMTAB, 0, &dynsym_builder_);
- dynamic_builder_.AddDynamicTag(DT_SYMENT, sizeof(Elf32_Sym));
+static void PushHalf(std::vector<uint8_t>* buf, int data) {
+ buf->push_back(data & 0xff);
+ buf->push_back((data >> 8) & 0xff);
}
-void ElfWriterQuick::ElfBuilder::SetupRequiredSymbols() {
- dynsym_builder_.AddSymbol("oatdata", &rodata_builder_, 0, true,
- rodata_builder_.size_, STB_GLOBAL, STT_OBJECT);
- dynsym_builder_.AddSymbol("oatexec", &text_builder_, 0, true,
- text_builder_.size_, STB_GLOBAL, STT_OBJECT);
- dynsym_builder_.AddSymbol("oatlastword", &text_builder_, text_builder_.size_ - 4,
- true, 4, STB_GLOBAL, STT_OBJECT);
-}
-
-void ElfWriterQuick::ElfDynamicBuilder::AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un) {
- if (tag == DT_NULL) {
- return;
- }
- dynamics_.push_back({nullptr, tag, d_un});
-}
-
-void ElfWriterQuick::ElfDynamicBuilder::AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un,
- ElfSectionBuilder* section) {
- if (tag == DT_NULL) {
- return;
- }
- dynamics_.push_back({section, tag, d_un});
-}
-
-std::vector<Elf32_Dyn> ElfWriterQuick::ElfDynamicBuilder::GetDynamics(Elf32_Word strsz,
- Elf32_Word soname) {
- std::vector<Elf32_Dyn> ret;
- for (auto it = dynamics_.cbegin(); it != dynamics_.cend(); ++it) {
- if (it->section_) {
- // We are adding an address relative to a section.
- ret.push_back(
- {it->tag_, {it->off_ + it->section_->section_.sh_addr}});
- } else {
- ret.push_back({it->tag_, {it->off_}});
- }
- }
- ret.push_back({DT_STRSZ, {strsz}});
- ret.push_back({DT_SONAME, {soname}});
- ret.push_back({DT_NULL, {0}});
- return ret;
-}
-
-std::vector<Elf32_Sym> ElfWriterQuick::ElfSymtabBuilder::GenerateSymtab() {
- std::vector<Elf32_Sym> ret;
- Elf32_Sym undef_sym;
- memset(&undef_sym, 0, sizeof(undef_sym));
- undef_sym.st_shndx = SHN_UNDEF;
- ret.push_back(undef_sym);
-
- for (auto it = symbols_.cbegin(); it != symbols_.cend(); ++it) {
- Elf32_Sym sym;
- memset(&sym, 0, sizeof(sym));
- sym.st_name = it->name_idx_;
- if (it->is_relative_) {
- sym.st_value = it->addr_ + it->section_->section_.sh_offset;
- } else {
- sym.st_value = it->addr_;
- }
- sym.st_size = it->size_;
- sym.st_other = it->other_;
- sym.st_shndx = it->section_->section_index_;
- sym.st_info = it->info_;
-
- ret.push_back(sym);
- }
- return ret;
-}
-
-std::string ElfWriterQuick::ElfSymtabBuilder::GenerateStrtab() {
- std::string tab;
- tab += '\0';
- for (auto it = symbols_.begin(); it != symbols_.end(); ++it) {
- it->name_idx_ = tab.size();
- tab += it->name_;
- tab += '\0';
- }
- strtab_.section_.sh_size = tab.size();
- return tab;
-}
-
-void ElfWriterQuick::ElfBuilder::AssignSectionStr(
- ElfSectionBuilder* builder, std::string* strtab) {
- builder->section_.sh_name = strtab->size();
- *strtab += builder->name_;
- *strtab += '\0';
- if (debug_logging_) {
- LOG(INFO) << "adding section name \"" << builder->name_ << "\" "
- << "to shstrtab at offset " << builder->section_.sh_name;
- }
-}
-
-// from bionic
-static unsigned elfhash(const char *_name) {
- const unsigned char *name = (const unsigned char *) _name;
- unsigned h = 0, g;
-
- while (*name) {
- h = (h << 4) + *name++;
- g = h & 0xf0000000;
- h ^= g;
- h ^= g >> 24;
- }
- return h;
-}
-
-
-std::vector<Elf32_Word> ElfWriterQuick::ElfSymtabBuilder::GenerateHashContents() {
- // Here is how The ELF hash table works.
- // There are 3 arrays to worry about.
- // * The symbol table where the symbol information is.
- // * The bucket array which is an array of indexes into the symtab and chain.
- // * The chain array which is also an array of indexes into the symtab and chain.
- //
- // Lets say the state is something like this.
- // +--------+ +--------+ +-----------+
- // | symtab | | bucket | | chain |
- // | nullptr | | 1 | | STN_UNDEF |
- // | <sym1> | | 4 | | 2 |
- // | <sym2> | | | | 5 |
- // | <sym3> | | | | STN_UNDEF |
- // | <sym4> | | | | 3 |
- // | <sym5> | | | | STN_UNDEF |
- // +--------+ +--------+ +-----------+
- //
- // The lookup process (in python psudocode) is
- //
- // def GetSym(name):
- // # NB STN_UNDEF == 0
- // indx = bucket[elfhash(name) % num_buckets]
- // while indx != STN_UNDEF:
- // if GetSymbolName(symtab[indx]) == name:
- // return symtab[indx]
- // indx = chain[indx]
- // return SYMBOL_NOT_FOUND
- //
- // Between bucket and chain arrays every symtab index must be present exactly
- // once (except for STN_UNDEF, which must be present 1 + num_bucket times).
-
- // Select number of buckets.
- // This is essentially arbitrary.
- Elf32_Word nbuckets;
- Elf32_Word chain_size = GetSize();
- if (symbols_.size() < 8) {
- nbuckets = 2;
- } else if (symbols_.size() < 32) {
- nbuckets = 4;
- } else if (symbols_.size() < 256) {
- nbuckets = 16;
- } else {
- // Have about 32 ids per bucket.
- nbuckets = RoundUp(symbols_.size()/32, 2);
- }
- std::vector<Elf32_Word> hash;
- hash.push_back(nbuckets);
- hash.push_back(chain_size);
- uint32_t bucket_offset = hash.size();
- uint32_t chain_offset = bucket_offset + nbuckets;
- hash.resize(hash.size() + nbuckets + chain_size, 0);
-
- Elf32_Word* buckets = hash.data() + bucket_offset;
- Elf32_Word* chain = hash.data() + chain_offset;
-
- // Set up the actual hash table.
- for (Elf32_Word i = 0; i < symbols_.size(); i++) {
- // Add 1 since we need to have the null symbol that is not in the symbols
- // list.
- Elf32_Word index = i + 1;
- Elf32_Word hash_val = static_cast<Elf32_Word>(elfhash(symbols_[i].name_.c_str())) % nbuckets;
- if (buckets[hash_val] == 0) {
- buckets[hash_val] = index;
- } else {
- hash_val = buckets[hash_val];
- CHECK_LT(hash_val, chain_size);
- while (chain[hash_val] != 0) {
- hash_val = chain[hash_val];
- CHECK_LT(hash_val, chain_size);
- }
- chain[hash_val] = index;
- // Check for loops. Works because if this is non-empty then there must be
- // another cell which already contains the same symbol index as this one,
- // which means some symbol has more then one name, which isn't allowed.
- CHECK_EQ(chain[index], static_cast<Elf32_Word>(0));
- }
- }
-
- return hash;
-}
-
-void ElfWriterQuick::ElfBuilder::SetupEhdr() {
- memset(&elf_header_, 0, sizeof(elf_header_));
- elf_header_.e_ident[EI_MAG0] = ELFMAG0;
- elf_header_.e_ident[EI_MAG1] = ELFMAG1;
- elf_header_.e_ident[EI_MAG2] = ELFMAG2;
- elf_header_.e_ident[EI_MAG3] = ELFMAG3;
- elf_header_.e_ident[EI_CLASS] = ELFCLASS32;
- elf_header_.e_ident[EI_DATA] = ELFDATA2LSB;
- elf_header_.e_ident[EI_VERSION] = EV_CURRENT;
- elf_header_.e_ident[EI_OSABI] = ELFOSABI_LINUX;
- elf_header_.e_ident[EI_ABIVERSION] = 0;
- elf_header_.e_type = ET_DYN;
- elf_header_.e_version = 1;
- elf_header_.e_entry = 0;
- elf_header_.e_ehsize = sizeof(Elf32_Ehdr);
- elf_header_.e_phentsize = sizeof(Elf32_Phdr);
- elf_header_.e_shentsize = sizeof(Elf32_Shdr);
- elf_header_.e_phoff = sizeof(Elf32_Ehdr);
-}
-
-void ElfWriterQuick::ElfBuilder::SetISA(InstructionSet isa) {
- switch (isa) {
- case kArm:
- // Fall through.
- case kThumb2: {
- elf_header_.e_machine = EM_ARM;
- elf_header_.e_flags = EF_ARM_EABI_VER5;
- break;
- }
- case kArm64: {
- elf_header_.e_machine = EM_AARCH64;
- elf_header_.e_flags = 0;
- break;
- }
- case kX86: {
- elf_header_.e_machine = EM_386;
- elf_header_.e_flags = 0;
- break;
- }
- case kX86_64: {
- elf_header_.e_machine = EM_X86_64;
- elf_header_.e_flags = 0;
- break;
- }
- case kMips: {
- elf_header_.e_machine = EM_MIPS;
- elf_header_.e_flags = (EF_MIPS_NOREORDER |
- EF_MIPS_PIC |
- EF_MIPS_CPIC |
- EF_MIPS_ABI_O32 |
- EF_MIPS_ARCH_32R2);
- break;
- }
- default: {
- fatal_error_ = true;
- LOG(FATAL) << "Unknown instruction set: " << isa;
- break;
- }
- }
-}
-
-void ElfWriterQuick::ElfSymtabBuilder::AddSymbol(
- const std::string& name, const ElfSectionBuilder* section, Elf32_Addr addr,
- bool is_relative, Elf32_Word size, uint8_t binding, uint8_t type, uint8_t other) {
- CHECK(section);
- ElfSymtabBuilder::ElfSymbolState state {name, section, addr, size, is_relative,
- MakeStInfo(binding, type), other, 0};
- symbols_.push_back(state);
-}
-
-bool ElfWriterQuick::Create(File* elf_file,
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
+bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Create(File* elf_file,
OatWriter* oat_writer,
const std::vector<const DexFile*>& dex_files,
const std::string& android_root,
@@ -888,150 +85,428 @@
return elf_writer.Write(oat_writer, dex_files, android_root, is_host);
}
-// Add patch information to this section. Each patch is a Elf32_Word that
-// identifies an offset from the start of the text section
-void ElfWriterQuick::ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug) {
- size_t size =
- compiler_driver_->GetCodeToPatch().size() +
- compiler_driver_->GetMethodsToPatch().size() +
- compiler_driver_->GetClassesToPatch().size();
- if (size == 0) {
- if (debug) {
- LOG(INFO) << "No patches to record";
- }
- return;
+std::vector<uint8_t>* ConstructCIEFrameX86(bool is_x86_64) {
+ std::vector<uint8_t>* cfi_info = new std::vector<uint8_t>;
+
+ // Length (will be filled in later in this routine).
+ if (is_x86_64) {
+ PushWord(cfi_info, 0xffffffff); // Indicates 64bit
+ PushWord(cfi_info, 0);
+ PushWord(cfi_info, 0);
+ } else {
+ PushWord(cfi_info, 0);
}
- buffer->resize(size * sizeof(uintptr_t));
- if (debug) {
- LOG(INFO) << "Patches reserved for " << size;
+
+ // CIE id: always 0.
+ if (is_x86_64) {
+ PushWord(cfi_info, 0);
+ PushWord(cfi_info, 0);
+ } else {
+ PushWord(cfi_info, 0);
+ }
+
+ // Version: always 1.
+ cfi_info->push_back(0x01);
+
+ // Augmentation: 'zR\0'
+ cfi_info->push_back(0x7a);
+ cfi_info->push_back(0x52);
+ cfi_info->push_back(0x0);
+
+ // Code alignment: 1.
+ EncodeUnsignedLeb128(1, cfi_info);
+
+ // Data alignment.
+ if (is_x86_64) {
+ EncodeSignedLeb128(-8, cfi_info);
+ } else {
+ EncodeSignedLeb128(-4, cfi_info);
+ }
+
+ // Return address register.
+ if (is_x86_64) {
+ // R16(RIP)
+ cfi_info->push_back(0x10);
+ } else {
+ // R8(EIP)
+ cfi_info->push_back(0x08);
+ }
+
+ // Augmentation length: 1.
+ cfi_info->push_back(1);
+
+ // Augmentation data.
+ if (is_x86_64) {
+ // 0x04 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8).
+ cfi_info->push_back(0x04);
+ } else {
+ // 0x03 ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4).
+ cfi_info->push_back(0x03);
+ }
+
+ // Initial instructions.
+ if (is_x86_64) {
+ // DW_CFA_def_cfa R7(RSP) 8.
+ cfi_info->push_back(0x0c);
+ cfi_info->push_back(0x07);
+ cfi_info->push_back(0x08);
+
+ // DW_CFA_offset R16(RIP) 1 (* -8).
+ cfi_info->push_back(0x90);
+ cfi_info->push_back(0x01);
+ } else {
+ // DW_CFA_def_cfa R4(ESP) 4.
+ cfi_info->push_back(0x0c);
+ cfi_info->push_back(0x04);
+ cfi_info->push_back(0x04);
+
+ // DW_CFA_offset R8(EIP) 1 (* -4).
+ cfi_info->push_back(0x88);
+ cfi_info->push_back(0x01);
+ }
+
+ // Padding to a multiple of 4
+ while ((cfi_info->size() & 3) != 0) {
+ // DW_CFA_nop is encoded as 0.
+ cfi_info->push_back(0);
+ }
+
+ // Set the length of the CIE inside the generated bytes.
+ if (is_x86_64) {
+ uint32_t length = cfi_info->size() - 12;
+ UpdateWord(cfi_info, 4, length);
+ } else {
+ uint32_t length = cfi_info->size() - 4;
+ UpdateWord(cfi_info, 0, length);
+ }
+ return cfi_info;
+}
+
+std::vector<uint8_t>* ConstructCIEFrame(InstructionSet isa) {
+ switch (isa) {
+ case kX86:
+ return ConstructCIEFrameX86(false);
+ case kX86_64:
+ return ConstructCIEFrameX86(true);
+
+ default:
+ // Not implemented.
+ return nullptr;
}
}
-bool ElfWriterQuick::Write(OatWriter* oat_writer,
+class OatWriterWrapper : public CodeOutput {
+ public:
+ explicit OatWriterWrapper(OatWriter* oat_writer) : oat_writer_(oat_writer) {}
+
+ void SetCodeOffset(size_t offset) {
+ oat_writer_->SetOatDataOffset(offset);
+ }
+ bool Write(OutputStream* out) OVERRIDE {
+ return oat_writer_->Write(out);
+ }
+ private:
+ OatWriter* oat_writer_;
+};
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+ OatWriter* oat_writer);
+
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
+bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>::Write(OatWriter* oat_writer,
const std::vector<const DexFile*>& dex_files_unused,
const std::string& android_root_unused,
bool is_host_unused) {
- const bool debug = false;
- const bool add_symbols = oat_writer->DidAddSymbols();
+ constexpr bool debug = false;
const OatHeader& oat_header = oat_writer->GetOatHeader();
- Elf32_Word oat_data_size = oat_header.GetExecutableOffset();
+ Elf_Word oat_data_size = oat_header.GetExecutableOffset();
uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
- ElfBuilder builder(oat_writer, elf_file_, compiler_driver_->GetInstructionSet(), 0,
- oat_data_size, oat_data_size, oat_exec_size, add_symbols, debug);
+ OatWriterWrapper wrapper(oat_writer);
- if (add_symbols) {
- AddDebugSymbols(builder, oat_writer, debug);
+ std::unique_ptr<ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr> > builder(
+ new ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>(
+ &wrapper,
+ elf_file_,
+ compiler_driver_->GetInstructionSet(),
+ 0,
+ oat_data_size,
+ oat_data_size,
+ oat_exec_size,
+ compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols(),
+ debug));
+
+ if (!builder->Init()) {
+ return false;
}
- bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
- if (generateDebugInformation) {
- ElfRawSectionBuilder debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0);
- ElfRawSectionBuilder eh_frame(".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0);
- eh_frame.SetBuffer(*compiler_driver_->GetCallFrameInformation());
-
- FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
- debug_abbrev.GetBuffer(), debug_str.GetBuffer());
- builder.RegisterRawSection(debug_info);
- builder.RegisterRawSection(debug_abbrev);
- builder.RegisterRawSection(eh_frame);
- builder.RegisterRawSection(debug_str);
+ if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ WriteDebugSymbols(compiler_driver_, builder.get(), oat_writer);
}
if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {
- ElfRawSectionBuilder oat_patches(".oat_patches", SHT_OAT_PATCH, 0, NULL, 0,
- sizeof(size_t), sizeof(size_t));
- ReservePatchSpace(oat_patches.GetBuffer(), debug);
- builder.RegisterRawSection(oat_patches);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> oat_patches(
+ ".oat_patches", SHT_OAT_PATCH, 0, NULL, 0, sizeof(uintptr_t), sizeof(uintptr_t));
+ const std::vector<uintptr_t>& locations = oat_writer->GetAbsolutePatchLocations();
+ const uint8_t* begin = reinterpret_cast<const uint8_t*>(&locations[0]);
+ const uint8_t* end = begin + locations.size() * sizeof(locations[0]);
+ oat_patches.GetBuffer()->assign(begin, end);
+ if (debug) {
+ LOG(INFO) << "Prepared .oat_patches for " << locations.size() << " patches.";
+ }
+ builder->RegisterRawSection(oat_patches);
}
- return builder.Write();
+ return builder->Write();
}
-void ElfWriterQuick::AddDebugSymbols(ElfBuilder& builder, OatWriter* oat_writer, bool debug) {
+class LineTableGenerator FINAL : public Leb128Encoder {
+ public:
+ LineTableGenerator(int line_base, int line_range, int opcode_base,
+ std::vector<uint8_t>* data, uintptr_t current_address,
+ size_t current_line)
+ : Leb128Encoder(data), line_base_(line_base), line_range_(line_range),
+ opcode_base_(opcode_base), current_address_(current_address),
+ current_line_(current_line), current_file_index_(0) {}
+
+ void PutDelta(unsigned delta_addr, int delta_line) {
+ current_line_ += delta_line;
+ current_address_ += delta_addr;
+
+ if (delta_line >= line_base_ && delta_line < line_base_ + line_range_) {
+ unsigned special_opcode = (delta_line - line_base_) +
+ (line_range_ * delta_addr) + opcode_base_;
+ if (special_opcode <= 255) {
+ PushByte(data_, special_opcode);
+ return;
+ }
+ }
+
+ // generate standart opcode for address advance
+ if (delta_addr != 0) {
+ PushByte(data_, DW_LNS_advance_pc);
+ PushBackUnsigned(delta_addr);
+ }
+
+ // generate standart opcode for line delta
+ if (delta_line != 0) {
+ PushByte(data_, DW_LNS_advance_line);
+ PushBackSigned(delta_line);
+ }
+
+ // generate standart opcode for new LTN entry
+ PushByte(data_, DW_LNS_copy);
+ }
+
+ void SetAddr(uintptr_t addr) {
+ if (current_address_ == addr) {
+ return;
+ }
+
+ current_address_ = addr;
+
+ PushByte(data_, 0); // extended opcode:
+ PushByte(data_, 1 + 4); // length: opcode_size + address_size
+ PushByte(data_, DW_LNE_set_address);
+ PushWord(data_, addr);
+ }
+
+ void SetLine(unsigned line) {
+ int delta_line = line - current_line_;
+ if (delta_line) {
+ current_line_ = line;
+ PushByte(data_, DW_LNS_advance_line);
+ PushBackSigned(delta_line);
+ }
+ }
+
+ void SetFile(unsigned file_index) {
+ if (current_file_index_ != file_index) {
+ current_file_index_ = file_index;
+ PushByte(data_, DW_LNS_set_file);
+ PushBackUnsigned(file_index);
+ }
+ }
+
+ void EndSequence() {
+ // End of Line Table Program
+ // 0(=ext), 1(len), DW_LNE_end_sequence
+ PushByte(data_, 0);
+ PushByte(data_, 1);
+ PushByte(data_, DW_LNE_end_sequence);
+ }
+
+ private:
+ const int line_base_;
+ const int line_range_;
+ const int opcode_base_;
+ uintptr_t current_address_;
+ size_t current_line_;
+ unsigned current_file_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(LineTableGenerator);
+};
+
+// TODO: rewriting it using DexFile::DecodeDebugInfo needs unneeded stuff.
+static void GetLineInfoForJava(const uint8_t* dbgstream, const SrcMap& pc2dex,
+ SrcMap* result, uint32_t start_pc = 0) {
+ if (dbgstream == nullptr) {
+ return;
+ }
+
+ int adjopcode;
+ uint32_t dex_offset = 0;
+ uint32_t java_line = DecodeUnsignedLeb128(&dbgstream);
+
+ // skip parameters
+ for (uint32_t param_count = DecodeUnsignedLeb128(&dbgstream); param_count != 0; --param_count) {
+ DecodeUnsignedLeb128(&dbgstream);
+ }
+
+ for (bool is_end = false; is_end == false; ) {
+ uint8_t opcode = *dbgstream;
+ dbgstream++;
+ switch (opcode) {
+ case DexFile::DBG_END_SEQUENCE:
+ is_end = true;
+ break;
+
+ case DexFile::DBG_ADVANCE_PC:
+ dex_offset += DecodeUnsignedLeb128(&dbgstream);
+ break;
+
+ case DexFile::DBG_ADVANCE_LINE:
+ java_line += DecodeSignedLeb128(&dbgstream);
+ break;
+
+ case DexFile::DBG_START_LOCAL:
+ case DexFile::DBG_START_LOCAL_EXTENDED:
+ DecodeUnsignedLeb128(&dbgstream);
+ DecodeUnsignedLeb128(&dbgstream);
+ DecodeUnsignedLeb128(&dbgstream);
+
+ if (opcode == DexFile::DBG_START_LOCAL_EXTENDED) {
+ DecodeUnsignedLeb128(&dbgstream);
+ }
+ break;
+
+ case DexFile::DBG_END_LOCAL:
+ case DexFile::DBG_RESTART_LOCAL:
+ DecodeUnsignedLeb128(&dbgstream);
+ break;
+
+ case DexFile::DBG_SET_PROLOGUE_END:
+ case DexFile::DBG_SET_EPILOGUE_BEGIN:
+ case DexFile::DBG_SET_FILE:
+ break;
+
+ default:
+ adjopcode = opcode - DexFile::DBG_FIRST_SPECIAL;
+ dex_offset += adjopcode / DexFile::DBG_LINE_RANGE;
+ java_line += DexFile::DBG_LINE_BASE + (adjopcode % DexFile::DBG_LINE_RANGE);
+
+ for (SrcMap::const_iterator found = pc2dex.FindByTo(dex_offset);
+ found != pc2dex.end() && found->to_ == static_cast<int32_t>(dex_offset);
+ found++) {
+ result->push_back({found->from_ + start_pc, static_cast<int32_t>(java_line)});
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * @brief Generate the DWARF debug_info and debug_abbrev sections
+ * @param oat_writer The Oat file Writer.
+ * @param dbg_info Compilation unit information.
+ * @param dbg_abbrev Abbreviations used to generate dbg_info.
+ * @param dbg_str Debug strings.
+ */
+static void FillInCFIInformation(OatWriter* oat_writer,
+ std::vector<uint8_t>* dbg_info,
+ std::vector<uint8_t>* dbg_abbrev,
+ std::vector<uint8_t>* dbg_str,
+ std::vector<uint8_t>* dbg_line,
+ uint32_t text_section_offset) {
const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
- ElfSymtabBuilder* symtab = &builder.symtab_builder_;
- for (auto it = method_info.begin(); it != method_info.end(); ++it) {
- symtab->AddSymbol(it->method_name_, &builder.text_builder_, it->low_pc_, true,
- it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
- }
-}
-static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) {
- (*buf)[offset+0] = data;
- (*buf)[offset+1] = data >> 8;
- (*buf)[offset+2] = data >> 16;
- (*buf)[offset+3] = data >> 24;
-}
+ uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat");
-static void PushWord(std::vector<uint8_t>*buf, int data) {
- buf->push_back(data & 0xff);
- buf->push_back((data >> 8) & 0xff);
- buf->push_back((data >> 16) & 0xff);
- buf->push_back((data >> 24) & 0xff);
-}
-
-static void PushHalf(std::vector<uint8_t>*buf, int data) {
- buf->push_back(data & 0xff);
- buf->push_back((data >> 8) & 0xff);
-}
-
-void ElfWriterQuick::FillInCFIInformation(OatWriter* oat_writer,
- std::vector<uint8_t>* dbg_info,
- std::vector<uint8_t>* dbg_abbrev,
- std::vector<uint8_t>* dbg_str) {
// Create the debug_abbrev section with boilerplate information.
// We only care about low_pc and high_pc right now for the compilation
// unit and methods.
// Tag 1: Compilation unit: DW_TAG_compile_unit.
- dbg_abbrev->push_back(1);
- dbg_abbrev->push_back(DW_TAG_compile_unit);
+ PushByte(dbg_abbrev, 1);
+ PushByte(dbg_abbrev, DW_TAG_compile_unit);
// There are children (the methods).
- dbg_abbrev->push_back(DW_CHILDREN_yes);
+ PushByte(dbg_abbrev, DW_CHILDREN_yes);
+
+ // DW_AT_producer DW_FORM_data1.
+ // REVIEW: we can get rid of dbg_str section if
+ // DW_FORM_string (immediate string) was used everywhere instead of
+ // DW_FORM_strp (ref to string from .debug_str section).
+ // DW_FORM_strp makes sense only if we reuse the strings.
+ PushByte(dbg_abbrev, DW_AT_producer);
+ PushByte(dbg_abbrev, DW_FORM_strp);
// DW_LANG_Java DW_FORM_data1.
- dbg_abbrev->push_back(DW_AT_language);
- dbg_abbrev->push_back(DW_FORM_data1);
+ PushByte(dbg_abbrev, DW_AT_language);
+ PushByte(dbg_abbrev, DW_FORM_data1);
// DW_AT_low_pc DW_FORM_addr.
- dbg_abbrev->push_back(DW_AT_low_pc);
- dbg_abbrev->push_back(DW_FORM_addr);
+ PushByte(dbg_abbrev, DW_AT_low_pc);
+ PushByte(dbg_abbrev, DW_FORM_addr);
// DW_AT_high_pc DW_FORM_addr.
- dbg_abbrev->push_back(DW_AT_high_pc);
- dbg_abbrev->push_back(DW_FORM_addr);
+ PushByte(dbg_abbrev, DW_AT_high_pc);
+ PushByte(dbg_abbrev, DW_FORM_addr);
+
+ if (dbg_line != nullptr) {
+ // DW_AT_stmt_list DW_FORM_sec_offset.
+ PushByte(dbg_abbrev, DW_AT_stmt_list);
+ PushByte(dbg_abbrev, DW_FORM_sec_offset);
+ }
// End of DW_TAG_compile_unit.
PushHalf(dbg_abbrev, 0);
// Tag 2: Compilation unit: DW_TAG_subprogram.
- dbg_abbrev->push_back(2);
- dbg_abbrev->push_back(DW_TAG_subprogram);
+ PushByte(dbg_abbrev, 2);
+ PushByte(dbg_abbrev, DW_TAG_subprogram);
// There are no children.
- dbg_abbrev->push_back(DW_CHILDREN_no);
+ PushByte(dbg_abbrev, DW_CHILDREN_no);
// Name of the method.
- dbg_abbrev->push_back(DW_AT_name);
- dbg_abbrev->push_back(DW_FORM_strp);
+ PushByte(dbg_abbrev, DW_AT_name);
+ PushByte(dbg_abbrev, DW_FORM_strp);
// DW_AT_low_pc DW_FORM_addr.
- dbg_abbrev->push_back(DW_AT_low_pc);
- dbg_abbrev->push_back(DW_FORM_addr);
+ PushByte(dbg_abbrev, DW_AT_low_pc);
+ PushByte(dbg_abbrev, DW_FORM_addr);
// DW_AT_high_pc DW_FORM_addr.
- dbg_abbrev->push_back(DW_AT_high_pc);
- dbg_abbrev->push_back(DW_FORM_addr);
+ PushByte(dbg_abbrev, DW_AT_high_pc);
+ PushByte(dbg_abbrev, DW_FORM_addr);
// End of DW_TAG_subprogram.
PushHalf(dbg_abbrev, 0);
// Start the debug_info section with the header information
// 'unit_length' will be filled in later.
+ int cunit_length = dbg_info->size();
PushWord(dbg_info, 0);
// 'version' - 3.
@@ -1041,55 +516,284 @@
PushWord(dbg_info, 0);
// Address size: 4.
- dbg_info->push_back(4);
+ PushByte(dbg_info, 4);
// Start the description for the compilation unit.
// This uses tag 1.
- dbg_info->push_back(1);
+ PushByte(dbg_info, 1);
+
+ // The producer is Android dex2oat.
+ PushWord(dbg_info, producer_str_offset);
// The language is Java.
- dbg_info->push_back(DW_LANG_Java);
+ PushByte(dbg_info, DW_LANG_Java);
- // Leave space for low_pc and high_pc.
- int low_pc_offset = dbg_info->size();
+ // low_pc and high_pc.
+ uint32_t cunit_low_pc = 0 - 1;
+ uint32_t cunit_high_pc = 0;
+ int cunit_low_pc_pos = dbg_info->size();
PushWord(dbg_info, 0);
PushWord(dbg_info, 0);
- // Walk through the information in the method table, and enter into dbg_info.
- const std::vector<OatWriter::DebugInfo>& dbg = oat_writer->GetCFIMethodInfo();
- uint32_t low_pc = 0xFFFFFFFFU;
- uint32_t high_pc = 0;
+ if (dbg_line == nullptr) {
+ for (size_t i = 0; i < method_info.size(); ++i) {
+ const OatWriter::DebugInfo &dbg = method_info[i];
- for (uint32_t i = 0; i < dbg.size(); i++) {
- const OatWriter::DebugInfo& info = dbg[i];
- if (info.low_pc_ < low_pc) {
- low_pc = info.low_pc_;
+ cunit_low_pc = std::min(cunit_low_pc, dbg.low_pc_);
+ cunit_high_pc = std::max(cunit_high_pc, dbg.high_pc_);
+
+ // Start a new TAG: subroutine (2).
+ PushByte(dbg_info, 2);
+
+ // Enter name, low_pc, high_pc.
+ PushWord(dbg_info, PushStr(dbg_str, dbg.method_name_));
+ PushWord(dbg_info, dbg.low_pc_ + text_section_offset);
+ PushWord(dbg_info, dbg.high_pc_ + text_section_offset);
}
- if (info.high_pc_ > high_pc) {
- high_pc = info.high_pc_;
+ } else {
+ // TODO: in gdb info functions <regexp> - reports Java functions, but
+ // source file is <unknown> because .debug_line is formed as one
+ // compilation unit. To fix this it is possible to generate
+ // a separate compilation unit for every distinct Java source.
+ // Each of the these compilation units can have several non-adjacent
+ // method ranges.
+
+ // Line number table offset
+ PushWord(dbg_info, dbg_line->size());
+
+ size_t lnt_length = dbg_line->size();
+ PushWord(dbg_line, 0);
+
+ PushHalf(dbg_line, 4); // LNT Version DWARF v4 => 4
+
+ size_t lnt_hdr_length = dbg_line->size();
+ PushWord(dbg_line, 0); // TODO: 64-bit uses 8-byte here
+
+ PushByte(dbg_line, 1); // minimum_instruction_length (ubyte)
+ PushByte(dbg_line, 1); // maximum_operations_per_instruction (ubyte) = always 1
+ PushByte(dbg_line, 1); // default_is_stmt (ubyte)
+
+ const int8_t LINE_BASE = -5;
+ PushByte(dbg_line, LINE_BASE); // line_base (sbyte)
+
+ const uint8_t LINE_RANGE = 14;
+ PushByte(dbg_line, LINE_RANGE); // line_range (ubyte)
+
+ const uint8_t OPCODE_BASE = 13;
+ PushByte(dbg_line, OPCODE_BASE); // opcode_base (ubyte)
+
+ // Standard_opcode_lengths (array of ubyte).
+ PushByte(dbg_line, 0); PushByte(dbg_line, 1); PushByte(dbg_line, 1);
+ PushByte(dbg_line, 1); PushByte(dbg_line, 1); PushByte(dbg_line, 0);
+ PushByte(dbg_line, 0); PushByte(dbg_line, 0); PushByte(dbg_line, 1);
+ PushByte(dbg_line, 0); PushByte(dbg_line, 0); PushByte(dbg_line, 1);
+
+ PushByte(dbg_line, 0); // include_directories (sequence of path names) = EMPTY
+
+ // File_names (sequence of file entries).
+ std::unordered_map<const char*, size_t> files;
+ for (size_t i = 0; i < method_info.size(); ++i) {
+ const OatWriter::DebugInfo &dbg = method_info[i];
+ // TODO: add package directory to the file name
+ const char* file_name = dbg.src_file_name_ == nullptr ? "null" : dbg.src_file_name_;
+ auto found = files.find(file_name);
+ if (found == files.end()) {
+ size_t file_index = 1 + files.size();
+ files[file_name] = file_index;
+ PushStr(dbg_line, file_name);
+ PushByte(dbg_line, 0); // include directory index = LEB128(0) - no directory
+ PushByte(dbg_line, 0); // modification time = LEB128(0) - NA
+ PushByte(dbg_line, 0); // file length = LEB128(0) - NA
+ }
+ }
+ PushByte(dbg_line, 0); // End of file_names.
+
+ // Set lnt header length.
+ UpdateWord(dbg_line, lnt_hdr_length, dbg_line->size() - lnt_hdr_length - 4);
+
+ // Generate Line Number Program code, one long program for all methods.
+ LineTableGenerator line_table_generator(LINE_BASE, LINE_RANGE, OPCODE_BASE,
+ dbg_line, 0, 1);
+
+ SrcMap pc2java_map;
+ for (size_t i = 0; i < method_info.size(); ++i) {
+ const OatWriter::DebugInfo &dbg = method_info[i];
+ const char* file_name = (dbg.src_file_name_ == nullptr) ? "null" : dbg.src_file_name_;
+ size_t file_index = files[file_name];
+ DCHECK_NE(file_index, 0U) << file_name;
+
+ cunit_low_pc = std::min(cunit_low_pc, dbg.low_pc_);
+ cunit_high_pc = std::max(cunit_high_pc, dbg.high_pc_);
+
+ // Start a new TAG: subroutine (2).
+ PushByte(dbg_info, 2);
+
+ // Enter name, low_pc, high_pc.
+ PushWord(dbg_info, PushStr(dbg_str, dbg.method_name_));
+ PushWord(dbg_info, dbg.low_pc_ + text_section_offset);
+ PushWord(dbg_info, dbg.high_pc_ + text_section_offset);
+
+ GetLineInfoForJava(dbg.dbgstream_, dbg.compiled_method_->GetSrcMappingTable(),
+ &pc2java_map, dbg.low_pc_);
+ pc2java_map.DeltaFormat({dbg.low_pc_, 1}, dbg.high_pc_);
+ if (!pc2java_map.empty()) {
+ line_table_generator.SetFile(file_index);
+ line_table_generator.SetAddr(dbg.low_pc_ + text_section_offset);
+ line_table_generator.SetLine(1);
+ for (auto& src_map_elem : pc2java_map) {
+ line_table_generator.PutDelta(src_map_elem.from_, src_map_elem.to_);
+ }
+ pc2java_map.clear();
+ }
}
- // Start a new TAG: subroutine (2).
- dbg_info->push_back(2);
+ // End Sequence should have the highest address set.
+ line_table_generator.SetAddr(cunit_high_pc + text_section_offset);
+ line_table_generator.EndSequence();
- // Enter the name into the string table (and NUL terminate).
- uint32_t str_offset = dbg_str->size();
- dbg_str->insert(dbg_str->end(), info.method_name_.begin(), info.method_name_.end());
- dbg_str->push_back('\0');
-
- // Enter name, low_pc, high_pc.
- PushWord(dbg_info, str_offset);
- PushWord(dbg_info, info.low_pc_);
- PushWord(dbg_info, info.high_pc_);
+ // set lnt length
+ UpdateWord(dbg_line, lnt_length, dbg_line->size() - lnt_length - 4);
}
// One byte terminator
- dbg_info->push_back(0);
+ PushByte(dbg_info, 0);
- // We have now walked all the methods. Fill in lengths and low/high PCs.
- UpdateWord(dbg_info, 0, dbg_info->size() - 4);
- UpdateWord(dbg_info, low_pc_offset, low_pc);
- UpdateWord(dbg_info, low_pc_offset + 4, high_pc);
+ // Fill in cunit's low_pc and high_pc.
+ UpdateWord(dbg_info, cunit_low_pc_pos, cunit_low_pc + text_section_offset);
+ UpdateWord(dbg_info, cunit_low_pc_pos + 4, cunit_high_pc + text_section_offset);
+
+ // We have now walked all the methods. Fill in lengths.
+ UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4);
}
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
+static void WriteDebugSymbols(const CompilerDriver* compiler_driver,
+ ElfBuilder<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn,
+ Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder,
+ OatWriter* oat_writer) {
+ std::unique_ptr<std::vector<uint8_t>> cfi_info(
+ ConstructCIEFrame(compiler_driver->GetInstructionSet()));
+
+ Elf_Addr text_section_address = builder->text_builder_.section_.sh_addr;
+
+ // Iterate over the compiled methods.
+ const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
+ ElfSymtabBuilder<Elf_Word, Elf_Sword, Elf_Addr,
+ Elf_Sym, Elf_Shdr>* symtab = &builder->symtab_builder_;
+ for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+ symtab->AddSymbol(it->method_name_, &builder->text_builder_, it->low_pc_, true,
+ it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+
+ // Include CFI for compiled method, if possible.
+ if (cfi_info.get() != nullptr) {
+ DCHECK(it->compiled_method_ != nullptr);
+
+ // Copy in the FDE, if present
+ const std::vector<uint8_t>* fde = it->compiled_method_->GetCFIInfo();
+ if (fde != nullptr) {
+ // Copy the information into cfi_info and then fix the address in the new copy.
+ int cur_offset = cfi_info->size();
+ cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+ bool is_64bit = *(reinterpret_cast<const uint32_t*>(fde->data())) == 0xffffffff;
+
+ // Set the 'CIE_pointer' field.
+ uint64_t CIE_pointer = cur_offset + (is_64bit ? 12 : 4);
+ uint64_t offset_to_update = CIE_pointer;
+ if (is_64bit) {
+ (*cfi_info)[offset_to_update+0] = CIE_pointer;
+ (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+ (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+ (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+ (*cfi_info)[offset_to_update+4] = CIE_pointer >> 32;
+ (*cfi_info)[offset_to_update+5] = CIE_pointer >> 40;
+ (*cfi_info)[offset_to_update+6] = CIE_pointer >> 48;
+ (*cfi_info)[offset_to_update+7] = CIE_pointer >> 56;
+ } else {
+ (*cfi_info)[offset_to_update+0] = CIE_pointer;
+ (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
+ (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
+ (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
+ }
+
+ // Set the 'initial_location' field.
+ offset_to_update += is_64bit ? 8 : 4;
+ if (is_64bit) {
+ const uint64_t quick_code_start = it->low_pc_ + text_section_address;
+ (*cfi_info)[offset_to_update+0] = quick_code_start;
+ (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+ (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+ (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+ (*cfi_info)[offset_to_update+4] = quick_code_start >> 32;
+ (*cfi_info)[offset_to_update+5] = quick_code_start >> 40;
+ (*cfi_info)[offset_to_update+6] = quick_code_start >> 48;
+ (*cfi_info)[offset_to_update+7] = quick_code_start >> 56;
+ } else {
+ const uint32_t quick_code_start = it->low_pc_ + text_section_address;
+ (*cfi_info)[offset_to_update+0] = quick_code_start;
+ (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+ (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+ (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+ }
+ }
+ }
+ }
+
+ bool hasCFI = (cfi_info.get() != nullptr);
+ bool hasLineInfo = false;
+ for (auto& dbg_info : oat_writer->GetCFIMethodInfo()) {
+ if (dbg_info.dbgstream_ != nullptr &&
+ !dbg_info.compiled_method_->GetSrcMappingTable().empty()) {
+ hasLineInfo = true;
+ break;
+ }
+ }
+
+ if (hasLineInfo || hasCFI) {
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_info(".debug_info",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_abbrev(".debug_abbrev",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_str(".debug_str",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> debug_line(".debug_line",
+ SHT_PROGBITS,
+ 0, nullptr, 0, 1, 0);
+
+ FillInCFIInformation(oat_writer, debug_info.GetBuffer(),
+ debug_abbrev.GetBuffer(), debug_str.GetBuffer(),
+ hasLineInfo ? debug_line.GetBuffer() : nullptr,
+ text_section_address);
+
+ builder->RegisterRawSection(debug_info);
+ builder->RegisterRawSection(debug_abbrev);
+
+ if (hasCFI) {
+ ElfRawSectionBuilder<Elf_Word, Elf_Sword, Elf_Shdr> eh_frame(".eh_frame",
+ SHT_PROGBITS,
+ SHF_ALLOC,
+ nullptr, 0, 4, 0);
+ eh_frame.SetBuffer(std::move(*cfi_info.get()));
+ builder->RegisterRawSection(eh_frame);
+ }
+
+ if (hasLineInfo) {
+ builder->RegisterRawSection(debug_line);
+ }
+
+ builder->RegisterRawSection(debug_str);
+ }
+}
+
+// Explicit instantiations
+template class ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>;
+template class ElfWriterQuick<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn,
+ Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>;
+
} // namespace art
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index 0c22ae2..4990ed0 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -19,10 +19,12 @@
#include "elf_utils.h"
#include "elf_writer.h"
-#include "instruction_set.h"
namespace art {
+template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr,
+ typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr,
+ typename Elf_Phdr, typename Elf_Shdr>
class ElfWriterQuick FINAL : public ElfWriter {
public:
// Write an ELF file. Returns true on success, false on failure.
@@ -47,267 +49,15 @@
: ElfWriter(driver, elf_file) {}
~ElfWriterQuick() {}
- class ElfBuilder;
- void AddDebugSymbols(ElfBuilder& builder,
- OatWriter* oat_writer,
- bool debug);
- void ReservePatchSpace(std::vector<uint8_t>* buffer, bool debug);
-
- class ElfSectionBuilder {
- public:
- ElfSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
- const ElfSectionBuilder *link, Elf32_Word info, Elf32_Word align,
- Elf32_Word entsize)
- : name_(sec_name), link_(link) {
- memset(§ion_, 0, sizeof(section_));
- section_.sh_type = type;
- section_.sh_flags = flags;
- section_.sh_info = info;
- section_.sh_addralign = align;
- section_.sh_entsize = entsize;
- }
-
- virtual ~ElfSectionBuilder() {}
-
- Elf32_Shdr section_;
- Elf32_Word section_index_ = 0;
-
- protected:
- const std::string name_;
- const ElfSectionBuilder* link_;
-
- Elf32_Word GetLink() {
- return (link_) ? link_->section_index_ : 0;
- }
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfDynamicBuilder : public ElfSectionBuilder {
- public:
- void AddDynamicTag(Elf32_Sword tag, Elf32_Word d_un);
- void AddDynamicTag(Elf32_Sword tag, Elf32_Word offset, ElfSectionBuilder* section);
-
- ElfDynamicBuilder(const std::string& sec_name, ElfSectionBuilder *link)
- : ElfSectionBuilder(sec_name, SHT_DYNAMIC, SHF_ALLOC | SHF_ALLOC, link,
- 0, kPageSize, sizeof(Elf32_Dyn)) {}
- ~ElfDynamicBuilder() {}
-
- protected:
- struct ElfDynamicState {
- ElfSectionBuilder* section_;
- Elf32_Sword tag_;
- Elf32_Word off_;
- };
- std::vector<ElfDynamicState> dynamics_;
- Elf32_Word GetSize() {
- // Add 1 for the DT_NULL, 1 for DT_STRSZ, and 1 for DT_SONAME. All of
- // these must be added when we actually put the file together because
- // their values are very dependent on state.
- return dynamics_.size() + 3;
- }
-
- // Create the actual dynamic vector. strsz should be the size of the .dynstr
- // table and soname_off should be the offset of the soname in .dynstr.
- // Since niether can be found prior to final layout we will wait until here
- // to add them.
- std::vector<Elf32_Dyn> GetDynamics(Elf32_Word strsz, Elf32_Word soname_off);
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfRawSectionBuilder : public ElfSectionBuilder {
- public:
- ElfRawSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
- const ElfSectionBuilder* link, Elf32_Word info, Elf32_Word align,
- Elf32_Word entsize)
- : ElfSectionBuilder(sec_name, type, flags, link, info, align, entsize) {}
- ~ElfRawSectionBuilder() {}
- std::vector<uint8_t>* GetBuffer() { return &buf_; }
- void SetBuffer(std::vector<uint8_t> buf) { buf_ = buf; }
-
- protected:
- std::vector<uint8_t> buf_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfOatSectionBuilder : public ElfSectionBuilder {
- public:
- ElfOatSectionBuilder(const std::string& sec_name, Elf32_Word size, Elf32_Word offset,
- Elf32_Word type, Elf32_Word flags)
- : ElfSectionBuilder(sec_name, type, flags, NULL, 0, kPageSize, 0),
- offset_(offset), size_(size) {}
- ~ElfOatSectionBuilder() {}
-
- protected:
- // Offset of the content within the file.
- Elf32_Word offset_;
- // Size of the content within the file.
- Elf32_Word size_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfSymtabBuilder : public ElfSectionBuilder {
- public:
- // Add a symbol with given name to this symtab. The symbol refers to
- // 'relative_addr' within the given section and has the given attributes.
- void AddSymbol(const std::string& name,
- const ElfSectionBuilder* section,
- Elf32_Addr addr,
- bool is_relative,
- Elf32_Word size,
- uint8_t binding,
- uint8_t type,
- uint8_t other = 0);
-
- ElfSymtabBuilder(const std::string& sec_name, Elf32_Word type,
- const std::string& str_name, Elf32_Word str_type, bool alloc)
- : ElfSectionBuilder(sec_name, type, ((alloc) ? SHF_ALLOC : 0U), &strtab_, 0,
- sizeof(Elf32_Word), sizeof(Elf32_Sym)),
- str_name_(str_name), str_type_(str_type),
- strtab_(str_name, str_type, ((alloc) ? SHF_ALLOC : 0U), NULL, 0, 1, 1) {}
- ~ElfSymtabBuilder() {}
-
- protected:
- std::vector<Elf32_Word> GenerateHashContents();
- std::string GenerateStrtab();
- std::vector<Elf32_Sym> GenerateSymtab();
-
- Elf32_Word GetSize() {
- // 1 is for the implicit NULL symbol.
- return symbols_.size() + 1;
- }
-
- struct ElfSymbolState {
- const std::string name_;
- const ElfSectionBuilder* section_;
- Elf32_Addr addr_;
- Elf32_Word size_;
- bool is_relative_;
- uint8_t info_;
- uint8_t other_;
- // Used during Write() to temporarially hold name index in the strtab.
- Elf32_Word name_idx_;
- };
-
- // Information for the strsym for dynstr sections.
- const std::string str_name_;
- Elf32_Word str_type_;
- // The symbols in the same order they will be in the symbol table.
- std::vector<ElfSymbolState> symbols_;
- ElfSectionBuilder strtab_;
-
- private:
- friend class ElfBuilder;
- };
-
- class ElfBuilder FINAL {
- public:
- ElfBuilder(OatWriter* oat_writer,
- File* elf_file,
- InstructionSet isa,
- Elf32_Word rodata_relative_offset,
- Elf32_Word rodata_size,
- Elf32_Word text_relative_offset,
- Elf32_Word text_size,
- const bool add_symbols,
- bool debug = false)
- : oat_writer_(oat_writer),
- elf_file_(elf_file),
- add_symbols_(add_symbols),
- debug_logging_(debug),
- text_builder_(".text", text_size, text_relative_offset, SHT_PROGBITS,
- SHF_ALLOC | SHF_EXECINSTR),
- rodata_builder_(".rodata", rodata_size, rodata_relative_offset,
- SHT_PROGBITS, SHF_ALLOC),
- dynsym_builder_(".dynsym", SHT_DYNSYM, ".dynstr", SHT_STRTAB, true),
- symtab_builder_(".symtab", SHT_SYMTAB, ".strtab", SHT_STRTAB, false),
- hash_builder_(".hash", SHT_HASH, SHF_ALLOC, &dynsym_builder_, 0,
- sizeof(Elf32_Word), sizeof(Elf32_Word)),
- dynamic_builder_(".dynamic", &dynsym_builder_),
- shstrtab_builder_(".shstrtab", SHT_STRTAB, 0, NULL, 0, 1, 1) {
- SetupEhdr();
- SetupDynamic();
- SetupRequiredSymbols();
- SetISA(isa);
- }
- ~ElfBuilder() {}
-
- bool Write();
-
- // Adds the given raw section to the builder. This will copy it. The caller
- // is responsible for deallocating their copy.
- void RegisterRawSection(ElfRawSectionBuilder bld) {
- other_builders_.push_back(bld);
- }
-
- private:
- OatWriter* oat_writer_;
- File* elf_file_;
- const bool add_symbols_;
- const bool debug_logging_;
-
- bool fatal_error_ = false;
-
- Elf32_Ehdr elf_header_;
-
- public:
- ElfOatSectionBuilder text_builder_;
- ElfOatSectionBuilder rodata_builder_;
- ElfSymtabBuilder dynsym_builder_;
- ElfSymtabBuilder symtab_builder_;
- ElfSectionBuilder hash_builder_;
- ElfDynamicBuilder dynamic_builder_;
- ElfSectionBuilder shstrtab_builder_;
- std::vector<ElfRawSectionBuilder> other_builders_;
-
- private:
- void SetISA(InstructionSet isa);
- void SetupEhdr();
-
- // Sets up a bunch of the required Dynamic Section entries.
- // Namely it will initialize all the mandatory ones that it can.
- // Specifically:
- // DT_HASH
- // DT_STRTAB
- // DT_SYMTAB
- // DT_SYMENT
- //
- // Some such as DT_SONAME, DT_STRSZ and DT_NULL will be put in later.
- void SetupDynamic();
-
- // Sets up the basic dynamic symbols that are needed, namely all those we
- // can know already.
- //
- // Specifically adds:
- // oatdata
- // oatexec
- // oatlastword
- void SetupRequiredSymbols();
- void AssignSectionStr(ElfSectionBuilder *builder, std::string* strtab);
-
- bool IncludingDebugSymbols() { return add_symbols_ && symtab_builder_.GetSize() > 1; }
- };
-
- /*
- * @brief Generate the DWARF debug_info and debug_abbrev sections
- * @param oat_writer The Oat file Writer.
- * @param dbg_info Compilation unit information.
- * @param dbg_abbrev Abbreviations used to generate dbg_info.
- * @param dbg_str Debug strings.
- */
- void FillInCFIInformation(OatWriter* oat_writer, std::vector<uint8_t>* dbg_info,
- std::vector<uint8_t>* dbg_abbrev, std::vector<uint8_t>* dbg_str);
-
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
};
+// Explicitly instantiated in elf_writer_quick.cc
+typedef ElfWriterQuick<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> ElfWriterQuick32;
+typedef ElfWriterQuick<Elf64_Word, Elf64_Sword, Elf64_Addr, Elf64_Dyn,
+ Elf64_Sym, Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr> ElfWriterQuick64;
+
} // namespace art
#endif // ART_COMPILER_ELF_WRITER_QUICK_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3d119bb..2a37049 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -21,6 +21,7 @@
#include <vector>
#include "base/unix_file/fd_file.h"
+#include "class_linker.h"
#include "common_compiler_test.h"
#include "elf_fixup.h"
#include "gc/space/image_space.h"
@@ -61,6 +62,8 @@
oat_filename += "oat";
ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
+ const uintptr_t requested_image_base = ART_BASE_ADDRESS;
+ ImageWriter writer(*compiler_driver_, requested_image_base);
{
{
jobject class_loader = NULL;
@@ -78,15 +81,15 @@
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
t.NewTiming("WriteElf");
- ScopedObjectAccess soa(Thread::Current());
SafeMap<std::string, std::string> key_value_store;
- OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), &timings,
- &key_value_store);
- bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
- !kIsTargetBuild,
- class_linker->GetBootClassPath(),
- &oat_writer,
- oat_file.GetFile());
+ OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(),
+ &writer, &timings, &key_value_store);
+ bool success = writer.PrepareImageAddressSpace() &&
+ compiler_driver_->WriteElf(GetTestAndroidRoot(),
+ !kIsTargetBuild,
+ class_linker->GetBootClassPath(),
+ &oat_writer,
+ oat_file.GetFile());
ASSERT_TRUE(success);
}
}
@@ -94,11 +97,9 @@
std::unique_ptr<File> dup_oat(OS::OpenFileReadWrite(oat_file.GetFilename().c_str()));
ASSERT_TRUE(dup_oat.get() != NULL);
- const uintptr_t requested_image_base = ART_BASE_ADDRESS;
{
- ImageWriter writer(*compiler_driver_.get());
- bool success_image = writer.Write(image_file.GetFilename(), requested_image_base,
- dup_oat->GetPath(), dup_oat->GetPath());
+ bool success_image =
+ writer.Write(image_file.GetFilename(), dup_oat->GetPath(), dup_oat->GetPath());
ASSERT_TRUE(success_image);
bool success_fixup = ElfFixup::Fixup(dup_oat.get(), writer.GetOatDataBegin());
ASSERT_TRUE(success_fixup);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index c6fc115..1c8b8d5 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -29,7 +29,6 @@
#include "driver/compiler_driver.h"
#include "elf_file.h"
#include "elf_utils.h"
-#include "elf_patcher.h"
#include "elf_writer.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
@@ -68,15 +67,38 @@
namespace art {
+bool ImageWriter::PrepareImageAddressSpace() {
+ {
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ PruneNonImageClasses(); // Remove junk
+ ComputeLazyFieldsForImageClasses(); // Add useful information
+ ComputeEagerResolvedStrings();
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ heap->CollectGarbage(false); // Remove garbage.
+
+ if (!AllocMemory()) {
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ ScopedObjectAccess soa(Thread::Current());
+ CheckNonImageClassesRemoved();
+ }
+
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ CalculateNewObjectOffsets();
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+ return true;
+}
+
bool ImageWriter::Write(const std::string& image_filename,
- uintptr_t image_begin,
const std::string& oat_filename,
const std::string& oat_location) {
CHECK(!image_filename.empty());
- CHECK_NE(image_begin, 0U);
- image_begin_ = reinterpret_cast<byte*>(image_begin);
-
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
@@ -115,35 +137,18 @@
oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
quick_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetQuickToInterpreterBridgeOffset();
- {
- Thread::Current()->TransitionFromSuspendedToRunnable();
- PruneNonImageClasses(); // Remove junk
- ComputeLazyFieldsForImageClasses(); // Add useful information
- ComputeEagerResolvedStrings();
- Thread::Current()->TransitionFromRunnableToSuspended(kNative);
- }
- gc::Heap* heap = Runtime::Current()->GetHeap();
- heap->CollectGarbage(false); // Remove garbage.
- if (!AllocMemory()) {
- return false;
- }
-
- if (kIsDebugBuild) {
- ScopedObjectAccess soa(Thread::Current());
- CheckNonImageClassesRemoved();
- }
-
- Thread::Current()->TransitionFromSuspendedToRunnable();
size_t oat_loaded_size = 0;
size_t oat_data_offset = 0;
ElfWriter::GetOatElfInformation(oat_file.get(), oat_loaded_size, oat_data_offset);
- CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset);
- CopyAndFixupObjects();
- PatchOatCodeAndMethods(oat_file.get());
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ CreateHeader(oat_loaded_size, oat_data_offset);
+ CopyAndFixupObjects();
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+ SetOatChecksumFromElfFile(oat_file.get());
+
std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str()));
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
if (image_file.get() == NULL) {
@@ -232,7 +237,7 @@
size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize);
std::string error_msg;
image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, PROT_READ | PROT_WRITE,
- true, &error_msg));
+ false, &error_msg));
if (UNLIKELY(image_.get() == nullptr)) {
LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
return false;
@@ -527,8 +532,7 @@
writer->WalkFieldsInOrder(obj);
}
-void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) {
- CHECK_NE(0U, oat_loaded_size);
+void ImageWriter::CalculateNewObjectOffsets() {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
Handle<ObjectArray<Object>> image_roots(hs.NewHandle(CreateImageRoots()));
@@ -543,14 +547,19 @@
{
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
// TODO: Image spaces only?
- const char* old = self->StartAssertNoThreadSuspension("ImageWriter");
DCHECK_LT(image_end_, image_->Size());
// Clear any pre-existing monitors which may have been in the monitor words.
heap->VisitObjects(WalkFieldsCallback, this);
- self->EndAssertNoThreadSuspension(old);
}
- const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize);
+ image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots.Get()));
+
+ // Note that image_end_ is left at end of used space
+}
+
+void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
+ CHECK_NE(0U, oat_loaded_size);
+ const byte* oat_file_begin = GetOatFileBegin();
const byte* oat_file_end = oat_file_begin + oat_loaded_size;
oat_data_begin_ = oat_file_begin + oat_data_offset;
const byte* oat_data_end = oat_data_begin_ + oat_file_->Size();
@@ -560,37 +569,33 @@
const size_t heap_bytes_per_bitmap_byte = kBitsPerByte * kObjectAlignment;
const size_t bitmap_bytes = RoundUp(image_end_, heap_bytes_per_bitmap_byte) /
heap_bytes_per_bitmap_byte;
- ImageHeader image_header(PointerToLowMemUInt32(image_begin_),
- static_cast<uint32_t>(image_end_),
- RoundUp(image_end_, kPageSize),
- RoundUp(bitmap_bytes, kPageSize),
- PointerToLowMemUInt32(GetImageAddress(image_roots.Get())),
- oat_file_->GetOatHeader().GetChecksum(),
- PointerToLowMemUInt32(oat_file_begin),
- PointerToLowMemUInt32(oat_data_begin_),
- PointerToLowMemUInt32(oat_data_end),
- PointerToLowMemUInt32(oat_file_end));
- memcpy(image_->Begin(), &image_header, sizeof(image_header));
-
- // Note that image_end_ is left at end of used space
+ new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_),
+ static_cast<uint32_t>(image_end_),
+ RoundUp(image_end_, kPageSize),
+ RoundUp(bitmap_bytes, kPageSize),
+ image_roots_address_,
+ oat_file_->GetOatHeader().GetChecksum(),
+ PointerToLowMemUInt32(oat_file_begin),
+ PointerToLowMemUInt32(oat_data_begin_),
+ PointerToLowMemUInt32(oat_data_end),
+ PointerToLowMemUInt32(oat_file_end));
}
+
void ImageWriter::CopyAndFixupObjects()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Thread* self = Thread::Current();
- const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
+ ScopedAssertNoThreadSuspension ants(Thread::Current(), "ImageWriter");
gc::Heap* heap = Runtime::Current()->GetHeap();
// TODO: heap validation can't handle this fix up pass
heap->DisableObjectValidation();
// TODO: Image spaces only?
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ WriterMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
heap->VisitObjects(CopyAndFixupObjectsCallback, this);
// Fix up the object previously had hash codes.
for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) {
hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second), false);
}
saved_hashes_.clear();
- self->EndAssertNoThreadSuspension(old_cause);
}
void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
@@ -739,23 +744,17 @@
// The resolution method has a special trampoline to call.
if (UNLIKELY(orig == Runtime::Current()->GetResolutionMethod())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_));
-#endif
copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_));
} else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_));
-#endif
copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_));
} else {
// We assume all methods have code. If they don't currently then we set them to the use the
// resolution trampoline. Abstract methods never have code and so we need to make sure their
// use results in an AbstractMethodError. We use the interpreter to achieve this.
if (UNLIKELY(orig->IsAbstract())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_to_interpreter_bridge_offset_));
-#endif
copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_to_interpreter_bridge_offset_));
copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*>
(const_cast<byte*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_))));
@@ -765,9 +764,8 @@
copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code);
// Portable entrypoint:
- bool portable_is_interpreted = false;
-#if defined(ART_USE_PORTABLE_COMPILER)
const byte* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset());
+ bool portable_is_interpreted = false;
if (portable_code != nullptr &&
(!orig->IsStatic() || orig->IsConstructor() || orig->GetDeclaringClass()->IsInitialized())) {
// We have code for a non-static or initialized method, just use the code.
@@ -787,7 +785,7 @@
portable_code = GetOatAddress(portable_resolution_trampoline_offset_);
}
copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code);
-#endif
+
// JNI entrypoint:
if (orig->IsNative()) {
// The native method's pointer is set to a stub to lookup via dlsym.
@@ -820,19 +818,12 @@
return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
}
-void ImageWriter::PatchOatCodeAndMethods(File* elf_file) {
+void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) {
std::string error_msg;
std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file, PROT_READ|PROT_WRITE,
MAP_SHARED, &error_msg));
if (elf.get() == nullptr) {
- LOG(FATAL) << "Unable patch oat file: " << error_msg;
- return;
- }
- if (!ElfPatcher::Patch(&compiler_driver_, elf.get(), oat_file_,
- reinterpret_cast<uintptr_t>(oat_data_begin_),
- GetImageAddressCallback, reinterpret_cast<void*>(this),
- &error_msg)) {
- LOG(FATAL) << "unable to patch oat file: " << error_msg;
+ LOG(FATAL) << "Unable open oat file: " << error_msg;
return;
}
OatHeader* oat_header = GetOatHeaderFromElf(elf.get());
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e8bcf7f..bdf0614 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -37,17 +37,39 @@
// Write a Space built during compilation for use during execution.
class ImageWriter {
public:
- explicit ImageWriter(const CompilerDriver& compiler_driver)
- : compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
+ ImageWriter(const CompilerDriver& compiler_driver, uintptr_t image_begin)
+ : compiler_driver_(compiler_driver), image_begin_(reinterpret_cast<byte*>(image_begin)),
+ image_end_(0), image_roots_address_(0), oat_file_(NULL),
oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
- interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
- portable_resolution_trampoline_offset_(0), quick_generic_jni_trampoline_offset_(0),
- quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0) {}
+ interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0),
+ portable_imt_conflict_trampoline_offset_(0), portable_resolution_trampoline_offset_(0),
+ portable_to_interpreter_bridge_offset_(0), quick_generic_jni_trampoline_offset_(0),
+ quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0),
+ quick_to_interpreter_bridge_offset_(0) {
+ CHECK_NE(image_begin, 0U);
+ }
~ImageWriter() {}
+ bool PrepareImageAddressSpace();
+
+ bool IsImageAddressSpaceReady() const {
+ return image_roots_address_ != 0u;
+ }
+
+ mirror::Object* GetImageAddress(mirror::Object* object) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (object == NULL) {
+ return NULL;
+ }
+ return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
+ }
+
+ byte* GetOatFileBegin() const {
+ return image_begin_ + RoundUp(image_end_, kPageSize);
+ }
+
bool Write(const std::string& image_filename,
- uintptr_t image_begin,
const std::string& oat_filename,
const std::string& oat_location)
LOCKS_EXCLUDED(Locks::mutator_lock_);
@@ -75,14 +97,6 @@
return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
}
- mirror::Object* GetImageAddress(mirror::Object* object) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (object == NULL) {
- return NULL;
- }
- return reinterpret_cast<mirror::Object*>(image_begin_ + GetImageOffset(object));
- }
-
mirror::Object* GetLocalAddress(mirror::Object* object) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
size_t offset = GetImageOffset(object);
@@ -131,7 +145,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Lays out where the image objects will be at runtime.
- void CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset)
+ void CalculateNewObjectOffsets()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -162,23 +178,25 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Patches references in OatFile to expect runtime addresses.
- void PatchOatCodeAndMethods(File* elf_file)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetOatChecksumFromElfFile(File* elf_file);
const CompilerDriver& compiler_driver_;
+ // Beginning target image address for the output image.
+ byte* image_begin_;
+
+ // Offset to the free space in image_.
+ size_t image_end_;
+
+ // The image roots address in the image.
+ uint32_t image_roots_address_;
+
// oat file with code for this image
OatFile* oat_file_;
// Memory mapped for generating the image.
std::unique_ptr<MemMap> image_;
- // Offset to the free space in image_.
- size_t image_end_;
-
- // Beginning target image address for the output image.
- byte* image_begin_;
-
// Saved hashes (objects are inside of the image so that they don't move).
std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_;
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index ed1175b..a21004c 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -80,10 +80,8 @@
CompileMethod(method);
ASSERT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr)
<< method_name << " " << method_sig;
-#if defined(ART_USE_PORTABLE_COMPILER)
ASSERT_TRUE(method->GetEntryPointFromPortableCompiledCode() != nullptr)
<< method_name << " " << method_sig;
-#endif
}
}
}
@@ -225,13 +223,9 @@
SetUpForTest(false, "bar", "(I)I", nullptr);
// calling through stub will link with &Java_MyClassNatives_bar
- ScopedObjectAccess soa(Thread::Current());
std::string reason;
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
- ASSERT_TRUE(
- Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", class_loader, &reason)) << reason;
+ ASSERT_TRUE(Runtime::Current()->GetJavaVM()->LoadNativeLibrary(env_, "", class_loader_, &reason))
+ << reason;
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
EXPECT_EQ(25, result);
@@ -244,13 +238,9 @@
SetUpForTest(true, "sbar", "(I)I", nullptr);
// calling through stub will link with &Java_MyClassNatives_sbar
- ScopedObjectAccess soa(Thread::Current());
std::string reason;
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader_)));
- ASSERT_TRUE(
- Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", class_loader, &reason)) << reason;
+ ASSERT_TRUE(Runtime::Current()->GetJavaVM()->LoadNativeLibrary(env_, "", class_loader_, &reason))
+ << reason;
jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
EXPECT_EQ(43, result);
@@ -978,10 +968,10 @@
check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
// Here, we just call the method incorrectly; we should catch that too.
- env_->CallVoidMethod(jobj_, jmethod_);
+ env_->CallObjectMethod(jobj_, jmethod_);
check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass()");
- env_->CallStaticVoidMethod(jklass_, jmethod_);
- check_jni_abort_catcher.Check("calling non-static method java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass() with CallStaticVoidMethodV");
+ env_->CallStaticObjectMethod(jklass_, jmethod_);
+ check_jni_abort_catcher.Check("calling non-static method java.lang.Class MyClassNatives.instanceMethodThatShouldReturnClass() with CallStaticObjectMethodV");
}
JNI_TEST(UpcallReturnTypeChecking_Instance)
@@ -998,10 +988,10 @@
check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
// Here, we just call the method incorrectly; we should catch that too.
- env_->CallStaticVoidMethod(jklass_, jmethod_);
+ env_->CallStaticObjectMethod(jklass_, jmethod_);
check_jni_abort_catcher.Check("attempt to return an instance of java.lang.String from java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass()");
- env_->CallVoidMethod(jobj_, jmethod_);
- check_jni_abort_catcher.Check("calling static method java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass() with CallVoidMethodV");
+ env_->CallObjectMethod(jobj_, jmethod_);
+ check_jni_abort_catcher.Check("calling static method java.lang.Class MyClassNatives.staticMethodThatShouldReturnClass() with CallObjectMethodV");
}
JNI_TEST(UpcallReturnTypeChecking_Static)
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index c38cfaf..78a228b 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "jni_compiler.h"
+
#include <algorithm>
#include <memory>
#include <vector>
@@ -27,7 +29,7 @@
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints.h"
-#include "jni_internal.h"
+#include "jni_env_ext.h"
#include "mirror/art_method.h"
#include "utils/assembler.h"
#include "utils/managed_register.h"
@@ -90,6 +92,7 @@
// Assembler that holds generated instructions
std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set));
+ jni_asm->InitializeFrameDescriptionEntry();
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
@@ -432,12 +435,14 @@
std::vector<uint8_t> managed_code(cs);
MemoryRegion code(&managed_code[0], managed_code.size());
__ FinalizeInstructions(code);
+ jni_asm->FinalizeFrameDescriptionEntry();
return new CompiledMethod(driver,
instruction_set,
managed_code,
frame_size,
main_jni_conv->CoreSpillMask(),
- main_jni_conv->FpSpillMask());
+ main_jni_conv->FpSpillMask(),
+ jni_asm->GetFrameDescriptionEntry());
}
// Copy a single parameter from the managed to the JNI calling convention
@@ -543,10 +548,9 @@
}
}
-} // namespace art
-
-extern "C" art::CompiledMethod* ArtQuickJniCompileMethod(art::CompilerDriver* compiler,
- uint32_t access_flags, uint32_t method_idx,
- const art::DexFile& dex_file) {
+CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags,
+ uint32_t method_idx, const DexFile& dex_file) {
return ArtJniCompileMethodInternal(compiler, access_flags, method_idx, dex_file);
}
+
+} // namespace art
diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h
new file mode 100644
index 0000000..46277f1
--- /dev/null
+++ b/compiler/jni/quick/jni_compiler.h
@@ -0,0 +1,32 @@
+/*
+ * 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_COMPILER_JNI_QUICK_JNI_COMPILER_H_
+#define ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_
+
+#include "dex_file.h"
+
+namespace art {
+
+class CompilerDriver;
+class CompiledMethod;
+
+CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, uint32_t access_flags,
+ uint32_t method_idx, const DexFile& dex_file);
+
+} // namespace art
+
+#endif // ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
index 5990e8c..3aeecad 100644
--- a/compiler/llvm/compiler_llvm.cc
+++ b/compiler/llvm/compiler_llvm.cc
@@ -141,7 +141,7 @@
cunit->SetDexCompilationUnit(dex_compilation_unit);
cunit->SetCompilerDriver(compiler_driver_);
// TODO: consolidate ArtCompileMethods
- CompileOneMethod(*compiler_driver_,
+ CompileOneMethod(compiler_driver_,
compiler_driver_->GetCompiler(),
dex_compilation_unit->GetCodeItem(),
dex_compilation_unit->GetAccessFlags(),
@@ -172,68 +172,62 @@
}
-} // namespace llvm
-} // namespace art
-
-static art::llvm::CompilerLLVM* ContextOf(art::CompilerDriver* driver) {
+static CompilerLLVM* ContextOf(art::CompilerDriver* driver) {
void *compiler_context = driver->GetCompilerContext();
CHECK(compiler_context != NULL);
- return reinterpret_cast<art::llvm::CompilerLLVM*>(compiler_context);
+ return reinterpret_cast<CompilerLLVM*>(compiler_context);
}
-static art::llvm::CompilerLLVM* ContextOf(const art::CompilerDriver& driver) {
+static CompilerLLVM* ContextOf(const art::CompilerDriver& driver) {
void *compiler_context = driver.GetCompilerContext();
CHECK(compiler_context != NULL);
- return reinterpret_cast<art::llvm::CompilerLLVM*>(compiler_context);
+ return reinterpret_cast<CompilerLLVM*>(compiler_context);
}
-extern "C" void ArtInitCompilerContext(art::CompilerDriver* driver) {
+void ArtInitCompilerContext(CompilerDriver* driver) {
CHECK(driver->GetCompilerContext() == nullptr);
- art::llvm::CompilerLLVM* compiler_llvm = new art::llvm::CompilerLLVM(driver,
- driver->GetInstructionSet());
+ CompilerLLVM* compiler_llvm = new CompilerLLVM(driver, driver->GetInstructionSet());
driver->SetCompilerContext(compiler_llvm);
}
-extern "C" void ArtUnInitCompilerContext(art::CompilerDriver* driver) {
+void ArtUnInitCompilerContext(CompilerDriver* driver) {
delete ContextOf(driver);
driver->SetCompilerContext(nullptr);
}
-extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver* driver,
- const art::DexFile::CodeItem* code_item,
- uint32_t access_flags,
- art::InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- jobject class_loader,
- const art::DexFile& dex_file) {
+
+CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
+ uint32_t access_flags, InvokeType invoke_type,
+ uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
+ const DexFile& dex_file) {
UNUSED(class_def_idx); // TODO: this is used with Compiler::RequiresConstructorBarrier.
- art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
+ ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
- art::DexCompilationUnit dex_compilation_unit(
- NULL, class_loader, class_linker, dex_file, code_item,
- class_def_idx, method_idx, access_flags, driver->GetVerifiedMethod(&dex_file, method_idx));
- art::llvm::CompilerLLVM* compiler_llvm = ContextOf(driver);
- art::CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type);
+ DexCompilationUnit dex_compilation_unit(nullptr, class_loader, class_linker, dex_file, code_item,
+ class_def_idx, method_idx, access_flags,
+ driver->GetVerifiedMethod(&dex_file, method_idx));
+ CompilerLLVM* compiler_llvm = ContextOf(driver);
+ CompiledMethod* result = compiler_llvm->CompileDexMethod(&dex_compilation_unit, invoke_type);
return result;
}
-extern "C" art::CompiledMethod* ArtLLVMJniCompileMethod(art::CompilerDriver* driver,
- uint32_t access_flags, uint32_t method_idx,
- const art::DexFile& dex_file) {
- art::ClassLinker *class_linker = art::Runtime::Current()->GetClassLinker();
+CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
+ uint32_t method_idx, const DexFile& dex_file) {
+ ClassLinker *class_linker = Runtime::Current()->GetClassLinker();
- art::DexCompilationUnit dex_compilation_unit(
- nullptr, nullptr, class_linker, dex_file, nullptr,
- 0, method_idx, access_flags, nullptr);
+ DexCompilationUnit dex_compilation_unit(nullptr, nullptr, class_linker, dex_file, nullptr,
+ 0, method_idx, access_flags, nullptr);
- art::llvm::CompilerLLVM* compiler_llvm = ContextOf(driver);
- art::CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit);
+ CompilerLLVM* compiler_llvm = ContextOf(driver);
+ CompiledMethod* result = compiler_llvm->CompileNativeMethod(&dex_compilation_unit);
return result;
}
-extern "C" void compilerLLVMSetBitcodeFileName(const art::CompilerDriver& driver,
- const std::string& filename) {
+void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
ContextOf(driver)->SetBitcodeFileName(filename);
}
+
+} // namespace llvm
+} // namespace art
+
diff --git a/compiler/llvm/compiler_llvm.h b/compiler/llvm/compiler_llvm.h
index cc74deb..7d29198 100644
--- a/compiler/llvm/compiler_llvm.h
+++ b/compiler/llvm/compiler_llvm.h
@@ -95,6 +95,19 @@
DISALLOW_COPY_AND_ASSIGN(CompilerLLVM);
};
+void ArtInitCompilerContext(CompilerDriver* driver);
+
+void ArtUnInitCompilerContext(CompilerDriver* driver);
+
+CompiledMethod* ArtCompileMethod(CompilerDriver* driver, const DexFile::CodeItem* code_item,
+ uint32_t access_flags, InvokeType invoke_type,
+ uint16_t class_def_idx, uint32_t method_idx, jobject class_loader,
+ const DexFile& dex_file);
+
+CompiledMethod* ArtLLVMJniCompileMethod(CompilerDriver* driver, uint32_t access_flags,
+ uint32_t method_idx, const DexFile& dex_file);
+
+void compilerLLVMSetBitcodeFileName(const CompilerDriver& driver, const std::string& filename);
} // namespace llvm
} // namespace art
diff --git a/compiler/llvm/llvm_compiler.cc b/compiler/llvm/llvm_compiler.cc
new file mode 100644
index 0000000..55af614
--- /dev/null
+++ b/compiler/llvm/llvm_compiler.cc
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include "llvm_compiler.h"
+
+#ifdef ART_USE_PORTABLE_COMPILER
+#include "compiler.h"
+#include "compiler_llvm.h"
+#include "dex/portable/mir_to_gbc.h"
+#include "dex_file.h"
+#include "elf_writer_mclinker.h"
+#include "mirror/art_method-inl.h"
+#endif
+
+namespace art {
+
+#ifdef ART_USE_PORTABLE_COMPILER
+
+namespace llvm {
+
+// Thread-local storage compiler worker threads
+class LLVMCompilerTls : public CompilerTls {
+ public:
+ LLVMCompilerTls() : llvm_info_(nullptr) {}
+ ~LLVMCompilerTls() {}
+
+ void* GetLLVMInfo() { return llvm_info_; }
+
+ void SetLLVMInfo(void* llvm_info) { llvm_info_ = llvm_info; }
+
+ private:
+ void* llvm_info_;
+};
+
+
+
+class LLVMCompiler FINAL : public Compiler {
+ public:
+ explicit LLVMCompiler(CompilerDriver* driver) : Compiler(driver, 1000) {}
+
+ CompilerTls* CreateNewCompilerTls() {
+ return new LLVMCompilerTls();
+ }
+
+ void Init() const OVERRIDE {
+ ArtInitCompilerContext(GetCompilerDriver());
+ }
+
+ void UnInit() const OVERRIDE {
+ ArtUnInitCompilerContext(GetCompilerDriver());
+ }
+
+ bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+ OVERRIDE {
+ return true;
+ }
+
+ CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const OVERRIDE {
+ CompiledMethod* method = TryCompileWithSeaIR(code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file);
+ if (method != nullptr) {
+ return method;
+ }
+
+ return ArtCompileMethod(GetCompilerDriver(),
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file);
+ }
+
+ CompiledMethod* JniCompile(uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file) const OVERRIDE {
+ return ArtLLVMJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
+ }
+
+ uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const {
+ return reinterpret_cast<uintptr_t>(method->GetEntryPointFromPortableCompiledCode());
+ }
+
+ bool WriteElf(art::File* file,
+ OatWriter* oat_writer,
+ const std::vector<const art::DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host, const CompilerDriver& driver) const
+ OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return art::ElfWriterMclinker::Create(
+ file, oat_writer, dex_files, android_root, is_host, driver);
+ }
+
+ Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+ return PortableCodeGenerator(
+ cu, cu->mir_graph.get(), &cu->arena,
+ reinterpret_cast<art::llvm::LlvmCompilationUnit*>(compilation_unit));
+ }
+
+ void InitCompilationUnit(CompilationUnit& cu) const {
+ // Fused long branches not currently useful in bitcode.
+ cu.disable_opt |=
+ (1 << kBranchFusing) |
+ (1 << kSuppressExceptionEdges);
+ }
+
+ bool IsPortable() const OVERRIDE {
+ return true;
+ }
+
+ void SetBitcodeFileName(const CompilerDriver& driver, const std::string& filename) {
+ typedef void (*SetBitcodeFileNameFn)(const CompilerDriver&, const std::string&);
+
+ SetBitcodeFileNameFn set_bitcode_file_name =
+ reinterpret_cast<SetBitcodeFileNameFn>(compilerLLVMSetBitcodeFileName);
+
+ set_bitcode_file_name(driver, filename);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LLVMCompiler);
+};
+
+} // namespace llvm
+#endif
+
+Compiler* CreateLLVMCompiler(CompilerDriver* driver) {
+#ifdef ART_USE_PORTABLE_COMPILER
+ return new llvm::LLVMCompiler(driver);
+#else
+ return nullptr;
+#endif
+}
+
+} // namespace art
diff --git a/compiler/llvm/llvm_compiler.h b/compiler/llvm/llvm_compiler.h
new file mode 100644
index 0000000..da6d0e9
--- /dev/null
+++ b/compiler/llvm/llvm_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 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_LLVM_LLVM_COMPILER_H_
+#define ART_COMPILER_LLVM_LLVM_COMPILER_H_
+
+namespace art {
+
+class Compiler;
+class CompilerDriver;
+
+Compiler* CreateLLVMCompiler(CompilerDriver* driver);
+
+}
+
+#endif // ART_COMPILER_LLVM_LLVM_COMPILER_H_
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 11d1728..0b1f9e2 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "class_linker.h"
#include "common_compiler_test.h"
#include "compiler.h"
#include "dex/verification_results.h"
@@ -113,7 +114,6 @@
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
- ScopedObjectAccess soa(Thread::Current());
ScratchFile tmp;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
@@ -122,6 +122,7 @@
4096U,
0,
compiler_driver_.get(),
+ nullptr,
&timings,
&key_value_store);
bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
@@ -151,6 +152,7 @@
&dex_file_checksum);
ASSERT_TRUE(oat_dex_file != nullptr);
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
+ ScopedObjectAccess soa(Thread::Current());
for (size_t i = 0; i < dex_file->NumClassDefs(); i++) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const byte* class_data = dex_file->GetClassData(class_def);
@@ -187,7 +189,7 @@
EXPECT_EQ(84U, sizeof(OatHeader));
EXPECT_EQ(8U, sizeof(OatMethodOffsets));
EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+ EXPECT_EQ(91 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
}
TEST_F(OatTest, OatHeaderIsValid) {
@@ -196,13 +198,13 @@
std::vector<const DexFile*> dex_files;
uint32_t image_file_location_oat_checksum = 0;
uint32_t image_file_location_oat_begin = 0;
- OatHeader* oat_header = OatHeader::Create(instruction_set,
- instruction_set_features,
- &dex_files,
- image_file_location_oat_checksum,
- image_file_location_oat_begin,
- nullptr);
- ASSERT_NE(oat_header, nullptr);
+ std::unique_ptr<OatHeader> oat_header(OatHeader::Create(instruction_set,
+ instruction_set_features,
+ &dex_files,
+ image_file_location_oat_checksum,
+ image_file_location_oat_begin,
+ nullptr));
+ ASSERT_NE(oat_header.get(), nullptr);
ASSERT_TRUE(oat_header->IsValid());
char* magic = const_cast<char*>(oat_header->GetMagic());
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 5bf19ed..dd64368 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,6 +18,7 @@
#include <zlib.h>
+#include "base/allocator.h"
#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
@@ -26,6 +27,7 @@
#include "dex_file-inl.h"
#include "dex/verification_results.h"
#include "gc/space/space.h"
+#include "image_writer.h"
#include "mirror/art_method-inl.h"
#include "mirror/array.h"
#include "mirror/class_loader.h"
@@ -35,10 +37,270 @@
#include "safe_map.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
+#include "utils/arm/assembler_thumb2.h"
#include "verifier/method_verifier.h"
namespace art {
+class OatWriter::RelativeCallPatcher {
+ public:
+ virtual ~RelativeCallPatcher() { }
+
+ // Reserve space for relative call thunks if needed, return adjusted offset.
+ // After all methods have been processed it's call one last time with compiled_method == nullptr.
+ virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) = 0;
+
+ // Write relative call thunks if needed, return adjusted offset.
+ virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0;
+
+ // Patch method code. The input displacement is relative to the patched location,
+ // the patcher may need to adjust it if the correct base is different.
+ virtual void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+ uint32_t target_offset) = 0;
+
+ protected:
+ RelativeCallPatcher() { }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RelativeCallPatcher);
+};
+
+class OatWriter::NoRelativeCallPatcher FINAL : public RelativeCallPatcher {
+ public:
+ NoRelativeCallPatcher() { }
+
+ uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+ return offset; // No space reserved; no patches expected.
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ return offset; // No thunks added; no patches expected.
+ }
+
+ void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ LOG(FATAL) << "Unexpected relative patch.";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoRelativeCallPatcher);
+};
+
+class OatWriter::X86RelativeCallPatcher FINAL : public RelativeCallPatcher {
+ public:
+ X86RelativeCallPatcher() { }
+
+ uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+ return offset; // No space reserved; no limit on relative call distance.
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ return offset; // No thunks added; no limit on relative call distance.
+ }
+
+ void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ DCHECK_LE(literal_offset + 4u, code->size());
+ // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+ uint32_t displacement = target_offset - patch_offset;
+ displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch.
+
+ typedef __attribute__((__aligned__(1))) int32_t unaligned_int32_t;
+ reinterpret_cast<unaligned_int32_t*>(&(*code)[literal_offset])[0] = displacement;
+ }
+
+ private:
+ // PC displacement from patch location; x86 PC for relative calls points to the next
+ // instruction and the patch location is 4 bytes earlier.
+ static constexpr int32_t kPcDisplacement = 4;
+
+ DISALLOW_COPY_AND_ASSIGN(X86RelativeCallPatcher);
+};
+
+class OatWriter::Thumb2RelativeCallPatcher FINAL : public RelativeCallPatcher {
+ public:
+ explicit Thumb2RelativeCallPatcher(OatWriter* writer)
+ : writer_(writer), thunk_code_(CompileThunkCode()),
+ thunk_locations_(), current_thunk_to_write_(0u), unprocessed_patches_() {
+ }
+
+ uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method) OVERRIDE {
+ // NOTE: The final thunk can be reserved from InitCodeMethodVisitor::EndClass() while it
+ // may be written early by WriteCodeMethodVisitor::VisitMethod() for a deduplicated chunk
+ // of code. To avoid any alignment discrepancies for the final chunk, we always align the
+ // offset after reserving of writing any chunk.
+ if (UNLIKELY(compiled_method == nullptr)) {
+ uint32_t aligned_offset = CompiledMethod::AlignCode(offset, kThumb2);
+ bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset);
+ if (needs_thunk) {
+ thunk_locations_.push_back(aligned_offset);
+ offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), kThumb2);
+ }
+ return offset;
+ }
+ DCHECK(compiled_method->GetQuickCode() != nullptr);
+ uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
+ uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
+ uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
+ if (!unprocessed_patches_.empty() &&
+ next_aligned_offset - unprocessed_patches_.front().second > kMaxPositiveDisplacement) {
+ bool needs_thunk = ReserveSpaceProcessPatches(next_aligned_offset);
+ if (needs_thunk) {
+ // A single thunk will cover all pending patches.
+ unprocessed_patches_.clear();
+ uint32_t thunk_location = compiled_method->AlignCode(offset);
+ thunk_locations_.push_back(thunk_location);
+ offset = CompiledMethod::AlignCode(thunk_location + thunk_code_.size(), kThumb2);
+ }
+ }
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() == kLinkerPatchCallRelative) {
+ unprocessed_patches_.emplace_back(patch.TargetMethod(),
+ quick_code_offset + patch.LiteralOffset());
+ }
+ }
+ return offset;
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ if (current_thunk_to_write_ == thunk_locations_.size()) {
+ return offset;
+ }
+ uint32_t aligned_offset = CompiledMethod::AlignCode(offset, kThumb2);
+ if (UNLIKELY(aligned_offset == thunk_locations_[current_thunk_to_write_])) {
+ ++current_thunk_to_write_;
+ uint32_t aligned_code_delta = aligned_offset - offset;
+ if (aligned_code_delta != 0u && !writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+ return 0u;
+ }
+ if (!out->WriteFully(thunk_code_.data(), thunk_code_.size())) {
+ return 0u;
+ }
+ writer_->size_relative_call_thunks_ += thunk_code_.size();
+ uint32_t thunk_end_offset = aligned_offset + thunk_code_.size();
+ // Align after writing chunk, see the ReserveSpace() above.
+ offset = CompiledMethod::AlignCode(thunk_end_offset, kThumb2);
+ aligned_code_delta = offset - thunk_end_offset;
+ if (aligned_code_delta != 0u && !writer_->WriteCodeAlignment(out, aligned_code_delta)) {
+ return 0u;
+ }
+ }
+ return offset;
+ }
+
+ void Patch(std::vector<uint8_t>* code, uint32_t literal_offset, uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ DCHECK_LE(literal_offset + 4u, code->size());
+ DCHECK_EQ(literal_offset & 1u, 0u);
+ DCHECK_EQ(patch_offset & 1u, 0u);
+ DCHECK_EQ(target_offset & 1u, 1u); // Thumb2 mode bit.
+ // Unsigned arithmetic with its well-defined overflow behavior is just fine here.
+ uint32_t displacement = target_offset - 1u - patch_offset;
+ // NOTE: With unsigned arithmetic we do mean to use && rather than || below.
+ if (displacement > kMaxPositiveDisplacement && displacement < -kMaxNegativeDisplacement) {
+ // Unwritten thunks have higher offsets, check if it's within range.
+ DCHECK(current_thunk_to_write_ == thunk_locations_.size() ||
+ thunk_locations_[current_thunk_to_write_] > patch_offset);
+ if (current_thunk_to_write_ != thunk_locations_.size() &&
+ thunk_locations_[current_thunk_to_write_] - patch_offset < kMaxPositiveDisplacement) {
+ displacement = thunk_locations_[current_thunk_to_write_] - patch_offset;
+ } else {
+ // We must have a previous thunk then.
+ DCHECK_NE(current_thunk_to_write_, 0u);
+ DCHECK_LT(thunk_locations_[current_thunk_to_write_ - 1], patch_offset);
+ displacement = thunk_locations_[current_thunk_to_write_ - 1] - patch_offset;
+ DCHECK(displacement >= -kMaxNegativeDisplacement);
+ }
+ }
+ displacement -= kPcDisplacement; // The base PC is at the end of the 4-byte patch.
+ DCHECK_EQ(displacement & 1u, 0u);
+ DCHECK((displacement >> 24) == 0u || (displacement >> 24) == 255u); // 25-bit signed.
+ uint32_t signbit = (displacement >> 31) & 0x1;
+ uint32_t i1 = (displacement >> 23) & 0x1;
+ uint32_t i2 = (displacement >> 22) & 0x1;
+ uint32_t imm10 = (displacement >> 12) & 0x03ff;
+ uint32_t imm11 = (displacement >> 1) & 0x07ff;
+ uint32_t j1 = i1 ^ (signbit ^ 1);
+ uint32_t j2 = i2 ^ (signbit ^ 1);
+ uint32_t value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | imm11;
+ value |= 0xf000d000; // BL
+
+ uint8_t* addr = &(*code)[literal_offset];
+ // Check that we're just overwriting an existing BL.
+ DCHECK_EQ(addr[1] & 0xf8, 0xf0);
+ DCHECK_EQ(addr[3] & 0xd0, 0xd0);
+ // Write the new BL.
+ addr[0] = (value >> 16) & 0xff;
+ addr[1] = (value >> 24) & 0xff;
+ addr[2] = (value >> 0) & 0xff;
+ addr[3] = (value >> 8) & 0xff;
+ }
+
+ private:
+ bool ReserveSpaceProcessPatches(uint32_t next_aligned_offset) {
+ // Process as many patches as possible, stop only on unresolved targets or calls too far back.
+ while (!unprocessed_patches_.empty()) {
+ uint32_t patch_offset = unprocessed_patches_.front().second;
+ auto it = writer_->method_offset_map_.find(unprocessed_patches_.front().first);
+ if (it == writer_->method_offset_map_.end()) {
+ // If still unresolved, check if we have a thunk within range.
+ DCHECK(thunk_locations_.empty() || thunk_locations_.back() <= patch_offset);
+ if (thunk_locations_.empty() ||
+ patch_offset - thunk_locations_.back() > kMaxNegativeDisplacement) {
+ return next_aligned_offset - patch_offset > kMaxPositiveDisplacement;
+ }
+ } else if (it->second >= patch_offset) {
+ DCHECK_LE(it->second - patch_offset, kMaxPositiveDisplacement);
+ } else {
+ // When calling back, check if we have a thunk that's closer than the actual target.
+ uint32_t target_offset = (thunk_locations_.empty() || it->second > thunk_locations_.back())
+ ? it->second
+ : thunk_locations_.back();
+ DCHECK_GT(patch_offset, target_offset);
+ if (patch_offset - target_offset > kMaxNegativeDisplacement) {
+ return true;
+ }
+ }
+ unprocessed_patches_.pop_front();
+ }
+ return false;
+ }
+
+ static std::vector<uint8_t> CompileThunkCode() {
+ // The thunk just uses the entry point in the ArtMethod. This works even for calls
+ // to the generic JNI and interpreter trampolines.
+ arm::Thumb2Assembler assembler;
+ assembler.LoadFromOffset(
+ arm::kLoadWord, arm::PC, arm::R0,
+ mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
+ assembler.bkpt(0);
+ std::vector<uint8_t> thunk_code(assembler.CodeSize());
+ MemoryRegion code(thunk_code.data(), thunk_code.size());
+ assembler.FinalizeInstructions(code);
+ return thunk_code;
+ }
+
+ // PC displacement from patch location; Thumb2 PC is always at instruction address + 4.
+ static constexpr int32_t kPcDisplacement = 4;
+
+ // Maximum positive and negative displacement measured from the patch location.
+ // (Signed 25 bit displacement with the last bit 0 has range [-2^24, 2^24-2] measured from
+ // the Thumb2 PC pointing right after the BL, i.e. 4 bytes later than the patch location.)
+ static constexpr uint32_t kMaxPositiveDisplacement = (1u << 24) - 2 + kPcDisplacement;
+ static constexpr uint32_t kMaxNegativeDisplacement = (1u << 24) - kPcDisplacement;
+
+ OatWriter* const writer_;
+ const std::vector<uint8_t> thunk_code_;
+ std::vector<uint32_t> thunk_locations_;
+ size_t current_thunk_to_write_;
+
+ // ReserveSpace() tracks unprocessed patches.
+ typedef std::pair<MethodReference, uint32_t> UnprocessedPatch;
+ std::deque<UnprocessedPatch> unprocessed_patches_;
+
+ DISALLOW_COPY_AND_ASSIGN(Thumb2RelativeCallPatcher);
+};
+
#define DCHECK_OFFSET() \
DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
<< "file_offset=" << file_offset << " relative_offset=" << relative_offset
@@ -52,10 +314,14 @@
uintptr_t image_file_location_oat_begin,
int32_t image_patch_delta,
const CompilerDriver* compiler,
+ ImageWriter* image_writer,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store)
: compiler_driver_(compiler),
+ image_writer_(image_writer),
dex_files_(&dex_files),
+ size_(0u),
+ oat_data_offset_(0u),
image_file_location_oat_checksum_(image_file_location_oat_checksum),
image_file_location_oat_begin_(image_file_location_oat_begin),
image_patch_delta_(image_patch_delta),
@@ -80,6 +346,7 @@
size_method_header_(0),
size_code_(0),
size_code_alignment_(0),
+ size_relative_call_thunks_(0),
size_mapping_table_(0),
size_vmap_table_(0),
size_gc_map_(0),
@@ -91,9 +358,27 @@
size_oat_class_type_(0),
size_oat_class_status_(0),
size_oat_class_method_bitmaps_(0),
- size_oat_class_method_offsets_(0) {
+ size_oat_class_method_offsets_(0),
+ method_offset_map_() {
CHECK(key_value_store != nullptr);
+ switch (compiler_driver_->GetInstructionSet()) {
+ case kX86:
+ case kX86_64:
+ relative_call_patcher_.reset(new X86RelativeCallPatcher);
+ break;
+ case kArm:
+ // Fall through: we generate Thumb2 code for "arm".
+ case kThumb2:
+ relative_call_patcher_.reset(new Thumb2RelativeCallPatcher(this));
+ break;
+ case kArm64:
+ // TODO: Implement relative calls for arm64.
+ default:
+ relative_call_patcher_.reset(new NoRelativeCallPatcher);
+ break;
+ }
+
size_t offset;
{
TimingLogger::ScopedTiming split("InitOatHeader", timings);
@@ -126,6 +411,7 @@
size_ = offset;
CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
+ CHECK_EQ(compiler->IsImage(), image_writer_ != nullptr);
CHECK_EQ(compiler->IsImage(),
key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end());
CHECK_ALIGNED(image_patch_delta_, kPageSize);
@@ -139,7 +425,7 @@
struct OatWriter::GcMapDataAccess {
static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
- return &compiled_method->GetGcMap();
+ return compiled_method->GetGcMap();
}
static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
@@ -315,6 +601,7 @@
OatClass* oat_class = new OatClass(offset_, compiled_methods_,
num_non_null_compiled_methods_, status);
writer_->oat_classes_.push_back(oat_class);
+ oat_class->UpdateChecksum(writer_->oat_header_);
offset_ += oat_class->SizeOf();
return DexMethodVisitor::EndClass();
}
@@ -328,6 +615,16 @@
public:
InitCodeMethodVisitor(OatWriter* writer, size_t offset)
: OatDexMethodVisitor(writer, offset) {
+ writer_->absolute_patch_locations_.reserve(
+ writer_->compiler_driver_->GetNonRelativeLinkerPatchCount());
+ }
+
+ bool EndClass() {
+ OatDexMethodVisitor::EndClass();
+ if (oat_class_index_ == writer_->oat_classes_.size()) {
+ offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, nullptr);
+ }
+ return true;
}
bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
@@ -349,6 +646,7 @@
oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
} else {
CHECK(quick_code != nullptr);
+ offset_ = writer_->relative_call_patcher_->ReserveSpace(offset_, compiled_method);
offset_ = compiled_method->AlignCode(offset_);
DCHECK_ALIGNED_PARAM(offset_,
GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
@@ -357,7 +655,6 @@
uint32_t thumb_offset = compiled_method->CodeDelta();
quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
- bool force_debug_capture = false;
bool deduped = false;
// Deduplicate code arrays.
@@ -369,6 +666,18 @@
dedupe_map_.PutBefore(lb, compiled_method, quick_code_offset);
}
+ MethodReference method_ref(dex_file_, it.GetMemberIndex());
+ auto method_lb = writer_->method_offset_map_.lower_bound(method_ref);
+ if (method_lb != writer_->method_offset_map_.end() &&
+ !writer_->method_offset_map_.key_comp()(method_ref, method_lb->first)) {
+ // TODO: Should this be a hard failure?
+ LOG(WARNING) << "Multiple definitions of "
+ << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
+ << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : "");
+ } else {
+ writer_->method_offset_map_.PutBefore(method_lb, method_ref, quick_code_offset);
+ }
+
// Update quick method header.
DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
@@ -392,55 +701,39 @@
frame_size_in_bytes, core_spill_mask, fp_spill_mask,
code_size);
- // Update checksum if this wasn't a duplicate.
if (!deduped) {
- writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header));
+ // Update offsets. (Checksum is updated when writing.)
offset_ += sizeof(*method_header); // Method header is prepended before code.
- writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
offset_ += code_size;
- }
-
- uint32_t quick_code_start = quick_code_offset - writer_->oat_header_->GetExecutableOffset();
- std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
- if (cfi_info != nullptr) {
- // Copy in the FDE, if present
- const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
- if (fde != nullptr) {
- // Copy the information into cfi_info and then fix the address in the new copy.
- int cur_offset = cfi_info->size();
- cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
- // Set the 'CIE_pointer' field to cur_offset+4.
- uint32_t CIE_pointer = cur_offset + 4;
- uint32_t offset_to_update = cur_offset + sizeof(uint32_t);
- (*cfi_info)[offset_to_update+0] = CIE_pointer;
- (*cfi_info)[offset_to_update+1] = CIE_pointer >> 8;
- (*cfi_info)[offset_to_update+2] = CIE_pointer >> 16;
- (*cfi_info)[offset_to_update+3] = CIE_pointer >> 24;
-
- // Set the 'initial_location' field to address the start of the method.
- offset_to_update = cur_offset + 2*sizeof(uint32_t);
- (*cfi_info)[offset_to_update+0] = quick_code_start;
- (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
- (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
- (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
- force_debug_capture = true;
+ // Record absolute patch locations.
+ if (!compiled_method->GetPatches().empty()) {
+ uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset();
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() != kLinkerPatchCallRelative) {
+ writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset());
+ }
+ }
}
}
+ if (writer_->compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) {
+ // Record debug information for this function if we are doing that.
- if (writer_->compiler_driver_->DidIncludeDebugSymbols() || force_debug_capture) {
- // Record debug information for this function if we are doing that or
- // we have CFI and so need it.
std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
if (deduped) {
- // TODO We should place the DEDUPED tag on the first instance of a
- // deduplicated symbol so that it will show up in a debuggerd crash
- // report.
+ // TODO We should place the DEDUPED tag on the first instance of a deduplicated symbol
+ // so that it will show up in a debuggerd crash report.
name += " [ DEDUPED ]";
}
- writer_->method_info_.push_back(DebugInfo(name, quick_code_start,
- quick_code_start + code_size));
+
+ const uint32_t quick_code_start = quick_code_offset -
+ writer_->oat_header_->GetExecutableOffset();
+ const DexFile::CodeItem *code_item = it.GetMethodCodeItem();
+ writer_->method_info_.push_back(DebugInfo(name,
+ dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)),
+ quick_code_start, quick_code_start + code_size,
+ code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item),
+ compiled_method));
}
}
@@ -457,13 +750,15 @@
} else {
status = mirror::Class::kStatusNotReady;
}
- const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
- size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
- bool is_native = it.MemberIsNative();
- CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
- << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
- << (status < mirror::Class::kStatusVerified) << " " << status << " "
- << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ std::vector<uint8_t> const * gc_map = compiled_method->GetGcMap();
+ if (gc_map != nullptr) {
+ size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
+ bool is_native = it.MemberIsNative();
+ CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
+ << gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+ << (status < mirror::Class::kStatusVerified) << " " << status << " "
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+ }
}
DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
@@ -498,7 +793,7 @@
DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
- uint32_t map_size = map->size() * sizeof((*map)[0]);
+ uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
if (map_size != 0u) {
auto lb = dedupe_map_.lower_bound(map);
if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map, lb->first)) {
@@ -550,7 +845,14 @@
NullHandle<mirror::ClassLoader>(),
NullHandle<mirror::ArtMethod>(),
invoke_type);
- CHECK(method != NULL) << PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+ if (method == nullptr) {
+ LOG(ERROR) << "Unexpected failure to resolve a method: "
+ << PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+ soa.Self()->AssertPendingException();
+ mirror::Throwable* exc = soa.Self()->GetException(nullptr);
+ std::string dump = exc->Dump();
+ LOG(FATAL) << dump;
+ }
// Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
method->SetQuickOatCodeOffset(offsets.code_offset_);
method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
@@ -562,13 +864,51 @@
class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
public:
WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
- size_t relative_offset)
+ size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
: OatDexMethodVisitor(writer, relative_offset),
out_(out),
- file_offset_(file_offset) {
+ file_offset_(file_offset),
+ self_(Thread::Current()),
+ old_no_thread_suspension_cause_(self_->StartAssertNoThreadSuspension("OatWriter patching")),
+ class_linker_(Runtime::Current()->GetClassLinker()),
+ dex_cache_(nullptr) {
+ if (writer_->image_writer_ != nullptr) {
+ // If we're creating the image, the address space must be ready so that we can apply patches.
+ CHECK(writer_->image_writer_->IsImageAddressSpaceReady());
+ patched_code_.reserve(16 * KB);
+ }
+ self_->TransitionFromSuspendedToRunnable();
}
- bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+ ~WriteCodeMethodVisitor() UNLOCK_FUNCTION(Locks::mutator_lock_) {
+ self_->EndAssertNoThreadSuspension(old_no_thread_suspension_cause_);
+ self_->TransitionFromRunnableToSuspended(kNative);
+ }
+
+ bool StartClass(const DexFile* dex_file, size_t class_def_index)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ OatDexMethodVisitor::StartClass(dex_file, class_def_index);
+ if (dex_cache_ == nullptr || dex_cache_->GetDexFile() != dex_file) {
+ dex_cache_ = class_linker_->FindDexCache(*dex_file);
+ }
+ return true;
+ }
+
+ bool EndClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool result = OatDexMethodVisitor::EndClass();
+ if (oat_class_index_ == writer_->oat_classes_.size()) {
+ DCHECK(result); // OatDexMethodVisitor::EndClass() never fails.
+ offset_ = writer_->relative_call_patcher_->WriteThunks(out_, offset_);
+ if (UNLIKELY(offset_ == 0u)) {
+ PLOG(ERROR) << "Failed to write final relative call thunks";
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
@@ -579,18 +919,18 @@
const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
if (quick_code != nullptr) {
CHECK(compiled_method->GetPortableCode() == nullptr);
+ offset_ = writer_->relative_call_patcher_->WriteThunks(out, offset_);
+ if (offset_ == 0u) {
+ ReportWriteFailure("relative call thunk", it);
+ return false;
+ }
uint32_t aligned_offset = compiled_method->AlignCode(offset_);
uint32_t aligned_code_delta = aligned_offset - offset_;
if (aligned_code_delta != 0) {
- static const uint8_t kPadding[] = {
- 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
- };
- DCHECK_LE(aligned_code_delta, sizeof(kPadding));
- if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
+ if (!writer_->WriteCodeAlignment(out, aligned_code_delta)) {
ReportWriteFailure("code alignment padding", it);
return false;
}
- writer_->size_code_alignment_ += aligned_code_delta;
offset_ += aligned_code_delta;
DCHECK_OFFSET_();
}
@@ -605,7 +945,9 @@
offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
if (method_offsets.code_offset_ >= offset_) {
- const OatQuickMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
+ const OatQuickMethodHeader& method_header =
+ oat_class->method_headers_[method_offsets_index_];
+ writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
if (!out->WriteFully(&method_header, sizeof(method_header))) {
ReportWriteFailure("method header", it);
return false;
@@ -613,6 +955,31 @@
writer_->size_method_header_ += sizeof(method_header);
offset_ += sizeof(method_header);
DCHECK_OFFSET_();
+
+ if (!compiled_method->GetPatches().empty()) {
+ patched_code_ = *quick_code;
+ quick_code = &patched_code_;
+ for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ if (patch.Type() == kLinkerPatchCallRelative) {
+ // NOTE: Relative calls across oat files are not supported.
+ uint32_t target_offset = GetTargetOffset(patch);
+ uint32_t literal_offset = patch.LiteralOffset();
+ writer_->relative_call_patcher_->Patch(&patched_code_, literal_offset,
+ offset_ + literal_offset, target_offset);
+ } else if (patch.Type() == kLinkerPatchCall) {
+ uint32_t target_offset = GetTargetOffset(patch);
+ PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset);
+ } else if (patch.Type() == kLinkerPatchMethod) {
+ mirror::ArtMethod* method = GetTargetMethod(patch);
+ PatchObjectAddress(&patched_code_, patch.LiteralOffset(), method);
+ } else if (patch.Type() == kLinkerPatchType) {
+ mirror::Class* type = GetTargetType(patch);
+ PatchObjectAddress(&patched_code_, patch.LiteralOffset(), type);
+ }
+ }
+ }
+
+ writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
if (!out->WriteFully(&(*quick_code)[0], code_size)) {
ReportWriteFailure("method code", it);
return false;
@@ -631,11 +998,81 @@
private:
OutputStream* const out_;
size_t const file_offset_;
+ Thread* const self_;
+ const char* const old_no_thread_suspension_cause_; // TODO: Use ScopedAssertNoThreadSuspension.
+ ClassLinker* const class_linker_;
+ mirror::DexCache* dex_cache_;
+ std::vector<uint8_t> patched_code_;
void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) {
PLOG(ERROR) << "Failed to write " << what << " for "
<< PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation();
}
+
+ mirror::ArtMethod* GetTargetMethod(const LinkerPatch& patch)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ MethodReference ref = patch.TargetMethod();
+ mirror::DexCache* dex_cache =
+ (dex_file_ == ref.dex_file) ? dex_cache_ : class_linker_->FindDexCache(*ref.dex_file);
+ mirror::ArtMethod* method = dex_cache->GetResolvedMethod(ref.dex_method_index);
+ CHECK(method != nullptr);
+ return method;
+ }
+
+ uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ auto target_it = writer_->method_offset_map_.find(patch.TargetMethod());
+ uint32_t target_offset =
+ (target_it != writer_->method_offset_map_.end()) ? target_it->second : 0u;
+ // If there's no compiled code, point to the correct trampoline.
+ if (UNLIKELY(target_offset == 0)) {
+ mirror::ArtMethod* target = GetTargetMethod(patch);
+ DCHECK(target != nullptr);
+ DCHECK_EQ(target->GetQuickOatCodeOffset(), 0u);
+ target_offset = target->IsNative()
+ ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+ : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
+ }
+ return target_offset;
+ }
+
+ mirror::Class* GetTargetType(const LinkerPatch& patch)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::DexCache* dex_cache = (dex_file_ == patch.TargetTypeDexFile())
+ ? dex_cache_ : class_linker_->FindDexCache(*patch.TargetTypeDexFile());
+ mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
+ CHECK(type != nullptr);
+ return type;
+ }
+
+ void PatchObjectAddress(std::vector<uint8_t>* code, uint32_t offset, mirror::Object* object)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // NOTE: Direct method pointers across oat files don't use linker patches. However, direct
+ // type pointers across oat files do. (TODO: Investigate why.)
+ if (writer_->image_writer_ != nullptr) {
+ object = writer_->image_writer_->GetImageAddress(object);
+ }
+ uint32_t address = PointerToLowMemUInt32(object);
+ DCHECK_LE(offset + 4, code->size());
+ uint8_t* data = &(*code)[offset];
+ data[0] = address & 0xffu;
+ data[1] = (address >> 8) & 0xffu;
+ data[2] = (address >> 16) & 0xffu;
+ data[3] = (address >> 24) & 0xffu;
+ }
+
+ void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // NOTE: Direct calls across oat files don't use linker patches.
+ DCHECK(writer_->image_writer_ != nullptr);
+ uint32_t address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+ writer_->oat_data_offset_ + target_offset);
+ DCHECK_LE(offset + 4, code->size());
+ uint8_t* data = &(*code)[offset];
+ data[0] = address & 0xffu;
+ data[1] = (address >> 8) & 0xffu;
+ data[2] = (address >> 16) & 0xffu;
+ data[3] = (address >> 24) & 0xffu;
+ }
};
template <typename DataAccess>
@@ -661,7 +1098,7 @@
// Write deduplicated map.
const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
- size_t map_size = map->size() * sizeof((*map)[0]);
+ size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
DCHECK((map_size == 0u && map_offset == 0u) ||
(map_size != 0u && map_offset != 0u && map_offset <= offset_))
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
@@ -877,11 +1314,17 @@
}
bool OatWriter::Write(OutputStream* out) {
- const size_t file_offset = out->Seek(0, kSeekCurrent);
+ const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
+ if (raw_file_offset == (off_t) -1) {
+ LOG(ERROR) << "Failed to get file offset in " << out->GetLocation();
+ return false;
+ }
+ const size_t file_offset = static_cast<size_t>(raw_file_offset);
+ // Reserve space for header. It will be written last - after updating the checksum.
size_t header_size = oat_header_->GetHeaderSize();
- if (!out->WriteFully(oat_header_, header_size)) {
- PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
+ if (out->Seek(header_size, kSeekCurrent) == (off_t) -1) {
+ PLOG(ERROR) << "Failed to reserve space for oat header in " << out->GetLocation();
return false;
}
size_oat_header_ += sizeof(OatHeader);
@@ -892,7 +1335,12 @@
return false;
}
- size_t relative_offset = out->Seek(0, kSeekCurrent) - file_offset;
+ off_t tables_end_offset = out->Seek(0, kSeekCurrent);
+ if (tables_end_offset == (off_t) -1) {
+ LOG(ERROR) << "Failed to seek to oat code position in " << out->GetLocation();
+ return false;
+ }
+ size_t relative_offset = static_cast<size_t>(tables_end_offset) - file_offset;
relative_offset = WriteMaps(out, file_offset, relative_offset);
if (relative_offset == 0) {
LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
@@ -911,6 +1359,12 @@
return false;
}
+ const off_t oat_end_file_offset = out->Seek(0, kSeekCurrent);
+ if (oat_end_file_offset == (off_t) -1) {
+ LOG(ERROR) << "Failed to get oat end file offset in " << out->GetLocation();
+ return false;
+ }
+
if (kIsDebugBuild) {
uint32_t size_total = 0;
#define DO_STAT(x) \
@@ -936,6 +1390,7 @@
DO_STAT(size_method_header_);
DO_STAT(size_code_);
DO_STAT(size_code_alignment_);
+ DO_STAT(size_relative_call_thunks_);
DO_STAT(size_mapping_table_);
DO_STAT(size_vmap_table_);
DO_STAT(size_gc_map_);
@@ -951,13 +1406,29 @@
#undef DO_STAT
VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
- CHECK_EQ(file_offset + size_total, static_cast<uint32_t>(out->Seek(0, kSeekCurrent)));
+ CHECK_EQ(file_offset + size_total, static_cast<size_t>(oat_end_file_offset));
CHECK_EQ(size_, size_total);
}
- CHECK_EQ(file_offset + size_, static_cast<uint32_t>(out->Seek(0, kSeekCurrent)));
+ CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset));
CHECK_EQ(size_, relative_offset);
+ // Write the header now that the checksum is final.
+ if (out->Seek(file_offset, kSeekSet) == (off_t) -1) {
+ PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation();
+ return false;
+ }
+ DCHECK_EQ(raw_file_offset, out->Seek(0, kSeekCurrent));
+ if (!out->WriteFully(oat_header_, header_size)) {
+ PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation();
+ return false;
+ }
+ if (out->Seek(oat_end_file_offset, kSeekSet) == (off_t) -1) {
+ PLOG(ERROR) << "Failed to seek to end after writing oat header to " << out->GetLocation();
+ return false;
+ }
+ DCHECK_EQ(oat_end_file_offset, out->Seek(0, kSeekCurrent));
+
return true;
}
@@ -1084,6 +1555,18 @@
return relative_offset;
}
+bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) {
+ static const uint8_t kPadding[] = {
+ 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
+ };
+ DCHECK_LE(aligned_code_delta, sizeof(kPadding));
+ if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
+ return false;
+ }
+ size_code_alignment_ += aligned_code_delta;
+ return true;
+}
+
OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) {
offset_ = offset;
const std::string& location(dex_file.GetLocation());
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 945048e..5545ba8 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -23,6 +23,7 @@
#include "driver/compiler_driver.h"
#include "mem_map.h"
+#include "method_reference.h"
#include "oat.h"
#include "mirror/class.h"
#include "safe_map.h"
@@ -30,6 +31,8 @@
namespace art {
class BitVector;
+class CompiledMethod;
+class ImageWriter;
class OutputStream;
// OatHeader variable length with count of D OatDexFiles
@@ -81,6 +84,7 @@
uintptr_t image_file_location_oat_begin,
int32_t image_patch_delta,
const CompilerDriver* compiler,
+ ImageWriter* image_writer,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store);
@@ -92,27 +96,38 @@
return size_;
}
+ const std::vector<uintptr_t>& GetAbsolutePatchLocations() const {
+ return absolute_patch_locations_;
+ }
+
+ void SetOatDataOffset(size_t oat_data_offset) {
+ oat_data_offset_ = oat_data_offset;
+ }
+
bool Write(OutputStream* out);
~OatWriter();
struct DebugInfo {
- DebugInfo(const std::string& method_name, uint32_t low_pc, uint32_t high_pc)
- : method_name_(method_name), low_pc_(low_pc), high_pc_(high_pc) {
+ DebugInfo(const std::string& method_name, const char* src_file_name,
+ uint32_t low_pc, uint32_t high_pc, const uint8_t* dbgstream,
+ CompiledMethod* compiled_method)
+ : method_name_(method_name), src_file_name_(src_file_name),
+ low_pc_(low_pc), high_pc_(high_pc), dbgstream_(dbgstream),
+ compiled_method_(compiled_method) {
}
- std::string method_name_;
+ std::string method_name_; // Note: this name is a pretty-printed name.
+ const char* src_file_name_;
uint32_t low_pc_;
uint32_t high_pc_;
+ const uint8_t* dbgstream_;
+ CompiledMethod* compiled_method_;
};
const std::vector<DebugInfo>& GetCFIMethodInfo() const {
return method_info_;
}
- bool DidAddSymbols() const {
- return compiler_driver_->DidIncludeDebugSymbols();
- }
-
private:
// The DataAccess classes are helper classes that provide access to members related to
// a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
@@ -156,6 +171,8 @@
size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
+ bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
+
class OatDexFile {
public:
explicit OatDexFile(size_t offset, const DexFile& dex_file);
@@ -244,6 +261,7 @@
std::vector<DebugInfo> method_info_;
const CompilerDriver* const compiler_driver_;
+ ImageWriter* const image_writer_;
// note OatFile does not take ownership of the DexFiles
const std::vector<const DexFile*>* dex_files_;
@@ -251,6 +269,9 @@
// Size required for Oat data structures.
size_t size_;
+ // Offset of the oat data from the start of the mmapped region of the elf file.
+ size_t oat_data_offset_;
+
// dependencies on the image.
uint32_t image_file_location_oat_checksum_;
uintptr_t image_file_location_oat_begin_;
@@ -292,6 +313,7 @@
uint32_t size_method_header_;
uint32_t size_code_;
uint32_t size_code_alignment_;
+ uint32_t size_relative_call_thunks_;
uint32_t size_mapping_table_;
uint32_t size_vmap_table_;
uint32_t size_gc_map_;
@@ -305,6 +327,18 @@
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
+ class RelativeCallPatcher;
+ class NoRelativeCallPatcher;
+ class X86RelativeCallPatcher;
+ class Thumb2RelativeCallPatcher;
+
+ std::unique_ptr<RelativeCallPatcher> relative_call_patcher_;
+
+ // The locations of absolute patches relative to the start of the executable section.
+ std::vector<uintptr_t> absolute_patch_locations_;
+
+ SafeMap<MethodReference, uint32_t, MethodReferenceComparator> method_offset_map_;
+
struct CodeOffsetsKeyComparator {
bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
@@ -317,6 +351,18 @@
if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
return &lhs->GetVmapTable() < &rhs->GetVmapTable();
}
+ const auto& lhs_patches = lhs->GetPatches();
+ const auto& rhs_patches = rhs->GetPatches();
+ if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
+ return lhs_patches.size() < rhs_patches.size();
+ }
+ auto rit = rhs_patches.begin();
+ for (const LinkerPatch& lpatch : lhs_patches) {
+ if (UNLIKELY(!(lpatch == *rit))) {
+ return lpatch < *rit;
+ }
+ ++rit;
+ }
return false;
}
};
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 43e6b83..5015bd0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -138,13 +138,15 @@
template<typename T>
void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_offset) {
+ int32_t target_offset = instruction.GetTargetOffset();
+ PotentiallyAddSuspendCheck(target_offset, dex_offset);
HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
T* comparison = new (arena_) T(first, second);
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
- HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset());
+ HBasicBlock* target = FindBlockStartingAt(dex_offset + target_offset);
DCHECK(target != nullptr);
current_block_->AddSuccessor(target);
target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits());
@@ -155,12 +157,14 @@
template<typename T>
void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_offset) {
+ int32_t target_offset = instruction.GetTargetOffset();
+ PotentiallyAddSuspendCheck(target_offset, dex_offset);
HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
T* comparison = new (arena_) T(value, GetIntConstant(0));
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
- HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset());
+ HBasicBlock* target = FindBlockStartingAt(dex_offset + target_offset);
DCHECK(target != nullptr);
current_block_->AddSuccessor(target);
target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits());
@@ -179,9 +183,9 @@
// Setup the graph with the entry block and exit block.
graph_ = new (arena_) HGraph(arena_);
- entry_block_ = new (arena_) HBasicBlock(graph_);
+ entry_block_ = new (arena_) HBasicBlock(graph_, 0);
graph_->AddBlock(entry_block_);
- exit_block_ = new (arena_) HBasicBlock(graph_);
+ exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
graph_->SetEntryBlock(entry_block_);
graph_->SetExitBlock(exit_block_);
@@ -209,6 +213,8 @@
// Add the exit block at the end to give it the highest id.
graph_->AddBlock(exit_block_);
exit_block_->AddInstruction(new (arena_) HExit());
+ // Add the suspend check to the entry block.
+ entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
entry_block_->AddInstruction(new (arena_) HGoto());
return graph_;
}
@@ -235,7 +241,7 @@
branch_targets_.SetSize(code_end - code_ptr);
// Create the first block for the dex instructions, single successor of the entry block.
- HBasicBlock* block = new (arena_) HBasicBlock(graph_);
+ HBasicBlock* block = new (arena_) HBasicBlock(graph_, 0);
branch_targets_.Put(0, block);
entry_block_->AddSuccessor(block);
@@ -248,13 +254,13 @@
int32_t target = instruction.GetTargetOffset() + dex_offset;
// Create a block for the target instruction.
if (FindBlockStartingAt(target) == nullptr) {
- block = new (arena_) HBasicBlock(graph_);
+ block = new (arena_) HBasicBlock(graph_, target);
branch_targets_.Put(target, block);
}
dex_offset += instruction.SizeInCodeUnits();
code_ptr += instruction.SizeInCodeUnits();
if ((code_ptr < code_end) && (FindBlockStartingAt(dex_offset) == nullptr)) {
- block = new (arena_) HBasicBlock(graph_);
+ block = new (arena_) HBasicBlock(graph_, dex_offset);
branch_targets_.Put(dex_offset, block);
}
} else {
@@ -325,18 +331,61 @@
bool is_range,
uint32_t* args,
uint32_t register_index) {
+ Instruction::Code opcode = instruction.Opcode();
+ InvokeType invoke_type;
+ switch (opcode) {
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ invoke_type = kStatic;
+ break;
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ invoke_type = kDirect;
+ break;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ invoke_type = kVirtual;
+ break;
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ invoke_type = kInterface;
+ break;
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_SUPER:
+ invoke_type = kSuper;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke op: " << opcode;
+ return false;
+ }
+
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(method_id.proto_idx_);
const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_);
Primitive::Type return_type = Primitive::GetType(descriptor[0]);
- bool is_instance_call =
- instruction.Opcode() != Instruction::INVOKE_STATIC
- && instruction.Opcode() != Instruction::INVOKE_STATIC_RANGE;
+ bool is_instance_call = invoke_type != kStatic;
const size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1);
- // Treat invoke-direct like static calls for now.
- HInvoke* invoke = new (arena_) HInvokeStatic(
- arena_, number_of_arguments, return_type, dex_offset, method_idx);
+ HInvoke* invoke = nullptr;
+ if (invoke_type == kVirtual) {
+ MethodReference target_method(dex_file_, method_idx);
+ uintptr_t direct_code;
+ uintptr_t direct_method;
+ int vtable_index;
+ // TODO: Add devirtualization support.
+ compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_offset, true, true,
+ &invoke_type, &target_method, &vtable_index,
+ &direct_code, &direct_method);
+ if (vtable_index == -1) {
+ return false;
+ }
+ invoke = new (arena_) HInvokeVirtual(
+ arena_, number_of_arguments, return_type, dex_offset, vtable_index);
+ } else {
+ // Treat invoke-direct like static calls for now.
+ invoke = new (arena_) HInvokeStatic(
+ arena_, number_of_arguments, return_type, dex_offset, method_idx);
+ }
size_t start_index = 0;
Temporaries temps(graph_, is_instance_call ? 1 : 0);
@@ -413,6 +462,7 @@
current_block_->AddInstruction(new (arena_) HInstanceFieldSet(
null_check,
value,
+ field_type,
resolved_field->GetOffset()));
} else {
current_block_->AddInstruction(new (arena_) HInstanceFieldGet(
@@ -453,14 +503,23 @@
if (is_put) {
HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
// TODO: Insert a type check node if the type is Object.
- current_block_->AddInstruction(new (arena_) HArraySet(object, index, value, dex_offset));
+ current_block_->AddInstruction(new (arena_) HArraySet(
+ object, index, value, anticipated_type, dex_offset));
} else {
current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type));
UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
}
}
-bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) {
+void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset) {
+ if (target_offset <= 0) {
+ // Unconditionnally add a suspend check to backward branches. We can remove
+ // them after we recognize loops in the graph.
+ current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_offset));
+ }
+}
+
+bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_offset) {
if (current_block_ == nullptr) {
return true; // Dead code
}
@@ -578,7 +637,9 @@
case Instruction::GOTO:
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
- HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset);
+ int32_t offset = instruction.GetTargetOffset();
+ PotentiallyAddSuspendCheck(offset, dex_offset);
+ HBasicBlock* target = FindBlockStartingAt(offset + dex_offset);
DCHECK(target != nullptr);
current_block_->AddInstruction(new (arena_) HGoto());
current_block_->AddSuccessor(target);
@@ -602,7 +663,8 @@
}
case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_DIRECT: {
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_VIRTUAL: {
uint32_t method_idx = instruction.VRegB_35c();
uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
uint32_t args[5];
@@ -614,7 +676,8 @@
}
case Instruction::INVOKE_STATIC_RANGE:
- case Instruction::INVOKE_DIRECT_RANGE: {
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE: {
uint32_t method_idx = instruction.VRegB_3rc();
uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
uint32_t register_index = instruction.VRegC();
@@ -750,6 +813,13 @@
ARRAY_XX(_CHAR, Primitive::kPrimChar);
ARRAY_XX(_SHORT, Primitive::kPrimShort);
+ case Instruction::ARRAY_LENGTH: {
+ HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+ current_block_->AddInstruction(new (arena_) HArrayLength(object));
+ UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+ break;
+ }
+
default:
return false;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 170c427..e143786 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -54,7 +54,7 @@
// Analyzes the dex instruction and adds HInstruction to the graph
// to execute that instruction. Returns whether the instruction can
// be handled.
- bool AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset);
+ bool AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_offset);
// Finds all instructions that start a new block, and populates branch_targets_ with
// the newly created blocks.
@@ -70,6 +70,7 @@
HLocal* GetLocalAt(int register_index) const;
void UpdateLocal(int register_index, HInstruction* instruction) const;
HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
+ void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset);
// Temporarily returns whether the compiler supports the parameters
// of the method.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index bd8c27e..2a9a7b3 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -19,11 +19,13 @@
#include "code_generator_arm.h"
#include "code_generator_x86.h"
#include "code_generator_x86_64.h"
+#include "compiled_method.h"
#include "dex/verified_method.h"
#include "driver/dex_compilation_unit.h"
#include "gc_map_builder.h"
#include "leb128.h"
#include "mapping_table.h"
+#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
#include "verifier/dex_gc_map.h"
#include "vmap_table.h"
@@ -40,17 +42,19 @@
if (!is_leaf) {
MarkNotLeaf();
}
- ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
- + GetGraph()->GetNumberOfLocalVRegs()
- + GetGraph()->GetNumberOfTemporaries()
- + 1 /* filler */);
+ ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
+ + GetGraph()->GetNumberOfTemporaries()
+ + 1 /* filler */,
+ 0, /* the baseline compiler does not have live registers at slow path */
+ GetGraph()->GetMaximumNumberOfOutVRegs()
+ + 1 /* current method */);
GenerateFrameEntry();
+ HGraphVisitor* location_builder = GetLocationBuilder();
+ HGraphVisitor* instruction_visitor = GetInstructionVisitor();
for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
HBasicBlock* block = blocks.Get(i);
Bind(GetLabelOf(block));
- HGraphVisitor* location_builder = GetLocationBuilder();
- HGraphVisitor* instruction_visitor = GetInstructionVisitor();
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
current->Accept(location_builder);
@@ -75,10 +79,10 @@
block_labels_.SetSize(blocks.Size());
GenerateFrameEntry();
+ HGraphVisitor* instruction_visitor = GetInstructionVisitor();
for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
HBasicBlock* block = blocks.Get(i);
Bind(GetLabelOf(block));
- HGraphVisitor* instruction_visitor = GetInstructionVisitor();
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
current->Accept(instruction_visitor);
@@ -109,10 +113,15 @@
return -1;
}
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots) {
+void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
+ size_t maximum_number_of_live_registers,
+ size_t number_of_out_slots) {
+ first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
+
SetFrameSize(RoundUp(
number_of_spill_slots * kVRegSize
- + kVRegSize // Art method
+ + number_of_out_slots * kVRegSize
+ + maximum_number_of_live_registers * GetWordSize()
+ FrameEntrySpillSize(),
kStackAlignment));
}
@@ -297,7 +306,7 @@
}
}
-void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data) const {
+void CodeGenerator::BuildMappingTable(std::vector<uint8_t>* data, SrcMap* src_map) const {
uint32_t pc2dex_data_size = 0u;
uint32_t pc2dex_entries = pc_infos_.Size();
uint32_t pc2dex_offset = 0u;
@@ -305,6 +314,10 @@
uint32_t dex2pc_data_size = 0u;
uint32_t dex2pc_entries = 0u;
+ if (src_map != nullptr) {
+ src_map->reserve(pc2dex_entries);
+ }
+
// We currently only have pc2dex entries.
for (size_t i = 0; i < pc2dex_entries; i++) {
struct PcInfo pc_info = pc_infos_.Get(i);
@@ -312,6 +325,9 @@
pc2dex_data_size += SignedLeb128Size(pc_info.dex_pc - pc2dex_dalvik_offset);
pc2dex_offset = pc_info.native_pc;
pc2dex_dalvik_offset = pc_info.dex_pc;
+ if (src_map != nullptr) {
+ src_map->push_back(SrcMapElem({pc2dex_offset, pc2dex_dalvik_offset}));
+ }
}
uint32_t total_entries = pc2dex_entries + dex2pc_entries;
@@ -368,4 +384,158 @@
*data = vmap_encoder.GetData();
}
+void CodeGenerator::BuildStackMaps(std::vector<uint8_t>* data) {
+ uint32_t size = stack_map_stream_.ComputeNeededSize();
+ data->resize(size);
+ MemoryRegion region(data->data(), size);
+ stack_map_stream_.FillIn(region);
+}
+
+void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) {
+ // Collect PC infos for the mapping table.
+ struct PcInfo pc_info;
+ pc_info.dex_pc = dex_pc;
+ pc_info.native_pc = GetAssembler()->CodeSize();
+ pc_infos_.Add(pc_info);
+
+ // Populate stack map information.
+
+ if (instruction == nullptr) {
+ // For stack overflow checks.
+ stack_map_stream_.AddStackMapEntry(dex_pc, pc_info.native_pc, 0, 0, 0, 0);
+ return;
+ }
+
+ LocationSummary* locations = instruction->GetLocations();
+ HEnvironment* environment = instruction->GetEnvironment();
+
+ size_t environment_size = instruction->EnvironmentSize();
+
+ size_t register_mask = 0;
+ size_t inlining_depth = 0;
+ stack_map_stream_.AddStackMapEntry(
+ dex_pc, pc_info.native_pc, register_mask,
+ locations->GetStackMask(), environment_size, inlining_depth);
+
+ // Walk over the environment, and record the location of dex registers.
+ for (size_t i = 0; i < environment_size; ++i) {
+ HInstruction* current = environment->GetInstructionAt(i);
+ if (current == nullptr) {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kNone, 0);
+ continue;
+ }
+
+ Location location = locations->GetEnvironmentAt(i);
+ switch (location.GetKind()) {
+ case Location::kConstant: {
+ DCHECK(current == location.GetConstant());
+ if (current->IsLongConstant()) {
+ int64_t value = current->AsLongConstant()->GetValue();
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+ ++i;
+ DCHECK_LT(i, environment_size);
+ } else {
+ DCHECK(current->IsIntConstant());
+ int32_t value = current->AsIntConstant()->GetValue();
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+ }
+ break;
+ }
+
+ case Location::kStackSlot: {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+ break;
+ }
+
+ case Location::kDoubleStackSlot: {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack, location.GetStackIndex());
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInStack,
+ location.GetHighStackIndex(kVRegSize));
+ ++i;
+ DCHECK_LT(i, environment_size);
+ break;
+ }
+
+ case Location::kRegister : {
+ int id = location.reg().RegId();
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+ if (current->GetType() == Primitive::kPrimDouble
+ || current->GetType() == Primitive::kPrimLong) {
+ stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kInRegister, id);
+ ++i;
+ DCHECK_LT(i, environment_size);
+ }
+ break;
+ }
+
+ default:
+ LOG(FATAL) << "Unexpected kind " << location.GetKind();
+ }
+ }
+}
+
+size_t CodeGenerator::GetStackOffsetOfSavedRegister(size_t index) {
+ return first_register_slot_in_slow_path_ + index * GetWordSize();
+}
+
+void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) {
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ uint32_t count = 0;
+ for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+ if (register_set->ContainsCoreRegister(i)) {
+ size_t stack_offset = GetStackOffsetOfSavedRegister(count);
+ ++count;
+ SaveCoreRegister(Location::StackSlot(stack_offset), i);
+ // If the register holds an object, update the stack mask.
+ if (locations->RegisterContainsObject(i)) {
+ locations->SetStackBit(stack_offset / kVRegSize);
+ }
+ }
+ }
+
+ for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+ if (register_set->ContainsFloatingPointRegister(i)) {
+ LOG(FATAL) << "Unimplemented";
+ }
+ }
+}
+
+void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) {
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ uint32_t count = 0;
+ for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) {
+ if (register_set->ContainsCoreRegister(i)) {
+ size_t stack_offset = GetStackOffsetOfSavedRegister(count);
+ ++count;
+ RestoreCoreRegister(Location::StackSlot(stack_offset), i);
+ }
+ }
+
+ for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) {
+ if (register_set->ContainsFloatingPointRegister(i)) {
+ LOG(FATAL) << "Unimplemented";
+ }
+ }
+}
+
+void CodeGenerator::ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const {
+ LocationSummary* locations = suspend_check->GetLocations();
+ HBasicBlock* block = suspend_check->GetBlock();
+ DCHECK(block->GetLoopInformation()->GetSuspendCheck() == suspend_check);
+ DCHECK(block->IsLoopHeader());
+
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HInstruction* current = it.Current();
+ LiveInterval* interval = current->GetLiveInterval();
+ // We only need to clear bits of loop phis containing objects and allocated in register.
+ // Loop phis allocated on stack already have the object in the stack.
+ if (current->GetType() == Primitive::kPrimNot
+ && interval->HasRegister()
+ && interval->HasSpillSlot()) {
+ locations->ClearStackBit(interval->GetSpillSlot() / kVRegSize);
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b31c3a3..b58f3b3 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -23,6 +23,7 @@
#include "locations.h"
#include "memory_region.h"
#include "nodes.h"
+#include "stack_map_stream.h"
#include "utils/assembler.h"
namespace art {
@@ -32,6 +33,7 @@
class CodeGenerator;
class DexCompilationUnit;
+class SrcMap;
class CodeAllocator {
public:
@@ -96,7 +98,9 @@
virtual HGraphVisitor* GetInstructionVisitor() = 0;
virtual Assembler* GetAssembler() = 0;
virtual size_t GetWordSize() const = 0;
- void ComputeFrameSize(size_t number_of_spill_slots);
+ void ComputeFrameSize(size_t number_of_spill_slots,
+ size_t maximum_number_of_live_registers,
+ size_t number_of_out_slots);
virtual size_t FrameEntrySpillSize() const = 0;
int32_t GetStackSlot(HLocal* local) const;
Location GetTemporaryLocation(HTemporary* temp) const;
@@ -112,13 +116,10 @@
virtual void DumpCoreRegister(std::ostream& stream, int reg) const = 0;
virtual void DumpFloatingPointRegister(std::ostream& stream, int reg) const = 0;
virtual InstructionSet GetInstructionSet() const = 0;
+ virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) = 0;
+ virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) = 0;
- void RecordPcInfo(uint32_t dex_pc) {
- struct PcInfo pc_info;
- pc_info.dex_pc = dex_pc;
- pc_info.native_pc = GetAssembler()->CodeSize();
- pc_infos_.Add(pc_info);
- }
+ void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc);
void AddSlowPath(SlowPathCode* slow_path) {
slow_paths_.Add(slow_path);
@@ -126,10 +127,13 @@
void GenerateSlowPaths();
- void BuildMappingTable(std::vector<uint8_t>* vector) const;
+ void BuildMappingTable(std::vector<uint8_t>* vector, SrcMap* src_map) const;
void BuildVMapTable(std::vector<uint8_t>* vector) const;
void BuildNativeGCMap(
std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
+ void BuildStackMaps(std::vector<uint8_t>* vector);
+ void SaveLiveRegisters(LocationSummary* locations);
+ void RestoreLiveRegisters(LocationSummary* locations);
bool IsLeafMethod() const {
return is_leaf_;
@@ -139,15 +143,25 @@
is_leaf_ = false;
}
+ // Clears the spill slots taken by loop phis in the `LocationSummary` of the
+ // suspend check. This is called when the code generator generates code
+ // for the suspend check at the back edge (instead of where the suspend check
+ // is, which is the loop entry). At this point, the spill slots for the phis
+ // have not been written to.
+ void ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const;
+
protected:
CodeGenerator(HGraph* graph, size_t number_of_registers)
: frame_size_(kUninitializedFrameSize),
+ core_spill_mask_(0),
+ first_register_slot_in_slow_path_(0),
graph_(graph),
block_labels_(graph->GetArena(), 0),
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)),
- is_leaf_(true) {}
+ is_leaf_(true),
+ stack_map_stream_(graph->GetArena()) {}
~CodeGenerator() {}
// Register allocation logic.
@@ -166,9 +180,11 @@
// Frame size required for this method.
uint32_t frame_size_;
uint32_t core_spill_mask_;
+ uint32_t first_register_slot_in_slow_path_;
private:
void InitLocations(HInstruction* instruction);
+ size_t GetStackOffsetOfSavedRegister(size_t index);
HGraph* const graph_;
@@ -182,6 +198,8 @@
bool is_leaf_;
+ StackMapStream stack_map_stream_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2c954a0..1876cb9 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -20,6 +20,7 @@
#include "gc/accounting/card_table.h"
#include "mirror/array.h"
#include "mirror/art_method.h"
+#include "mirror/class.h"
#include "thread.h"
#include "utils/assembler.h"
#include "utils/arm/assembler_arm.h"
@@ -61,18 +62,18 @@
class NullCheckSlowPathARM : public SlowPathCode {
public:
- explicit NullCheckSlowPathARM(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+ explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
};
@@ -90,12 +91,50 @@
DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
};
+class SuspendCheckSlowPathARM : public SlowPathCode {
+ public:
+ explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
+ : instruction_(instruction), successor_(successor) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+ int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value();
+ __ ldr(LR, Address(TR, offset));
+ __ blx(LR);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ if (successor_ == nullptr) {
+ __ b(GetReturnLabel());
+ } else {
+ __ b(codegen->GetLabelOf(successor_));
+ }
+ }
+
+ Label* GetReturnLabel() {
+ DCHECK(successor_ == nullptr);
+ return &return_label_;
+ }
+
+ private:
+ HSuspendCheck* const instruction_;
+ // If not null, the block to branch to after the suspend check.
+ HBasicBlock* const successor_;
+
+ // If `successor_` is null, the label to branch to after the suspend check.
+ Label return_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM);
+};
+
class BoundsCheckSlowPathARM : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathARM(uint32_t dex_pc,
- Location index_location,
- Location length_location)
- : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+ BoundsCheckSlowPathARM(HBoundsCheck* instruction,
+ Location index_location,
+ Location length_location)
+ : instruction_(instruction),
+ index_location_(index_location),
+ length_location_(length_location) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = reinterpret_cast<CodeGeneratorARM*>(codegen);
@@ -106,11 +145,11 @@
int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HBoundsCheck* const instruction_;
const Location index_location_;
const Location length_location_;
@@ -156,6 +195,14 @@
stream << ArmManagedRegister::FromDRegister(DRegister(reg));
}
+void CodeGeneratorARM::SaveCoreRegister(Location stack_location, uint32_t reg_id) {
+ __ str(static_cast<Register>(reg_id), Address(SP, stack_location.GetStackIndex()));
+}
+
+void CodeGeneratorARM::RestoreCoreRegister(Location stack_location, uint32_t reg_id) {
+ __ ldr(static_cast<Register>(reg_id), Address(SP, stack_location.GetStackIndex()));
+}
+
CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
: CodeGenerator(graph, kNumberOfRegIds),
location_builder_(graph, this),
@@ -277,7 +324,7 @@
} else {
__ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
__ ldr(IP, Address(IP, 0));
- RecordPcInfo(0);
+ RecordPcInfo(nullptr, 0);
}
}
@@ -526,9 +573,22 @@
void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
HBasicBlock* successor = got->GetSuccessor();
- if (GetGraph()->GetExitBlock() == successor) {
- codegen_->GenerateFrameExit();
- } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+ DCHECK(!successor->IsExitBlock());
+
+ HBasicBlock* block = got->GetBlock();
+ HInstruction* previous = got->GetPrevious();
+
+ HLoopInformation* info = block->GetLoopInformation();
+ if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+ GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+ return;
+ }
+
+ if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+ GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+ }
+ if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
__ b(codegen_->GetLabelOf(successor));
}
}
@@ -545,14 +605,14 @@
}
void LocationsBuilderARM::VisitIf(HIf* if_instr) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
HInstruction* cond = if_instr->InputAt(0);
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
- locations->SetInAt(0, Location::Any());
+ locations->SetInAt(0, Location::RequiresRegister());
}
- if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
@@ -564,7 +624,7 @@
DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
__ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
ShifterOperand(0));
- __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
+ __ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), NE);
} else {
// Condition has not been materialized, use its inputs as the comparison and its
// condition as the branch condition.
@@ -595,13 +655,13 @@
void LocationsBuilderARM::VisitCondition(HCondition* comp) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
- comp->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
@@ -695,7 +755,8 @@
}
void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
switch (store->InputAt(1)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -713,25 +774,24 @@
default:
LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
}
- store->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
}
void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
}
void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitLongConstant(HLongConstant* constant) {
@@ -747,7 +807,8 @@
}
void LocationsBuilderARM::VisitReturn(HReturn* ret) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
switch (ret->InputAt(0)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -766,8 +827,6 @@
default:
LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
}
-
- ret->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
@@ -794,40 +853,7 @@
}
void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
- locations->AddTemp(ArmCoreLocation(R0));
-
- InvokeDexCallingConventionVisitor calling_convention_visitor;
- for (size_t i = 0; i < invoke->InputCount(); i++) {
- HInstruction* input = invoke->InputAt(i);
- locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
- }
-
- switch (invoke->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- locations->SetOut(ArmCoreLocation(R0));
- break;
-
- case Primitive::kPrimLong:
- locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
- break;
-
- case Primitive::kPrimVoid:
- break;
-
- case Primitive::kPrimDouble:
- case Primitive::kPrimFloat:
- LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
- break;
- }
-
- invoke->SetLocations(locations);
+ HandleInvoke(invoke);
}
void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
@@ -859,12 +885,78 @@
// LR()
__ blx(LR);
- codegen_->RecordPcInfo(invoke->GetDexPc());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
+void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ HandleInvoke(invoke);
+}
+
+void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+ locations->AddTemp(ArmCoreLocation(R0));
+
+ InvokeDexCallingConventionVisitor calling_convention_visitor;
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
+ HInstruction* input = invoke->InputAt(i);
+ locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
+ }
+
+ switch (invoke->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ locations->SetOut(ArmCoreLocation(R0));
+ break;
+
+ case Primitive::kPrimLong:
+ locations->SetOut(Location::RegisterLocation(ArmManagedRegister::FromRegisterPair(R0_R1)));
+ break;
+
+ case Primitive::kPrimVoid:
+ break;
+
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
+ break;
+ }
+}
+
+
+void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister();
+ uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
+ invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ // temp = object->GetClass();
+ if (receiver.IsStackSlot()) {
+ __ ldr(temp, Address(SP, receiver.GetStackIndex()));
+ __ ldr(temp, Address(temp, class_offset));
+ } else {
+ __ ldr(temp, Address(receiver.AsArm().AsCoreRegister(), class_offset));
+ }
+ // temp = temp->GetMethodAt(method_offset);
+ uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value();
+ __ ldr(temp, Address(temp, method_offset));
+ // LR = temp->GetEntryPoint();
+ __ ldr(LR, Address(temp, entry_point));
+ // LR();
+ __ blx(LR);
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
void LocationsBuilderARM::VisitAdd(HAdd* add) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -884,7 +976,6 @@
default:
LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
}
- add->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
@@ -924,7 +1015,8 @@
}
void LocationsBuilderARM::VisitSub(HSub* sub) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -944,7 +1036,6 @@
default:
LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
}
- sub->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
@@ -985,13 +1076,12 @@
}
void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(1)));
locations->SetOut(ArmCoreLocation(R0));
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
@@ -1003,12 +1093,13 @@
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
if (location.IsStackSlot()) {
location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -1016,7 +1107,6 @@
location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
}
locations->SetOut(location);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitParameterValue(HParameterValue* instruction) {
@@ -1024,10 +1114,10 @@
}
void LocationsBuilderARM::VisitNot(HNot* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitNot(HNot* instruction) {
@@ -1037,11 +1127,11 @@
}
void LocationsBuilderARM::VisitCompare(HCompare* compare) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- compare->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
@@ -1081,12 +1171,12 @@
}
void LocationsBuilderARM::VisitPhi(HPhi* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
locations->SetInAt(i, Location::Any());
}
locations->SetOut(Location::Any());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) {
@@ -1094,22 +1184,22 @@
}
void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Temporary registers for the write barrier.
- if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ if (instruction->GetFieldType() == Primitive::kPrimNot) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsArm().AsCoreRegister();
uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -1154,10 +1244,10 @@
}
void LocationsBuilderARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1214,16 +1304,15 @@
}
void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction->GetDexPc());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
@@ -1237,11 +1326,11 @@
}
void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
@@ -1340,27 +1429,27 @@
}
void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
- if (value_type == Primitive::kPrimNot) {
+ Primitive::Type value_type = instruction->GetComponentType();
+ bool is_object = value_type == Primitive::kPrimNot;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+ if (is_object) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, ArmCoreLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, ArmCoreLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, ArmCoreLocation(calling_convention.GetRegisterAt(2)));
- codegen_->MarkNotLeaf();
} else {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetInAt(2, Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsArm().AsCoreRegister();
Location index = locations->InAt(1);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1408,7 +1497,7 @@
int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
__ ldr(LR, Address(TR, offset));
__ blx(LR);
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
break;
}
@@ -1436,10 +1525,10 @@
}
void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) {
@@ -1451,18 +1540,18 @@
}
void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM(
- instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
Register index = locations->InAt(0).AsArm().AsCoreRegister();
@@ -1497,6 +1586,41 @@
codegen_->GetMoveResolver()->EmitNativeCode(instruction);
}
+void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) {
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) {
+ HBasicBlock* block = instruction->GetBlock();
+ if (block->GetLoopInformation() != nullptr) {
+ DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
+ // The back edge will generate the suspend check.
+ return;
+ }
+ if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
+ // The goto will generate the suspend check.
+ return;
+ }
+ GenerateSuspendCheck(instruction, nullptr);
+}
+
+void InstructionCodeGeneratorARM::GenerateSuspendCheck(HSuspendCheck* instruction,
+ HBasicBlock* successor) {
+ SuspendCheckSlowPathARM* slow_path =
+ new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction, successor);
+ codegen_->AddSlowPath(slow_path);
+
+ __ AddConstant(R4, R4, -1);
+ __ cmp(R4, ShifterOperand(0));
+ if (successor == nullptr) {
+ __ b(slow_path->GetEntryLabel(), LE);
+ __ Bind(slow_path->GetReturnLabel());
+ } else {
+ __ b(codegen_->GetLabelOf(successor), GT);
+ __ b(slow_path->GetEntryLabel());
+ }
+}
+
ArmAssembler* ParallelMoveResolverARM::GetAssembler() const {
return codegen_->GetAssembler();
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 610625c..8c86b7a 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -83,7 +83,7 @@
class LocationsBuilderARM : public HGraphVisitor {
public:
- explicit LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
+ LocationsBuilderARM(HGraph* graph, CodeGeneratorARM* codegen)
: HGraphVisitor(graph), codegen_(codegen) {}
#define DECLARE_VISIT_INSTRUCTION(name) \
@@ -93,6 +93,8 @@
#undef DECLARE_VISIT_INSTRUCTION
+ void HandleInvoke(HInvoke* invoke);
+
private:
CodeGeneratorARM* const codegen_;
InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -115,6 +117,11 @@
void LoadCurrentMethod(Register reg);
private:
+ // Generate code for the given suspend check. If not null, `successor`
+ // is the block to branch to if the suspend check is not needed, and after
+ // the suspend call.
+ void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
+
ArmAssembler* const assembler_;
CodeGeneratorARM* const codegen_;
@@ -124,12 +131,14 @@
class CodeGeneratorARM : public CodeGenerator {
public:
explicit CodeGeneratorARM(HGraph* graph);
- virtual ~CodeGeneratorARM() { }
+ virtual ~CodeGeneratorARM() {}
virtual void GenerateFrameEntry() OVERRIDE;
virtual void GenerateFrameExit() OVERRIDE;
virtual void Bind(Label* label) OVERRIDE;
virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+ virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
+ virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
virtual size_t GetWordSize() const OVERRIDE {
return kArmWordSize;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f544d47..ea67dfd 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -15,17 +15,18 @@
*/
#include "code_generator_x86.h"
+
+#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
+#include "mirror/array.h"
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "thread.h"
#include "utils/assembler.h"
#include "utils/stack_checks.h"
#include "utils/x86/assembler_x86.h"
#include "utils/x86/managed_register_x86.h"
-#include "entrypoints/quick/quick_entrypoints.h"
-#include "mirror/array.h"
-#include "mirror/art_method.h"
-#include "thread.h"
-
namespace art {
x86::X86ManagedRegister Location::AsX86() const {
@@ -61,16 +62,16 @@
class NullCheckSlowPathX86 : public SlowPathCode {
public:
- explicit NullCheckSlowPathX86(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+ explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowNullPointer)));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
};
@@ -91,10 +92,10 @@
class BoundsCheckSlowPathX86 : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathX86(uint32_t dex_pc,
- Location index_location,
- Location length_location)
- : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+ BoundsCheckSlowPathX86(HBoundsCheck* instruction,
+ Location index_location,
+ Location length_location)
+ : instruction_(instruction), index_location_(index_location), length_location_(length_location) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86* x86_codegen = reinterpret_cast<CodeGeneratorX86*>(codegen);
@@ -103,17 +104,48 @@
x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(0)), index_location_);
x86_codegen->Move32(X86CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowArrayBounds)));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HBoundsCheck* const instruction_;
const Location index_location_;
const Location length_location_;
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86);
};
+class SuspendCheckSlowPathX86 : public SlowPathCode {
+ public:
+ explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor)
+ : instruction_(instruction), successor_(successor) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pTestSuspend)));
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ if (successor_ == nullptr) {
+ __ jmp(GetReturnLabel());
+ } else {
+ __ jmp(codegen->GetLabelOf(successor_));
+ }
+ }
+
+ Label* GetReturnLabel() {
+ DCHECK(successor_ == nullptr);
+ return &return_label_;
+ }
+
+ private:
+ HSuspendCheck* const instruction_;
+ HBasicBlock* const successor_;
+ Label return_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86);
+};
+
#undef __
#define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
@@ -139,6 +171,14 @@
stream << X86ManagedRegister::FromXmmRegister(XmmRegister(reg));
}
+void CodeGeneratorX86::SaveCoreRegister(Location stack_location, uint32_t reg_id) {
+ __ movl(Address(ESP, stack_location.GetStackIndex()), static_cast<Register>(reg_id));
+}
+
+void CodeGeneratorX86::RestoreCoreRegister(Location stack_location, uint32_t reg_id) {
+ __ movl(static_cast<Register>(reg_id), Address(ESP, stack_location.GetStackIndex()));
+}
+
CodeGeneratorX86::CodeGeneratorX86(HGraph* graph)
: CodeGenerator(graph, kNumberOfRegIds),
location_builder_(graph, this),
@@ -244,7 +284,7 @@
bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
__ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
- RecordPcInfo(0);
+ RecordPcInfo(nullptr, 0);
}
// The return PC has already been pushed on the stack.
@@ -398,6 +438,7 @@
__ popl(Address(ESP, calling_convention.GetStackOffsetOf(argument_index + 1)));
}
} else {
+ DCHECK(destination.IsDoubleStackSlot());
if (source.IsRegister()) {
__ movl(Address(ESP, destination.GetStackIndex()), source.AsX86().AsRegisterPairLow());
__ movl(Address(ESP, destination.GetHighStackIndex(kX86WordSize)),
@@ -484,9 +525,22 @@
void InstructionCodeGeneratorX86::VisitGoto(HGoto* got) {
HBasicBlock* successor = got->GetSuccessor();
- if (GetGraph()->GetExitBlock() == successor) {
- codegen_->GenerateFrameExit();
- } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+ DCHECK(!successor->IsExitBlock());
+
+ HBasicBlock* block = got->GetBlock();
+ HInstruction* previous = got->GetPrevious();
+
+ HLoopInformation* info = block->GetLoopInformation();
+ if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+ GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+ return;
+ }
+
+ if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+ GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+ }
+ if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
__ jmp(codegen_->GetLabelOf(successor));
}
}
@@ -503,14 +557,14 @@
}
void LocationsBuilderX86::VisitIf(HIf* if_instr) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
HInstruction* cond = if_instr->InputAt(0);
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
locations->SetInAt(0, Location::Any());
}
- if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
@@ -518,14 +572,18 @@
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
- // Materialized condition, compare against 0
- Location lhs = if_instr->GetLocations()->InAt(0);
- if (lhs.IsRegister()) {
- __ cmpl(lhs.AsX86().AsCpuRegister(), Immediate(0));
- } else {
- __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+ // Moves do not affect the eflags register, so if the condition is evaluated
+ // just before the if, we don't need to evaluate it again.
+ if (!condition->IsBeforeWhenDisregardMoves(if_instr)) {
+ // Materialized condition, compare against 0
+ Location lhs = if_instr->GetLocations()->InAt(0);
+ if (lhs.IsRegister()) {
+ __ cmpl(lhs.AsX86().AsCpuRegister(), Immediate(0));
+ } else {
+ __ cmpl(Address(ESP, lhs.GetStackIndex()), Immediate(0));
+ }
}
- __ j(kEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+ __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
} else {
Location lhs = condition->GetLocations()->InAt(0);
Location rhs = condition->GetLocations()->InAt(1);
@@ -564,7 +622,8 @@
}
void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
switch (store->InputAt(1)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -589,18 +648,21 @@
}
void LocationsBuilderX86::VisitCondition(HCondition* comp) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
- comp->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
if (comp->NeedsMaterialization()) {
LocationSummary* locations = comp->GetLocations();
+ Register reg = locations->Out().AsX86().AsCpuRegister();
+ // Clear register: setcc only sets the low byte.
+ __ xorl(reg, reg);
if (locations->InAt(1).IsRegister()) {
__ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
locations->InAt(1).AsX86().AsCpuRegister());
@@ -612,7 +674,7 @@
__ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
Address(ESP, locations->InAt(1).GetStackIndex()));
}
- __ setb(X86Condition(comp->GetCondition()), locations->Out().AsX86().AsCpuRegister());
+ __ setb(X86Condition(comp->GetCondition()), reg);
}
}
@@ -665,18 +727,18 @@
}
void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
}
void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitLongConstant(HLongConstant* constant) {
@@ -693,7 +755,8 @@
}
void LocationsBuilderX86::VisitReturn(HReturn* ret) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
switch (ret->InputAt(0)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -712,7 +775,6 @@
default:
LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
}
- ret->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
@@ -740,8 +802,42 @@
}
void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
+ HandleInvoke(invoke);
+}
+
+void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+ Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister();
+ uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
+ size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
+ invoke->GetIndexInDexCache() * kX86WordSize;
+
+ // TODO: Implement all kinds of calls:
+ // 1) boot -> boot
+ // 2) app -> boot
+ // 3) app -> app
+ //
+ // Currently we implement the app -> app logic, which looks up in the resolve cache.
+
+ // temp = method;
+ LoadCurrentMethod(temp);
+ // temp = temp->dex_cache_resolved_methods_;
+ __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
+ // temp = temp[index_in_cache]
+ __ movl(temp, Address(temp, index_in_cache));
+ // (temp + offset_of_quick_compiled_code)()
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ HandleInvoke(invoke);
+}
+
+void LocationsBuilderX86::HandleInvoke(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
locations->AddTemp(X86CpuLocation(EAX));
InvokeDexCallingConventionVisitor calling_convention_visitor;
@@ -776,34 +872,32 @@
invoke->SetLocations(locations);
}
-void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister();
- uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
- size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
- invoke->GetIndexInDexCache() * kX86WordSize;
-
- // TODO: Implement all kinds of calls:
- // 1) boot -> boot
- // 2) app -> boot
- // 3) app -> app
- //
- // Currently we implement the app -> app logic, which looks up in the resolve cache.
-
- // temp = method;
- LoadCurrentMethod(temp);
- // temp = temp->dex_cache_resolved_methods_;
- __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
- // temp = temp[index_in_cache]
- __ movl(temp, Address(temp, index_in_cache));
- // (temp + offset_of_quick_compiled_code)()
+ uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() +
+ invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ // temp = object->GetClass();
+ if (receiver.IsStackSlot()) {
+ __ movl(temp, Address(ESP, receiver.GetStackIndex()));
+ __ movl(temp, Address(temp, class_offset));
+ } else {
+ __ movl(temp, Address(receiver.AsX86().AsCpuRegister(), class_offset));
+ }
+ // temp = temp->GetMethodAt(method_offset);
+ __ movl(temp, Address(temp, method_offset));
+ // call temp->GetEntryPoint();
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(invoke->GetDexPc());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderX86::VisitAdd(HAdd* add) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -823,7 +917,6 @@
default:
LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
}
- add->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
@@ -876,7 +969,8 @@
}
void LocationsBuilderX86::VisitSub(HSub* sub) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
@@ -896,7 +990,6 @@
default:
LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
}
- sub->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
@@ -949,13 +1042,12 @@
}
void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
locations->SetOut(X86CpuLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(0)));
locations->AddTemp(X86CpuLocation(calling_convention.GetRegisterAt(1)));
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
@@ -966,12 +1058,13 @@
__ fs()->call(
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
if (location.IsStackSlot()) {
location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -979,17 +1072,16 @@
location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
}
locations->SetOut(location);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitParameterValue(HParameterValue* instruction) {
}
void LocationsBuilderX86::VisitNot(HNot* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNot(HNot* instruction) {
@@ -1000,11 +1092,11 @@
}
void LocationsBuilderX86::VisitCompare(HCompare* compare) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
locations->SetOut(Location::RequiresRegister());
- compare->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
@@ -1050,12 +1142,12 @@
}
void LocationsBuilderX86::VisitPhi(HPhi* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
locations->SetInAt(i, Location::Any());
}
locations->SetOut(Location::Any());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitPhi(HPhi* instruction) {
@@ -1063,9 +1155,10 @@
}
void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
if (field_type == Primitive::kPrimBoolean || field_type == Primitive::kPrimByte) {
// Ensure the value is in a byte register.
locations->SetInAt(1, X86CpuLocation(EAX));
@@ -1078,14 +1171,13 @@
// Ensure the card is in a byte register.
locations->AddTemp(X86CpuLocation(ECX));
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsX86().AsCpuRegister();
uint32_t offset = instruction->GetFieldOffset().Uint32Value();
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -1144,10 +1236,10 @@
}
void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1205,16 +1297,15 @@
}
void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::Any());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction->GetDexPc());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
@@ -1231,11 +1322,11 @@
}
void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
@@ -1331,14 +1422,16 @@
}
void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction,
+ value_type == Primitive::kPrimNot ? LocationSummary::kCall : LocationSummary::kNoCall);
+
if (value_type == Primitive::kPrimNot) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, X86CpuLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, X86CpuLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, X86CpuLocation(calling_convention.GetRegisterAt(2)));
- codegen_->MarkNotLeaf();
} else {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
@@ -1349,15 +1442,13 @@
locations->SetInAt(2, Location::RequiresRegister());
}
}
-
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsX86().AsCpuRegister();
Location index = locations->InAt(1);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1401,7 +1492,7 @@
case Primitive::kPrimNot: {
DCHECK(!codegen_->IsLeafMethod());
__ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject)));
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
break;
}
@@ -1446,18 +1537,18 @@
}
void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86(
- instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
Register index = locations->InAt(0).AsX86().AsCpuRegister();
@@ -1483,6 +1574,40 @@
codegen_->GetMoveResolver()->EmitNativeCode(instruction);
}
+void LocationsBuilderX86::VisitSuspendCheck(HSuspendCheck* instruction) {
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction) {
+ HBasicBlock* block = instruction->GetBlock();
+ if (block->GetLoopInformation() != nullptr) {
+ DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
+ // The back edge will generate the suspend check.
+ return;
+ }
+ if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
+ // The goto will generate the suspend check.
+ return;
+ }
+ GenerateSuspendCheck(instruction, nullptr);
+}
+
+void InstructionCodeGeneratorX86::GenerateSuspendCheck(HSuspendCheck* instruction,
+ HBasicBlock* successor) {
+ SuspendCheckSlowPathX86* slow_path =
+ new (GetGraph()->GetArena()) SuspendCheckSlowPathX86(instruction, successor);
+ codegen_->AddSlowPath(slow_path);
+ __ fs()->cmpw(Address::Absolute(
+ Thread::ThreadFlagsOffset<kX86WordSize>().Int32Value()), Immediate(0));
+ if (successor == nullptr) {
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetReturnLabel());
+ } else {
+ __ j(kEqual, codegen_->GetLabelOf(successor));
+ __ jmp(slow_path->GetEntryLabel());
+ }
+}
+
X86Assembler* ParallelMoveResolverX86::GetAssembler() const {
return codegen_->GetAssembler();
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 7c50204..23145bf 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -94,6 +94,8 @@
#undef DECLARE_VISIT_INSTRUCTION
+ void HandleInvoke(HInvoke* invoke);
+
private:
CodeGeneratorX86* const codegen_;
InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -117,6 +119,11 @@
X86Assembler* GetAssembler() const { return assembler_; }
private:
+ // Generate code for the given suspend check. If not null, `successor`
+ // is the block to branch to if the suspend check is not needed, and after
+ // the suspend call.
+ void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor);
+
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
@@ -132,6 +139,8 @@
virtual void GenerateFrameExit() OVERRIDE;
virtual void Bind(Label* label) OVERRIDE;
virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+ virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
+ virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
virtual size_t GetWordSize() const OVERRIDE {
return kX86WordSize;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e1807dc..78c7d9d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -20,6 +20,7 @@
#include "gc/accounting/card_table.h"
#include "mirror/array.h"
#include "mirror/art_method.h"
+#include "mirror/class.h"
#include "mirror/object_reference.h"
#include "thread.h"
#include "utils/assembler.h"
@@ -35,7 +36,7 @@
namespace x86_64 {
-static constexpr bool kExplicitStackOverflowCheck = true;
+static constexpr bool kExplicitStackOverflowCheck = false;
// Some x86_64 instructions require a register to be available as temp.
static constexpr Register TMP = R11;
@@ -65,17 +66,17 @@
class NullCheckSlowPathX86_64 : public SlowPathCode {
public:
- explicit NullCheckSlowPathX86_64(uint32_t dex_pc) : dex_pc_(dex_pc) {}
+ explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
__ gs()->call(
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
};
@@ -95,12 +96,45 @@
DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
};
+class SuspendCheckSlowPathX86_64 : public SlowPathCode {
+ public:
+ explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
+ : instruction_(instruction), successor_(successor) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+ __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pTestSuspend), true));
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ if (successor_ == nullptr) {
+ __ jmp(GetReturnLabel());
+ } else {
+ __ jmp(codegen->GetLabelOf(successor_));
+ }
+ }
+
+ Label* GetReturnLabel() {
+ DCHECK(successor_ == nullptr);
+ return &return_label_;
+ }
+
+ private:
+ HSuspendCheck* const instruction_;
+ HBasicBlock* const successor_;
+ Label return_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64);
+};
+
class BoundsCheckSlowPathX86_64 : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathX86_64(uint32_t dex_pc,
- Location index_location,
- Location length_location)
- : dex_pc_(dex_pc), index_location_(index_location), length_location_(length_location) {}
+ BoundsCheckSlowPathX86_64(HBoundsCheck* instruction,
+ Location index_location,
+ Location length_location)
+ : instruction_(instruction),
+ index_location_(index_location),
+ length_location_(length_location) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86_64* x64_codegen = reinterpret_cast<CodeGeneratorX86_64*>(codegen);
@@ -110,11 +144,11 @@
x64_codegen->Move(X86_64CpuLocation(calling_convention.GetRegisterAt(1)), length_location_);
__ gs()->call(Address::Absolute(
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
- codegen->RecordPcInfo(dex_pc_);
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
}
private:
- const uint32_t dex_pc_;
+ HBoundsCheck* const instruction_;
const Location index_location_;
const Location length_location_;
@@ -146,6 +180,14 @@
stream << X86_64ManagedRegister::FromXmmRegister(FloatRegister(reg));
}
+void CodeGeneratorX86_64::SaveCoreRegister(Location stack_location, uint32_t reg_id) {
+ __ movq(Address(CpuRegister(RSP), stack_location.GetStackIndex()), CpuRegister(reg_id));
+}
+
+void CodeGeneratorX86_64::RestoreCoreRegister(Location stack_location, uint32_t reg_id) {
+ __ movq(CpuRegister(reg_id), Address(CpuRegister(RSP), stack_location.GetStackIndex()));
+}
+
CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph)
: CodeGenerator(graph, kNumberOfRegIds),
location_builder_(graph, this),
@@ -208,25 +250,26 @@
static const int kFakeReturnRegister = 16;
core_spill_mask_ |= (1 << kFakeReturnRegister);
+ bool skip_overflow_check = IsLeafMethod()
+ && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
+
+ if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+ __ testq(CpuRegister(RAX), Address(
+ CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
+ RecordPcInfo(nullptr, 0);
+ }
+
// The return PC has already been pushed on the stack.
__ subq(CpuRegister(RSP),
Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
- bool skip_overflow_check = IsLeafMethod()
- && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
+ if (!skip_overflow_check && kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
+ AddSlowPath(slow_path);
- if (!skip_overflow_check) {
- if (kExplicitStackOverflowCheck) {
- SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
- AddSlowPath(slow_path);
-
- __ gs()->cmpq(CpuRegister(RSP),
- Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
- __ j(kLess, slow_path->GetEntryLabel());
- } else {
- __ testq(CpuRegister(RAX), Address(
- CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
- }
+ __ gs()->cmpq(CpuRegister(RSP),
+ Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
+ __ j(kLess, slow_path->GetEntryLabel());
}
__ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
@@ -365,9 +408,22 @@
void InstructionCodeGeneratorX86_64::VisitGoto(HGoto* got) {
HBasicBlock* successor = got->GetSuccessor();
- if (GetGraph()->GetExitBlock() == successor) {
- codegen_->GenerateFrameExit();
- } else if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
+ DCHECK(!successor->IsExitBlock());
+
+ HBasicBlock* block = got->GetBlock();
+ HInstruction* previous = got->GetPrevious();
+
+ HLoopInformation* info = block->GetLoopInformation();
+ if (info != nullptr && info->IsBackEdge(block) && info->HasSuspendCheck()) {
+ codegen_->ClearSpillSlotsFromLoopPhisInStackMap(info->GetSuspendCheck());
+ GenerateSuspendCheck(info->GetSuspendCheck(), successor);
+ return;
+ }
+
+ if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
+ GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
+ }
+ if (!codegen_->GoesToNextBlock(got->GetBlock(), successor)) {
__ jmp(codegen_->GetLabelOf(successor));
}
}
@@ -384,14 +440,14 @@
}
void LocationsBuilderX86_64::VisitIf(HIf* if_instr) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(if_instr);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(if_instr, LocationSummary::kNoCall);
HInstruction* cond = if_instr->InputAt(0);
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
locations->SetInAt(0, Location::Any());
}
- if_instr->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
@@ -399,14 +455,18 @@
DCHECK(cond->IsCondition());
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
- // Materialized condition, compare against 0.
- Location lhs = if_instr->GetLocations()->InAt(0);
- if (lhs.IsRegister()) {
- __ cmpl(lhs.AsX86_64().AsCpuRegister(), Immediate(0));
- } else {
- __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0));
+ // Moves do not affect the eflags register, so if the condition is evaluated
+ // just before the if, we don't need to evaluate it again.
+ if (!condition->IsBeforeWhenDisregardMoves(if_instr)) {
+ // Materialized condition, compare against 0.
+ Location lhs = if_instr->GetLocations()->InAt(0);
+ if (lhs.IsRegister()) {
+ __ cmpl(lhs.AsX86_64().AsCpuRegister(), Immediate(0));
+ } else {
+ __ cmpl(Address(CpuRegister(RSP), lhs.GetStackIndex()), Immediate(0));
+ }
}
- __ j(kEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
+ __ j(kNotEqual, codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
} else {
Location lhs = condition->GetLocations()->InAt(0);
Location rhs = condition->GetLocations()->InAt(1);
@@ -443,7 +503,8 @@
}
void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
switch (store->InputAt(1)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -461,25 +522,27 @@
default:
LOG(FATAL) << "Unimplemented local type " << store->InputAt(1)->GetType();
}
- store->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) {
}
void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(comp, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
- comp->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
if (comp->NeedsMaterialization()) {
LocationSummary* locations = comp->GetLocations();
+ CpuRegister reg = locations->Out().AsX86_64().AsCpuRegister();
+ // Clear register: setcc only sets the low byte.
+ __ xorq(reg, reg);
if (locations->InAt(1).IsRegister()) {
__ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
locations->InAt(1).AsX86_64().AsCpuRegister());
@@ -490,8 +553,7 @@
__ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
}
- __ setcc(X86_64Condition(comp->GetCondition()),
- comp->GetLocations()->Out().AsX86_64().AsCpuRegister());
+ __ setcc(X86_64Condition(comp->GetCondition()), reg);
}
}
@@ -544,11 +606,11 @@
}
void LocationsBuilderX86_64::VisitCompare(HCompare* compare) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- compare->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
@@ -577,18 +639,18 @@
}
void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) {
}
void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
locations->SetOut(Location::ConstantLocation(constant));
- constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) {
@@ -604,7 +666,8 @@
}
void LocationsBuilderX86_64::VisitReturn(HReturn* ret) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ret);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(ret, LocationSummary::kNoCall);
switch (ret->InputAt(0)->GetType()) {
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
@@ -619,7 +682,6 @@
default:
LOG(FATAL) << "Unimplemented return type " << ret->InputAt(0)->GetType();
}
- ret->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitReturn(HReturn* ret) {
@@ -685,37 +747,7 @@
}
void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
- locations->AddTemp(X86_64CpuLocation(RDI));
-
- InvokeDexCallingConventionVisitor calling_convention_visitor;
- for (size_t i = 0; i < invoke->InputCount(); ++i) {
- HInstruction* input = invoke->InputAt(i);
- locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
- }
-
- switch (invoke->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimLong:
- locations->SetOut(X86_64CpuLocation(RAX));
- break;
-
- case Primitive::kPrimVoid:
- break;
-
- case Primitive::kPrimDouble:
- case Primitive::kPrimFloat:
- LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
- break;
- }
-
- invoke->SetLocations(locations);
+ HandleInvoke(invoke);
}
void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
@@ -741,11 +773,71 @@
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(invoke->GetDexPc());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ HandleInvoke(invoke);
+}
+
+void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall);
+ locations->AddTemp(X86_64CpuLocation(RDI));
+
+ InvokeDexCallingConventionVisitor calling_convention_visitor;
+ for (size_t i = 0; i < invoke->InputCount(); i++) {
+ HInstruction* input = invoke->InputAt(i);
+ locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType()));
+ }
+
+ switch (invoke->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot:
+ case Primitive::kPrimLong:
+ locations->SetOut(X86_64CpuLocation(RAX));
+ break;
+
+ case Primitive::kPrimVoid:
+ break;
+
+ case Primitive::kPrimDouble:
+ case Primitive::kPrimFloat:
+ LOG(FATAL) << "Unimplemented return type " << invoke->GetType();
+ break;
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsX86_64().AsCpuRegister();
+ size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() +
+ invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry);
+ LocationSummary* locations = invoke->GetLocations();
+ Location receiver = locations->InAt(0);
+ size_t class_offset = mirror::Object::ClassOffset().SizeValue();
+ // temp = object->GetClass();
+ if (receiver.IsStackSlot()) {
+ __ movq(temp, Address(CpuRegister(RSP), receiver.GetStackIndex()));
+ __ movq(temp, Address(temp, class_offset));
+ } else {
+ __ movq(temp, Address(receiver.AsX86_64().AsCpuRegister(), class_offset));
+ }
+ // temp = temp->GetMethodAt(method_offset);
+ __ movl(temp, Address(temp, method_offset));
+ // call temp->GetEntryPoint();
+ __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+
+ DCHECK(!codegen_->IsLeafMethod());
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
void LocationsBuilderX86_64::VisitAdd(HAdd* add) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(add, LocationSummary::kNoCall);
switch (add->GetResultType()) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -770,7 +862,6 @@
default:
LOG(FATAL) << "Unimplemented add type " << add->GetResultType();
}
- add->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
@@ -811,7 +902,8 @@
}
void LocationsBuilderX86_64::VisitSub(HSub* sub) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(sub, LocationSummary::kNoCall);
switch (sub->GetResultType()) {
case Primitive::kPrimInt: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -836,7 +928,6 @@
default:
LOG(FATAL) << "Unimplemented sub type " << sub->GetResultType();
}
- sub->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
@@ -877,10 +968,9 @@
}
void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
- codegen_->MarkNotLeaf();
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall);
locations->SetOut(X86_64CpuLocation(RAX));
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
@@ -892,11 +982,12 @@
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
}
void LocationsBuilderX86_64::VisitParameterValue(HParameterValue* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
if (location.IsStackSlot()) {
location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
@@ -904,7 +995,6 @@
location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
}
locations->SetOut(location);
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitParameterValue(HParameterValue* instruction) {
@@ -912,10 +1002,10 @@
}
void LocationsBuilderX86_64::VisitNot(HNot* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitNot(HNot* instruction) {
@@ -926,12 +1016,12 @@
}
void LocationsBuilderX86_64::VisitPhi(HPhi* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
for (size_t i = 0, e = instruction->InputCount(); i < e; ++i) {
locations->SetInAt(i, Location::Any());
}
locations->SetOut(Location::Any());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitPhi(HPhi* instruction) {
@@ -939,15 +1029,15 @@
}
void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Temporary registers for the write barrier.
- if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ if (instruction->GetFieldType() == Primitive::kPrimNot) {
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
@@ -955,7 +1045,7 @@
CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
CpuRegister value = locations->InAt(1).AsX86_64().AsCpuRegister();
size_t offset = instruction->GetFieldOffset().SizeValue();
- Primitive::Type field_type = instruction->InputAt(1)->GetType();
+ Primitive::Type field_type = instruction->GetFieldType();
switch (field_type) {
case Primitive::kPrimBoolean:
@@ -996,10 +1086,10 @@
}
void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -1050,16 +1140,15 @@
}
void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::Any());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction->GetDexPc());
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
@@ -1076,11 +1165,11 @@
}
void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
@@ -1173,27 +1262,27 @@
}
void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
- if (value_type == Primitive::kPrimNot) {
+ Primitive::Type value_type = instruction->GetComponentType();
+ bool is_object = value_type == Primitive::kPrimNot;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
+ instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall);
+ if (is_object) {
InvokeRuntimeCallingConvention calling_convention;
locations->SetInAt(0, X86_64CpuLocation(calling_convention.GetRegisterAt(0)));
locations->SetInAt(1, X86_64CpuLocation(calling_convention.GetRegisterAt(1)));
locations->SetInAt(2, X86_64CpuLocation(calling_convention.GetRegisterAt(2)));
- codegen_->MarkNotLeaf();
} else {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
locations->SetInAt(2, Location::RequiresRegister());
}
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) {
LocationSummary* locations = instruction->GetLocations();
CpuRegister obj = locations->InAt(0).AsX86_64().AsCpuRegister();
Location index = locations->InAt(1);
- Primitive::Type value_type = instruction->InputAt(2)->GetType();
+ Primitive::Type value_type = instruction->GetComponentType();
switch (value_type) {
case Primitive::kPrimBoolean:
@@ -1237,7 +1326,7 @@
case Primitive::kPrimNot: {
__ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true));
DCHECK(!codegen_->IsLeafMethod());
- codegen_->RecordPcInfo(instruction->GetDexPc());
+ codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
break;
}
@@ -1263,10 +1352,10 @@
}
void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) {
@@ -1278,18 +1367,18 @@
}
void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// TODO: Have a normalization phase that makes this instruction never used.
locations->SetOut(Location::SameAsFirstInput());
- instruction->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) {
LocationSummary* locations = instruction->GetLocations();
SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(
- instruction->GetDexPc(), locations->InAt(0), locations->InAt(1));
+ instruction, locations->InAt(0), locations->InAt(1));
codegen_->AddSlowPath(slow_path);
CpuRegister index = locations->InAt(0).AsX86_64().AsCpuRegister();
@@ -1330,6 +1419,40 @@
codegen_->GetMoveResolver()->EmitNativeCode(instruction);
}
+void LocationsBuilderX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath);
+}
+
+void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instruction) {
+ HBasicBlock* block = instruction->GetBlock();
+ if (block->GetLoopInformation() != nullptr) {
+ DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
+ // The back edge will generate the suspend check.
+ return;
+ }
+ if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
+ // The goto will generate the suspend check.
+ return;
+ }
+ GenerateSuspendCheck(instruction, nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::GenerateSuspendCheck(HSuspendCheck* instruction,
+ HBasicBlock* successor) {
+ SuspendCheckSlowPathX86_64* slow_path =
+ new (GetGraph()->GetArena()) SuspendCheckSlowPathX86_64(instruction, successor);
+ codegen_->AddSlowPath(slow_path);
+ __ gs()->cmpw(Address::Absolute(
+ Thread::ThreadFlagsOffset<kX86_64WordSize>().Int32Value(), true), Immediate(0));
+ if (successor == nullptr) {
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetReturnLabel());
+ } else {
+ __ j(kEqual, codegen_->GetLabelOf(successor));
+ __ jmp(slow_path->GetEntryLabel());
+ }
+}
+
X86_64Assembler* ParallelMoveResolverX86_64::GetAssembler() const {
return codegen_->GetAssembler();
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 44552ea..a299cf6 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -91,6 +91,8 @@
#undef DECLARE_VISIT_INSTRUCTION
+ void HandleInvoke(HInvoke* invoke);
+
private:
CodeGeneratorX86_64* const codegen_;
InvokeDexCallingConventionVisitor parameter_visitor_;
@@ -114,6 +116,11 @@
X86_64Assembler* GetAssembler() const { return assembler_; }
private:
+ // Generate code for the given suspend check. If not null, `successor`
+ // is the block to branch to if the suspend check is not needed, and after
+ // the suspend call.
+ void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
+
X86_64Assembler* const assembler_;
CodeGeneratorX86_64* const codegen_;
@@ -129,6 +136,8 @@
virtual void GenerateFrameExit() OVERRIDE;
virtual void Bind(Label* label) OVERRIDE;
virtual void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
+ virtual void SaveCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
+ virtual void RestoreCoreRegister(Location stack_location, uint32_t reg_id) OVERRIDE;
virtual size_t GetWordSize() const OVERRIDE {
return kX86_64WordSize;
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index d7ac10d..7161eed 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -15,7 +15,9 @@
*/
#include "builder.h"
-#include "code_generator.h"
+#include "code_generator_arm.h"
+#include "code_generator_x86.h"
+#include "code_generator_x86_64.h"
#include "common_compiler_test.h"
#include "dex_file.h"
#include "dex_instruction.h"
@@ -47,7 +49,6 @@
DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator);
};
-#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
static void Run(const InternalCodeAllocator& allocator,
const CodeGenerator& codegen,
bool has_result,
@@ -64,7 +65,6 @@
CHECK_EQ(result, expected);
}
}
-#endif
static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) {
ArenaPool pool;
@@ -72,28 +72,30 @@
HGraphBuilder builder(&arena);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
+ // Remove suspend checks, they cannot be executed in this context.
+ RemoveSuspendChecks(graph);
ASSERT_NE(graph, nullptr);
InternalCodeAllocator allocator;
- CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86);
+ x86::CodeGeneratorX86 codegenX86(graph);
// We avoid doing a stack overflow check that requires the runtime being setup,
// by making sure the compiler knows the methods we are running are leaf methods.
- codegen->CompileBaseline(&allocator, true);
-#if defined(__i386__)
- Run(allocator, *codegen, has_result, expected);
-#endif
+ codegenX86.CompileBaseline(&allocator, true);
+ if (kRuntimeISA == kX86) {
+ Run(allocator, codegenX86, has_result, expected);
+ }
- codegen = CodeGenerator::Create(&arena, graph, kArm);
- codegen->CompileBaseline(&allocator, true);
-#if defined(__arm__)
- Run(allocator, *codegen, has_result, expected);
-#endif
+ arm::CodeGeneratorARM codegenARM(graph);
+ codegenARM.CompileBaseline(&allocator, true);
+ if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) {
+ Run(allocator, codegenARM, has_result, expected);
+ }
- codegen = CodeGenerator::Create(&arena, graph, kX86_64);
- codegen->CompileBaseline(&allocator, true);
-#if defined(__x86_64__)
- Run(allocator, *codegen, has_result, expected);
-#endif
+ x86_64::CodeGeneratorX86_64 codegenX86_64(graph);
+ codegenX86_64.CompileBaseline(&allocator, true);
+ if (kRuntimeISA == kX86_64) {
+ Run(allocator, codegenX86_64, has_result, expected);
+ }
}
TEST(CodegenTest, ReturnVoid) {
diff --git a/compiler/optimizing/constant_propagation.cc b/compiler/optimizing/constant_propagation.cc
new file mode 100644
index 0000000..d675164
--- /dev/null
+++ b/compiler/optimizing/constant_propagation.cc
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include "constant_propagation.h"
+
+namespace art {
+
+void ConstantPropagation::Run() {
+ // Process basic blocks in reverse post-order in the dominator tree,
+ // so that an instruction turned into a constant, used as input of
+ // another instruction, may possibly be used to turn that second
+ // instruction into a constant as well.
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ // Traverse this block's instructions in (forward) order and
+ // replace the ones that can be statically evaluated by a
+ // compile-time counterpart.
+ for (HInstructionIterator it(block->GetInstructions());
+ !it.Done(); it.Advance()) {
+ HInstruction* inst = it.Current();
+ // Constant folding: replace `c <- a op b' with a compile-time
+ // evaluation of `a op b' if `a' and `b' are constant.
+ if (inst->IsBinaryOperation()) {
+ HConstant* constant =
+ inst->AsBinaryOperation()->TryStaticEvaluation(graph_->GetArena());
+ if (constant != nullptr) {
+ inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant);
+ }
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/constant_propagation.h b/compiler/optimizing/constant_propagation.h
new file mode 100644
index 0000000..0729881
--- /dev/null
+++ b/compiler/optimizing/constant_propagation.h
@@ -0,0 +1,43 @@
+/*
+ * 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_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_
+#define ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Optimization pass performing a simple constant propagation on the
+ * SSA form.
+ */
+class ConstantPropagation : public ValueObject {
+ public:
+ explicit ConstantPropagation(HGraph* graph)
+ : graph_(graph) {}
+
+ void Run();
+
+ private:
+ HGraph* const graph_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstantPropagation);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_CONSTANT_PROPAGATION_H_
diff --git a/compiler/optimizing/constant_propagation_test.cc b/compiler/optimizing/constant_propagation_test.cc
new file mode 100644
index 0000000..5c8c709
--- /dev/null
+++ b/compiler/optimizing/constant_propagation_test.cc
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ */
+
+#include "constant_propagation.h"
+#include "dead_code_elimination.h"
+#include "pretty_printer.h"
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void TestCode(const uint16_t* data,
+ const std::string& expected_before,
+ const std::string& expected_after_cp,
+ const std::string& expected_after_dce) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = CreateCFG(&allocator, data);
+ ASSERT_NE(graph, nullptr);
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+
+ StringPrettyPrinter printer_before(graph);
+ printer_before.VisitInsertionOrder();
+ std::string actual_before = printer_before.str();
+ ASSERT_EQ(expected_before, actual_before);
+
+ ConstantPropagation(graph).Run();
+
+ StringPrettyPrinter printer_after_cp(graph);
+ printer_after_cp.VisitInsertionOrder();
+ std::string actual_after_cp = printer_after_cp.str();
+ ASSERT_EQ(expected_after_cp, actual_after_cp);
+
+ DeadCodeElimination(graph).Run();
+
+ StringPrettyPrinter printer_after_dce(graph);
+ printer_after_dce.VisitInsertionOrder();
+ std::string actual_after_dce = printer_after_dce.str();
+ ASSERT_EQ(expected_after_dce, actual_after_dce);
+
+ SSAChecker ssa_checker(&allocator, graph);
+ ssa_checker.VisitInsertionOrder();
+ ASSERT_TRUE(ssa_checker.IsValid());
+}
+
+
+/**
+ * Tiny three-register program exercising int constant folding on addition.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v0 <- 1 0. const/4 v0, #+1
+ * v1 <- 2 1. const/4 v1, #+2
+ * v2 <- v0 + v1 2. add-int v2, v0, v1
+ * return v2 4. return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingOnAddition1) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 << 8 | 1 << 12,
+ Instruction::CONST_4 | 1 << 8 | 2 << 12,
+ Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::RETURN | 2 << 8);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9]\n"
+ " 5: IntConstant [9]\n"
+ " 14: SuspendCheck\n"
+ " 15: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 9: Add(3, 5) [12]\n"
+ " 12: Return(9)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 13: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
+ { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
+ { " 9: Add(3, 5) [12]\n", " 16: IntConstant [12]\n" },
+ { " 12: Return(9)\n", " 12: Return(16)\n" }
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 3: IntConstant\n", removed },
+ { " 5: IntConstant\n", removed }
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Small three-register program exercising int constant folding on addition.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v0 <- 1 0. const/4 v0, #+1
+ * v1 <- 2 1. const/4 v1, #+2
+ * v0 <- v0 + v1 2. add-int/2addr v0, v1
+ * v1 <- 3 3. const/4 v1, #+3
+ * v2 <- 4 4. const/4 v2, #+4
+ * v1 <- v1 + v2 5. add-int/2addr v1, v2
+ * v2 <- v0 + v1 6. add-int v2, v0, v1
+ * return v2 8. return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingOnAddition2) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 << 8 | 1 << 12,
+ Instruction::CONST_4 | 1 << 8 | 2 << 12,
+ Instruction::ADD_INT_2ADDR | 0 << 8 | 1 << 12,
+ Instruction::CONST_4 | 1 << 8 | 3 << 12,
+ Instruction::CONST_4 | 2 << 8 | 4 << 12,
+ Instruction::ADD_INT_2ADDR | 1 << 8 | 2 << 12,
+ Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::RETURN | 2 << 8);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9]\n"
+ " 5: IntConstant [9]\n"
+ " 11: IntConstant [17]\n"
+ " 13: IntConstant [17]\n"
+ " 26: SuspendCheck\n"
+ " 27: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 9: Add(3, 5) [21]\n"
+ " 17: Add(11, 13) [21]\n"
+ " 21: Add(9, 17) [24]\n"
+ " 24: Return(21)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 25: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
+ { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
+ { " 11: IntConstant [17]\n", " 11: IntConstant\n" },
+ { " 13: IntConstant [17]\n", " 13: IntConstant\n" },
+ { " 9: Add(3, 5) [21]\n", " 28: IntConstant\n" },
+ { " 17: Add(11, 13) [21]\n", " 29: IntConstant\n" },
+ { " 21: Add(9, 17) [24]\n", " 30: IntConstant [24]\n" },
+ { " 24: Return(21)\n", " 24: Return(30)\n" }
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 3: IntConstant\n", removed },
+ { " 5: IntConstant\n", removed },
+ { " 11: IntConstant\n", removed },
+ { " 13: IntConstant\n", removed },
+ { " 28: IntConstant\n", removed },
+ { " 29: IntConstant\n", removed }
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Tiny three-register program exercising int constant folding on subtraction.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v0 <- 3 0. const/4 v0, #+3
+ * v1 <- 2 1. const/4 v1, #+2
+ * v2 <- v0 - v1 2. sub-int v2, v0, v1
+ * return v2 4. return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingOnSubtraction) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 << 8 | 3 << 12,
+ Instruction::CONST_4 | 1 << 8 | 2 << 12,
+ Instruction::SUB_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::RETURN | 2 << 8);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9]\n"
+ " 5: IntConstant [9]\n"
+ " 14: SuspendCheck\n"
+ " 15: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 9: Sub(3, 5) [12]\n"
+ " 12: Return(9)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 13: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
+ { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
+ { " 9: Sub(3, 5) [12]\n", " 16: IntConstant [12]\n" },
+ { " 12: Return(9)\n", " 12: Return(16)\n" }
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 3: IntConstant\n", removed },
+ { " 5: IntConstant\n", removed }
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+#define SIX_REGISTERS_CODE_ITEM(...) \
+ { 6, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
+
+/**
+ * Tiny three-register-pair program exercising long constant folding
+ * on addition.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * (v0, v1) <- 1 0. const-wide/16 v0, #+1
+ * (v2, v3) <- 2 2. const-wide/16 v2, #+2
+ * (v4, v5) <-
+ * (v0, v1) + (v1, v2) 4. add-long v4, v0, v2
+ * return (v4, v5) 6. return-wide v4
+ */
+TEST(ConstantPropagation, LongConstantFoldingOnAddition) {
+ const uint16_t data[] = SIX_REGISTERS_CODE_ITEM(
+ Instruction::CONST_WIDE_16 | 0 << 8, 1,
+ Instruction::CONST_WIDE_16 | 2 << 8, 2,
+ Instruction::ADD_LONG | 4 << 8, 0 | 2 << 8,
+ Instruction::RETURN_WIDE | 4 << 8);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 6: LongConstant [12]\n"
+ " 8: LongConstant [12]\n"
+ " 17: SuspendCheck\n"
+ " 18: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 12: Add(6, 8) [15]\n"
+ " 15: Return(12)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 16: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 6: LongConstant [12]\n", " 6: LongConstant\n" },
+ { " 8: LongConstant [12]\n", " 8: LongConstant\n" },
+ { " 12: Add(6, 8) [15]\n", " 19: LongConstant [15]\n" },
+ { " 15: Return(12)\n", " 15: Return(19)\n" }
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 6: LongConstant\n", removed },
+ { " 8: LongConstant\n", removed }
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Tiny three-register-pair program exercising long constant folding
+ * on subtraction.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * (v0, v1) <- 3 0. const-wide/16 v0, #+3
+ * (v2, v3) <- 2 2. const-wide/16 v2, #+2
+ * (v4, v5) <-
+ * (v0, v1) - (v1, v2) 4. sub-long v4, v0, v2
+ * return (v4, v5) 6. return-wide v4
+ */
+TEST(ConstantPropagation, LongConstantFoldingOnSubtraction) {
+ const uint16_t data[] = SIX_REGISTERS_CODE_ITEM(
+ Instruction::CONST_WIDE_16 | 0 << 8, 3,
+ Instruction::CONST_WIDE_16 | 2 << 8, 2,
+ Instruction::SUB_LONG | 4 << 8, 0 | 2 << 8,
+ Instruction::RETURN_WIDE | 4 << 8);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 6: LongConstant [12]\n"
+ " 8: LongConstant [12]\n"
+ " 17: SuspendCheck\n"
+ " 18: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 12: Sub(6, 8) [15]\n"
+ " 15: Return(12)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 16: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 6: LongConstant [12]\n", " 6: LongConstant\n" },
+ { " 8: LongConstant [12]\n", " 8: LongConstant\n" },
+ { " 12: Sub(6, 8) [15]\n", " 19: LongConstant [15]\n" },
+ { " 15: Return(12)\n", " 15: Return(19)\n" }
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 6: LongConstant\n", removed },
+ { " 8: LongConstant\n", removed }
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+/**
+ * Three-register program with jumps leading to the creation of many
+ * blocks.
+ *
+ * The intent of this test is to ensure that all constant expressions
+ * are actually evaluated at compile-time, thanks to the reverse
+ * (forward) post-order traversal of the the dominator tree.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v0 <- 0 0. const/4 v0, #+0
+ * v1 <- 1 1. const/4 v1, #+1
+ * v2 <- v0 + v1 2. add-int v2, v0, v1
+ * goto L2 4. goto +4
+ * L1: v1 <- v0 + 3 5. add-int/lit16 v1, v0, #+3
+ * goto L3 7. goto +4
+ * L2: v0 <- v2 + 2 8. add-int/lit16 v0, v2, #+2
+ * goto L1 10. goto +(-5)
+ * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4
+ * return v2 13. return v2
+ */
+TEST(ConstantPropagation, IntConstantFoldingAndJumps) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 << 8 | 0 << 12,
+ Instruction::CONST_4 | 1 << 8 | 1 << 12,
+ Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::GOTO | 4 << 8,
+ Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 3,
+ Instruction::GOTO | 4 << 8,
+ Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 2,
+ static_cast<uint16_t>(Instruction::GOTO | -5 << 8),
+ Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 4,
+ Instruction::RETURN | 2 << 8);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9]\n"
+ " 5: IntConstant [9]\n"
+ " 13: IntConstant [14]\n"
+ " 18: IntConstant [19]\n"
+ " 24: IntConstant [25]\n"
+ " 30: SuspendCheck\n"
+ " 31: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
+ " 9: Add(3, 5) [19]\n"
+ " 11: Goto 3\n"
+ "BasicBlock 2, pred: 3, succ: 4\n"
+ " 14: Add(19, 13) [25]\n"
+ " 16: Goto 4\n"
+ "BasicBlock 3, pred: 1, succ: 2\n"
+ " 19: Add(9, 18) [14]\n"
+ " 21: SuspendCheck\n"
+ " 22: Goto 2\n"
+ "BasicBlock 4, pred: 2, succ: 5\n"
+ " 25: Add(14, 24) [28]\n"
+ " 28: Return(25)\n"
+ "BasicBlock 5, pred: 4\n"
+ " 29: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
+ { " 5: IntConstant [9]\n", " 5: IntConstant []\n" },
+ { " 13: IntConstant [14]\n", " 13: IntConstant\n" },
+ { " 18: IntConstant [19]\n", " 18: IntConstant\n" },
+ { " 24: IntConstant [25]\n", " 24: IntConstant\n" },
+ { " 9: Add(3, 5) [19]\n", " 32: IntConstant []\n" },
+ { " 14: Add(19, 13) [25]\n", " 34: IntConstant\n" },
+ { " 19: Add(9, 18) [14]\n", " 33: IntConstant []\n" },
+ { " 25: Add(14, 24) [28]\n", " 35: IntConstant [28]\n" },
+ { " 28: Return(25)\n", " 28: Return(35)\n"}
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 3: IntConstant\n", removed },
+ { " 13: IntConstant\n", removed },
+ { " 18: IntConstant\n", removed },
+ { " 24: IntConstant\n", removed },
+ { " 34: IntConstant\n", removed },
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+
+/**
+ * Three-register program with a constant (static) condition.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v1 <- 1 0. const/4 v1, #+1
+ * v0 <- 0 1. const/4 v0, #+0
+ * if v1 >= 0 goto L1 2. if-gez v1, +3
+ * v0 <- v1 4. move v0, v1
+ * L1: v2 <- v0 + v1 5. add-int v2, v0, v1
+ * return-void 7. return
+ */
+TEST(ConstantPropagation, ConstantCondition) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 1 << 8 | 1 << 12,
+ Instruction::CONST_4 | 0 << 8 | 0 << 12,
+ Instruction::IF_GEZ | 1 << 8, 3,
+ Instruction::MOVE | 0 << 8 | 1 << 12,
+ Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::RETURN_VOID);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [15, 22, 8]\n"
+ " 5: IntConstant [22, 8]\n"
+ " 19: SuspendCheck\n"
+ " 20: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5, 2\n"
+ " 8: GreaterThanOrEqual(3, 5) [9]\n"
+ " 9: If(8)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 12: Goto 3\n"
+ "BasicBlock 3, pred: 2, 5, succ: 4\n"
+ " 22: Phi(3, 5) [15]\n"
+ " 15: Add(22, 3)\n"
+ " 17: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 18: Exit\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 21: Goto 3\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_cp_diff = {
+ { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [15, 22]\n" },
+ { " 5: IntConstant [22, 8]\n", " 5: IntConstant [22]\n" },
+ { " 8: GreaterThanOrEqual(3, 5) [9]\n", " 23: IntConstant [9]\n" },
+ { " 9: If(8)\n", " 9: If(23)\n" }
+ };
+ std::string expected_after_cp = Patch(expected_before, expected_cp_diff);
+
+ // Expected difference after dead code elimination.
+ diff_t expected_dce_diff = {
+ { " 3: IntConstant [15, 22]\n", " 3: IntConstant [22]\n" },
+ { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" },
+ { " 15: Add(22, 3)\n", removed }
+ };
+ std::string expected_after_dce = Patch(expected_after_cp, expected_dce_diff);
+
+ TestCode(data, expected_before, expected_after_cp, expected_after_dce);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
new file mode 100644
index 0000000..fe2adc7
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#include "dead_code_elimination.h"
+
+#include "base/bit_vector-inl.h"
+
+namespace art {
+
+void DeadCodeElimination::Run() {
+ // Process basic blocks in post-order in the dominator tree, so that
+ // a dead instruction depending on another dead instruction is
+ // removed.
+ for (HPostOrderIterator b(*graph_); !b.Done(); b.Advance()) {
+ HBasicBlock* block = b.Current();
+ // Traverse this block's instructions in backward order and remove
+ // the unused ones.
+ HBackwardInstructionIterator i(block->GetInstructions());
+ // Skip the first iteration, as the last instruction of a block is
+ // a branching instruction.
+ DCHECK(i.Current()->IsControlFlow());
+ for (i.Advance(); !i.Done(); i.Advance()) {
+ HInstruction* inst = i.Current();
+ DCHECK(!inst->IsControlFlow());
+ if (!inst->HasSideEffects() && !inst->HasUses() && !inst->IsSuspendCheck()) {
+ block->RemoveInstruction(inst);
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h
new file mode 100644
index 0000000..48739be
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -0,0 +1,43 @@
+/*
+ * 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_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Optimization pass performing dead code elimination (removal of
+ * unused variables/instructions) on the SSA form.
+ */
+class DeadCodeElimination : public ValueObject {
+ public:
+ explicit DeadCodeElimination(HGraph* graph)
+ : graph_(graph) {}
+
+ void Run();
+
+ private:
+ HGraph* const graph_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
new file mode 100644
index 0000000..245bcb2
--- /dev/null
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include "dead_code_elimination.h"
+#include "pretty_printer.h"
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+static void TestCode(const uint16_t* data,
+ const std::string& expected_before,
+ const std::string& expected_after) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = CreateCFG(&allocator, data);
+ ASSERT_NE(graph, nullptr);
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+
+ StringPrettyPrinter printer_before(graph);
+ printer_before.VisitInsertionOrder();
+ std::string actual_before = printer_before.str();
+ ASSERT_EQ(actual_before, expected_before);
+
+ DeadCodeElimination(graph).Run();
+
+ StringPrettyPrinter printer_after(graph);
+ printer_after.VisitInsertionOrder();
+ std::string actual_after = printer_after.str();
+ ASSERT_EQ(actual_after, expected_after);
+
+ SSAChecker ssa_checker(&allocator, graph);
+ ssa_checker.VisitInsertionOrder();
+ ASSERT_TRUE(ssa_checker.IsValid());
+}
+
+
+/**
+ * Small three-register program.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v1 <- 1 0. const/4 v1, #+1
+ * v0 <- 0 1. const/4 v0, #+0
+ * if v1 >= 0 goto L1 2. if-gez v1, +3
+ * v0 <- v1 4. move v0, v1
+ * L1: v2 <- v0 + v1 5. add-int v2, v0, v1
+ * return-void 7. return
+ */
+TEST(DeadCodeElimination, AdditionAndConditionalJump) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 1 << 8 | 1 << 12,
+ Instruction::CONST_4 | 0 << 8 | 0 << 12,
+ Instruction::IF_GEZ | 1 << 8, 3,
+ Instruction::MOVE | 0 << 8 | 1 << 12,
+ Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::RETURN_VOID);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [15, 22, 8]\n"
+ " 5: IntConstant [22, 8]\n"
+ " 19: SuspendCheck\n"
+ " 20: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5, 2\n"
+ " 8: GreaterThanOrEqual(3, 5) [9]\n"
+ " 9: If(8)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 12: Goto 3\n"
+ "BasicBlock 3, pred: 2, 5, succ: 4\n"
+ " 22: Phi(3, 5) [15]\n"
+ " 15: Add(22, 3)\n"
+ " 17: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 18: Exit\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 21: Goto 3\n";
+
+ diff_t expected_diff = {
+ { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [22, 8]\n" },
+ { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" },
+ { " 15: Add(22, 3)\n", removed }
+ };
+ std::string expected_after = Patch(expected_before, expected_diff);
+
+ TestCode(data, expected_before, expected_after);
+}
+
+/**
+ * Three-register program with jumps leading to the creation of many
+ * blocks.
+ *
+ * The intent of this test is to ensure that all dead instructions are
+ * actually pruned at compile-time, thanks to the (backward)
+ * post-order traversal of the the dominator tree.
+ *
+ * 16-bit
+ * offset
+ * ------
+ * v0 <- 0 0. const/4 v0, #+0
+ * v1 <- 1 1. const/4 v1, #+1
+ * v2 <- v0 + v1 2. add-int v2, v0, v1
+ * goto L2 4. goto +4
+ * L1: v1 <- v0 + 3 5. add-int/lit16 v1, v0, #+3
+ * goto L3 7. goto +4
+ * L2: v0 <- v2 + 2 8. add-int/lit16 v0, v2, #+2
+ * goto L1 10. goto +(-5)
+ * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4
+ * return 13. return-void
+ */
+TEST(DeadCodeElimination, AdditionsAndInconditionalJumps) {
+ const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 << 8 | 0 << 12,
+ Instruction::CONST_4 | 1 << 8 | 1 << 12,
+ Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
+ Instruction::GOTO | 4 << 8,
+ Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 3,
+ Instruction::GOTO | 4 << 8,
+ Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 2,
+ static_cast<uint16_t>(Instruction::GOTO | -5 << 8),
+ Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 4,
+ Instruction::RETURN_VOID);
+
+ std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9]\n"
+ " 5: IntConstant [9]\n"
+ " 13: IntConstant [14]\n"
+ " 18: IntConstant [19]\n"
+ " 24: IntConstant [25]\n"
+ " 29: SuspendCheck\n"
+ " 30: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
+ " 9: Add(3, 5) [19]\n"
+ " 11: Goto 3\n"
+ "BasicBlock 2, pred: 3, succ: 4\n"
+ " 14: Add(19, 13) [25]\n"
+ " 16: Goto 4\n"
+ "BasicBlock 3, pred: 1, succ: 2\n"
+ " 19: Add(9, 18) [14]\n"
+ " 21: SuspendCheck\n"
+ " 22: Goto 2\n"
+ "BasicBlock 4, pred: 2, succ: 5\n"
+ " 25: Add(14, 24)\n"
+ " 27: ReturnVoid\n"
+ "BasicBlock 5, pred: 4\n"
+ " 28: Exit\n";
+
+ // Expected difference after constant propagation.
+ diff_t expected_diff = {
+ { " 13: IntConstant [14]\n", removed },
+ { " 24: IntConstant [25]\n", removed },
+ { " 14: Add(19, 13) [25]\n", removed },
+ // The SuspendCheck instruction following this Add instruction
+ // inserts the latter in an environment, thus making it "used" and
+ // therefore non removable. It ensues that some other Add and
+ // IntConstant instructions cannot be removed, as they are direct
+ // or indirect inputs of the initial Add instruction.
+ { " 19: Add(9, 18) [14]\n", " 19: Add(9, 18) []\n" },
+ { " 25: Add(14, 24)\n", removed },
+ };
+ std::string expected_after = Patch(expected_before, expected_diff);
+
+ TestCode(data, expected_before, expected_after);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index fab9f7a..c36b143 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -27,9 +27,8 @@
namespace art {
-static HGraph* TestCode(const uint16_t* data, ArenaPool* pool) {
- ArenaAllocator allocator(pool);
- HGraphBuilder builder(&allocator);
+static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) {
+ HGraphBuilder builder(allocator);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
graph->BuildDominatorTree();
@@ -44,7 +43,8 @@
Instruction::RETURN_VOID);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
}
@@ -56,7 +56,8 @@
Instruction::RETURN);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
}
@@ -71,7 +72,8 @@
Instruction::RETURN);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
}
@@ -87,7 +89,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
}
@@ -101,7 +104,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
ASSERT_EQ(graph->GetBlocks().Get(i)->GetLoopInformation(), nullptr);
}
@@ -146,7 +150,8 @@
Instruction::RETURN_VOID);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // pre header
@@ -173,7 +178,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // goto block
@@ -197,7 +203,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // goto block
@@ -222,7 +229,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // pre header
@@ -248,7 +256,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // pre header
@@ -271,9 +280,9 @@
Instruction::GOTO | 0xFB00,
Instruction::RETURN | 0 << 8);
-
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // pre header of outer loop
@@ -302,9 +311,9 @@
Instruction::GOTO | 0xFE00, // second loop
Instruction::RETURN | 0 << 8);
-
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // pre header of first loop
@@ -333,7 +342,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
ASSERT_TRUE(graph->GetBlocks().Get(3)->IsLoopHeader());
HLoopInformation* info = graph->GetBlocks().Get(3)->GetLoopInformation();
ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges().Get(0)));
@@ -347,7 +357,8 @@
Instruction::RETURN | 0 << 8);
ArenaPool arena;
- HGraph* graph = TestCode(data, &arena);
+ ArenaAllocator allocator(&arena);
+ HGraph* graph = TestCode(data, &allocator);
TestBlock(graph, 0, false, -1); // entry block
TestBlock(graph, 1, false, -1); // pre header of first loop
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
new file mode 100644
index 0000000..589b44a
--- /dev/null
+++ b/compiler/optimizing/graph_checker.cc
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+
+#include "graph_checker.h"
+
+#include <string>
+#include <map>
+#include <sstream>
+
+#include "base/bit_vector-inl.h"
+
+namespace art {
+
+void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
+ current_block_ = block;
+
+ // Check consistency with respect to predecessors of `block`.
+ const GrowableArray<HBasicBlock*>& predecessors = block->GetPredecessors();
+ std::map<HBasicBlock*, size_t> predecessors_count;
+ for (size_t i = 0, e = predecessors.Size(); i < e; ++i) {
+ HBasicBlock* p = predecessors.Get(i);
+ ++predecessors_count[p];
+ }
+ for (auto& pc : predecessors_count) {
+ HBasicBlock* p = pc.first;
+ size_t p_count_in_block_predecessors = pc.second;
+ const GrowableArray<HBasicBlock*>& p_successors = p->GetSuccessors();
+ size_t block_count_in_p_successors = 0;
+ for (size_t j = 0, f = p_successors.Size(); j < f; ++j) {
+ if (p_successors.Get(j) == block) {
+ ++block_count_in_p_successors;
+ }
+ }
+ if (p_count_in_block_predecessors != block_count_in_p_successors) {
+ std::stringstream error;
+ error << "Block " << block->GetBlockId()
+ << " lists " << p_count_in_block_predecessors
+ << " occurrences of block " << p->GetBlockId()
+ << " in its predecessors, whereas block " << p->GetBlockId()
+ << " lists " << block_count_in_p_successors
+ << " occurrences of block " << block->GetBlockId()
+ << " in its successors.";
+ errors_.Insert(error.str());
+ }
+ }
+
+ // Check consistency with respect to successors of `block`.
+ const GrowableArray<HBasicBlock*>& successors = block->GetSuccessors();
+ std::map<HBasicBlock*, size_t> successors_count;
+ for (size_t i = 0, e = successors.Size(); i < e; ++i) {
+ HBasicBlock* s = successors.Get(i);
+ ++successors_count[s];
+ }
+ for (auto& sc : successors_count) {
+ HBasicBlock* s = sc.first;
+ size_t s_count_in_block_successors = sc.second;
+ const GrowableArray<HBasicBlock*>& s_predecessors = s->GetPredecessors();
+ size_t block_count_in_s_predecessors = 0;
+ for (size_t j = 0, f = s_predecessors.Size(); j < f; ++j) {
+ if (s_predecessors.Get(j) == block) {
+ ++block_count_in_s_predecessors;
+ }
+ }
+ if (s_count_in_block_successors != block_count_in_s_predecessors) {
+ std::stringstream error;
+ error << "Block " << block->GetBlockId()
+ << " lists " << s_count_in_block_successors
+ << " occurrences of block " << s->GetBlockId()
+ << " in its successors, whereas block " << s->GetBlockId()
+ << " lists " << block_count_in_s_predecessors
+ << " occurrences of block " << block->GetBlockId()
+ << " in its predecessors.";
+ errors_.Insert(error.str());
+ }
+ }
+
+ // Ensure `block` ends with a branch instruction.
+ HInstruction* last_inst = block->GetLastInstruction();
+ if (last_inst == nullptr || !last_inst->IsControlFlow()) {
+ std::stringstream error;
+ error << "Block " << block->GetBlockId()
+ << " does not end with a branch instruction.";
+ errors_.Insert(error.str());
+ }
+
+ // Visit this block's list of phis.
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ // Ensure this block's list of phis contains only phis.
+ if (!it.Current()->IsPhi()) {
+ std::stringstream error;
+ error << "Block " << current_block_->GetBlockId()
+ << " has a non-phi in its phi list.";
+ errors_.Insert(error.str());
+ }
+ it.Current()->Accept(this);
+ }
+
+ // Visit this block's list of instructions.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done();
+ it.Advance()) {
+ // Ensure this block's list of instructions does not contains phis.
+ if (it.Current()->IsPhi()) {
+ std::stringstream error;
+ error << "Block " << current_block_->GetBlockId()
+ << " has a phi in its non-phi list.";
+ errors_.Insert(error.str());
+ }
+ it.Current()->Accept(this);
+ }
+}
+
+void GraphChecker::VisitInstruction(HInstruction* instruction) {
+ // Ensure `instruction` is associated with `current_block_`.
+ if (instruction->GetBlock() != current_block_) {
+ std::stringstream error;
+ if (instruction->IsPhi()) {
+ error << "Phi ";
+ } else {
+ error << "Instruction ";
+ }
+ error << instruction->GetId() << " in block "
+ << current_block_->GetBlockId();
+ if (instruction->GetBlock() != nullptr) {
+ error << " associated with block "
+ << instruction->GetBlock()->GetBlockId() << ".";
+ } else {
+ error << " not associated with any block.";
+ }
+ errors_.Insert(error.str());
+ }
+
+ // Ensure the inputs of `instruction` are defined in a block of the graph.
+ for (HInputIterator input_it(instruction); !input_it.Done();
+ input_it.Advance()) {
+ HInstruction* input = input_it.Current();
+ const HInstructionList& list = input->IsPhi()
+ ? input->GetBlock()->GetPhis()
+ : input->GetBlock()->GetInstructions();
+ if (!list.Contains(input)) {
+ std::stringstream error;
+ error << "Input " << input->GetId()
+ << " of instruction " << instruction->GetId()
+ << " is not defined in a basic block of the control-flow graph.";
+ errors_.Insert(error.str());
+ }
+ }
+
+ // Ensure the uses of `instruction` are defined in a block of the graph.
+ for (HUseIterator<HInstruction> use_it(instruction->GetUses());
+ !use_it.Done(); use_it.Advance()) {
+ HInstruction* use = use_it.Current()->GetUser();
+ const HInstructionList& list = use->IsPhi()
+ ? use->GetBlock()->GetPhis()
+ : use->GetBlock()->GetInstructions();
+ if (!list.Contains(use)) {
+ std::stringstream error;
+ error << "User " << use->GetId()
+ << " of instruction " << instruction->GetId()
+ << " is not defined in a basic block of the control-flow graph.";
+ errors_.Insert(error.str());
+ }
+ }
+}
+
+void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
+ super_type::VisitBasicBlock(block);
+
+ // Ensure there is no critical edge (i.e., an edge connecting a
+ // block with multiple successors to a block with multiple
+ // predecessors).
+ if (block->GetSuccessors().Size() > 1) {
+ for (size_t j = 0; j < block->GetSuccessors().Size(); ++j) {
+ HBasicBlock* successor = block->GetSuccessors().Get(j);
+ if (successor->GetPredecessors().Size() > 1) {
+ std::stringstream error;
+ error << "Critical edge between blocks " << block->GetBlockId()
+ << " and " << successor->GetBlockId() << ".";
+ errors_.Insert(error.str());
+ }
+ }
+ }
+
+ if (block->IsLoopHeader()) {
+ CheckLoop(block);
+ }
+}
+
+void SSAChecker::CheckLoop(HBasicBlock* loop_header) {
+ int id = loop_header->GetBlockId();
+
+ // Ensure the pre-header block is first in the list of
+ // predecessors of a loop header.
+ if (!loop_header->IsLoopPreHeaderFirstPredecessor()) {
+ std::stringstream error;
+ error << "Loop pre-header is not the first predecessor of the loop header "
+ << id << ".";
+ errors_.Insert(error.str());
+ }
+
+ // Ensure the loop header has only two predecessors and that only the
+ // second one is a back edge.
+ if (loop_header->GetPredecessors().Size() < 2) {
+ std::stringstream error;
+ error << "Loop header " << id << " has less than two predecessors.";
+ errors_.Insert(error.str());
+ } else if (loop_header->GetPredecessors().Size() > 2) {
+ std::stringstream error;
+ error << "Loop header " << id << " has more than two predecessors.";
+ errors_.Insert(error.str());
+ } else {
+ HLoopInformation* loop_information = loop_header->GetLoopInformation();
+ HBasicBlock* first_predecessor = loop_header->GetPredecessors().Get(0);
+ if (loop_information->IsBackEdge(first_predecessor)) {
+ std::stringstream error;
+ error << "First predecessor of loop header " << id << " is a back edge.";
+ errors_.Insert(error.str());
+ }
+ HBasicBlock* second_predecessor = loop_header->GetPredecessors().Get(1);
+ if (!loop_information->IsBackEdge(second_predecessor)) {
+ std::stringstream error;
+ error << "Second predecessor of loop header " << id
+ << " is not a back edge.";
+ errors_.Insert(error.str());
+ }
+ }
+
+ // Ensure there is only one back edge per loop.
+ size_t num_back_edges =
+ loop_header->GetLoopInformation()->GetBackEdges().Size();
+ if (num_back_edges != 1) {
+ std::stringstream error;
+ error << "Loop defined by header " << id << " has "
+ << num_back_edges << " back edge(s).";
+ errors_.Insert(error.str());
+ }
+
+ // Ensure all blocks in the loop are dominated by the loop header.
+ const ArenaBitVector& loop_blocks =
+ loop_header->GetLoopInformation()->GetBlocks();
+ for (uint32_t i : loop_blocks.Indexes()) {
+ HBasicBlock* loop_block = GetGraph()->GetBlocks().Get(i);
+ if (!loop_header->Dominates(loop_block)) {
+ std::stringstream error;
+ error << "Loop block " << loop_block->GetBlockId()
+ << " not dominated by loop header " << id;
+ errors_.Insert(error.str());
+ }
+ }
+}
+
+void SSAChecker::VisitInstruction(HInstruction* instruction) {
+ super_type::VisitInstruction(instruction);
+
+ // Ensure an instruction dominates all its uses (or in the present
+ // case, that all uses of an instruction (used as input) are
+ // dominated by its definition).
+ for (HInputIterator input_it(instruction); !input_it.Done();
+ input_it.Advance()) {
+ HInstruction* input = input_it.Current();
+ if (!input->Dominates(instruction)) {
+ std::stringstream error;
+ error << "Instruction " << input->GetId()
+ << " in block " << input->GetBlock()->GetBlockId()
+ << " does not dominate use " << instruction->GetId()
+ << " in block " << current_block_->GetBlockId() << ".";
+ errors_.Insert(error.str());
+ }
+ }
+}
+
+void SSAChecker::VisitPhi(HPhi* phi) {
+ VisitInstruction(phi);
+
+ // Ensure the first input of a phi is not itself.
+ if (phi->InputAt(0) == phi) {
+ std::stringstream error;
+ error << "Loop phi " << phi->GetId()
+ << " in block " << phi->GetBlock()->GetBlockId()
+ << " is its own first input.";
+ errors_.Insert(error.str());
+ }
+
+ // Ensure the number of phi inputs is the same as the number of
+ // its predecessors.
+ const GrowableArray<HBasicBlock*>& predecessors =
+ phi->GetBlock()->GetPredecessors();
+ if (phi->InputCount() != predecessors.Size()) {
+ std::stringstream error;
+ error << "Phi " << phi->GetId()
+ << " in block " << phi->GetBlock()->GetBlockId()
+ << " has " << phi->InputCount() << " inputs, but block "
+ << phi->GetBlock()->GetBlockId() << " has "
+ << predecessors.Size() << " predecessors.";
+ errors_.Insert(error.str());
+ } else {
+ // Ensure phi input at index I either comes from the Ith
+ // predecessor or from a block that dominates this predecessor.
+ for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+ HInstruction* input = phi->InputAt(i);
+ HBasicBlock* predecessor = predecessors.Get(i);
+ if (!(input->GetBlock() == predecessor
+ || input->GetBlock()->Dominates(predecessor))) {
+ std::stringstream error;
+ error << "Input " << input->GetId() << " at index " << i
+ << " of phi " << phi->GetId()
+ << " from block " << phi->GetBlock()->GetBlockId()
+ << " is not defined in predecessor number " << i
+ << " nor in a block dominating it.";
+ errors_.Insert(error.str());
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
new file mode 100644
index 0000000..34a770b
--- /dev/null
+++ b/compiler/optimizing/graph_checker.h
@@ -0,0 +1,83 @@
+/*
+ * 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_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
+#define ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
+
+#include "nodes.h"
+
+namespace art {
+
+// A control-flow graph visitor performing various checks.
+class GraphChecker : public HGraphVisitor {
+ public:
+ GraphChecker(ArenaAllocator* allocator, HGraph* graph)
+ : HGraphVisitor(graph),
+ allocator_(allocator),
+ errors_(allocator, 0) {}
+
+ // Check `block`.
+ virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ // Check `instruction`.
+ virtual void VisitInstruction(HInstruction* instruction) OVERRIDE;
+
+ // Was the last visit of the graph valid?
+ bool IsValid() const {
+ return errors_.IsEmpty();
+ }
+
+ // Get the list of detected errors.
+ const GrowableArray<std::string>& GetErrors() const {
+ return errors_;
+ }
+
+ protected:
+ ArenaAllocator* const allocator_;
+ // The block currently visited.
+ HBasicBlock* current_block_ = nullptr;
+ // Errors encountered while checking the graph.
+ GrowableArray<std::string> errors_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GraphChecker);
+};
+
+
+// An SSA graph visitor performing various checks.
+class SSAChecker : public GraphChecker {
+ public:
+ typedef GraphChecker super_type;
+
+ SSAChecker(ArenaAllocator* allocator, HGraph* graph)
+ : GraphChecker(allocator, graph) {}
+
+ // Perform SSA form checks on `block`.
+ virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+ // Loop-related checks from block `loop_header`.
+ void CheckLoop(HBasicBlock* loop_header);
+
+ // Perform SSA form checks on instructions.
+ virtual void VisitInstruction(HInstruction* instruction) OVERRIDE;
+ virtual void VisitPhi(HPhi* phi) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SSAChecker);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
new file mode 100644
index 0000000..ea06920
--- /dev/null
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#include "graph_checker.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+/**
+ * Create a simple control-flow graph composed of two blocks:
+ *
+ * BasicBlock 0, succ: 1
+ * 0: Goto 1
+ * BasicBlock 1, pred: 0
+ * 1: Exit
+ */
+HGraph* CreateSimpleCFG(ArenaAllocator* allocator) {
+ HGraph* graph = new (allocator) HGraph(allocator);
+ HBasicBlock* entry_block = new (allocator) HBasicBlock(graph);
+ entry_block->AddInstruction(new (allocator) HGoto());
+ graph->AddBlock(entry_block);
+ graph->SetEntryBlock(entry_block);
+ HBasicBlock* exit_block = new (allocator) HBasicBlock(graph);
+ exit_block->AddInstruction(new (allocator) HExit());
+ graph->AddBlock(exit_block);
+ graph->SetExitBlock(exit_block);
+ entry_block->AddSuccessor(exit_block);
+ return graph;
+}
+
+
+static void TestCode(const uint16_t* data) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = CreateCFG(&allocator, data);
+ ASSERT_NE(graph, nullptr);
+
+ GraphChecker graph_checker(&allocator, graph);
+ graph_checker.VisitInsertionOrder();
+ ASSERT_TRUE(graph_checker.IsValid());
+}
+
+static void TestCodeSSA(const uint16_t* data) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = CreateCFG(&allocator, data);
+ ASSERT_NE(graph, nullptr);
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+
+ SSAChecker ssa_checker(&allocator, graph);
+ ssa_checker.VisitInsertionOrder();
+ ASSERT_TRUE(ssa_checker.IsValid());
+}
+
+
+TEST(GraphChecker, ReturnVoid) {
+ const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+
+TEST(GraphChecker, CFG1) {
+ const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+ Instruction::GOTO | 0x100,
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+
+TEST(GraphChecker, CFG2) {
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 3,
+ Instruction::GOTO | 0x100,
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+
+TEST(GraphChecker, CFG3) {
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 3,
+ Instruction::GOTO | 0x100,
+ Instruction::GOTO | 0xFF00);
+
+ TestCode(data);
+}
+
+// Test case with an invalid graph containing inconsistent
+// predecessor/successor arcs in CFG.
+TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = CreateSimpleCFG(&allocator);
+ GraphChecker graph_checker(&allocator, graph);
+ graph_checker.VisitInsertionOrder();
+ ASSERT_TRUE(graph_checker.IsValid());
+
+ // Remove the entry block from the exit block's predecessors, to create an
+ // inconsistent successor/predecessor relation.
+ graph->GetExitBlock()->RemovePredecessor(graph->GetEntryBlock());
+ graph_checker.VisitInsertionOrder();
+ ASSERT_FALSE(graph_checker.IsValid());
+}
+
+// Test case with an invalid graph containing a non-branch last
+// instruction in a block.
+TEST(GraphChecker, BlockEndingWithNonBranchInstruction) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = CreateSimpleCFG(&allocator);
+ GraphChecker graph_checker(&allocator, graph);
+ graph_checker.VisitInsertionOrder();
+ ASSERT_TRUE(graph_checker.IsValid());
+
+ // Remove the sole instruction of the exit block (composed of a
+ // single Exit instruction) to make it invalid (i.e. not ending by a
+ // branch instruction).
+ HBasicBlock* exit_block = graph->GetExitBlock();
+ HInstruction* last_inst = exit_block->GetLastInstruction();
+ exit_block->RemoveInstruction(last_inst);
+
+ graph_checker.VisitInsertionOrder();
+ ASSERT_FALSE(graph_checker.IsValid());
+}
+
+TEST(SSAChecker, SSAPhi) {
+ // This code creates one Phi function during the conversion to SSA form.
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 3,
+ Instruction::CONST_4 | 4 << 12 | 0,
+ Instruction::RETURN | 0 << 8);
+
+ TestCodeSSA(data);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f011e85..0fb4737 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -81,6 +81,25 @@
}
}
+ char GetTypeId(Primitive::Type type) {
+ // Note that Primitive::Descriptor would not work for us
+ // because it does not handle reference types (that is kPrimNot).
+ switch (type) {
+ case Primitive::kPrimBoolean: return 'z';
+ case Primitive::kPrimByte: return 'b';
+ case Primitive::kPrimChar: return 'c';
+ case Primitive::kPrimShort: return 's';
+ case Primitive::kPrimInt: return 'i';
+ case Primitive::kPrimLong: return 'j';
+ case Primitive::kPrimFloat: return 'f';
+ case Primitive::kPrimDouble: return 'd';
+ case Primitive::kPrimNot: return 'l';
+ case Primitive::kPrimVoid: return 'v';
+ }
+ LOG(FATAL) << "Unreachable";
+ return 'v';
+ }
+
void PrintPredecessors(HBasicBlock* block) {
AddIndent();
output_ << "predecessors";
@@ -110,6 +129,12 @@
}
} else if (location.IsConstant()) {
output_ << "constant";
+ HConstant* constant = location.GetConstant();
+ if (constant->IsIntConstant()) {
+ output_ << " " << constant->AsIntConstant()->GetValue();
+ } else if (constant->IsLongConstant()) {
+ output_ << " " << constant->AsLongConstant()->GetValue();
+ }
} else if (location.IsInvalid()) {
output_ << "invalid";
} else if (location.IsStackSlot()) {
@@ -140,7 +165,7 @@
if (instruction->InputCount() > 0) {
output_ << " [ ";
for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
- output_ << "v" << inputs.Current()->GetId() << " ";
+ output_ << GetTypeId(inputs.Current()->GetType()) << inputs.Current()->GetId() << " ";
}
output_ << "]";
}
@@ -175,7 +200,8 @@
HInstruction* instruction = it.Current();
AddIndent();
int bci = 0;
- output_ << bci << " " << instruction->NumberOfUses() << " v" << instruction->GetId() << " ";
+ output_ << bci << " " << instruction->NumberOfUses()
+ << " " << GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
instruction->Accept(this);
output_ << kEndInstructionMarker << std::endl;
}
@@ -214,7 +240,8 @@
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
AddIndent();
HInstruction* instruction = it.Current();
- output_ << instruction->GetId() << " v" << instruction->GetId() << "[ ";
+ output_ << instruction->GetId() << " " << GetTypeId(instruction->GetType())
+ << instruction->GetId() << "[ ";
for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
output_ << inputs.Current()->GetId() << " ";
}
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 7cd74e9..6e2c6fd 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -25,8 +25,10 @@
class DexCompilationUnit;
class HGraph;
+// TODO: Create an analysis/optimization abstraction.
static const char* kLivenessPassName = "liveness";
static const char* kRegisterAllocatorPassName = "register";
+static const char* kGVNPassName = "gvn";
/**
* If enabled, emits compilation information suitable for the c1visualizer tool
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
new file mode 100644
index 0000000..027b3d4
--- /dev/null
+++ b/compiler/optimizing/gvn.cc
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+#include "gvn.h"
+
+namespace art {
+
+void GlobalValueNumberer::Run() {
+ ComputeSideEffects();
+
+ sets_.Put(graph_->GetEntryBlock()->GetBlockId(), new (allocator_) ValueSet(allocator_));
+
+ // Do reverse post order to ensure the non back-edge predecessors of a block are
+ // visited before the block itself.
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ VisitBasicBlock(it.Current());
+ }
+}
+
+void GlobalValueNumberer::UpdateLoopEffects(HLoopInformation* info, SideEffects effects) {
+ int id = info->GetHeader()->GetBlockId();
+ loop_effects_.Put(id, loop_effects_.Get(id).Union(effects));
+}
+
+void GlobalValueNumberer::ComputeSideEffects() {
+ if (kIsDebugBuild) {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ SideEffects effects = GetBlockEffects(block);
+ DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+ if (block->IsLoopHeader()) {
+ effects = GetLoopEffects(block);
+ DCHECK(!effects.HasSideEffects() && !effects.HasDependencies());
+ }
+ }
+ }
+
+ // Do a post order visit to ensure we visit a loop header after its loop body.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+
+ SideEffects effects = SideEffects::None();
+ // Update `effects` with the side effects of all instructions in this block.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ effects = effects.Union(instruction->GetSideEffects());
+ if (effects.HasAllSideEffects()) {
+ break;
+ }
+ }
+
+ block_effects_.Put(block->GetBlockId(), effects);
+
+ if (block->IsLoopHeader()) {
+ // The side effects of the loop header are part of the loop.
+ UpdateLoopEffects(block->GetLoopInformation(), effects);
+ HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
+ if (pre_header->IsInLoop()) {
+ // Update the side effects of the outer loop with the side effects of the inner loop.
+ // Note that this works because we know all the blocks of the inner loop are visited
+ // before the loop header of the outer loop.
+ UpdateLoopEffects(pre_header->GetLoopInformation(), GetLoopEffects(block));
+ }
+ } else if (block->IsInLoop()) {
+ // Update the side effects of the loop with the side effects of this block.
+ UpdateLoopEffects(block->GetLoopInformation(), effects);
+ }
+ }
+}
+
+SideEffects GlobalValueNumberer::GetLoopEffects(HBasicBlock* block) const {
+ DCHECK(block->IsLoopHeader());
+ return loop_effects_.Get(block->GetBlockId());
+}
+
+SideEffects GlobalValueNumberer::GetBlockEffects(HBasicBlock* block) const {
+ return block_effects_.Get(block->GetBlockId());
+}
+
+static bool IsLoopExit(HBasicBlock* block, HBasicBlock* successor) {
+ HLoopInformation* block_info = block->GetLoopInformation();
+ HLoopInformation* other_info = successor->GetLoopInformation();
+ return block_info != other_info && (other_info == nullptr || block_info->IsIn(*other_info));
+}
+
+void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
+ if (kIsDebugBuild) {
+ // Check that all non back-edge processors have been visited.
+ for (size_t i = 0, e = block->GetPredecessors().Size(); i < e; ++i) {
+ HBasicBlock* predecessor = block->GetPredecessors().Get(i);
+ DCHECK(visited_.Get(predecessor->GetBlockId())
+ || (block->GetLoopInformation() != nullptr
+ && (block->GetLoopInformation()->GetBackEdges().Get(0) == predecessor)));
+ }
+ visited_.Put(block->GetBlockId(), true);
+ }
+
+ ValueSet* set = sets_.Get(block->GetBlockId());
+
+ if (block->IsLoopHeader()) {
+ set->Kill(GetLoopEffects(block));
+ }
+
+ HInstruction* current = block->GetFirstInstruction();
+ while (current != nullptr) {
+ set->Kill(current->GetSideEffects());
+ // Save the next instruction in case `current` is removed from the graph.
+ HInstruction* next = current->GetNext();
+ if (current->CanBeMoved()) {
+ HInstruction* existing = set->Lookup(current);
+ if (existing != nullptr) {
+ current->ReplaceWith(existing);
+ current->GetBlock()->RemoveInstruction(current);
+ } else {
+ set->Add(current);
+ }
+ }
+ current = next;
+ }
+
+ if (block == graph_->GetEntryBlock()) {
+ // The entry block should only accumulate constant instructions, and
+ // the builder puts constants only in the entry block.
+ // Therefore, there is no need to propagate the value set to the next block.
+ DCHECK_EQ(block->GetDominatedBlocks().Size(), 1u);
+ HBasicBlock* dominated = block->GetDominatedBlocks().Get(0);
+ sets_.Put(dominated->GetBlockId(), new (allocator_) ValueSet(allocator_));
+ return;
+ }
+
+ // Copy the value set to dominated blocks. We can re-use
+ // the current set for the last dominated block because we are done visiting
+ // this block.
+ for (size_t i = 0, e = block->GetDominatedBlocks().Size(); i < e; ++i) {
+ HBasicBlock* dominated = block->GetDominatedBlocks().Get(i);
+ sets_.Put(dominated->GetBlockId(), i == e - 1 ? set : set->Copy());
+ }
+
+ // Kill instructions in the value set of each successor. If the successor
+ // is a loop exit, then we use the side effects of the loop. If not, we use
+ // the side effects of this block.
+ for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
+ HBasicBlock* successor = block->GetSuccessors().Get(i);
+ if (successor->IsLoopHeader()
+ && successor->GetLoopInformation()->GetBackEdges().Get(0) == block) {
+ // In case of a back edge, we already have visited the loop header.
+ // We should not update its value set, because the last dominated block
+ // of the loop header uses the same value set.
+ DCHECK(visited_.Get(successor->GetBlockId()));
+ continue;
+ }
+ DCHECK(!visited_.Get(successor->GetBlockId()));
+ ValueSet* successor_set = sets_.Get(successor->GetBlockId());
+ // The dominator sets the set, and we are guaranteed to have visited it already.
+ DCHECK(successor_set != nullptr);
+
+ // If this block dominates this successor there is nothing to do.
+ // Also if the set is empty, there is nothing to kill.
+ if (successor->GetDominator() != block && !successor_set->IsEmpty()) {
+ if (block->IsInLoop() && IsLoopExit(block, successor)) {
+ // All instructions killed in the loop must be killed for a loop exit.
+ SideEffects effects = GetLoopEffects(block->GetLoopInformation()->GetHeader());
+ sets_.Get(successor->GetBlockId())->Kill(effects);
+ } else {
+ // Following block (that might be in the same loop).
+ // Just kill instructions based on this block's side effects.
+ sets_.Get(successor->GetBlockId())->Kill(GetBlockEffects(block));
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
new file mode 100644
index 0000000..41b3ceb
--- /dev/null
+++ b/compiler/optimizing/gvn.h
@@ -0,0 +1,230 @@
+/*
+ * 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_COMPILER_OPTIMIZING_GVN_H_
+#define ART_COMPILER_OPTIMIZING_GVN_H_
+
+#include <gtest/gtest.h>
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * A node in the collision list of a ValueSet. Encodes the instruction,
+ * the hash code, and the next node in the collision list.
+ */
+class ValueSetNode : public ArenaObject {
+ public:
+ ValueSetNode(HInstruction* instruction, size_t hash_code, ValueSetNode* next)
+ : instruction_(instruction), hash_code_(hash_code), next_(next) {}
+
+ size_t GetHashCode() const { return hash_code_; }
+ HInstruction* GetInstruction() const { return instruction_; }
+ ValueSetNode* GetNext() const { return next_; }
+ void SetNext(ValueSetNode* node) { next_ = node; }
+
+ private:
+ HInstruction* const instruction_;
+ const size_t hash_code_;
+ ValueSetNode* next_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueSetNode);
+};
+
+/**
+ * A ValueSet holds instructions that can replace other instructions. It is updated
+ * through the `Add` method, and the `Kill` method. The `Kill` method removes
+ * instructions that are affected by the given side effect.
+ *
+ * The `Lookup` method returns an equivalent instruction to the given instruction
+ * if there is one in the set. In GVN, we would say those instructions have the
+ * same "number".
+ */
+class ValueSet : public ArenaObject {
+ public:
+ explicit ValueSet(ArenaAllocator* allocator)
+ : allocator_(allocator), number_of_entries_(0), collisions_(nullptr) {
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ table_[i] = nullptr;
+ }
+ }
+
+ // Adds an instruction in the set.
+ void Add(HInstruction* instruction) {
+ DCHECK(Lookup(instruction) == nullptr);
+ size_t hash_code = instruction->ComputeHashCode();
+ size_t index = hash_code % kDefaultNumberOfEntries;
+ if (table_[index] == nullptr) {
+ table_[index] = instruction;
+ } else {
+ collisions_ = new (allocator_) ValueSetNode(instruction, hash_code, collisions_);
+ }
+ ++number_of_entries_;
+ }
+
+ // If in the set, returns an equivalent instruction to the given instruction. Returns
+ // null otherwise.
+ HInstruction* Lookup(HInstruction* instruction) const {
+ size_t hash_code = instruction->ComputeHashCode();
+ size_t index = hash_code % kDefaultNumberOfEntries;
+ HInstruction* existing = table_[index];
+ if (existing != nullptr && existing->Equals(instruction)) {
+ return existing;
+ }
+
+ for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+ if (node->GetHashCode() == hash_code) {
+ existing = node->GetInstruction();
+ if (existing->Equals(instruction)) {
+ return existing;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // Removes all instructions in the set that are affected by the given side effects.
+ void Kill(SideEffects side_effects) {
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ HInstruction* instruction = table_[i];
+ if (instruction != nullptr && instruction->GetSideEffects().DependsOn(side_effects)) {
+ table_[i] = nullptr;
+ --number_of_entries_;
+ }
+ }
+
+ ValueSetNode* current = collisions_;
+ ValueSetNode* previous = nullptr;
+ while (current != nullptr) {
+ HInstruction* instruction = current->GetInstruction();
+ if (instruction->GetSideEffects().DependsOn(side_effects)) {
+ if (previous == nullptr) {
+ collisions_ = current->GetNext();
+ } else {
+ previous->SetNext(current->GetNext());
+ }
+ --number_of_entries_;
+ } else {
+ previous = current;
+ }
+ current = current->GetNext();
+ }
+ }
+
+ // Returns a copy of this set.
+ ValueSet* Copy() const {
+ ValueSet* copy = new (allocator_) ValueSet(allocator_);
+
+ for (size_t i = 0; i < kDefaultNumberOfEntries; ++i) {
+ copy->table_[i] = table_[i];
+ }
+
+ // Note that the order will be inverted in the copy. This is fine, as the order is not
+ // relevant for a ValueSet.
+ for (ValueSetNode* node = collisions_; node != nullptr; node = node->GetNext()) {
+ copy->collisions_ = new (allocator_) ValueSetNode(
+ node->GetInstruction(), node->GetHashCode(), copy->collisions_);
+ }
+
+ copy->number_of_entries_ = number_of_entries_;
+ return copy;
+ }
+
+ bool IsEmpty() const { return number_of_entries_ == 0; }
+ size_t GetNumberOfEntries() const { return number_of_entries_; }
+
+ private:
+ static constexpr size_t kDefaultNumberOfEntries = 8;
+
+ ArenaAllocator* const allocator_;
+
+ // The number of entries in the set.
+ size_t number_of_entries_;
+
+ // The internal implementation of the set. It uses a combination of a hash code based
+ // fixed-size list, and a linked list to handle hash code collisions.
+ // TODO: Tune the fixed size list original size, and support growing it.
+ ValueSetNode* collisions_;
+ HInstruction* table_[kDefaultNumberOfEntries];
+
+ DISALLOW_COPY_AND_ASSIGN(ValueSet);
+};
+
+/**
+ * Optimization phase that removes redundant instruction.
+ */
+class GlobalValueNumberer : public ValueObject {
+ public:
+ GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph)
+ : allocator_(allocator),
+ graph_(graph),
+ block_effects_(allocator, graph->GetBlocks().Size()),
+ loop_effects_(allocator, graph->GetBlocks().Size()),
+ sets_(allocator, graph->GetBlocks().Size()),
+ visited_(allocator, graph->GetBlocks().Size()) {
+ size_t number_of_blocks = graph->GetBlocks().Size();
+ block_effects_.SetSize(number_of_blocks);
+ loop_effects_.SetSize(number_of_blocks);
+ sets_.SetSize(number_of_blocks);
+ visited_.SetSize(number_of_blocks);
+
+ for (size_t i = 0; i < number_of_blocks; ++i) {
+ block_effects_.Put(i, SideEffects::None());
+ loop_effects_.Put(i, SideEffects::None());
+ }
+ }
+
+ void Run();
+
+ private:
+ // Per-block GVN. Will also update the ValueSet of the dominated and
+ // successor blocks.
+ void VisitBasicBlock(HBasicBlock* block);
+
+ // Compute side effects of individual blocks and loops. The GVN algorithm
+ // will use these side effects to update the ValueSet of individual blocks.
+ void ComputeSideEffects();
+
+ void UpdateLoopEffects(HLoopInformation* info, SideEffects effects);
+ SideEffects GetLoopEffects(HBasicBlock* block) const;
+ SideEffects GetBlockEffects(HBasicBlock* block) const;
+
+ ArenaAllocator* const allocator_;
+ HGraph* const graph_;
+
+ // Side effects of individual blocks, that is the union of the side effects
+ // of the instructions in the block.
+ GrowableArray<SideEffects> block_effects_;
+
+ // Side effects of loops, that is the union of the side effects of the
+ // blocks contained in that loop.
+ GrowableArray<SideEffects> loop_effects_;
+
+ // ValueSet for blocks. Initially null, but for an individual block they
+ // are allocated and populated by the dominator, and updated by all blocks
+ // in the path from the dominator to the block.
+ GrowableArray<ValueSet*> sets_;
+
+ // Mark visisted blocks. Only used for debugging.
+ GrowableArray<bool> visited_;
+
+ FRIEND_TEST(GVNTest, LoopSideEffects);
+ DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_GVN_H_
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
new file mode 100644
index 0000000..ad6e338
--- /dev/null
+++ b/compiler/optimizing/gvn_test.cc
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+#include "builder.h"
+#include "gvn.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(GVNTest, LocalFieldElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+ entry->AddInstruction(parameter);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+
+ block->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+ block->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+ HInstruction* to_remove = block->GetLastInstruction();
+ block->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(43)));
+ HInstruction* different_offset = block->GetLastInstruction();
+ // Kill the value.
+ block->AddInstruction(new (&allocator) HInstanceFieldSet(
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+ block->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimNot, MemberOffset(42)));
+ HInstruction* use_after_kill = block->GetLastInstruction();
+ block->AddInstruction(new (&allocator) HExit());
+
+ ASSERT_EQ(to_remove->GetBlock(), block);
+ ASSERT_EQ(different_offset->GetBlock(), block);
+ ASSERT_EQ(use_after_kill->GetBlock(), block);
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ GlobalValueNumberer(&allocator, graph).Run();
+
+ ASSERT_TRUE(to_remove->GetBlock() == nullptr);
+ ASSERT_EQ(different_offset->GetBlock(), block);
+ ASSERT_EQ(use_after_kill->GetBlock(), block);
+}
+
+TEST(GVNTest, GlobalFieldElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+ entry->AddInstruction(parameter);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+
+ block->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
+ HBasicBlock* then = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* else_ = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* join = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(then);
+ graph->AddBlock(else_);
+ graph->AddBlock(join);
+
+ block->AddSuccessor(then);
+ block->AddSuccessor(else_);
+ then->AddSuccessor(join);
+ else_->AddSuccessor(join);
+
+ then->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ then->AddInstruction(new (&allocator) HGoto());
+ else_->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ else_->AddInstruction(new (&allocator) HGoto());
+ join->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ join->AddInstruction(new (&allocator) HExit());
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ GlobalValueNumberer(&allocator, graph).Run();
+
+ // Check that all field get instructions have been GVN'ed.
+ ASSERT_TRUE(then->GetFirstInstruction()->IsGoto());
+ ASSERT_TRUE(else_->GetFirstInstruction()->IsGoto());
+ ASSERT_TRUE(join->GetFirstInstruction()->IsExit());
+}
+
+TEST(GVNTest, LoopFieldElimination) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+ entry->AddInstruction(parameter);
+
+ HBasicBlock* block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(block);
+ entry->AddSuccessor(block);
+ block->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ block->AddInstruction(new (&allocator) HGoto());
+
+ HBasicBlock* loop_header = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* loop_body = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* exit = new (&allocator) HBasicBlock(graph);
+
+ graph->AddBlock(loop_header);
+ graph->AddBlock(loop_body);
+ graph->AddBlock(exit);
+ block->AddSuccessor(loop_header);
+ loop_header->AddSuccessor(loop_body);
+ loop_header->AddSuccessor(exit);
+ loop_body->AddSuccessor(loop_header);
+
+ loop_header->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ HInstruction* field_get_in_loop_header = loop_header->GetLastInstruction();
+ loop_header->AddInstruction(new (&allocator) HIf(block->GetLastInstruction()));
+
+ // Kill inside the loop body to prevent field gets inside the loop header
+ // and the body to be GVN'ed.
+ loop_body->AddInstruction(new (&allocator) HInstanceFieldSet(
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+ HInstruction* field_set = loop_body->GetLastInstruction();
+ loop_body->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ HInstruction* field_get_in_loop_body = loop_body->GetLastInstruction();
+ loop_body->AddInstruction(new (&allocator) HGoto());
+
+ exit->AddInstruction(
+ new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
+ HInstruction* field_get_in_exit = exit->GetLastInstruction();
+ exit->AddInstruction(new (&allocator) HExit());
+
+ ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
+ ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
+ ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ graph->FindNaturalLoops();
+ GlobalValueNumberer(&allocator, graph).Run();
+
+ // Check that all field get instructions are still there.
+ ASSERT_EQ(field_get_in_loop_header->GetBlock(), loop_header);
+ ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
+ // The exit block is dominated by the loop header, whose field get
+ // does not get killed by the loop flags.
+ ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
+
+ // Now remove the field set, and check that all field get instructions have been GVN'ed.
+ loop_body->RemoveInstruction(field_set);
+ GlobalValueNumberer(&allocator, graph).Run();
+
+ ASSERT_TRUE(field_get_in_loop_header->GetBlock() == nullptr);
+ ASSERT_TRUE(field_get_in_loop_body->GetBlock() == nullptr);
+ ASSERT_TRUE(field_get_in_exit->GetBlock() == nullptr);
+}
+
+// Test that inner loops affect the side effects of the outer loop.
+TEST(GVNTest, LoopSideEffects) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+
+ HBasicBlock* outer_loop_header = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* outer_loop_body = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* outer_loop_exit = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* inner_loop_header = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* inner_loop_body = new (&allocator) HBasicBlock(graph);
+ HBasicBlock* inner_loop_exit = new (&allocator) HBasicBlock(graph);
+
+ graph->AddBlock(outer_loop_header);
+ graph->AddBlock(outer_loop_body);
+ graph->AddBlock(outer_loop_exit);
+ graph->AddBlock(inner_loop_header);
+ graph->AddBlock(inner_loop_body);
+ graph->AddBlock(inner_loop_exit);
+
+ entry->AddSuccessor(outer_loop_header);
+ outer_loop_header->AddSuccessor(outer_loop_body);
+ outer_loop_header->AddSuccessor(outer_loop_exit);
+ outer_loop_body->AddSuccessor(inner_loop_header);
+ inner_loop_header->AddSuccessor(inner_loop_body);
+ inner_loop_header->AddSuccessor(inner_loop_exit);
+ inner_loop_body->AddSuccessor(inner_loop_header);
+ inner_loop_exit->AddSuccessor(outer_loop_header);
+
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimBoolean);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(new (&allocator) HGoto());
+ outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
+ outer_loop_body->AddInstruction(new (&allocator) HGoto());
+ inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
+ inner_loop_body->AddInstruction(new (&allocator) HGoto());
+ inner_loop_exit->AddInstruction(new (&allocator) HGoto());
+ outer_loop_exit->AddInstruction(new (&allocator) HExit());
+
+ graph->BuildDominatorTree();
+ graph->TransformToSSA();
+ graph->FindNaturalLoops();
+
+ ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
+ *outer_loop_header->GetLoopInformation()));
+
+ // Check that the loops don't have side effects.
+ {
+ // Make one block with a side effect.
+ entry->AddInstruction(new (&allocator) HInstanceFieldSet(
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42)));
+
+ GlobalValueNumberer gvn(&allocator, graph);
+ gvn.Run();
+
+ ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
+ ASSERT_FALSE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
+ ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+ }
+
+ // Check that the side effects of the outer loop does not affect the inner loop.
+ {
+ outer_loop_body->InsertInstructionBefore(
+ new (&allocator) HInstanceFieldSet(
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+ outer_loop_body->GetLastInstruction());
+
+ GlobalValueNumberer gvn(&allocator, graph);
+ gvn.Run();
+
+ ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
+ ASSERT_TRUE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
+ ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
+ ASSERT_FALSE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+ }
+
+ // Check that the side effects of the inner loop affects the outer loop.
+ {
+ outer_loop_body->RemoveInstruction(outer_loop_body->GetFirstInstruction());
+ inner_loop_body->InsertInstructionBefore(
+ new (&allocator) HInstanceFieldSet(
+ parameter, parameter, Primitive::kPrimNot, MemberOffset(42)),
+ inner_loop_body->GetLastInstruction());
+
+ GlobalValueNumberer gvn(&allocator, graph);
+ gvn.Run();
+
+ ASSERT_TRUE(gvn.GetBlockEffects(entry).HasSideEffects());
+ ASSERT_FALSE(gvn.GetBlockEffects(outer_loop_body).HasSideEffects());
+ ASSERT_TRUE(gvn.GetLoopEffects(outer_loop_header).HasSideEffects());
+ ASSERT_TRUE(gvn.GetLoopEffects(inner_loop_header).HasSideEffects());
+ }
+}
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
new file mode 100644
index 0000000..a0de73d
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#include "instruction_simplifier.h"
+
+namespace art {
+
+void InstructionSimplifier::Run() {
+ VisitInsertionOrder();
+}
+
+void InstructionSimplifier::VisitSuspendCheck(HSuspendCheck* check) {
+ HBasicBlock* block = check->GetBlock();
+ // Currently always keep the suspend check at entry.
+ if (block->IsEntryBlock()) return;
+
+ // Currently always keep suspend checks at loop entry.
+ if (block->IsLoopHeader() && block->GetFirstInstruction() == check) {
+ DCHECK(block->GetLoopInformation()->GetSuspendCheck() == check);
+ return;
+ }
+
+ // Remove the suspend check that was added at build time for the baseline
+ // compiler.
+ block->RemoveInstruction(check);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
new file mode 100644
index 0000000..b2f3f52
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -0,0 +1,39 @@
+/*
+ * 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_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
+
+#include "nodes.h"
+
+namespace art {
+
+/**
+ * Implements optimizations specific to each instruction.
+ */
+class InstructionSimplifier : public HGraphVisitor {
+ public:
+ explicit InstructionSimplifier(HGraph* graph) : HGraphVisitor(graph) {}
+
+ void Run();
+
+ private:
+ virtual void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index e4f9371..6dd4207 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -19,6 +19,7 @@
#include "base/stringprintf.h"
#include "builder.h"
#include "code_generator.h"
+#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "graph_visualizer.h"
@@ -45,8 +46,8 @@
graph->TransformToSSA();
graph->FindNaturalLoops();
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
ASSERT_EQ(liveness.GetLinearPostOrder().Size(), number_of_blocks);
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index a6e5ca9..03f8625 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -16,6 +16,7 @@
#include "builder.h"
#include "code_generator.h"
+#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "nodes.h"
@@ -31,6 +32,9 @@
HGraphBuilder builder(allocator);
const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
HGraph* graph = builder.BuildGraph(*item);
+ // Suspend checks implementation may change in the future, and this test relies
+ // on how instructions are ordered.
+ RemoveSuspendChecks(graph);
graph->BuildDominatorTree();
graph->TransformToSSA();
graph->FindNaturalLoops();
@@ -58,8 +62,8 @@
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
@@ -104,8 +108,8 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
@@ -142,7 +146,7 @@
* 22: phi
* 24: return
* |
- * 38: exit
+ * 28: exit
*/
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -153,8 +157,8 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
// Test for the 4 constant.
@@ -190,7 +194,7 @@
ASSERT_TRUE(range->GetNext() == nullptr);
}
-TEST(LiveRangesTest, Loop) {
+TEST(LiveRangesTest, Loop1) {
/*
* Test the following snippet:
* var a = 0;
@@ -229,8 +233,9 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildGraph(data, &allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ RemoveSuspendChecks(graph);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
// Test for the 0 constant.
@@ -267,4 +272,168 @@
ASSERT_TRUE(range->GetNext() == nullptr);
}
+TEST(LiveRangesTest, Loop2) {
+ /*
+ * Test the following snippet:
+ * var a = 0;
+ * while (a == a) {
+ * a = a + a;
+ * }
+ * return a;
+ *
+ * Which becomes the following graph (numbered by lifetime position):
+ * 2: constant0
+ * 4: goto
+ * |
+ * 8: goto
+ * |
+ * 10: phi
+ * 12: equal
+ * 14: if +++++
+ * | \ +
+ * | 18: suspend
+ * | 20: add
+ * | 22: goto
+ * |
+ * 26: return
+ * |
+ * 30: exit
+ *
+ * We want to make sure the phi at 10 has a lifetime hole after the add at 20.
+ */
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 6,
+ Instruction::ADD_INT, 0, 0,
+ Instruction::GOTO | 0xFB00,
+ Instruction::RETURN | 0 << 8);
+
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = BuildGraph(data, &allocator);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
+ liveness.Analyze();
+
+ // Test for the 0 constant.
+ HIntConstant* constant = liveness.GetInstructionFromSsaIndex(0)->AsIntConstant();
+ LiveInterval* interval = constant->GetLiveInterval();
+ LiveRange* range = interval->GetFirstRange();
+ ASSERT_EQ(2u, range->GetStart());
+ // Last use is the loop phi so instruction is live until
+ // the end of the pre loop header.
+ ASSERT_EQ(10u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+
+ // Test for the loop phi.
+ HPhi* phi = liveness.GetInstructionFromSsaIndex(1)->AsPhi();
+ interval = phi->GetLiveInterval();
+ range = interval->GetFirstRange();
+ ASSERT_EQ(10u, range->GetStart());
+ ASSERT_EQ(21u, range->GetEnd());
+ range = range->GetNext();
+ ASSERT_TRUE(range != nullptr);
+ ASSERT_EQ(24u, range->GetStart());
+ ASSERT_EQ(27u, range->GetEnd());
+
+ // Test for the add instruction.
+ HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd();
+ interval = add->GetLiveInterval();
+ range = interval->GetFirstRange();
+ ASSERT_EQ(20u, range->GetStart());
+ ASSERT_EQ(24u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+}
+
+TEST(LiveRangesTest, CFG4) {
+ /*
+ * Test the following snippet:
+ * var a = 0;
+ * var b = 4;
+ * if (a == a) {
+ * a = b + a;
+ * } else {
+ * a = b + a
+ * }
+ * return b;
+ *
+ * Which becomes the following graph (numbered by lifetime position):
+ * 2: constant0
+ * 4: constant4
+ * 6: goto
+ * |
+ * 10: equal
+ * 12: if
+ * / \
+ * 16: add 22: add
+ * 18: goto 24: goto
+ * \ /
+ * 26: phi
+ * 28: return
+ * |
+ * 32: exit
+ *
+ * We want to make sure the constant0 has a lifetime hole after the 16: add.
+ */
+ const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::CONST_4 | 4 << 12 | 1 << 8,
+ Instruction::IF_EQ, 5,
+ Instruction::ADD_INT, 1 << 8,
+ Instruction::GOTO | 0x300,
+ Instruction::ADD_INT, 1 << 8,
+ Instruction::RETURN | 1 << 8);
+
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = BuildGraph(data, &allocator);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
+ liveness.Analyze();
+
+ // Test for the 0 constant.
+ LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
+ LiveRange* range = interval->GetFirstRange();
+ ASSERT_EQ(2u, range->GetStart());
+ ASSERT_EQ(16u, range->GetEnd());
+ range = range->GetNext();
+ ASSERT_TRUE(range != nullptr);
+ ASSERT_EQ(20u, range->GetStart());
+ ASSERT_EQ(22u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+
+ // Test for the 4 constant.
+ interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval();
+ range = interval->GetFirstRange();
+ ASSERT_EQ(4u, range->GetStart());
+ ASSERT_EQ(29u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+
+ // Test for the first add.
+ HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd();
+ interval = add->GetLiveInterval();
+ range = interval->GetFirstRange();
+ ASSERT_EQ(16u, range->GetStart());
+ ASSERT_EQ(20u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+
+ // Test for the second add.
+ add = liveness.GetInstructionFromSsaIndex(3)->AsAdd();
+ interval = add->GetLiveInterval();
+ range = interval->GetFirstRange();
+ ASSERT_EQ(22u, range->GetStart());
+ ASSERT_EQ(26u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+
+ // Test for the phi, which is unused.
+ HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi();
+ ASSERT_EQ(phi->NumberOfUses(), 0u);
+ interval = phi->GetLiveInterval();
+ range = interval->GetFirstRange();
+ ASSERT_EQ(26u, range->GetStart());
+ ASSERT_EQ(28u, range->GetEnd());
+ ASSERT_TRUE(range->GetNext() == nullptr);
+}
+
} // namespace art
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 1a4d745..2d86169 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -16,6 +16,7 @@
#include "builder.h"
#include "code_generator.h"
+#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "nodes.h"
@@ -49,8 +50,8 @@
graph->BuildDominatorTree();
graph->TransformToSSA();
graph->FindNaturalLoops();
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, InstructionSet::kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
std::ostringstream buffer;
@@ -545,4 +546,51 @@
TestCode(data, expected);
}
+TEST(LivenessTest, Loop8) {
+ // var a = 0;
+ // while (a == a) {
+ // a = a + a;
+ // }
+ // return a;
+ //
+ // We want to test that the ins of the loop exit
+ // does contain the phi.
+ // Bitsets are made of:
+ // (constant0, phi, add)
+ const char* expected =
+ "Block 0\n"
+ " live in: (000)\n"
+ " live out: (100)\n"
+ " kill: (100)\n"
+ "Block 1\n" // pre loop header
+ " live in: (100)\n"
+ " live out: (000)\n"
+ " kill: (000)\n"
+ "Block 2\n" // loop header
+ " live in: (000)\n"
+ " live out: (010)\n"
+ " kill: (010)\n"
+ "Block 3\n" // back edge
+ " live in: (010)\n"
+ " live out: (000)\n"
+ " kill: (001)\n"
+ "Block 4\n" // return block
+ " live in: (010)\n"
+ " live out: (000)\n"
+ " kill: (000)\n"
+ "Block 5\n" // exit block
+ " live in: (000)\n"
+ " live out: (000)\n"
+ " kill: (000)\n";
+
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 6,
+ Instruction::ADD_INT, 0, 0,
+ Instruction::GOTO | 0xFB00,
+ Instruction::RETURN | 0 << 8);
+
+ TestCode(data, expected);
+}
+
} // namespace art
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 468cfb7..1c36cdf 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -20,13 +20,29 @@
namespace art {
-LocationSummary::LocationSummary(HInstruction* instruction)
+LocationSummary::LocationSummary(HInstruction* instruction, CallKind call_kind)
: inputs_(instruction->GetBlock()->GetGraph()->GetArena(), instruction->InputCount()),
- temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0) {
+ temps_(instruction->GetBlock()->GetGraph()->GetArena(), 0),
+ environment_(instruction->GetBlock()->GetGraph()->GetArena(),
+ instruction->EnvironmentSize()),
+ call_kind_(call_kind),
+ stack_mask_(nullptr),
+ register_mask_(0),
+ live_registers_() {
inputs_.SetSize(instruction->InputCount());
- for (size_t i = 0; i < instruction->InputCount(); i++) {
+ for (size_t i = 0; i < instruction->InputCount(); ++i) {
inputs_.Put(i, Location());
}
+ environment_.SetSize(instruction->EnvironmentSize());
+ for (size_t i = 0; i < instruction->EnvironmentSize(); ++i) {
+ environment_.Put(i, Location());
+ }
+ instruction->SetLocations(this);
+
+ if (NeedsSafepoint()) {
+ ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetArena();
+ stack_mask_ = new (arena) ArenaBitVector(arena, 0, true);
+ }
}
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index aaddb09..f358e05 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_LOCATIONS_H_
#include "base/bit_field.h"
+#include "base/bit_vector.h"
#include "utils/allocation.h"
#include "utils/growable_array.h"
#include "utils/managed_register.h"
@@ -43,13 +44,13 @@
// low bits are in the last parameter register, and the high
// bits are in a stack slot. The kQuickParameter kind is for
// handling this special case.
- kQuickParameter = 5,
+ kQuickParameter = 6,
// Unallocated location represents a location that is not fixed and can be
// allocated by a register allocator. Each unallocated location has
// a policy that specifies what kind of location is suitable. Payload
// contains register allocation policy.
- kUnallocated = 6,
+ kUnallocated = 7,
};
Location() : value_(kInvalid) {
@@ -59,8 +60,8 @@
COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError);
COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError);
COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kQuickParameter & kLocationTagMask) != kConstant, TagError);
COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError);
- COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError);
DCHECK(!IsValid());
}
@@ -173,7 +174,7 @@
x86_64::X86_64ManagedRegister AsX86_64() const;
Kind GetKind() const {
- return KindField::Decode(value_);
+ return IsConstant() ? kConstant : KindField::Decode(value_);
}
bool Equals(Location other) const {
@@ -265,6 +266,34 @@
uword value_;
};
+class RegisterSet : public ValueObject {
+ public:
+ RegisterSet() : core_registers_(0), floating_point_registers_(0) {}
+
+ void Add(Location loc) {
+ // TODO: floating point registers.
+ core_registers_ |= (1 << loc.reg().RegId());
+ }
+
+ bool ContainsCoreRegister(uint32_t id) {
+ return Contains(core_registers_, id);
+ }
+
+ bool ContainsFloatingPointRegister(uint32_t id) {
+ return Contains(floating_point_registers_, id);
+ }
+
+ static bool Contains(uint32_t register_set, uint32_t reg) {
+ return (register_set & (1 << reg)) != 0;
+ }
+
+ private:
+ uint32_t core_registers_;
+ uint32_t floating_point_registers_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterSet);
+};
+
/**
* The code generator computes LocationSummary for each instruction so that
* the instruction itself knows what code to generate: where to find the inputs
@@ -275,7 +304,13 @@
*/
class LocationSummary : public ArenaObject {
public:
- explicit LocationSummary(HInstruction* instruction);
+ enum CallKind {
+ kNoCall,
+ kCallOnSlowPath,
+ kCall
+ };
+
+ LocationSummary(HInstruction* instruction, CallKind call_kind = kNoCall);
void SetInAt(uint32_t at, Location location) {
inputs_.Put(at, location);
@@ -309,12 +344,74 @@
return temps_.Size();
}
+ void SetEnvironmentAt(uint32_t at, Location location) {
+ environment_.Put(at, location);
+ }
+
+ Location GetEnvironmentAt(uint32_t at) const {
+ return environment_.Get(at);
+ }
+
Location Out() const { return output_; }
+ bool CanCall() const { return call_kind_ != kNoCall; }
+ bool WillCall() const { return call_kind_ == kCall; }
+ bool OnlyCallsOnSlowPath() const { return call_kind_ == kCallOnSlowPath; }
+ bool NeedsSafepoint() const { return CanCall(); }
+
+ void SetStackBit(uint32_t index) {
+ stack_mask_->SetBit(index);
+ }
+
+ void ClearStackBit(uint32_t index) {
+ stack_mask_->ClearBit(index);
+ }
+
+ void SetRegisterBit(uint32_t reg_id) {
+ register_mask_ |= (1 << reg_id);
+ }
+
+ bool RegisterContainsObject(uint32_t reg_id) {
+ return RegisterSet::Contains(register_mask_, reg_id);
+ }
+
+ void AddLiveRegister(Location location) {
+ live_registers_.Add(location);
+ }
+
+ BitVector* GetStackMask() const {
+ return stack_mask_;
+ }
+
+ RegisterSet* GetLiveRegisters() {
+ return &live_registers_;
+ }
+
+ bool InputOverlapsWithOutputOrTemp(uint32_t input, bool is_environment) const {
+ if (is_environment) return true;
+ Location location = Out();
+ // TODO: Add more policies.
+ if (input == 0 && location.IsUnallocated() && location.GetPolicy() == Location::kSameAsFirstInput) {
+ return false;
+ }
+ return true;
+ }
+
private:
GrowableArray<Location> inputs_;
GrowableArray<Location> temps_;
+ GrowableArray<Location> environment_;
Location output_;
+ const CallKind call_kind_;
+
+ // Mask of objects that live in the stack.
+ BitVector* stack_mask_;
+
+ // Mask of objects that live in register.
+ uint32_t register_mask_;
+
+ // Registers that are in use at this position.
+ RegisterSet live_registers_;
DISALLOW_COPY_AND_ASSIGN(LocationSummary);
};
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 490d345..5c4ab8e 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -124,6 +124,7 @@
// dominator of the block. We can then start visiting its successors.
if (visits->Get(block->GetBlockId()) ==
block->GetPredecessors().Size() - block->NumberOfBackEdges()) {
+ block->GetDominator()->AddDominatedBlock(block);
reverse_post_order_.Add(block);
for (size_t i = 0; i < block->GetSuccessors().Size(); i++) {
VisitBlockForDominatorTree(block->GetSuccessors().Get(i), block, visits);
@@ -140,7 +141,7 @@
void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) {
// Insert a new node between `block` and `successor` to split the
// critical edge.
- HBasicBlock* new_block = new (arena_) HBasicBlock(this);
+ HBasicBlock* new_block = new (arena_) HBasicBlock(this, successor->GetDexPc());
AddBlock(new_block);
new_block->AddInstruction(new (arena_) HGoto());
block->ReplaceSuccessor(successor, new_block);
@@ -161,8 +162,10 @@
// If there are more than one back edge, make them branch to the same block that
// will become the only back edge. This simplifies finding natural loops in the
// graph.
- if (info->NumberOfBackEdges() > 1) {
- HBasicBlock* new_back_edge = new (arena_) HBasicBlock(this);
+ // Also, if the loop is a do/while (that is the back edge is an if), change the
+ // back edge to be a goto. This simplifies code generation of suspend cheks.
+ if (info->NumberOfBackEdges() > 1 || info->GetBackEdges().Get(0)->GetLastInstruction()->IsIf()) {
+ HBasicBlock* new_back_edge = new (arena_) HBasicBlock(this, header->GetDexPc());
AddBlock(new_back_edge);
new_back_edge->AddInstruction(new (arena_) HGoto());
for (size_t pred = 0, e = info->GetBackEdges().Size(); pred < e; ++pred) {
@@ -179,7 +182,7 @@
// loop.
size_t number_of_incomings = header->GetPredecessors().Size() - info->NumberOfBackEdges();
if (number_of_incomings != 1) {
- HBasicBlock* pre_header = new (arena_) HBasicBlock(this);
+ HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
AddBlock(pre_header);
pre_header->AddInstruction(new (arena_) HGoto());
@@ -194,6 +197,23 @@
}
pre_header->AddSuccessor(header);
}
+
+ // Make sure the second predecessor of a loop header is the back edge.
+ if (header->GetPredecessors().Get(1) != info->GetBackEdges().Get(0)) {
+ header->SwapPredecessors();
+ }
+
+ // Place the suspend check at the beginning of the header, so that live registers
+ // will be known when allocating registers. Note that code generation can still
+ // generate the suspend check at the back edge, but needs to be careful with
+ // loop phi spill slots (which are not written to at back edge).
+ HInstruction* first_instruction = header->GetFirstInstruction();
+ if (!first_instruction->IsSuspendCheck()) {
+ HSuspendCheck* check = new (arena_) HSuspendCheck(header->GetDexPc());
+ header->InsertInstructionBefore(check, first_instruction);
+ first_instruction = check;
+ }
+ info->SetSuspendCheck(first_instruction->AsSuspendCheck());
}
void HGraph::SimplifyCFG() {
@@ -307,6 +327,14 @@
instruction->SetId(GetGraph()->GetNextInstructionId());
}
+void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial,
+ HInstruction* replacement) {
+ DCHECK(initial->GetBlock() == this);
+ InsertInstructionBefore(replacement, initial);
+ initial->ReplaceWith(replacement);
+ RemoveInstruction(initial);
+}
+
static void Add(HInstructionList* instruction_list,
HBasicBlock* block,
HInstruction* instruction) {
@@ -337,6 +365,16 @@
for (size_t i = 0; i < instruction->InputCount(); i++) {
instruction->InputAt(i)->RemoveUser(instruction, i);
}
+
+ HEnvironment* environment = instruction->GetEnvironment();
+ if (environment != nullptr) {
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* vreg = environment->GetInstructionAt(i);
+ if (vreg != nullptr) {
+ vreg->RemoveEnvironmentUser(environment, i);
+ }
+ }
+ }
}
void HBasicBlock::RemoveInstruction(HInstruction* instruction) {
@@ -347,13 +385,16 @@
Remove(&phis_, this, phi);
}
-void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
- HUseListNode<HInstruction>* previous = nullptr;
- HUseListNode<HInstruction>* current = uses_;
+template <typename T>
+static void RemoveFromUseList(T* user,
+ size_t input_index,
+ HUseListNode<T>** list) {
+ HUseListNode<T>* previous = nullptr;
+ HUseListNode<T>* current = *list;
while (current != nullptr) {
if (current->GetUser() == user && current->GetIndex() == input_index) {
if (previous == NULL) {
- uses_ = current->GetTail();
+ *list = current->GetTail();
} else {
previous->SetTail(current->GetTail());
}
@@ -363,6 +404,14 @@
}
}
+void HInstruction::RemoveUser(HInstruction* user, size_t input_index) {
+ RemoveFromUseList(user, input_index, &uses_);
+}
+
+void HInstruction::RemoveEnvironmentUser(HEnvironment* user, size_t input_index) {
+ RemoveFromUseList(user, input_index, &env_uses_);
+}
+
void HInstructionList::AddInstruction(HInstruction* instruction) {
if (first_instruction_ == nullptr) {
DCHECK(last_instruction_ == nullptr);
@@ -392,6 +441,63 @@
}
}
+bool HInstructionList::Contains(HInstruction* instruction) const {
+ for (HInstructionIterator it(*this); !it.Done(); it.Advance()) {
+ if (it.Current() == instruction) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool HInstructionList::FoundBefore(const HInstruction* instruction1,
+ const HInstruction* instruction2) const {
+ DCHECK_EQ(instruction1->GetBlock(), instruction2->GetBlock());
+ for (HInstructionIterator it(*this); !it.Done(); it.Advance()) {
+ if (it.Current() == instruction1) {
+ return true;
+ }
+ if (it.Current() == instruction2) {
+ return false;
+ }
+ }
+ LOG(FATAL) << "Did not find an order between two instructions of the same block.";
+ return true;
+}
+
+bool HInstruction::Dominates(HInstruction* other_instruction) const {
+ HBasicBlock* block = GetBlock();
+ HBasicBlock* other_block = other_instruction->GetBlock();
+ if (block != other_block) {
+ return GetBlock()->Dominates(other_instruction->GetBlock());
+ } else {
+ // If both instructions are in the same block, ensure this
+ // instruction comes before `other_instruction`.
+ if (IsPhi()) {
+ if (!other_instruction->IsPhi()) {
+ // Phis appear before non phi-instructions so this instruction
+ // dominates `other_instruction`.
+ return true;
+ } else {
+ // There is no order among phis.
+ LOG(FATAL) << "There is no dominance between phis of a same block.";
+ return false;
+ }
+ } else {
+ // `this` is not a phi.
+ if (other_instruction->IsPhi()) {
+ // Phis appear before non phi-instructions so this instruction
+ // does not dominate `other_instruction`.
+ return false;
+ } else {
+ // Check whether this instruction comes before
+ // `other_instruction` in the instruction list.
+ return block->GetInstructions().FoundBefore(this, other_instruction);
+ }
+ }
+ }
+}
+
void HInstruction::ReplaceWith(HInstruction* other) {
DCHECK(other != nullptr);
for (HUseIterator<HInstruction> it(GetUses()); !it.Done(); it.Advance()) {
@@ -414,6 +520,10 @@
env_uses_ = nullptr;
}
+size_t HInstruction::EnvironmentSize() const {
+ return HasEnvironment() ? environment_->Size() : 0;
+}
+
void HPhi::AddInput(HInstruction* input) {
DCHECK(input->GetBlock() != nullptr);
inputs_.Add(input);
@@ -445,6 +555,18 @@
}
}
+HConstant* HBinaryOperation::TryStaticEvaluation(ArenaAllocator* allocator) const {
+ if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) {
+ int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(),
+ GetRight()->AsIntConstant()->GetValue());
+ return new(allocator) HIntConstant(value);
+ } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
+ int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
+ GetRight()->AsLongConstant()->GetValue());
+ return new(allocator) HLongConstant(value);
+ }
+ return nullptr;
+}
bool HCondition::NeedsMaterialization() const {
if (!HasOnlyOneUse()) {
@@ -456,12 +578,34 @@
return true;
}
- // TODO: should we allow intervening instructions with no side-effect between this condition
- // and the If instruction?
+ // TODO: if there is no intervening instructions with side-effect between this condition
+ // and the If instruction, we should move the condition just before the If.
if (GetNext() != user) {
return true;
}
return false;
}
+bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const {
+ HInstruction* previous = if_->GetPrevious();
+ while (previous != nullptr && previous->IsParallelMove()) {
+ previous = previous->GetPrevious();
+ }
+ return previous == this;
+}
+
+bool HInstruction::Equals(HInstruction* other) const {
+ if (!InstructionTypeEquals(other)) return false;
+ DCHECK_EQ(GetKind(), other->GetKind());
+ if (!InstructionDataEquals(other)) return false;
+ if (GetType() != other->GetType()) return false;
+ if (InputCount() != other->InputCount()) return false;
+
+ for (size_t i = 0, e = InputCount(); i < e; ++i) {
+ if (InputAt(i) != other->InputAt(i)) return false;
+ }
+ DCHECK_EQ(ComputeHashCode(), other->ComputeHashCode());
+ return true;
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index cb3dd0f..3d65366 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -32,12 +32,14 @@
class HIntConstant;
class HGraphVisitor;
class HPhi;
+class HSuspendCheck;
class LiveInterval;
class LocationSummary;
static const int kDefaultNumberOfBlocks = 8;
static const int kDefaultNumberOfSuccessors = 2;
static const int kDefaultNumberOfPredecessors = 2;
+static const int kDefaultNumberOfDominatedBlocks = 1;
static const int kDefaultNumberOfBackEdges = 1;
enum IfCondition {
@@ -56,6 +58,15 @@
void AddInstruction(HInstruction* instruction);
void RemoveInstruction(HInstruction* instruction);
+ // Return true if this list contains `instruction`.
+ bool Contains(HInstruction* instruction) const;
+
+ // Return true if `instruction1` is found before `instruction2` in
+ // this instruction list and false otherwise. Abort if none
+ // of these instructions is found.
+ bool FoundBefore(const HInstruction* instruction1,
+ const HInstruction* instruction2) const;
+
private:
HInstruction* first_instruction_;
HInstruction* last_instruction_;
@@ -191,13 +202,19 @@
public:
HLoopInformation(HBasicBlock* header, HGraph* graph)
: header_(header),
+ suspend_check_(nullptr),
back_edges_(graph->GetArena(), kDefaultNumberOfBackEdges),
- blocks_(graph->GetArena(), graph->GetBlocks().Size(), false) {}
+ // Make bit vector growable, as the number of blocks may change.
+ blocks_(graph->GetArena(), graph->GetBlocks().Size(), true) {}
HBasicBlock* GetHeader() const {
return header_;
}
+ HSuspendCheck* GetSuspendCheck() const { return suspend_check_; }
+ void SetSuspendCheck(HSuspendCheck* check) { suspend_check_ = check; }
+ bool HasSuspendCheck() const { return suspend_check_ != nullptr; }
+
void AddBackEdge(HBasicBlock* back_edge) {
back_edges_.Add(back_edge);
}
@@ -246,6 +263,7 @@
void PopulateRecursive(HBasicBlock* block);
HBasicBlock* header_;
+ HSuspendCheck* suspend_check_;
GrowableArray<HBasicBlock*> back_edges_;
ArenaBitVector blocks_;
@@ -253,19 +271,23 @@
};
static constexpr size_t kNoLifetime = -1;
+static constexpr uint32_t kNoDexPc = -1;
// A block in a method. Contains the list of instructions represented
// as a double linked list. Each block knows its predecessors and
// successors.
+
class HBasicBlock : public ArenaObject {
public:
- explicit HBasicBlock(HGraph* graph)
+ explicit HBasicBlock(HGraph* graph, uint32_t dex_pc = kNoDexPc)
: graph_(graph),
predecessors_(graph->GetArena(), kDefaultNumberOfPredecessors),
successors_(graph->GetArena(), kDefaultNumberOfSuccessors),
loop_information_(nullptr),
dominator_(nullptr),
+ dominated_blocks_(graph->GetArena(), kDefaultNumberOfDominatedBlocks),
block_id_(-1),
+ dex_pc_(dex_pc),
lifetime_start_(kNoLifetime),
lifetime_end_(kNoLifetime) {}
@@ -277,6 +299,18 @@
return successors_;
}
+ const GrowableArray<HBasicBlock*>& GetDominatedBlocks() const {
+ return dominated_blocks_;
+ }
+
+ bool IsEntryBlock() const {
+ return graph_->GetEntryBlock() == this;
+ }
+
+ bool IsExitBlock() const {
+ return graph_->GetExitBlock() == this;
+ }
+
void AddBackEdge(HBasicBlock* back_edge) {
if (loop_information_ == nullptr) {
loop_information_ = new (graph_->GetArena()) HLoopInformation(this, graph_);
@@ -292,6 +326,7 @@
HBasicBlock* GetDominator() const { return dominator_; }
void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
+ void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
int NumberOfBackEdges() const {
return loop_information_ == nullptr
@@ -331,6 +366,13 @@
block->successors_.Add(this);
}
+ void SwapPredecessors() {
+ DCHECK_EQ(predecessors_.Size(), 2u);
+ HBasicBlock* temp = predecessors_.Get(0);
+ predecessors_.Put(0, predecessors_.Get(1));
+ predecessors_.Put(1, temp);
+ }
+
size_t GetPredecessorIndexOf(HBasicBlock* predecessor) {
for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) {
if (predecessors_.Get(i) == predecessor) {
@@ -352,6 +394,9 @@
void AddInstruction(HInstruction* instruction);
void RemoveInstruction(HInstruction* instruction);
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
+ // Replace instruction `initial` with `replacement` within this block.
+ void ReplaceAndRemoveInstructionWith(HInstruction* initial,
+ HInstruction* replacement);
void AddPhi(HPhi* phi);
void RemovePhi(HPhi* phi);
@@ -359,6 +404,12 @@
return (loop_information_ != nullptr) && (loop_information_->GetHeader() == this);
}
+ bool IsLoopPreHeaderFirstPredecessor() const {
+ DCHECK(IsLoopHeader());
+ DCHECK(!GetPredecessors().IsEmpty());
+ return GetPredecessors().Get(0) == GetLoopInformation()->GetPreHeader();
+ }
+
HLoopInformation* GetLoopInformation() const {
return loop_information_;
}
@@ -393,6 +444,8 @@
void SetLifetimeStart(size_t start) { lifetime_start_ = start; }
void SetLifetimeEnd(size_t end) { lifetime_end_ = end; }
+ uint32_t GetDexPc() const { return dex_pc_; }
+
private:
HGraph* const graph_;
GrowableArray<HBasicBlock*> predecessors_;
@@ -401,7 +454,10 @@
HInstructionList phis_;
HLoopInformation* loop_information_;
HBasicBlock* dominator_;
+ GrowableArray<HBasicBlock*> dominated_blocks_;
int block_id_;
+ // The dex program counter of the first instruction of this block.
+ const uint32_t dex_pc_;
size_t lifetime_start_;
size_t lifetime_end_;
@@ -422,6 +478,7 @@
M(If) \
M(IntConstant) \
M(InvokeStatic) \
+ M(InvokeVirtual) \
M(LoadLocal) \
M(Local) \
M(LongConstant) \
@@ -443,19 +500,26 @@
M(BoundsCheck) \
M(NullCheck) \
M(Temporary) \
+ M(SuspendCheck) \
#define FOR_EACH_INSTRUCTION(M) \
FOR_EACH_CONCRETE_INSTRUCTION(M) \
- M(Constant)
+ M(Constant) \
+ M(BinaryOperation)
#define FORWARD_DECLARATION(type) class H##type;
FOR_EACH_INSTRUCTION(FORWARD_DECLARATION)
#undef FORWARD_DECLARATION
-#define DECLARE_INSTRUCTION(type) \
- virtual const char* DebugName() const { return #type; } \
- virtual H##type* As##type() { return this; } \
- virtual void Accept(HGraphVisitor* visitor) \
+#define DECLARE_INSTRUCTION(type) \
+ virtual InstructionKind GetKind() const { return k##type; } \
+ virtual const char* DebugName() const { return #type; } \
+ virtual const H##type* As##type() const OVERRIDE { return this; } \
+ virtual H##type* As##type() OVERRIDE { return this; } \
+ virtual bool InstructionTypeEquals(HInstruction* other) const { \
+ return other->Is##type(); \
+ } \
+ virtual void Accept(HGraphVisitor* visitor)
template <typename T>
class HUseListNode : public ArenaObject {
@@ -477,9 +541,72 @@
DISALLOW_COPY_AND_ASSIGN(HUseListNode);
};
+// Represents the side effects an instruction may have.
+class SideEffects : public ValueObject {
+ public:
+ SideEffects() : flags_(0) {}
+
+ static SideEffects None() {
+ return SideEffects(0);
+ }
+
+ static SideEffects All() {
+ return SideEffects(ChangesSomething().flags_ | DependsOnSomething().flags_);
+ }
+
+ static SideEffects ChangesSomething() {
+ return SideEffects((1 << kFlagChangesCount) - 1);
+ }
+
+ static SideEffects DependsOnSomething() {
+ int count = kFlagDependsOnCount - kFlagChangesCount;
+ return SideEffects(((1 << count) - 1) << kFlagChangesCount);
+ }
+
+ SideEffects Union(SideEffects other) const {
+ return SideEffects(flags_ | other.flags_);
+ }
+
+ bool HasSideEffects() const {
+ size_t all_bits_set = (1 << kFlagChangesCount) - 1;
+ return (flags_ & all_bits_set) != 0;
+ }
+
+ bool HasAllSideEffects() const {
+ size_t all_bits_set = (1 << kFlagChangesCount) - 1;
+ return all_bits_set == (flags_ & all_bits_set);
+ }
+
+ bool DependsOn(SideEffects other) const {
+ size_t depends_flags = other.ComputeDependsFlags();
+ return (flags_ & depends_flags) != 0;
+ }
+
+ bool HasDependencies() const {
+ int count = kFlagDependsOnCount - kFlagChangesCount;
+ size_t all_bits_set = (1 << count) - 1;
+ return ((flags_ >> kFlagChangesCount) & all_bits_set) != 0;
+ }
+
+ private:
+ static constexpr int kFlagChangesSomething = 0;
+ static constexpr int kFlagChangesCount = kFlagChangesSomething + 1;
+
+ static constexpr int kFlagDependsOnSomething = kFlagChangesCount;
+ static constexpr int kFlagDependsOnCount = kFlagDependsOnSomething + 1;
+
+ explicit SideEffects(size_t flags) : flags_(flags) {}
+
+ size_t ComputeDependsFlags() const {
+ return flags_ << kFlagChangesCount;
+ }
+
+ size_t flags_;
+};
+
class HInstruction : public ArenaObject {
public:
- HInstruction()
+ explicit HInstruction(SideEffects side_effects)
: previous_(nullptr),
next_(nullptr),
block_(nullptr),
@@ -490,10 +617,17 @@
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
- lifetime_position_(kNoLifetime) {}
+ lifetime_position_(kNoLifetime),
+ side_effects_(side_effects) {}
virtual ~HInstruction() {}
+#define DECLARE_KIND(type) k##type,
+ enum InstructionKind {
+ FOR_EACH_INSTRUCTION(DECLARE_KIND)
+ };
+#undef DECLARE_KIND
+
HInstruction* GetNext() const { return next_; }
HInstruction* GetPrevious() const { return previous_; }
@@ -501,8 +635,9 @@
void SetBlock(HBasicBlock* block) { block_ = block; }
bool IsInBlock() const { return block_ != nullptr; }
bool IsInLoop() const { return block_->IsInLoop(); }
+ bool IsLoopHeaderPhi() { return IsPhi() && block_->IsLoopHeader(); }
- virtual size_t InputCount() const = 0;
+ virtual size_t InputCount() const = 0;
virtual HInstruction* InputAt(size_t i) const = 0;
virtual void Accept(HGraphVisitor* visitor) = 0;
@@ -513,17 +648,20 @@
virtual bool NeedsEnvironment() const { return false; }
virtual bool IsControlFlow() const { return false; }
+ bool HasSideEffects() const { return side_effects_.HasSideEffects(); }
void AddUseAt(HInstruction* user, size_t index) {
uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HInstruction>(user, index, uses_);
}
void AddEnvUseAt(HEnvironment* user, size_t index) {
+ DCHECK(user != nullptr);
env_uses_ = new (block_->GetGraph()->GetArena()) HUseListNode<HEnvironment>(
user, index, env_uses_);
}
void RemoveUser(HInstruction* user, size_t index);
+ void RemoveEnvironmentUser(HEnvironment* user, size_t index);
HUseListNode<HInstruction>* GetUses() const { return uses_; }
HUseListNode<HEnvironment>* GetEnvUses() const { return env_uses_; }
@@ -542,6 +680,10 @@
return result;
}
+ // Does this instruction dominate `other_instruction`? Aborts if
+ // this instruction and `other_instruction` are both phis.
+ bool Dominates(HInstruction* other_instruction) const;
+
int GetId() const { return id_; }
void SetId(int id) { id_ = id; }
@@ -553,6 +695,10 @@
HEnvironment* GetEnvironment() const { return environment_; }
void SetEnvironment(HEnvironment* environment) { environment_ = environment; }
+ // Returns the number of entries in the environment. Typically, that is the
+ // number of dex registers in a method. It could be more in case of inlining.
+ size_t EnvironmentSize() const;
+
LocationSummary* GetLocations() const { return locations_; }
void SetLocations(LocationSummary* locations) { locations_ = locations; }
@@ -563,12 +709,41 @@
}
#define INSTRUCTION_TYPE_CHECK(type) \
- bool Is##type() { return (As##type() != nullptr); } \
+ bool Is##type() const { return (As##type() != nullptr); } \
+ virtual const H##type* As##type() const { return nullptr; } \
virtual H##type* As##type() { return nullptr; }
FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
#undef INSTRUCTION_TYPE_CHECK
+ // Returns whether the instruction can be moved within the graph.
+ virtual bool CanBeMoved() const { return false; }
+
+ // Returns whether the two instructions are of the same kind.
+ virtual bool InstructionTypeEquals(HInstruction* other) const { return false; }
+
+ // Returns whether any data encoded in the two instructions is equal.
+ // This method does not look at the inputs. Both instructions must be
+ // of the same type, otherwise the method has undefined behavior.
+ virtual bool InstructionDataEquals(HInstruction* other) const { return false; }
+
+ // Returns whether two instructions are equal, that is:
+ // 1) They have the same type and contain the same data,
+ // 2) Their inputs are identical.
+ bool Equals(HInstruction* other) const;
+
+ virtual InstructionKind GetKind() const = 0;
+
+ virtual size_t ComputeHashCode() const {
+ size_t result = GetKind();
+ for (size_t i = 0, e = InputCount(); i < e; ++i) {
+ result = (result * 31) + InputAt(i)->GetId();
+ }
+ return result;
+ }
+
+ SideEffects GetSideEffects() const { return side_effects_; }
+
size_t GetLifetimePosition() const { return lifetime_position_; }
void SetLifetimePosition(size_t position) { lifetime_position_ = position; }
LiveInterval* GetLiveInterval() const { return live_interval_; }
@@ -582,7 +757,7 @@
// An instruction gets an id when it is added to the graph.
// It reflects creation order. A negative id means the instruction
- // has not beed added to the graph.
+ // has not been added to the graph.
int id_;
// When doing liveness analysis, instructions that have uses get an SSA index.
@@ -594,6 +769,8 @@
// List of environments that contain this instruction.
HUseListNode<HEnvironment>* env_uses_;
+ // The environment associated with this instruction. Not null if the instruction
+ // might jump out of the method.
HEnvironment* environment_;
// Set by the code generator.
@@ -606,6 +783,8 @@
// order of blocks where this instruction's live interval start.
size_t lifetime_position_;
+ const SideEffects side_effects_;
+
friend class HBasicBlock;
friend class HInstructionList;
@@ -659,10 +838,16 @@
vregs_.Put(index, instruction);
}
+ HInstruction* GetInstructionAt(size_t index) const {
+ return vregs_.Get(index);
+ }
+
GrowableArray<HInstruction*>* GetVRegs() {
return &vregs_;
}
+ size_t Size() const { return vregs_.Size(); }
+
private:
GrowableArray<HInstruction*> vregs_;
@@ -776,7 +961,8 @@
template<intptr_t N>
class HTemplateInstruction: public HInstruction {
public:
- HTemplateInstruction<N>() : inputs_() {}
+ HTemplateInstruction<N>(SideEffects side_effects)
+ : HInstruction(side_effects), inputs_() {}
virtual ~HTemplateInstruction() {}
virtual size_t InputCount() const { return N; }
@@ -794,9 +980,10 @@
};
template<intptr_t N>
-class HExpression: public HTemplateInstruction<N> {
+class HExpression : public HTemplateInstruction<N> {
public:
- explicit HExpression<N>(Primitive::Type type) : type_(type) {}
+ HExpression<N>(Primitive::Type type, SideEffects side_effects)
+ : HTemplateInstruction<N>(side_effects), type_(type) {}
virtual ~HExpression() {}
virtual Primitive::Type GetType() const { return type_; }
@@ -809,7 +996,7 @@
// instruction that branches to the exit block.
class HReturnVoid : public HTemplateInstruction<0> {
public:
- HReturnVoid() {}
+ HReturnVoid() : HTemplateInstruction(SideEffects::None()) {}
virtual bool IsControlFlow() const { return true; }
@@ -823,7 +1010,7 @@
// instruction that branches to the exit block.
class HReturn : public HTemplateInstruction<1> {
public:
- explicit HReturn(HInstruction* value) {
+ explicit HReturn(HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
SetRawInputAt(0, value);
}
@@ -840,7 +1027,7 @@
// exit block.
class HExit : public HTemplateInstruction<0> {
public:
- HExit() {}
+ HExit() : HTemplateInstruction(SideEffects::None()) {}
virtual bool IsControlFlow() const { return true; }
@@ -853,14 +1040,14 @@
// Jumps from one block to another.
class HGoto : public HTemplateInstruction<0> {
public:
- HGoto() {}
+ HGoto() : HTemplateInstruction(SideEffects::None()) {}
+
+ virtual bool IsControlFlow() const { return true; }
HBasicBlock* GetSuccessor() const {
return GetBlock()->GetSuccessors().Get(0);
}
- virtual bool IsControlFlow() const { return true; }
-
DECLARE_INSTRUCTION(Goto);
private:
@@ -872,10 +1059,12 @@
// two successors.
class HIf : public HTemplateInstruction<1> {
public:
- explicit HIf(HInstruction* input) {
+ explicit HIf(HInstruction* input) : HTemplateInstruction(SideEffects::None()) {
SetRawInputAt(0, input);
}
+ virtual bool IsControlFlow() const { return true; }
+
HBasicBlock* IfTrueSuccessor() const {
return GetBlock()->GetSuccessors().Get(0);
}
@@ -884,8 +1073,6 @@
return GetBlock()->GetSuccessors().Get(1);
}
- virtual bool IsControlFlow() const { return true; }
-
DECLARE_INSTRUCTION(If);
virtual bool IsIfInstruction() const { return true; }
@@ -898,7 +1085,7 @@
public:
HBinaryOperation(Primitive::Type result_type,
HInstruction* left,
- HInstruction* right) : HExpression(result_type) {
+ HInstruction* right) : HExpression(result_type, SideEffects::None()) {
SetRawInputAt(0, left);
SetRawInputAt(1, right);
}
@@ -909,6 +1096,20 @@
virtual bool IsCommutative() { return false; }
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
+ // Try to statically evaluate `operation` and return an HConstant
+ // containing the result of this evaluation. If `operation` cannot
+ // be evaluated as a constant, return nullptr.
+ HConstant* TryStaticEvaluation(ArenaAllocator* allocator) const;
+
+ // Apply this operation to `x` and `y`.
+ virtual int32_t Evaluate(int32_t x, int32_t y) const = 0;
+ virtual int64_t Evaluate(int64_t x, int64_t y) const = 0;
+
+ DECLARE_INSTRUCTION(BinaryOperation);
+
private:
DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
};
@@ -919,8 +1120,15 @@
: HBinaryOperation(Primitive::kPrimBoolean, first, second) {}
virtual bool IsCommutative() { return true; }
+
+ // For register allocation purposes, returns whether this instruction needs to be
+ // materialized (that is, not just be in the processor flags).
bool NeedsMaterialization() const;
+ // For code generation purposes, returns whether this instruction is just before
+ // `if_`, and disregard moves in between.
+ bool IsBeforeWhenDisregardMoves(HIf* if_) const;
+
DECLARE_INSTRUCTION(Condition);
virtual IfCondition GetCondition() const = 0;
@@ -935,6 +1143,9 @@
HEqual(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x == y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x == y; }
+
DECLARE_INSTRUCTION(Equal);
virtual IfCondition GetCondition() const {
@@ -950,6 +1161,9 @@
HNotEqual(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x != y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x != y; }
+
DECLARE_INSTRUCTION(NotEqual);
virtual IfCondition GetCondition() const {
@@ -965,6 +1179,9 @@
HLessThan(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x < y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x < y; }
+
DECLARE_INSTRUCTION(LessThan);
virtual IfCondition GetCondition() const {
@@ -980,6 +1197,9 @@
HLessThanOrEqual(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x <= y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x <= y; }
+
DECLARE_INSTRUCTION(LessThanOrEqual);
virtual IfCondition GetCondition() const {
@@ -995,6 +1215,9 @@
HGreaterThan(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x > y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x > y; }
+
DECLARE_INSTRUCTION(GreaterThan);
virtual IfCondition GetCondition() const {
@@ -1010,6 +1233,9 @@
HGreaterThanOrEqual(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x >= y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x >= y; }
+
DECLARE_INSTRUCTION(GreaterThanOrEqual);
virtual IfCondition GetCondition() const {
@@ -1031,6 +1257,19 @@
DCHECK_EQ(type, second->GetType());
}
+ virtual int32_t Evaluate(int32_t x, int32_t y) const {
+ return
+ x == y ? 0 :
+ x > y ? 1 :
+ -1;
+ }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const {
+ return
+ x == y ? 0 :
+ x > y ? 1 :
+ -1;
+ }
+
DECLARE_INSTRUCTION(Compare);
private:
@@ -1040,7 +1279,8 @@
// A local in the graph. Corresponds to a Dex register.
class HLocal : public HTemplateInstruction<0> {
public:
- explicit HLocal(uint16_t reg_number) : reg_number_(reg_number) {}
+ explicit HLocal(uint16_t reg_number)
+ : HTemplateInstruction(SideEffects::None()), reg_number_(reg_number) {}
DECLARE_INSTRUCTION(Local);
@@ -1056,7 +1296,8 @@
// Load a given local. The local is an input of this instruction.
class HLoadLocal : public HExpression<1> {
public:
- explicit HLoadLocal(HLocal* local, Primitive::Type type) : HExpression(type) {
+ HLoadLocal(HLocal* local, Primitive::Type type)
+ : HExpression(type, SideEffects::None()) {
SetRawInputAt(0, local);
}
@@ -1072,7 +1313,7 @@
// and the local.
class HStoreLocal : public HTemplateInstruction<2> {
public:
- HStoreLocal(HLocal* local, HInstruction* value) {
+ HStoreLocal(HLocal* local, HInstruction* value) : HTemplateInstruction(SideEffects::None()) {
SetRawInputAt(0, local);
SetRawInputAt(1, value);
}
@@ -1087,7 +1328,9 @@
class HConstant : public HExpression<0> {
public:
- explicit HConstant(Primitive::Type type) : HExpression(type) {}
+ explicit HConstant(Primitive::Type type) : HExpression(type, SideEffects::None()) {}
+
+ virtual bool CanBeMoved() const { return true; }
DECLARE_INSTRUCTION(Constant);
@@ -1103,6 +1346,12 @@
int32_t GetValue() const { return value_; }
+ virtual bool InstructionDataEquals(HInstruction* other) const {
+ return other->AsIntConstant()->value_ == value_;
+ }
+
+ virtual size_t ComputeHashCode() const { return GetValue(); }
+
DECLARE_INSTRUCTION(IntConstant);
private:
@@ -1117,6 +1366,12 @@
int64_t GetValue() const { return value_; }
+ virtual bool InstructionDataEquals(HInstruction* other) const {
+ return other->AsLongConstant()->value_ == value_;
+ }
+
+ virtual size_t ComputeHashCode() const { return static_cast<size_t>(GetValue()); }
+
DECLARE_INSTRUCTION(LongConstant);
private:
@@ -1131,7 +1386,8 @@
uint32_t number_of_arguments,
Primitive::Type return_type,
uint32_t dex_pc)
- : inputs_(arena, number_of_arguments),
+ : HInstruction(SideEffects::All()),
+ inputs_(arena, number_of_arguments),
return_type_(return_type),
dex_pc_(dex_pc) {
inputs_.SetSize(number_of_arguments);
@@ -1185,10 +1441,32 @@
DISALLOW_COPY_AND_ASSIGN(HInvokeStatic);
};
+class HInvokeVirtual : public HInvoke {
+ public:
+ HInvokeVirtual(ArenaAllocator* arena,
+ uint32_t number_of_arguments,
+ Primitive::Type return_type,
+ uint32_t dex_pc,
+ uint32_t vtable_index)
+ : HInvoke(arena, number_of_arguments, return_type, dex_pc),
+ vtable_index_(vtable_index) {}
+
+ uint32_t GetVTableIndex() const { return vtable_index_; }
+
+ DECLARE_INSTRUCTION(InvokeVirtual);
+
+ private:
+ const uint32_t vtable_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual);
+};
+
class HNewInstance : public HExpression<0> {
public:
- HNewInstance(uint32_t dex_pc, uint16_t type_index) : HExpression(Primitive::kPrimNot),
- dex_pc_(dex_pc), type_index_(type_index) {}
+ HNewInstance(uint32_t dex_pc, uint16_t type_index)
+ : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ dex_pc_(dex_pc),
+ type_index_(type_index) {}
uint32_t GetDexPc() const { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
@@ -1212,6 +1490,9 @@
virtual bool IsCommutative() { return true; }
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x + y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x + y; }
+
DECLARE_INSTRUCTION(Add);
private:
@@ -1225,6 +1506,9 @@
virtual bool IsCommutative() { return false; }
+ virtual int32_t Evaluate(int32_t x, int32_t y) const { return x + y; }
+ virtual int64_t Evaluate(int64_t x, int64_t y) const { return x + y; }
+
DECLARE_INSTRUCTION(Sub);
private:
@@ -1236,7 +1520,7 @@
class HParameterValue : public HExpression<0> {
public:
HParameterValue(uint8_t index, Primitive::Type parameter_type)
- : HExpression(parameter_type), index_(index) {}
+ : HExpression(parameter_type, SideEffects::None()), index_(index) {}
uint8_t GetIndex() const { return index_; }
@@ -1252,10 +1536,13 @@
class HNot : public HExpression<1> {
public:
- explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean) {
+ explicit HNot(HInstruction* input) : HExpression(Primitive::kPrimBoolean, SideEffects::None()) {
SetRawInputAt(0, input);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
DECLARE_INSTRUCTION(Not);
private:
@@ -1265,7 +1552,8 @@
class HPhi : public HInstruction {
public:
HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
- : inputs_(arena, number_of_inputs),
+ : HInstruction(SideEffects::None()),
+ inputs_(arena, number_of_inputs),
reg_number_(reg_number),
type_(type),
is_live_(false) {
@@ -1305,10 +1593,13 @@
class HNullCheck : public HExpression<1> {
public:
HNullCheck(HInstruction* value, uint32_t dex_pc)
- : HExpression(value->GetType()), dex_pc_(dex_pc) {
+ : HExpression(value->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
SetRawInputAt(0, value);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
virtual bool NeedsEnvironment() const { return true; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -1323,13 +1614,15 @@
class FieldInfo : public ValueObject {
public:
- explicit FieldInfo(MemberOffset field_offset)
- : field_offset_(field_offset) {}
+ FieldInfo(MemberOffset field_offset, Primitive::Type field_type)
+ : field_offset_(field_offset), field_type_(field_type) {}
MemberOffset GetFieldOffset() const { return field_offset_; }
+ Primitive::Type GetFieldType() const { return field_type_; }
private:
const MemberOffset field_offset_;
+ const Primitive::Type field_type_;
};
class HInstanceFieldGet : public HExpression<1> {
@@ -1337,11 +1630,23 @@
HInstanceFieldGet(HInstruction* value,
Primitive::Type field_type,
MemberOffset field_offset)
- : HExpression(field_type), field_info_(field_offset) {
+ : HExpression(field_type, SideEffects::DependsOnSomething()),
+ field_info_(field_offset, field_type) {
SetRawInputAt(0, value);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const {
+ size_t other_offset = other->AsInstanceFieldGet()->GetFieldOffset().SizeValue();
+ return other_offset == GetFieldOffset().SizeValue();
+ }
+
+ virtual size_t ComputeHashCode() const {
+ return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
+ }
+
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+ Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
DECLARE_INSTRUCTION(InstanceFieldGet);
@@ -1355,13 +1660,16 @@
public:
HInstanceFieldSet(HInstruction* object,
HInstruction* value,
+ Primitive::Type field_type,
MemberOffset field_offset)
- : field_info_(field_offset) {
+ : HTemplateInstruction(SideEffects::ChangesSomething()),
+ field_info_(field_offset, field_type) {
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+ Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
DECLARE_INSTRUCTION(InstanceFieldSet);
@@ -1374,11 +1682,14 @@
class HArrayGet : public HExpression<2> {
public:
HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type)
- : HExpression(type) {
+ : HExpression(type, SideEffects::DependsOnSomething()) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
DECLARE_INSTRUCTION(ArrayGet);
private:
@@ -1390,7 +1701,11 @@
HArraySet(HInstruction* array,
HInstruction* index,
HInstruction* value,
- uint32_t dex_pc) : dex_pc_(dex_pc) {
+ Primitive::Type component_type,
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::ChangesSomething()),
+ dex_pc_(dex_pc),
+ component_type_(component_type) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -1404,20 +1719,29 @@
uint32_t GetDexPc() const { return dex_pc_; }
+ Primitive::Type GetComponentType() const { return component_type_; }
+
DECLARE_INSTRUCTION(ArraySet);
private:
const uint32_t dex_pc_;
+ const Primitive::Type component_type_;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
class HArrayLength : public HExpression<1> {
public:
- explicit HArrayLength(HInstruction* array) : HExpression(Primitive::kPrimInt) {
+ explicit HArrayLength(HInstruction* array)
+ : HExpression(Primitive::kPrimInt, SideEffects::None()) {
+ // Note that arrays do not change length, so the instruction does not
+ // depend on any write.
SetRawInputAt(0, array);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
DECLARE_INSTRUCTION(ArrayLength);
private:
@@ -1427,12 +1751,15 @@
class HBoundsCheck : public HExpression<2> {
public:
HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
- : HExpression(index->GetType()), dex_pc_(dex_pc) {
+ : HExpression(index->GetType(), SideEffects::None()), dex_pc_(dex_pc) {
DCHECK(index->GetType() == Primitive::kPrimInt);
SetRawInputAt(0, index);
SetRawInputAt(1, length);
}
+ virtual bool CanBeMoved() const { return true; }
+ virtual bool InstructionDataEquals(HInstruction* other) const { return true; }
+
virtual bool NeedsEnvironment() const { return true; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -1454,7 +1781,7 @@
*/
class HTemporary : public HTemplateInstruction<0> {
public:
- explicit HTemporary(size_t index) : index_(index) {}
+ explicit HTemporary(size_t index) : HTemplateInstruction(SideEffects::None()), index_(index) {}
size_t GetIndex() const { return index_; }
@@ -1466,10 +1793,29 @@
DISALLOW_COPY_AND_ASSIGN(HTemporary);
};
+class HSuspendCheck : public HTemplateInstruction<0> {
+ public:
+ explicit HSuspendCheck(uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::None()), dex_pc_(dex_pc) {}
+
+ virtual bool NeedsEnvironment() const {
+ return true;
+ }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+
+ DECLARE_INSTRUCTION(SuspendCheck);
+
+ private:
+ const uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);
+};
+
class MoveOperands : public ArenaObject {
public:
- MoveOperands(Location source, Location destination)
- : source_(source), destination_(destination) {}
+ MoveOperands(Location source, Location destination, HInstruction* instruction)
+ : source_(source), destination_(destination), instruction_(instruction) {}
Location GetSource() const { return source_; }
Location GetDestination() const { return destination_; }
@@ -1517,9 +1863,16 @@
return source_.IsInvalid();
}
+ HInstruction* GetInstruction() const { return instruction_; }
+
private:
Location source_;
Location destination_;
+ // The instruction this move is assocatied with. Null when this move is
+ // for moving an input in the expected locations of user (including a phi user).
+ // This is only used in debug mode, to ensure we do not connect interval siblings
+ // in the same parallel move.
+ HInstruction* instruction_;
DISALLOW_COPY_AND_ASSIGN(MoveOperands);
};
@@ -1528,9 +1881,16 @@
class HParallelMove : public HTemplateInstruction<0> {
public:
- explicit HParallelMove(ArenaAllocator* arena) : moves_(arena, kDefaultNumberOfMoves) {}
+ explicit HParallelMove(ArenaAllocator* arena)
+ : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
void AddMove(MoveOperands* move) {
+ if (kIsDebugBuild && move->GetInstruction() != nullptr) {
+ for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+ DCHECK_NE(moves_.Get(i)->GetInstruction(), move->GetInstruction())
+ << "Doing parallel moves for the same instruction.";
+ }
+ }
moves_.Add(move);
}
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
new file mode 100644
index 0000000..b75bacb
--- /dev/null
+++ b/compiler/optimizing/nodes_test.cc
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include "nodes.h"
+#include "utils/arena_allocator.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+/**
+ * Test that removing instruction from the graph removes itself from user lists
+ * and environment lists.
+ */
+TEST(Node, RemoveInstruction) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+
+ HGraph* graph = new (&allocator) HGraph(&allocator);
+ HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(entry);
+ graph->SetEntryBlock(entry);
+ HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
+ entry->AddInstruction(parameter);
+ entry->AddInstruction(new (&allocator) HGoto());
+
+ HBasicBlock* first_block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(first_block);
+ entry->AddSuccessor(first_block);
+ HInstruction* null_check = new (&allocator) HNullCheck(parameter, 0);
+ first_block->AddInstruction(null_check);
+ first_block->AddInstruction(new (&allocator) HReturnVoid());
+
+ HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
+ graph->AddBlock(exit_block);
+ first_block->AddSuccessor(exit_block);
+ exit_block->AddInstruction(new (&allocator) HExit());
+
+ HEnvironment* environment = new (&allocator) HEnvironment(&allocator, 1);
+ null_check->SetEnvironment(environment);
+ environment->SetRawEnvAt(0, parameter);
+ parameter->AddEnvUseAt(null_check->GetEnvironment(), 0);
+
+ ASSERT_TRUE(parameter->HasEnvironmentUses());
+ ASSERT_TRUE(parameter->HasUses());
+
+ first_block->RemoveInstruction(null_check);
+
+ ASSERT_FALSE(parameter->HasEnvironmentUses());
+ ASSERT_FALSE(parameter->HasUses());
+}
+
+} // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8a5077b..65bdb18 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -14,15 +14,19 @@
* limitations under the License.
*/
+#include "optimizing_compiler.h"
+
#include <fstream>
#include <stdint.h>
#include "builder.h"
#include "code_generator.h"
-#include "compilers.h"
+#include "compiler.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "graph_visualizer.h"
+#include "gvn.h"
+#include "instruction_simplifier.h"
#include "nodes.h"
#include "register_allocator.h"
#include "ssa_phi_elimination.h"
@@ -36,7 +40,7 @@
*/
class CodeVectorAllocator FINAL : public CodeAllocator {
public:
- CodeVectorAllocator() { }
+ CodeVectorAllocator() {}
virtual uint8_t* Allocate(size_t size) {
size_ = size;
@@ -65,12 +69,133 @@
*/
static const char* kStringFilter = "";
-OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver) : QuickCompiler(driver) {
+class OptimizingCompiler FINAL : public Compiler {
+ public:
+ explicit OptimizingCompiler(CompilerDriver* driver);
+ ~OptimizingCompiler();
+
+ bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
+ OVERRIDE;
+
+ CompiledMethod* Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const OVERRIDE;
+
+ CompiledMethod* TryCompile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const;
+
+ // For the following methods we will use the fallback. This is a delegation pattern.
+ CompiledMethod* JniCompile(uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file) const OVERRIDE;
+
+ uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool WriteElf(art::File* file,
+ OatWriter* oat_writer,
+ const std::vector<const art::DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE;
+
+ void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;
+
+ void Init() const OVERRIDE;
+
+ void UnInit() const OVERRIDE;
+
+ private:
+ // Whether we should run any optimization or register allocation. If false, will
+ // just run the code generation after the graph was built.
+ const bool run_optimizations_;
+ mutable AtomicInteger total_compiled_methods_;
+ mutable AtomicInteger unoptimized_compiled_methods_;
+ mutable AtomicInteger optimized_compiled_methods_;
+
+ std::unique_ptr<std::ostream> visualizer_output_;
+
+ // Delegate to another compiler in case the optimizing compiler cannot compile a method.
+ // Currently the fallback is the quick compiler.
+ std::unique_ptr<Compiler> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
+};
+
+static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
+
+OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
+ : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
+ run_optimizations_(
+ driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
+ total_compiled_methods_(0),
+ unoptimized_compiled_methods_(0),
+ optimized_compiled_methods_(0),
+ delegate_(Create(driver, Compiler::Kind::kQuick)) {
if (kIsVisualizerEnabled) {
visualizer_output_.reset(new std::ofstream("art.cfg"));
}
}
+void OptimizingCompiler::Init() const {
+ delegate_->Init();
+}
+
+void OptimizingCompiler::UnInit() const {
+ delegate_->UnInit();
+}
+
+OptimizingCompiler::~OptimizingCompiler() {
+ if (total_compiled_methods_ == 0) {
+ LOG(INFO) << "Did not compile any method.";
+ } else {
+ size_t unoptimized_percent = (unoptimized_compiled_methods_ * 100 / total_compiled_methods_);
+ size_t optimized_percent = (optimized_compiled_methods_ * 100 / total_compiled_methods_);
+ LOG(INFO) << "Compiled " << total_compiled_methods_ << " methods: "
+ << unoptimized_percent << "% (" << unoptimized_compiled_methods_ << ") unoptimized, "
+ << optimized_percent << "% (" << optimized_compiled_methods_ << ") optimized.";
+ }
+}
+
+bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
+ CompilationUnit* cu) const {
+ return delegate_->CanCompileMethod(method_idx, dex_file, cu);
+}
+
+CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags,
+ uint32_t method_idx,
+ const DexFile& dex_file) const {
+ return delegate_->JniCompile(access_flags, method_idx, dex_file);
+}
+
+uintptr_t OptimizingCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
+ return delegate_->GetEntryPointOf(method);
+}
+
+bool OptimizingCompiler::WriteElf(art::File* file, OatWriter* oat_writer,
+ const std::vector<const art::DexFile*>& dex_files,
+ const std::string& android_root, bool is_host) const {
+ return delegate_->WriteElf(file, oat_writer, dex_files, android_root, is_host);
+}
+
+Backend* OptimizingCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const {
+ return delegate_->GetCodeGenerator(cu, compilation_unit);
+}
+
+void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu) const {
+ delegate_->InitCompilationUnit(cu);
+}
+
CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
@@ -78,6 +203,7 @@
uint32_t method_idx,
jobject class_loader,
const DexFile& dex_file) const {
+ total_compiled_methods_++;
InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
// Always use the thumb2 assembler: some runtime functionality (like implicit stack
// overflow checks) assume thumb2.
@@ -127,7 +253,8 @@
CodeVectorAllocator allocator;
- if (RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+ if (run_optimizations_ && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
+ optimized_compiled_methods_++;
graph->BuildDominatorTree();
graph->TransformToSSA();
visualizer.DumpGraph("ssa");
@@ -135,6 +262,9 @@
SsaRedundantPhiElimination(graph).Run();
SsaDeadPhiElimination(graph).Run();
+ InstructionSimplifier(graph).Run();
+ GlobalValueNumberer(graph->GetArena(), graph).Run();
+ visualizer.DumpGraph(kGVNPassName);
SsaLivenessAnalysis liveness(*graph, codegen);
liveness.Analyze();
@@ -145,9 +275,29 @@
visualizer.DumpGraph(kRegisterAllocatorPassName);
codegen->CompileOptimized(&allocator);
+
+ std::vector<uint8_t> mapping_table;
+ SrcMap src_mapping_table;
+ codegen->BuildMappingTable(&mapping_table,
+ GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+ &src_mapping_table : nullptr);
+
+ std::vector<uint8_t> stack_map;
+ codegen->BuildStackMaps(&stack_map);
+
+ return new CompiledMethod(GetCompilerDriver(),
+ instruction_set,
+ allocator.GetMemory(),
+ codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ 0, /* FPR spill mask, unused */
+ mapping_table,
+ stack_map);
} else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
LOG(FATAL) << "Could not allocate registers in optimizing compiler";
+ return nullptr;
} else {
+ unoptimized_compiled_methods_++;
codegen->CompileBaseline(&allocator);
// Run these phases to get some test coverage.
@@ -155,28 +305,56 @@
graph->TransformToSSA();
visualizer.DumpGraph("ssa");
graph->FindNaturalLoops();
+ SsaRedundantPhiElimination(graph).Run();
+ SsaDeadPhiElimination(graph).Run();
+ GlobalValueNumberer(graph->GetArena(), graph).Run();
SsaLivenessAnalysis liveness(*graph, codegen);
liveness.Analyze();
visualizer.DumpGraph(kLivenessPassName);
+
+ std::vector<uint8_t> mapping_table;
+ SrcMap src_mapping_table;
+ codegen->BuildMappingTable(&mapping_table,
+ GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
+ &src_mapping_table : nullptr);
+ std::vector<uint8_t> vmap_table;
+ codegen->BuildVMapTable(&vmap_table);
+ std::vector<uint8_t> gc_map;
+ codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+
+ return new CompiledMethod(GetCompilerDriver(),
+ instruction_set,
+ allocator.GetMemory(),
+ codegen->GetFrameSize(),
+ codegen->GetCoreSpillMask(),
+ 0, /* FPR spill mask, unused */
+ &src_mapping_table,
+ mapping_table,
+ vmap_table,
+ gc_map,
+ nullptr);
+ }
+}
+
+CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ jobject class_loader,
+ const DexFile& dex_file) const {
+ CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
+ method_idx, class_loader, dex_file);
+ if (method != nullptr) {
+ return method;
}
- std::vector<uint8_t> mapping_table;
- codegen->BuildMappingTable(&mapping_table);
- std::vector<uint8_t> vmap_table;
- codegen->BuildVMapTable(&vmap_table);
- std::vector<uint8_t> gc_map;
- codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
+ return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+ class_loader, dex_file);
+}
- return new CompiledMethod(GetCompilerDriver(),
- instruction_set,
- allocator.GetMemory(),
- codegen->GetFrameSize(),
- codegen->GetCoreSpillMask(),
- 0, /* FPR spill mask, unused */
- mapping_table,
- vmap_table,
- gc_map,
- nullptr);
+Compiler* CreateOptimizingCompiler(CompilerDriver* driver) {
+ return new OptimizingCompiler(driver);
}
} // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
new file mode 100644
index 0000000..a415eca
--- /dev/null
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -0,0 +1,29 @@
+/*
+ * 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_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
+#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
+
+namespace art {
+
+class Compiler;
+class CompilerDriver;
+
+Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
+
+}
+
+#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 36a6a21..6dd53e5 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -17,8 +17,14 @@
#ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
#define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
+#include "nodes.h"
+#include "builder.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
#include "ssa_liveness_analysis.h"
+#include "gtest/gtest.h"
+
namespace art {
#define NUM_INSTRUCTIONS(...) \
@@ -48,6 +54,46 @@
return interval;
}
+void RemoveSuspendChecks(HGraph* graph) {
+ for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) {
+ for (HInstructionIterator it(graph->GetBlocks().Get(i)->GetInstructions());
+ !it.Done();
+ it.Advance()) {
+ HInstruction* current = it.Current();
+ if (current->IsSuspendCheck()) {
+ current->GetBlock()->RemoveInstruction(current);
+ }
+ }
+ }
+}
+
+// Create a control-flow graph from Dex instructions.
+inline HGraph* CreateCFG(ArenaAllocator* allocator, const uint16_t* data) {
+ HGraphBuilder builder(allocator);
+ const DexFile::CodeItem* item =
+ reinterpret_cast<const DexFile::CodeItem*>(data);
+ HGraph* graph = builder.BuildGraph(*item);
+ return graph;
+}
+
+// Naive string diff data type.
+typedef std::list<std::pair<std::string, std::string>> diff_t;
+
+// An alias for the empty string used to make it clear that a line is
+// removed in a diff.
+static const std::string removed = "";
+
+// Naive patch command: apply a diff to a string.
+inline std::string Patch(const std::string& original, const diff_t& diff) {
+ std::string result = original;
+ for (const auto& p : diff) {
+ std::string::size_type pos = result.find(p.first);
+ EXPECT_NE(pos, std::string::npos);
+ result.replace(pos, p.first.size(), p.second);
+ }
+ return result;
+}
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 093856d..863e107 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -71,7 +71,8 @@
for (size_t i = 0; i < number_of_moves; ++i) {
moves->AddMove(new (allocator) MoveOperands(
Location::RegisterLocation(ManagedRegister(operands[i][0])),
- Location::RegisterLocation(ManagedRegister(operands[i][1]))));
+ Location::RegisterLocation(ManagedRegister(operands[i][1])),
+ nullptr));
}
return moves;
}
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 7e604e9..da6b294 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -45,7 +45,8 @@
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 2: Goto 1\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 0: ReturnVoid\n"
"BasicBlock 2, pred: 1\n"
@@ -57,7 +58,8 @@
TEST(PrettyPrinterTest, CFG1) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 3: Goto 1\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 0: Goto 2\n"
"BasicBlock 2, pred: 1, succ: 3\n"
@@ -76,7 +78,8 @@
TEST(PrettyPrinterTest, CFG2) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 4: Goto 1\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 0: Goto 2\n"
"BasicBlock 2, pred: 1, succ: 3\n"
@@ -97,15 +100,17 @@
TEST(PrettyPrinterTest, CFG3) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 4: Goto 1\n"
+ " 5: SuspendCheck\n"
+ " 6: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
" 0: Goto 3\n"
"BasicBlock 2, pred: 3, succ: 4\n"
" 1: ReturnVoid\n"
"BasicBlock 3, pred: 1, succ: 2\n"
- " 2: Goto 2\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 2\n"
"BasicBlock 4, pred: 2\n"
- " 3: Exit\n";
+ " 4: Exit\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x200,
@@ -132,11 +137,13 @@
TEST(PrettyPrinterTest, CFG4) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 2: Goto 1\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, 1, succ: 1\n"
- " 0: Goto 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
"BasicBlock 2\n"
- " 1: Exit\n";
+ " 2: Exit\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::NOP,
@@ -153,13 +160,15 @@
TEST(PrettyPrinterTest, CFG5) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 3: Goto 1\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, 2, succ: 3\n"
" 0: ReturnVoid\n"
"BasicBlock 2, succ: 1\n"
- " 1: Goto 1\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 1\n"
"BasicBlock 3, pred: 1\n"
- " 2: Exit\n";
+ " 3: Exit\n";
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID,
@@ -174,7 +183,8 @@
"BasicBlock 0, succ: 1\n"
" 0: Local [4, 3, 2]\n"
" 1: IntConstant [2]\n"
- " 10: Goto 1\n"
+ " 10: SuspendCheck\n"
+ " 11: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 2: StoreLocal(0, 1)\n"
" 3: LoadLocal(0) [5]\n"
@@ -202,7 +212,8 @@
"BasicBlock 0, succ: 1\n"
" 0: Local [4, 3, 2]\n"
" 1: IntConstant [2]\n"
- " 10: Goto 1\n"
+ " 11: SuspendCheck\n"
+ " 12: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 2: StoreLocal(0, 1)\n"
" 3: LoadLocal(0) [5]\n"
@@ -212,9 +223,10 @@
"BasicBlock 2, pred: 1, 3, succ: 3\n"
" 7: Goto 3\n"
"BasicBlock 3, pred: 1, 2, succ: 2\n"
- " 8: Goto 2\n"
+ " 8: SuspendCheck\n"
+ " 9: Goto 2\n"
"BasicBlock 4\n"
- " 9: Exit\n";
+ " 10: Exit\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -230,7 +242,8 @@
"BasicBlock 0, succ: 1\n"
" 0: Local [2]\n"
" 1: IntConstant [2]\n"
- " 5: Goto 1\n"
+ " 5: SuspendCheck\n"
+ " 6: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 2: StoreLocal(0, 1)\n"
" 3: ReturnVoid\n"
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index bd3a7d9..1d1d694 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -16,6 +16,7 @@
#include "register_allocator.h"
+#include "base/bit_vector-inl.h"
#include "code_generator.h"
#include "ssa_liveness_analysis.h"
@@ -30,18 +31,27 @@
: allocator_(allocator),
codegen_(codegen),
liveness_(liveness),
- unhandled_(allocator, 0),
+ unhandled_core_intervals_(allocator, 0),
+ unhandled_fp_intervals_(allocator, 0),
+ unhandled_(nullptr),
handled_(allocator, 0),
active_(allocator, 0),
inactive_(allocator, 0),
physical_register_intervals_(allocator, codegen->GetNumberOfRegisters()),
+ temp_intervals_(allocator, 4),
spill_slots_(allocator, kDefaultNumberOfSpillSlots),
+ safepoints_(allocator, 0),
processing_core_registers_(false),
number_of_registers_(-1),
registers_array_(nullptr),
- blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())) {
+ blocked_registers_(allocator->AllocArray<bool>(codegen->GetNumberOfRegisters())),
+ reserved_out_slots_(0),
+ maximum_number_of_live_registers_(0) {
codegen->SetupBlockedRegisters(blocked_registers_);
physical_register_intervals_.SetSize(codegen->GetNumberOfRegisters());
+ // Always reserve for the current method and the graph's max out registers.
+ // TODO: compute it instead.
+ reserved_out_slots_ = 1 + codegen->GetGraph()->GetMaximumNumberOfOutVRegs();
}
bool RegisterAllocator::CanAllocateRegistersFor(const HGraph& graph,
@@ -54,7 +64,6 @@
!it.Done();
it.Advance()) {
HInstruction* current = it.Current();
- if (current->NeedsEnvironment()) return false;
if (current->GetType() == Primitive::kPrimLong && instruction_set != kX86_64) return false;
if (current->GetType() == Primitive::kPrimFloat) return false;
if (current->GetType() == Primitive::kPrimDouble) return false;
@@ -64,17 +73,14 @@
}
static bool ShouldProcess(bool processing_core_registers, LiveInterval* interval) {
+ if (interval == nullptr) return false;
bool is_core_register = (interval->GetType() != Primitive::kPrimDouble)
&& (interval->GetType() != Primitive::kPrimFloat);
return processing_core_registers == is_core_register;
}
void RegisterAllocator::AllocateRegisters() {
- processing_core_registers_ = true;
AllocateRegistersInternal();
- processing_core_registers_ = false;
- AllocateRegistersInternal();
-
Resolve();
if (kIsDebugBuild) {
@@ -100,78 +106,139 @@
interval->AddRange(start, end);
}
-// TODO: make the register allocator understand instructions like HCondition
-// that may not need to be materialized. It doesn't need to allocate any
-// registers for it.
void RegisterAllocator::AllocateRegistersInternal() {
- number_of_registers_ = processing_core_registers_
- ? codegen_->GetNumberOfCoreRegisters()
- : codegen_->GetNumberOfFloatingPointRegisters();
-
- registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
-
// Iterate post-order, to ensure the list is sorted, and the last added interval
// is the one with the lowest start position.
- for (size_t i = liveness_.GetNumberOfSsaValues(); i > 0; --i) {
- HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i - 1);
- LiveInterval* current = instruction->GetLiveInterval();
- if (ShouldProcess(processing_core_registers_, current)) {
- DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
-
- LocationSummary* locations = instruction->GetLocations();
- if (locations->GetTempCount() != 0) {
- // Note that we already filtered out instructions requiring temporaries in
- // RegisterAllocator::CanAllocateRegistersFor.
- LOG(FATAL) << "Unimplemented";
- }
-
- // Some instructions define their output in fixed register/stack slot. We need
- // to ensure we know these locations before doing register allocation. For a
- // given register, we create an interval that covers these locations. The register
- // will be unavailable at these locations when trying to allocate one for an
- // interval.
- //
- // The backwards walking ensures the ranges are ordered on increasing start positions.
- Location output = locations->Out();
- size_t position = instruction->GetLifetimePosition();
- if (output.IsRegister()) {
- // Shift the interval's start by one to account for the blocked register.
- current->SetFrom(position + 1);
- current->SetRegister(output.reg().RegId());
- BlockRegister(output, position, position + 1, instruction->GetType());
- } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
- current->SetSpillSlot(output.GetStackIndex());
- }
- for (size_t i = 0; i < instruction->InputCount(); ++i) {
- Location input = locations->InAt(i);
- if (input.IsRegister()) {
- BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
- }
- }
-
- // Add the interval to the correct list.
- if (current->HasRegister()) {
- DCHECK(instruction->IsParameterValue());
- inactive_.Add(current);
- } else if (current->HasSpillSlot() || instruction->IsConstant()) {
- // Split before first register use.
- size_t first_register_use = current->FirstRegisterUse();
- if (first_register_use != kNoLifetime) {
- LiveInterval* split = Split(current, first_register_use - 1);
- // Don't add direclty to `unhandled_`, it needs to be sorted and the start
- // of this new interval might be after intervals already in the list.
- AddToUnhandled(split);
- } else {
- // Nothing to do, we won't allocate a register for this value.
- }
- } else {
- DCHECK(unhandled_.IsEmpty() || current->StartsBefore(unhandled_.Peek()));
- unhandled_.Add(current);
- }
+ for (HLinearPostOrderIterator it(liveness_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ ProcessInstruction(it.Current());
+ }
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ ProcessInstruction(it.Current());
}
}
+ number_of_registers_ = codegen_->GetNumberOfCoreRegisters();
+ registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+ processing_core_registers_ = true;
+ unhandled_ = &unhandled_core_intervals_;
LinearScan();
+
+ inactive_.Reset();
+ active_.Reset();
+ handled_.Reset();
+
+ number_of_registers_ = codegen_->GetNumberOfFloatingPointRegisters();
+ registers_array_ = allocator_->AllocArray<size_t>(number_of_registers_);
+ processing_core_registers_ = false;
+ unhandled_ = &unhandled_fp_intervals_;
+ // TODO: Enable FP register allocation.
+ DCHECK(unhandled_->IsEmpty());
+ LinearScan();
+}
+
+void RegisterAllocator::ProcessInstruction(HInstruction* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ size_t position = instruction->GetLifetimePosition();
+
+ if (locations == nullptr) return;
+
+ // Create synthesized intervals for temporaries.
+ for (size_t i = 0; i < locations->GetTempCount(); ++i) {
+ Location temp = locations->GetTemp(i);
+ if (temp.IsRegister()) {
+ BlockRegister(temp, position, position + 1, Primitive::kPrimInt);
+ } else {
+ LiveInterval* interval =
+ LiveInterval::MakeTempInterval(allocator_, instruction, Primitive::kPrimInt);
+ temp_intervals_.Add(interval);
+ interval->AddRange(position, position + 1);
+ unhandled_core_intervals_.Add(interval);
+ }
+ }
+
+ bool core_register = (instruction->GetType() != Primitive::kPrimDouble)
+ && (instruction->GetType() != Primitive::kPrimFloat);
+
+ GrowableArray<LiveInterval*>& unhandled = core_register
+ ? unhandled_core_intervals_
+ : unhandled_fp_intervals_;
+
+ if (locations->CanCall()) {
+ if (!instruction->IsSuspendCheck()) {
+ codegen_->MarkNotLeaf();
+ }
+ safepoints_.Add(instruction);
+ if (locations->OnlyCallsOnSlowPath()) {
+ // We add a synthesized range at this position to record the live registers
+ // at this position. Ideally, we could just update the safepoints when locations
+ // are updated, but we currently need to know the full stack size before updating
+ // locations (because of parameters and the fact that we don't have a frame pointer).
+ // And knowing the full stack size requires to know the maximum number of live
+ // registers at calls in slow paths.
+ // By adding the following interval in the algorithm, we can compute this
+ // maximum before updating locations.
+ LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction);
+ interval->AddRange(position, position + 1);
+ unhandled.Add(interval);
+ }
+ }
+
+ if (locations->WillCall()) {
+ // Block all registers.
+ for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) {
+ BlockRegister(Location::RegisterLocation(ManagedRegister(i)),
+ position,
+ position + 1,
+ Primitive::kPrimInt);
+ }
+ }
+
+ for (size_t i = 0; i < instruction->InputCount(); ++i) {
+ Location input = locations->InAt(i);
+ if (input.IsRegister()) {
+ BlockRegister(input, position, position + 1, instruction->InputAt(i)->GetType());
+ }
+ }
+
+ LiveInterval* current = instruction->GetLiveInterval();
+ if (current == nullptr) return;
+
+ DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
+ // Some instructions define their output in fixed register/stack slot. We need
+ // to ensure we know these locations before doing register allocation. For a
+ // given register, we create an interval that covers these locations. The register
+ // will be unavailable at these locations when trying to allocate one for an
+ // interval.
+ //
+ // The backwards walking ensures the ranges are ordered on increasing start positions.
+ Location output = locations->Out();
+ if (output.IsRegister()) {
+ // Shift the interval's start by one to account for the blocked register.
+ current->SetFrom(position + 1);
+ current->SetRegister(output.reg().RegId());
+ BlockRegister(output, position, position + 1, instruction->GetType());
+ } else if (output.IsStackSlot() || output.IsDoubleStackSlot()) {
+ current->SetSpillSlot(output.GetStackIndex());
+ }
+
+ // If needed, add interval to the list of unhandled intervals.
+ if (current->HasSpillSlot() || instruction->IsConstant()) {
+ // Split before first register use.
+ size_t first_register_use = current->FirstRegisterUse();
+ if (first_register_use != kNoLifetime) {
+ LiveInterval* split = Split(current, first_register_use);
+ // Don't add direclty to `unhandled`, it needs to be sorted and the start
+ // of this new interval might be after intervals already in the list.
+ AddSorted(&unhandled, split);
+ } else {
+ // Nothing to do, we won't allocate a register for this value.
+ }
+ } else {
+ DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek()));
+ unhandled.Add(current);
+ }
}
class AllRangesIterator : public ValueObject {
@@ -219,12 +286,20 @@
}
}
- return ValidateIntervals(intervals, spill_slots_.Size(), *codegen_, allocator_,
- processing_core_registers_, log_fatal_on_failure);
+ for (size_t i = 0, e = temp_intervals_.Size(); i < e; ++i) {
+ LiveInterval* temp = temp_intervals_.Get(i);
+ if (ShouldProcess(processing_core_registers_, temp)) {
+ intervals.Add(temp);
+ }
+ }
+
+ return ValidateIntervals(intervals, spill_slots_.Size(), reserved_out_slots_, *codegen_,
+ allocator_, processing_core_registers_, log_fatal_on_failure);
}
bool RegisterAllocator::ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
size_t number_of_spill_slots,
+ size_t number_of_out_slots,
const CodeGenerator& codegen,
ArenaAllocator* allocator,
bool processing_core_registers,
@@ -248,8 +323,9 @@
if (current->GetParent()->HasSpillSlot()
// Parameters have their own stack slot.
&& !(defined_by != nullptr && defined_by->IsParameterValue())) {
- BitVector* liveness_of_spill_slot = liveness_of_values.Get(
- number_of_registers + current->GetParent()->GetSpillSlot() / kVRegSize);
+ BitVector* liveness_of_spill_slot = liveness_of_values.Get(number_of_registers
+ + current->GetParent()->GetSpillSlot() / kVRegSize
+ - number_of_out_slots);
for (size_t j = it.CurrentRange()->GetStart(); j < it.CurrentRange()->GetEnd(); ++j) {
if (liveness_of_spill_slot->IsBitSet(j)) {
if (log_fatal_on_failure) {
@@ -271,7 +347,11 @@
if (liveness_of_register->IsBitSet(j)) {
if (log_fatal_on_failure) {
std::ostringstream message;
- message << "Register conflict at " << j << " for ";
+ message << "Register conflict at " << j << " ";
+ if (defined_by != nullptr) {
+ message << "(" << defined_by->DebugName() << ")";
+ }
+ message << "for ";
if (processing_core_registers) {
codegen.DumpCoreRegister(message, current->GetRegister());
} else {
@@ -308,10 +388,10 @@
// By the book implementation of a linear scan register allocator.
void RegisterAllocator::LinearScan() {
- while (!unhandled_.IsEmpty()) {
+ while (!unhandled_->IsEmpty()) {
// (1) Remove interval with the lowest start position from unhandled.
- LiveInterval* current = unhandled_.Pop();
- DCHECK(!current->IsFixed() && !current->HasRegister() && !current->HasSpillSlot());
+ LiveInterval* current = unhandled_->Pop();
+ DCHECK(!current->IsFixed() && !current->HasSpillSlot());
size_t position = current->GetStart();
// (2) Remove currently active intervals that are dead at this position.
@@ -345,6 +425,14 @@
}
}
+ if (current->IsSlowPathSafepoint()) {
+ // Synthesized interval to record the maximum number of live registers
+ // at safepoints. No need to allocate a register for it.
+ maximum_number_of_live_registers_ =
+ std::max(maximum_number_of_live_registers_, active_.Size());
+ continue;
+ }
+
// (4) Try to find an available register.
bool success = TryAllocateFreeReg(current);
@@ -380,7 +468,8 @@
DCHECK(inactive->HasRegister());
size_t next_intersection = inactive->FirstIntersectionWith(current);
if (next_intersection != kNoLifetime) {
- free_until[inactive->GetRegister()] = next_intersection;
+ free_until[inactive->GetRegister()] =
+ std::min(free_until[inactive->GetRegister()], next_intersection);
}
}
@@ -391,13 +480,19 @@
free_until[interval->GetRegister()] = 0;
}
- // Pick the register that is free the longest.
int reg = -1;
- for (size_t i = 0; i < number_of_registers_; ++i) {
- if (IsBlocked(i)) continue;
- if (reg == -1 || free_until[i] > free_until[reg]) {
- reg = i;
- if (free_until[i] == kMaxLifetimePosition) break;
+ if (current->HasRegister()) {
+ // Some instructions have a fixed register output.
+ reg = current->GetRegister();
+ DCHECK_NE(free_until[reg], 0u);
+ } else {
+ // Pick the register that is free the longest.
+ for (size_t i = 0; i < number_of_registers_; ++i) {
+ if (IsBlocked(i)) continue;
+ if (reg == -1 || free_until[i] > free_until[reg]) {
+ reg = i;
+ if (free_until[i] == kMaxLifetimePosition) break;
+ }
}
}
@@ -413,7 +508,7 @@
// the register is not available anymore.
LiveInterval* split = Split(current, free_until[reg]);
DCHECK(split != nullptr);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
}
return true;
}
@@ -491,8 +586,8 @@
// If the first use of that instruction is after the last use of the found
// register, we split this interval just before its first register use.
AllocateSpillSlotFor(current);
- LiveInterval* split = Split(current, first_register_use - 1);
- AddToUnhandled(split);
+ LiveInterval* split = Split(current, first_register_use);
+ AddSorted(unhandled_, split);
return false;
} else {
// Use this register and spill the active and inactives interval that
@@ -506,7 +601,7 @@
LiveInterval* split = Split(active, current->GetStart());
active_.DeleteAt(i);
handled_.Add(active);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
break;
}
}
@@ -518,12 +613,12 @@
if (next_intersection != kNoLifetime) {
if (inactive->IsFixed()) {
LiveInterval* split = Split(current, next_intersection);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
} else {
LiveInterval* split = Split(inactive, current->GetStart());
inactive_.DeleteAt(i);
handled_.Add(inactive);
- AddToUnhandled(split);
+ AddSorted(unhandled_, split);
--i;
}
}
@@ -534,16 +629,16 @@
}
}
-void RegisterAllocator::AddToUnhandled(LiveInterval* interval) {
+void RegisterAllocator::AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval) {
size_t insert_at = 0;
- for (size_t i = unhandled_.Size(); i > 0; --i) {
- LiveInterval* current = unhandled_.Get(i - 1);
+ for (size_t i = array->Size(); i > 0; --i) {
+ LiveInterval* current = array->Get(i - 1);
if (current->StartsAfter(interval)) {
insert_at = i;
break;
}
}
- unhandled_.InsertAt(insert_at, interval);
+ array->InsertAt(insert_at, interval);
}
LiveInterval* RegisterAllocator::Split(LiveInterval* interval, size_t position) {
@@ -590,14 +685,6 @@
}
size_t end = last_sibling->GetEnd();
- if (NeedTwoSpillSlot(parent->GetType())) {
- AllocateTwoSpillSlots(parent, end);
- } else {
- AllocateOneSpillSlot(parent, end);
- }
-}
-
-void RegisterAllocator::AllocateTwoSpillSlots(LiveInterval* parent, size_t end) {
// Find an available spill slot.
size_t slot = 0;
for (size_t e = spill_slots_.Size(); slot < e; ++slot) {
@@ -611,38 +698,28 @@
}
}
- if (slot == spill_slots_.Size()) {
- // We need a new spill slot.
- spill_slots_.Add(end);
- spill_slots_.Add(end);
- } else if (slot == spill_slots_.Size() - 1) {
- spill_slots_.Put(slot, end);
- spill_slots_.Add(end);
+ if (NeedTwoSpillSlot(parent->GetType())) {
+ if (slot == spill_slots_.Size()) {
+ // We need a new spill slot.
+ spill_slots_.Add(end);
+ spill_slots_.Add(end);
+ } else if (slot == spill_slots_.Size() - 1) {
+ spill_slots_.Put(slot, end);
+ spill_slots_.Add(end);
+ } else {
+ spill_slots_.Put(slot, end);
+ spill_slots_.Put(slot + 1, end);
+ }
} else {
- spill_slots_.Put(slot, end);
- spill_slots_.Put(slot + 1, end);
- }
-
- parent->SetSpillSlot(slot * kVRegSize);
-}
-
-void RegisterAllocator::AllocateOneSpillSlot(LiveInterval* parent, size_t end) {
- // Find an available spill slot.
- size_t slot = 0;
- for (size_t e = spill_slots_.Size(); slot < e; ++slot) {
- if (spill_slots_.Get(slot) <= parent->GetStart()) {
- break;
+ if (slot == spill_slots_.Size()) {
+ // We need a new spill slot.
+ spill_slots_.Add(end);
+ } else {
+ spill_slots_.Put(slot, end);
}
}
- if (slot == spill_slots_.Size()) {
- // We need a new spill slot.
- spill_slots_.Add(end);
- } else {
- spill_slots_.Put(slot, end);
- }
-
- parent->SetSpillSlot(slot * kVRegSize);
+ parent->SetSpillSlot((slot + reserved_out_slots_) * kVRegSize);
}
static Location ConvertToLocation(LiveInterval* interval) {
@@ -672,31 +749,38 @@
return instruction->GetLifetimePosition() == kInputMoveLifetimePosition;
}
-void RegisterAllocator::AddInputMoveFor(HInstruction* instruction,
+static bool IsValidDestination(Location destination) {
+ return destination.IsRegister() || destination.IsStackSlot() || destination.IsDoubleStackSlot();
+}
+
+void RegisterAllocator::AddInputMoveFor(HInstruction* user,
Location source,
Location destination) const {
+ DCHECK(IsValidDestination(destination));
if (source.Equals(destination)) return;
- DCHECK(instruction->AsPhi() == nullptr);
+ DCHECK(user->AsPhi() == nullptr);
- HInstruction* previous = instruction->GetPrevious();
+ HInstruction* previous = user->GetPrevious();
HParallelMove* move = nullptr;
if (previous == nullptr
|| previous->AsParallelMove() == nullptr
|| !IsInputMove(previous)) {
move = new (allocator_) HParallelMove(allocator_);
move->SetLifetimePosition(kInputMoveLifetimePosition);
- instruction->GetBlock()->InsertInstructionBefore(move, instruction);
+ user->GetBlock()->InsertInstructionBefore(move, user);
} else {
move = previous->AsParallelMove();
}
DCHECK(IsInputMove(move));
- move->AddMove(new (allocator_) MoveOperands(source, destination));
+ move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr));
}
void RegisterAllocator::InsertParallelMoveAt(size_t position,
+ HInstruction* instruction,
Location source,
Location destination) const {
+ DCHECK(IsValidDestination(destination));
if (source.Equals(destination)) return;
HInstruction* at = liveness_.GetInstructionFromPosition(position / 2);
@@ -719,14 +803,26 @@
} else {
// Move must happen before the instruction.
HInstruction* previous = at->GetPrevious();
- if (previous != nullptr && previous->AsParallelMove() != nullptr) {
+ if (previous != nullptr && previous->IsParallelMove()) {
// This is a parallel move for connecting siblings in a same block. We need to
// differentiate it with moves for connecting blocks, and input moves.
if (previous->GetLifetimePosition() != position) {
+ // If the previous instruction of the previous instruction is not a parallel
+ // move, we have to insert the new parallel move before the input or connecting
+ // block moves.
+ at = previous;
previous = previous->GetPrevious();
}
}
- if (previous == nullptr || previous->AsParallelMove() == nullptr) {
+ if (previous == nullptr
+ || !previous->IsParallelMove()
+ || previous->GetLifetimePosition() != position) {
+ // If the previous is a parallel move, then its position must be lower
+ // than the given `position`: it was added just after the non-parallel
+ // move instruction that precedes `instruction`.
+ DCHECK(previous == nullptr
+ || !previous->IsParallelMove()
+ || previous->GetLifetimePosition() < position);
move = new (allocator_) HParallelMove(allocator_);
move->SetLifetimePosition(position);
at->GetBlock()->InsertInstructionBefore(move, at);
@@ -734,12 +830,14 @@
move = previous->AsParallelMove();
}
}
- move->AddMove(new (allocator_) MoveOperands(source, destination));
+ move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
}
void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
+ HInstruction* instruction,
Location source,
Location destination) const {
+ DCHECK(IsValidDestination(destination));
if (source.Equals(destination)) return;
DCHECK_EQ(block->GetSuccessors().Size(), 1u);
@@ -748,7 +846,7 @@
HParallelMove* move;
// This is a parallel move for connecting blocks. We need to differentiate
// it with moves for connecting siblings in a same block, and output moves.
- if (previous == nullptr || previous->AsParallelMove() == nullptr
+ if (previous == nullptr || !previous->IsParallelMove()
|| previous->AsParallelMove()->GetLifetimePosition() != block->GetLifetimeEnd()) {
move = new (allocator_) HParallelMove(allocator_);
move->SetLifetimePosition(block->GetLifetimeEnd());
@@ -756,12 +854,14 @@
} else {
move = previous->AsParallelMove();
}
- move->AddMove(new (allocator_) MoveOperands(source, destination));
+ move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
}
void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
+ HInstruction* instruction,
Location source,
Location destination) const {
+ DCHECK(IsValidDestination(destination));
if (source.Equals(destination)) return;
HInstruction* first = block->GetFirstInstruction();
@@ -773,16 +873,17 @@
move->SetLifetimePosition(block->GetLifetimeStart());
block->InsertInstructionBefore(move, first);
}
- move->AddMove(new (allocator_) MoveOperands(source, destination));
+ move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
}
void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
Location source,
Location destination) const {
+ DCHECK(IsValidDestination(destination));
if (source.Equals(destination)) return;
if (instruction->AsPhi() != nullptr) {
- InsertParallelMoveAtEntryOf(instruction->GetBlock(), source, destination);
+ InsertParallelMoveAtEntryOf(instruction->GetBlock(), instruction, source, destination);
return;
}
@@ -796,7 +897,7 @@
move->SetLifetimePosition(position);
instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
}
- move->AddMove(new (allocator_) MoveOperands(source, destination));
+ move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
}
void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
@@ -819,12 +920,14 @@
// Walk over all uses covered by this interval, and update the location
// information.
while (use != nullptr && use->GetPosition() <= current->GetEnd()) {
- if (!use->GetIsEnvironment()) {
- LocationSummary* locations = use->GetUser()->GetLocations();
+ LocationSummary* locations = use->GetUser()->GetLocations();
+ if (use->GetIsEnvironment()) {
+ locations->SetEnvironmentAt(use->GetInputIndex(), source);
+ } else {
Location expected_location = locations->InAt(use->GetInputIndex());
if (expected_location.IsUnallocated()) {
locations->SetInAt(use->GetInputIndex(), source);
- } else {
+ } else if (!expected_location.IsConstant()) {
AddInputMoveFor(use->GetUser(), source, expected_location);
}
}
@@ -838,7 +941,38 @@
&& next_sibling->HasRegister()
&& current->GetEnd() == next_sibling->GetStart()) {
Location destination = ConvertToLocation(next_sibling);
- InsertParallelMoveAt(current->GetEnd(), source, destination);
+ InsertParallelMoveAt(current->GetEnd(), interval->GetDefinedBy(), source, destination);
+ }
+
+ // At each safepoint, we record stack and register information.
+ for (size_t i = 0, e = safepoints_.Size(); i < e; ++i) {
+ HInstruction* safepoint = safepoints_.Get(i);
+ size_t position = safepoint->GetLifetimePosition();
+ LocationSummary* locations = safepoint->GetLocations();
+ if (!current->Covers(position)) continue;
+
+ if ((current->GetType() == Primitive::kPrimNot) && current->GetParent()->HasSpillSlot()) {
+ locations->SetStackBit(current->GetParent()->GetSpillSlot() / kVRegSize);
+ }
+
+ switch (source.GetKind()) {
+ case Location::kRegister: {
+ locations->AddLiveRegister(source);
+ if (current->GetType() == Primitive::kPrimNot) {
+ locations->SetRegisterBit(source.reg().RegId());
+ }
+ break;
+ }
+ case Location::kStackSlot: // Fall-through
+ case Location::kDoubleStackSlot: // Fall-through
+ case Location::kConstant: {
+ // Nothing to do.
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unexpected location for object";
+ }
+ }
}
current = next_sibling;
} while (current != nullptr);
@@ -854,7 +988,11 @@
}
size_t from_position = from->GetLifetimeEnd() - 1;
- size_t to_position = to->GetLifetimeStart();
+ // When an instructions dies at entry of another, and the latter is the beginning
+ // of a block, the register allocator ensures the former has a register
+ // at block->GetLifetimeStart() + 1. Since this is at a block boundary, it must
+ // must be handled in this method.
+ size_t to_position = to->GetLifetimeStart() + 1;
LiveInterval* destination = nullptr;
LiveInterval* source = nullptr;
@@ -880,6 +1018,8 @@
return;
}
+ DCHECK(destination != nullptr && source != nullptr);
+
if (!destination->HasRegister()) {
// Values are eagerly spilled. Spill slot already contains appropriate value.
return;
@@ -888,10 +1028,16 @@
// If `from` has only one successor, we can put the moves at the exit of it. Otherwise
// we need to put the moves at the entry of `to`.
if (from->GetSuccessors().Size() == 1) {
- InsertParallelMoveAtExitOf(from, ConvertToLocation(source), ConvertToLocation(destination));
+ InsertParallelMoveAtExitOf(from,
+ interval->GetParent()->GetDefinedBy(),
+ ConvertToLocation(source),
+ ConvertToLocation(destination));
} else {
DCHECK_EQ(to->GetPredecessors().Size(), 1u);
- InsertParallelMoveAtEntryOf(to, ConvertToLocation(source), ConvertToLocation(destination));
+ InsertParallelMoveAtEntryOf(to,
+ interval->GetParent()->GetDefinedBy(),
+ ConvertToLocation(source),
+ ConvertToLocation(destination));
}
}
@@ -906,7 +1052,8 @@
}
void RegisterAllocator::Resolve() {
- codegen_->ComputeFrameSize(spill_slots_.Size());
+ codegen_->ComputeFrameSize(
+ spill_slots_.Size(), maximum_number_of_live_registers_, reserved_out_slots_);
// Adjust the Out Location of instructions.
// TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
@@ -973,10 +1120,24 @@
Location source = FindLocationAt(input->GetLiveInterval(),
predecessor->GetLastInstruction()->GetLifetimePosition());
Location destination = ConvertToLocation(phi->GetLiveInterval());
- InsertParallelMoveAtExitOf(predecessor, source, destination);
+ InsertParallelMoveAtExitOf(predecessor, nullptr, source, destination);
}
}
}
+
+ // Assign temp locations.
+ HInstruction* current = nullptr;
+ size_t temp_index = 0;
+ for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
+ LiveInterval* temp = temp_intervals_.Get(i);
+ if (temp->GetDefinedBy() != current) {
+ temp_index = 0;
+ current = temp->GetDefinedBy();
+ }
+ LocationSummary* locations = current->GetLocations();
+ locations->SetTempAt(
+ temp_index++, Location::RegisterLocation(ManagedRegister(temp->GetRegister())));
+ }
}
} // namespace art
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index be1c7ec..d4c233a 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -21,6 +21,8 @@
#include "primitive.h"
#include "utils/growable_array.h"
+#include "gtest/gtest.h"
+
namespace art {
class CodeGenerator;
@@ -59,6 +61,7 @@
// Helper method for validation. Used by unit testing.
static bool ValidateIntervals(const GrowableArray<LiveInterval*>& intervals,
size_t number_of_spill_slots,
+ size_t number_of_out_slots,
const CodeGenerator& codegen,
ArenaAllocator* allocator,
bool processing_core_registers,
@@ -83,8 +86,8 @@
bool AllocateBlockedReg(LiveInterval* interval);
void Resolve();
- // Add `interval` in the sorted list of unhandled intervals.
- void AddToUnhandled(LiveInterval* interval);
+ // Add `interval` in the given sorted list.
+ static void AddSorted(GrowableArray<LiveInterval*>* array, LiveInterval* interval);
// Split `interval` at the position `at`. The new interval starts at `at`.
LiveInterval* Split(LiveInterval* interval, size_t at);
@@ -97,8 +100,6 @@
// Allocate a spill slot for the given interval.
void AllocateSpillSlotFor(LiveInterval* interval);
- void AllocateOneSpillSlot(LiveInterval* interval, size_t end);
- void AllocateTwoSpillSlots(LiveInterval* interval, size_t end);
// Connect adjacent siblings within blocks.
void ConnectSiblings(LiveInterval* interval);
@@ -107,14 +108,24 @@
void ConnectSplitSiblings(LiveInterval* interval, HBasicBlock* from, HBasicBlock* to) const;
// Helper methods to insert parallel moves in the graph.
- void InsertParallelMoveAtExitOf(HBasicBlock* block, Location source, Location destination) const;
- void InsertParallelMoveAtEntryOf(HBasicBlock* block, Location source, Location destination) const;
+ void InsertParallelMoveAtExitOf(HBasicBlock* block,
+ HInstruction* instruction,
+ Location source,
+ Location destination) const;
+ void InsertParallelMoveAtEntryOf(HBasicBlock* block,
+ HInstruction* instruction,
+ Location source,
+ Location destination) const;
void InsertMoveAfter(HInstruction* instruction, Location source, Location destination) const;
- void AddInputMoveFor(HInstruction* instruction, Location source, Location destination) const;
- void InsertParallelMoveAt(size_t position, Location source, Location destination) const;
+ void AddInputMoveFor(HInstruction* user, Location source, Location destination) const;
+ void InsertParallelMoveAt(size_t position,
+ HInstruction* instruction,
+ Location source,
+ Location destination) const;
// Helper methods.
void AllocateRegistersInternal();
+ void ProcessInstruction(HInstruction* instruction);
bool ValidateInternal(bool log_fatal_on_failure) const;
void DumpInterval(std::ostream& stream, LiveInterval* interval) const;
@@ -122,9 +133,17 @@
CodeGenerator* const codegen_;
const SsaLivenessAnalysis& liveness_;
- // List of intervals that must be processed, ordered by start position. Last entry
- // is the interval that has the lowest start position.
- GrowableArray<LiveInterval*> unhandled_;
+ // List of intervals for core registers that must be processed, ordered by start
+ // position. Last entry is the interval that has the lowest start position.
+ // This list is initially populated before doing the linear scan.
+ GrowableArray<LiveInterval*> unhandled_core_intervals_;
+
+ // List of intervals for floating-point registers. Same comments as above.
+ GrowableArray<LiveInterval*> unhandled_fp_intervals_;
+
+ // Currently processed list of unhandled intervals. Either `unhandled_core_intervals_`
+ // or `unhandled_fp_intervals_`.
+ GrowableArray<LiveInterval*>* unhandled_;
// List of intervals that have been processed.
GrowableArray<LiveInterval*> handled_;
@@ -137,13 +156,20 @@
// That is, they have a lifetime hole that spans the start of the new interval.
GrowableArray<LiveInterval*> inactive_;
- // Fixed intervals for physical registers. Such an interval covers the positions
+ // Fixed intervals for physical registers. Such intervals cover the positions
// where an instruction requires a specific register.
GrowableArray<LiveInterval*> physical_register_intervals_;
+ // Intervals for temporaries. Such intervals cover the positions
+ // where an instruction requires a temporary.
+ GrowableArray<LiveInterval*> temp_intervals_;
+
// The spill slots allocated for live intervals.
GrowableArray<size_t> spill_slots_;
+ // Instructions that need a safepoint.
+ GrowableArray<HInstruction*> safepoints_;
+
// True if processing core registers. False if processing floating
// point registers.
bool processing_core_registers_;
@@ -157,6 +183,14 @@
// Blocked registers, as decided by the code generator.
bool* const blocked_registers_;
+ // Slots reserved for out arguments.
+ size_t reserved_out_slots_;
+
+ // The maximum live registers at safepoints.
+ size_t maximum_number_of_live_registers_;
+
+ FRIEND_TEST(RegisterAllocatorTest, FreeUntil);
+
DISALLOW_COPY_AND_ASSIGN(RegisterAllocator);
};
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index a7283ab..535a768 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -16,12 +16,14 @@
#include "builder.h"
#include "code_generator.h"
+#include "code_generator_x86.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "register_allocator.h"
#include "ssa_liveness_analysis.h"
+#include "ssa_phi_elimination.h"
#include "utils/arena_allocator.h"
#include "gtest/gtest.h"
@@ -40,10 +42,10 @@
graph->BuildDominatorTree();
graph->TransformToSSA();
graph->FindNaturalLoops();
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
- RegisterAllocator register_allocator(&allocator, codegen, liveness);
+ RegisterAllocator register_allocator(&allocator, &codegen, liveness);
register_allocator.AllocateRegisters();
return register_allocator.Validate(false);
}
@@ -56,7 +58,7 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
+ x86::CodeGeneratorX86 codegen(graph);
GrowableArray<LiveInterval*> intervals(&allocator, 0);
// Test with two intervals of the same range.
@@ -65,11 +67,11 @@
intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Reset();
}
@@ -80,11 +82,11 @@
static constexpr size_t ranges2[][2] = {{42, 43}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Reset();
}
@@ -95,11 +97,11 @@
static constexpr size_t ranges2[][2] = {{42, 43}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Reset();
}
@@ -110,11 +112,11 @@
static constexpr size_t ranges2[][2] = {{42, 47}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Reset();
}
@@ -126,16 +128,16 @@
static constexpr size_t ranges2[][2] = {{42, 47}};
intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Get(1)->SetRegister(0);
// Sibling of the first interval has no register allocated to it.
ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
intervals.Get(0)->GetNextSibling()->SetRegister(0);
ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
- intervals, 0, *codegen, &allocator, true, false));
+ intervals, 0, 0, codegen, &allocator, true, false));
}
}
@@ -297,10 +299,10 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kX86);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
- RegisterAllocator register_allocator(&allocator, codegen, liveness);
+ RegisterAllocator register_allocator(&allocator, &codegen, liveness);
register_allocator.AllocateRegisters();
ASSERT_TRUE(register_allocator.Validate(false));
@@ -329,15 +331,15 @@
ArenaPool pool;
ArenaAllocator allocator(&pool);
HGraph* graph = BuildSSAGraph(data, &allocator);
- CodeGenerator* codegen = CodeGenerator::Create(&allocator, graph, kArm);
- SsaLivenessAnalysis liveness(*graph, codegen);
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
liveness.Analyze();
HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd();
HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd();
ASSERT_EQ(last_add->InputAt(0), first_add);
LiveInterval* interval = first_add->GetLiveInterval();
- ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition() + 1);
+ ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition());
ASSERT_TRUE(interval->GetNextSibling() == nullptr);
// We need a register for the output of the instruction.
@@ -346,14 +348,103 @@
// Split at the next instruction.
interval = interval->SplitAt(first_add->GetLifetimePosition() + 2);
// The user of the split is the last add.
- ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
+ ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1);
// Split before the last add.
LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1);
// Ensure the current interval has no register use...
ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime);
// And the new interval has it for the last add.
- ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
+ ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() - 1);
+}
+
+TEST(RegisterAllocatorTest, DeadPhi) {
+ /* Test for a dead loop phi taking as back-edge input a phi that also has
+ * this loop phi as input. Walking backwards in SsaDeadPhiElimination
+ * does not solve the problem because the loop phi will be visited last.
+ *
+ * Test the following snippet:
+ * int a = 0
+ * do {
+ * if (true) {
+ * a = 2;
+ * }
+ * } while (true);
+ */
+
+ const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::CONST_4 | 1 << 8 | 0,
+ Instruction::IF_NE | 1 << 8 | 1 << 12, 3,
+ Instruction::CONST_4 | 2 << 12 | 0 << 8,
+ Instruction::GOTO | 0xFD00,
+ Instruction::RETURN_VOID);
+
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = BuildSSAGraph(data, &allocator);
+ SsaDeadPhiElimination(graph).Run();
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
+ liveness.Analyze();
+ RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+ register_allocator.AllocateRegisters();
+ ASSERT_TRUE(register_allocator.Validate(false));
+}
+
+/**
+ * Test that the TryAllocateFreeReg method works in the presence of inactive intervals
+ * that share the same register. It should split the interval it is currently
+ * allocating for at the minimum lifetime position between the two inactive intervals.
+ */
+TEST(RegisterAllocatorTest, FreeUntil) {
+ const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::RETURN);
+
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraph* graph = BuildSSAGraph(data, &allocator);
+ SsaDeadPhiElimination(graph).Run();
+ x86::CodeGeneratorX86 codegen(graph);
+ SsaLivenessAnalysis liveness(*graph, &codegen);
+ liveness.Analyze();
+ RegisterAllocator register_allocator(&allocator, &codegen, liveness);
+
+ // Add an artifical range to cover the temps that will be put in the unhandled list.
+ LiveInterval* unhandled = graph->GetEntryBlock()->GetFirstInstruction()->GetLiveInterval();
+ unhandled->AddLoopRange(0, 60);
+
+ // Add three temps holding the same register, and starting at different positions.
+ // Put the one that should be picked in the middle of the inactive list to ensure
+ // we do not depend on an order.
+ LiveInterval* interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt);
+ interval->SetRegister(0);
+ interval->AddRange(40, 50);
+ register_allocator.inactive_.Add(interval);
+
+ interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt);
+ interval->SetRegister(0);
+ interval->AddRange(20, 30);
+ register_allocator.inactive_.Add(interval);
+
+ interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt);
+ interval->SetRegister(0);
+ interval->AddRange(60, 70);
+ register_allocator.inactive_.Add(interval);
+
+ register_allocator.number_of_registers_ = 1;
+ register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
+ register_allocator.processing_core_registers_ = true;
+ register_allocator.unhandled_ = ®ister_allocator.unhandled_core_intervals_;
+
+ register_allocator.TryAllocateFreeReg(unhandled);
+
+ // Check that we have split the interval.
+ ASSERT_EQ(1u, register_allocator.unhandled_->Size());
+ // Check that we know need to find a new register where the next interval
+ // that uses the register starts.
+ ASSERT_EQ(20u, register_allocator.unhandled_->Get(0)->GetStart());
}
} // namespace art
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index fbdc0b9..cd13d81 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -16,6 +16,7 @@
#include "ssa_liveness_analysis.h"
+#include "base/bit_vector-inl.h"
#include "code_generator.h"
#include "nodes.h"
@@ -101,13 +102,14 @@
// to differentiate between the start and end of an instruction. Adding 2 to
// the lifetime position for each instruction ensures the start of an
// instruction is different than the end of the previous instruction.
+ HGraphVisitor* location_builder = codegen_->GetLocationBuilder();
for (HLinearOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
block->SetLifetimeStart(lifetime_position);
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
- current->Accept(codegen_->GetLocationBuilder());
+ current->Accept(location_builder);
LocationSummary* locations = current->GetLocations();
if (locations != nullptr && locations->Out().IsValid()) {
instructions_from_ssa_index_.Add(current);
@@ -187,6 +189,7 @@
}
// Add a range that covers this block to all instructions live_in because of successors.
+ // Instructions defined in this block will have their start of the range adjusted.
for (uint32_t idx : live_in->Indexes()) {
HInstruction* current = instructions_from_ssa_index_.Get(idx);
current->GetLiveInterval()->AddRange(block->GetLifetimeStart(), block->GetLifetimeEnd());
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 83035b5..c62e61b 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -47,7 +47,7 @@
};
/**
- * A live range contains the start and end of a range where an instruction
+ * A live range contains the start and end of a range where an instruction or a temporary
* is live.
*/
class LiveRange : public ArenaObject {
@@ -99,13 +99,16 @@
is_environment_(is_environment),
position_(position),
next_(next) {
- DCHECK(user->AsPhi() != nullptr || GetPosition() == user->GetLifetimePosition() + 1);
+ DCHECK(user->IsPhi()
+ || (GetPosition() == user->GetLifetimePosition() + 1)
+ || (GetPosition() == user->GetLifetimePosition()));
DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
}
size_t GetPosition() const { return position_; }
UsePosition* GetNext() const { return next_; }
+ void SetNext(UsePosition* next) { next_ = next; }
HInstruction* GetUser() const { return user_; }
@@ -122,7 +125,7 @@
const size_t input_index_;
const bool is_environment_;
const size_t position_;
- UsePosition* const next_;
+ UsePosition* next_;
DISALLOW_COPY_AND_ASSIGN(UsePosition);
};
@@ -133,7 +136,13 @@
*/
class LiveInterval : public ArenaObject {
public:
- LiveInterval(ArenaAllocator* allocator, Primitive::Type type, HInstruction* defined_by = nullptr)
+ LiveInterval(ArenaAllocator* allocator,
+ Primitive::Type type,
+ HInstruction* defined_by = nullptr,
+ bool is_fixed = false,
+ int reg = kNoRegister,
+ bool is_temp = false,
+ bool is_slow_path_safepoint = false)
: allocator_(allocator),
first_range_(nullptr),
last_range_(nullptr),
@@ -141,39 +150,73 @@
type_(type),
next_sibling_(nullptr),
parent_(this),
- register_(kNoRegister),
+ register_(reg),
spill_slot_(kNoSpillSlot),
- is_fixed_(false),
+ is_fixed_(is_fixed),
+ is_temp_(is_temp),
+ is_slow_path_safepoint_(is_slow_path_safepoint),
defined_by_(defined_by) {}
+ static LiveInterval* MakeSlowPathInterval(ArenaAllocator* allocator, HInstruction* instruction) {
+ return new (allocator) LiveInterval(
+ allocator, Primitive::kPrimVoid, instruction, false, kNoRegister, false, true);
+ }
+
static LiveInterval* MakeFixedInterval(ArenaAllocator* allocator, int reg, Primitive::Type type) {
- LiveInterval* interval = new (allocator) LiveInterval(allocator, type);
- interval->SetRegister(reg);
- interval->is_fixed_ = true;
- return interval;
+ return new (allocator) LiveInterval(allocator, type, nullptr, true, reg, false);
+ }
+
+ static LiveInterval* MakeTempInterval(ArenaAllocator* allocator,
+ HInstruction* defined_by,
+ Primitive::Type type) {
+ return new (allocator) LiveInterval(allocator, type, defined_by, false, kNoRegister, true);
}
bool IsFixed() const { return is_fixed_; }
+ bool IsSlowPathSafepoint() const { return is_slow_path_safepoint_; }
void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
// Set the use within the instruction.
- // TODO: Use the instruction's location to know whether the instruction can die
- // at entry, or needs to say alive within the user.
- size_t position = instruction->GetLifetimePosition() + 1;
+ size_t position = instruction->GetLifetimePosition();
+ if (instruction->GetLocations()->InputOverlapsWithOutputOrTemp(input_index, is_environment)) {
+ // If it overlaps, we need to make sure the user will not try to allocate a temp
+ // or its output to the same register.
+ ++position;
+ }
+ if ((first_use_ != nullptr)
+ && (first_use_->GetUser() == instruction)
+ && (first_use_->GetPosition() < position)) {
+ // The user uses the instruction multiple times, and one use dies before the other.
+ // We update the use list so that the latter is first.
+ DCHECK(first_use_->GetPosition() + 1 == position);
+ UsePosition* new_use = new (allocator_) UsePosition(
+ instruction, input_index, is_environment, position, first_use_->GetNext());
+ first_use_->SetNext(new_use);
+ if (first_range_->GetEnd() == first_use_->GetPosition()) {
+ first_range_->end_ = position;
+ }
+ return;
+ }
+
size_t start_block_position = instruction->GetBlock()->GetLifetimeStart();
- size_t end_block_position = instruction->GetBlock()->GetLifetimeEnd();
if (first_range_ == nullptr) {
// First time we see a use of that interval.
- first_range_ = last_range_ = new (allocator_) LiveRange(start_block_position, position, nullptr);
+ first_range_ = last_range_ = new (allocator_) LiveRange(
+ start_block_position, position, nullptr);
} else if (first_range_->GetStart() == start_block_position) {
- // There is a use later in the same block.
+ // There is a use later in the same block or in a following block.
+ // Note that in such a case, `AddRange` for the whole blocks has been called
+ // before arriving in this method, and this is the reason the start of
+ // `first_range_` is before the given `position`.
DCHECK_LE(position, first_range_->GetEnd());
- } else if (first_range_->GetStart() == end_block_position) {
- // Last use is in the following block.
- first_range_->start_ = start_block_position;
} else {
DCHECK(first_range_->GetStart() > position);
// There is a hole in the interval. Create a new range.
+ // Note that the start of `first_range_` can be equal to `end`: two blocks
+ // having adjacent lifetime positions are not necessarily
+ // predecessor/successor. When two blocks are predecessor/successor, the
+ // liveness algorithm has called `AddRange` before arriving in this method,
+ // and the check line 205 would succeed.
first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_);
}
first_use_ = new (allocator_) UsePosition(
@@ -181,7 +224,7 @@
}
void AddPhiUse(HInstruction* instruction, size_t input_index, HBasicBlock* block) {
- DCHECK(instruction->AsPhi() != nullptr);
+ DCHECK(instruction->IsPhi());
first_use_ = new (allocator_) UsePosition(
instruction, input_index, false, block->GetLifetimeEnd(), first_use_);
}
@@ -192,8 +235,10 @@
} else if (first_range_->GetStart() == end) {
// There is a use in the following block.
first_range_->start_ = start;
+ } else if (first_range_->GetStart() == start && first_range_->GetEnd() == end) {
+ DCHECK(is_fixed_);
} else {
- DCHECK(first_range_->GetStart() > end);
+ DCHECK_GT(first_range_->GetStart(), end);
// There is a hole in the interval. Create a new range.
first_range_ = new (allocator_) LiveRange(start, end, first_range_);
}
@@ -215,7 +260,11 @@
}
bool HasSpillSlot() const { return spill_slot_ != kNoSpillSlot; }
- void SetSpillSlot(int slot) { spill_slot_ = slot; }
+ void SetSpillSlot(int slot) {
+ DCHECK(!is_fixed_);
+ DCHECK(!is_temp_);
+ spill_slot_ = slot;
+ }
int GetSpillSlot() const { return spill_slot_; }
void SetFrom(size_t from) {
@@ -243,6 +292,9 @@
}
bool Covers(size_t position) const {
+ if (IsDeadAt(position)) {
+ return false;
+ }
LiveRange* current = first_range_;
while (current != nullptr) {
if (position >= current->GetStart() && position < current->GetEnd()) {
@@ -288,6 +340,9 @@
}
size_t FirstRegisterUseAfter(size_t position) const {
+ if (is_temp_) {
+ return position == GetStart() ? position : kNoLifetime;
+ }
if (position == GetStart() && defined_by_ != nullptr) {
LocationSummary* locations = defined_by_->GetLocations();
Location location = locations->Out();
@@ -310,7 +365,9 @@
if (use_position >= position && !use->GetIsEnvironment()) {
Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex());
if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) {
- return use_position;
+ // Return the lifetime just before the user, so that the interval has a register
+ // when entering the user.
+ return use->GetUser()->GetLifetimePosition() - 1;
}
}
use = use->GetNext();
@@ -342,6 +399,7 @@
* [position ... end)
*/
LiveInterval* SplitAt(size_t position) {
+ DCHECK(!is_temp_);
DCHECK(!is_fixed_);
DCHECK_GT(position, GetStart());
@@ -398,12 +456,12 @@
return nullptr;
}
- bool StartsBefore(LiveInterval* other) const {
+ bool StartsBeforeOrAt(LiveInterval* other) const {
return GetStart() <= other->GetStart();
}
bool StartsAfter(LiveInterval* other) const {
- return GetStart() >= other->GetStart();
+ return GetStart() > other->GetStart();
}
void Dump(std::ostream& stream) const {
@@ -453,7 +511,13 @@
int spill_slot_;
// Whether the interval is for a fixed register.
- bool is_fixed_;
+ const bool is_fixed_;
+
+ // Whether the interval is for a temporary.
+ const bool is_temp_;
+
+ // Whether the interval is for a safepoint that calls on slow path.
+ const bool is_slow_path_safepoint_;
// The instruction represented by this interval.
HInstruction* const defined_by_;
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 13fa03f..e02a182 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -26,6 +26,8 @@
HPhi* phi = it.Current()->AsPhi();
if (phi->HasEnvironmentUses()) {
// TODO: Do we want to keep that phi alive?
+ worklist_.Add(phi);
+ phi->SetLive();
continue;
}
for (HUseIterator<HInstruction> it(phi->GetUses()); !it.Done(); it.Advance()) {
@@ -53,8 +55,9 @@
}
}
- // Remove phis that are not live. Visit in post order to ensure
- // we only remove phis with no users (dead phis might use dead phis).
+ // Remove phis that are not live. Visit in post order so that phis
+ // that are not inputs of loop phis can be removed when they have
+ // no users left (dead phis might use dead phis).
for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
HInstruction* current = block->GetFirstPhi();
@@ -62,6 +65,17 @@
while (current != nullptr) {
next = current->GetNext();
if (current->AsPhi()->IsDead()) {
+ if (current->HasUses()) {
+ for (HUseIterator<HInstruction> it(current->GetUses()); !it.Done(); it.Advance()) {
+ HUseListNode<HInstruction>* user_node = it.Current();
+ HInstruction* user = user_node->GetUser();
+ DCHECK(user->IsLoopHeaderPhi());
+ DCHECK(user->AsPhi()->IsDead());
+ // Just put itself as an input. The phi will be removed in this loop anyway.
+ user->SetRawInputAt(user_node->GetIndex(), user);
+ current->RemoveUser(user, user_node->GetIndex());
+ }
+ }
block->RemovePhi(current->AsPhi());
}
current = next;
@@ -88,12 +102,15 @@
// Find if the inputs of the phi are the same instruction.
HInstruction* candidate = phi->InputAt(0);
- // A loop phi cannot have itself as the first phi.
+ // A loop phi cannot have itself as the first phi. Note that this
+ // check relies on our simplification pass ensuring the pre-header
+ // block is first in the list of predecessors of the loop header.
+ DCHECK(!phi->IsLoopHeaderPhi() || phi->GetBlock()->IsLoopPreHeaderFirstPredecessor());
DCHECK_NE(phi, candidate);
for (size_t i = 1; i < phi->InputCount(); ++i) {
HInstruction* input = phi->InputAt(i);
- // For a loop phi, If the input is the phi, the phi is still candidate for
+ // For a loop phi, if the input is the phi, the phi is still candidate for
// elimination.
if (input != candidate && input != phi) {
candidate = nullptr;
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 088a5c4..fffe5c2 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -84,6 +84,9 @@
ASSERT_NE(graph, nullptr);
graph->BuildDominatorTree();
+ // Suspend checks implementation may change in the future, and this test relies
+ // on how instructions are ordered.
+ RemoveSuspendChecks(graph);
graph->TransformToSSA();
ReNumberInstructions(graph);
@@ -204,8 +207,8 @@
"BasicBlock 2, pred: 3, 6, succ: 3\n"
" 4: Phi(6, 0) [6]\n"
" 5: Goto\n"
- "BasicBlock 3, pred: 2, 5, succ: 2\n"
- " 6: Phi(4, 0) [4]\n"
+ "BasicBlock 3, pred: 5, 2, succ: 2\n"
+ " 6: Phi(0, 4) [4]\n"
" 7: Goto\n"
"BasicBlock 4\n"
// Synthesized blocks to avoid critical edge.
@@ -295,8 +298,8 @@
" 2: Goto\n"
"BasicBlock 1, pred: 0, succ: 4\n"
" 3: Goto\n"
- "BasicBlock 2, pred: 3, 4, succ: 5, 3\n"
- " 4: Phi(1, 0) [9, 5, 5]\n"
+ "BasicBlock 2, pred: 4, 3, succ: 5, 3\n"
+ " 4: Phi(0, 1) [9, 5, 5]\n"
" 5: Equal(4, 4) [6]\n"
" 6: If(5)\n"
"BasicBlock 3, pred: 2, succ: 2\n"
@@ -336,8 +339,8 @@
" 6: Goto\n"
"BasicBlock 3, pred: 1, succ: 8\n"
" 7: Goto\n"
- "BasicBlock 4, pred: 5, 8, succ: 6, 5\n"
- " 8: Phi(8, 14) [8, 12, 9, 9]\n"
+ "BasicBlock 4, pred: 8, 5, succ: 6, 5\n"
+ " 8: Phi(14, 8) [8, 12, 9, 9]\n"
" 9: Equal(8, 8) [10]\n"
" 10: If(9)\n"
"BasicBlock 5, pred: 4, succ: 4\n"
diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/ssa_type_propagation.cc
index 53fa74e..a860cb7 100644
--- a/compiler/optimizing/ssa_type_propagation.cc
+++ b/compiler/optimizing/ssa_type_propagation.cc
@@ -28,7 +28,11 @@
case Primitive::kPrimNot:
return existing;
default:
- return new_type;
+ // Phis are initialized with a void type, so if we are asked
+ // to merge with a void type, we should use the existing one.
+ return new_type == Primitive::kPrimVoid
+ ? existing
+ : new_type;
}
}
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 5e1329e..0ea11ad 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -26,9 +26,9 @@
namespace art {
/**
- * Collects and builds a CodeInfo for a method.
+ * Collects and builds stack maps for a method. All the stack maps
+ * for a method are placed in a CodeInfo object.
*/
-template<typename T>
class StackMapStream : public ValueObject {
public:
explicit StackMapStream(ArenaAllocator* allocator)
@@ -47,7 +47,7 @@
// See runtime/stack_map.h to know what these fields contain.
struct StackMapEntry {
uint32_t dex_pc;
- T native_pc;
+ uint32_t native_pc_offset;
uint32_t register_mask;
BitVector* sp_mask;
uint32_t num_dex_registers;
@@ -66,14 +66,14 @@
};
void AddStackMapEntry(uint32_t dex_pc,
- T native_pc,
+ uint32_t native_pc_offset,
uint32_t register_mask,
BitVector* sp_mask,
uint32_t num_dex_registers,
uint8_t inlining_depth) {
StackMapEntry entry;
entry.dex_pc = dex_pc;
- entry.native_pc = native_pc;
+ entry.native_pc_offset = native_pc_offset;
entry.register_mask = register_mask;
entry.sp_mask = sp_mask;
entry.num_dex_registers = num_dex_registers;
@@ -82,7 +82,9 @@
entry.inline_infos_start_index = inline_infos_.Size();
stack_maps_.Add(entry);
- stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+ if (sp_mask != nullptr) {
+ stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet());
+ }
if (inlining_depth > 0) {
number_of_stack_maps_with_inline_info_++;
}
@@ -102,14 +104,14 @@
}
size_t ComputeNeededSize() const {
- return CodeInfo<T>::kFixedSize
+ return CodeInfo::kFixedSize
+ ComputeStackMapSize()
+ ComputeDexRegisterMapSize()
+ ComputeInlineInfoSize();
}
size_t ComputeStackMapSize() const {
- return stack_maps_.Size() * (StackMap<T>::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
+ return stack_maps_.Size() * (StackMap::kFixedSize + StackMaskEncodingSize(stack_mask_max_));
}
size_t ComputeDexRegisterMapSize() const {
@@ -130,11 +132,12 @@
}
size_t ComputeDexRegisterMapStart() const {
- return CodeInfo<T>::kFixedSize + ComputeStackMapSize();
+ return CodeInfo::kFixedSize + ComputeStackMapSize();
}
void FillIn(MemoryRegion region) {
- CodeInfo<T> code_info(region);
+ CodeInfo code_info(region);
+ code_info.SetOverallSize(region.size());
size_t stack_mask_size = StackMaskEncodingSize(stack_mask_max_);
uint8_t* memory_start = region.start();
@@ -153,13 +156,15 @@
uintptr_t next_dex_register_map_offset = 0;
uintptr_t next_inline_info_offset = 0;
for (size_t i = 0, e = stack_maps_.Size(); i < e; ++i) {
- StackMap<T> stack_map = code_info.GetStackMapAt(i);
+ StackMap stack_map = code_info.GetStackMapAt(i);
StackMapEntry entry = stack_maps_.Get(i);
stack_map.SetDexPc(entry.dex_pc);
- stack_map.SetNativePc(entry.native_pc);
+ stack_map.SetNativePcOffset(entry.native_pc_offset);
stack_map.SetRegisterMask(entry.register_mask);
- stack_map.SetStackMask(*entry.sp_mask);
+ if (entry.sp_mask != nullptr) {
+ stack_map.SetStackMask(*entry.sp_mask);
+ }
// Set the register map.
MemoryRegion region = dex_register_maps_region.Subregion(
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index a70259e..5ee6ae0 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -34,7 +34,7 @@
TEST(StackMapTest, Test1) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream<size_t> stream(&arena);
+ StackMapStream stream(&arena);
ArenaBitVector sp_mask(&arena, 0, false);
stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, 2, 0);
@@ -46,15 +46,15 @@
MemoryRegion region(memory, size);
stream.FillIn(region);
- CodeInfo<size_t> code_info(region);
+ CodeInfo code_info(region);
ASSERT_EQ(0u, code_info.GetStackMaskSize());
ASSERT_EQ(1u, code_info.GetNumberOfStackMaps());
- StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+ StackMap stack_map = code_info.GetStackMapAt(0);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
ASSERT_EQ(0u, stack_map.GetDexPc());
- ASSERT_EQ(64u, stack_map.GetNativePc());
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset());
ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
ASSERT_FALSE(stack_map.HasInlineInfo());
@@ -71,7 +71,7 @@
TEST(StackMapTest, Test2) {
ArenaPool pool;
ArenaAllocator arena(&pool);
- StackMapStream<size_t> stream(&arena);
+ StackMapStream stream(&arena);
ArenaBitVector sp_mask1(&arena, 0, true);
sp_mask1.SetBit(2);
@@ -93,15 +93,15 @@
MemoryRegion region(memory, size);
stream.FillIn(region);
- CodeInfo<size_t> code_info(region);
+ CodeInfo code_info(region);
ASSERT_EQ(1u, code_info.GetStackMaskSize());
ASSERT_EQ(2u, code_info.GetNumberOfStackMaps());
- StackMap<size_t> stack_map = code_info.GetStackMapAt(0);
+ StackMap stack_map = code_info.GetStackMapAt(0);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(64)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64)));
ASSERT_EQ(0u, stack_map.GetDexPc());
- ASSERT_EQ(64u, stack_map.GetNativePc());
+ ASSERT_EQ(64u, stack_map.GetNativePcOffset());
ASSERT_EQ(0x3u, stack_map.GetRegisterMask());
MemoryRegion stack_mask = stack_map.GetStackMask();
@@ -120,9 +120,9 @@
stack_map = code_info.GetStackMapAt(1);
ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(1u)));
- ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePc(128u)));
+ ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(128u)));
ASSERT_EQ(1u, stack_map.GetDexPc());
- ASSERT_EQ(128u, stack_map.GetNativePc());
+ ASSERT_EQ(128u, stack_map.GetNativePcOffset());
ASSERT_EQ(0xFFu, stack_map.GetRegisterMask());
stack_mask = stack_map.GetStackMask();
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
new file mode 100644
index 0000000..2e48ee8
--- /dev/null
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#include "builder.h"
+#include "dex_instruction.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+/**
+ * Check that the HGraphBuilder adds suspend checks to backward branches.
+ */
+
+static void TestCode(const uint16_t* data) {
+ ArenaPool pool;
+ ArenaAllocator allocator(&pool);
+ HGraphBuilder builder(&allocator);
+ const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
+ HGraph* graph = builder.BuildGraph(*item);
+ ASSERT_NE(graph, nullptr);
+
+ HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0);
+ HInstruction* first_instruction = first_block->GetFirstInstruction();
+ // Account for some tests having a store local as first instruction.
+ ASSERT_TRUE(first_instruction->IsSuspendCheck()
+ || first_instruction->GetNext()->IsSuspendCheck());
+}
+
+TEST(CodegenTest, CFG1) {
+ const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+ Instruction::NOP,
+ Instruction::GOTO | 0xFF00);
+
+ TestCode(data);
+}
+
+TEST(CodegenTest, CFG2) {
+ const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
+ Instruction::GOTO_32, 0, 0);
+
+ TestCode(data);
+}
+
+TEST(CodegenTest, CFG3) {
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQ, 0xFFFF,
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+
+TEST(CodegenTest, CFG4) {
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_NE, 0xFFFF,
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+
+TEST(CodegenTest, CFG5) {
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_EQZ, 0xFFFF,
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+
+TEST(CodegenTest, CFG6) {
+ const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
+ Instruction::CONST_4 | 0 | 0,
+ Instruction::IF_NEZ, 0xFFFF,
+ Instruction::RETURN_VOID);
+
+ TestCode(data);
+}
+} // namespace art
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index d5225c1..6da375a 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -16,7 +16,7 @@
#include "trampoline_compiler.h"
-#include "jni_internal.h"
+#include "jni_env_ext.h"
#include "utils/arm/assembler_arm.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/mips/assembler_mips.h"
diff --git a/compiler/utils/arena_allocator.cc b/compiler/utils/arena_allocator.cc
index da49524..516ac2b 100644
--- a/compiler/utils/arena_allocator.cc
+++ b/compiler/utils/arena_allocator.cc
@@ -35,12 +35,23 @@
const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
"Misc ",
"BasicBlock ",
+ "BBList "
+ "BBPreds ",
+ "DfsPreOrd ",
+ "DfsPostOrd ",
+ "DomPostOrd ",
+ "TopoOrd ",
+ "Lowering ",
"LIR ",
"LIR masks ",
+ "SwitchTbl ",
+ "FillArray ",
+ "SlowPaths ",
"MIR ",
"DataFlow ",
"GrowList ",
"GrowBitMap ",
+ "SSA2Dalvik ",
"Dalvik2SSA ",
"DebugInfo ",
"Successor ",
diff --git a/compiler/utils/arena_allocator.h b/compiler/utils/arena_allocator.h
index 7bfbb6f..b2f5ca9 100644
--- a/compiler/utils/arena_allocator.h
+++ b/compiler/utils/arena_allocator.h
@@ -44,12 +44,23 @@
enum ArenaAllocKind {
kArenaAllocMisc,
kArenaAllocBB,
+ kArenaAllocBBList,
+ kArenaAllocBBPredecessors,
+ kArenaAllocDfsPreOrder,
+ kArenaAllocDfsPostOrder,
+ kArenaAllocDomPostOrder,
+ kArenaAllocTopologicalSortOrder,
+ kArenaAllocLoweringInfo,
kArenaAllocLIR,
kArenaAllocLIRResourceMask,
+ kArenaAllocSwitchTable,
+ kArenaAllocFillArrayData,
+ kArenaAllocSlowPaths,
kArenaAllocMIR,
kArenaAllocDFInfo,
kArenaAllocGrowableArray,
kArenaAllocGrowableBitMap,
+ kArenaAllocSSAToDalvikMap,
kArenaAllocDalvikToSSAMap,
kArenaAllocDebugInfo,
kArenaAllocSuccessor,
diff --git a/compiler/utils/arena_bit_vector.cc b/compiler/utils/arena_bit_vector.cc
index 39f7d18..de35f3d 100644
--- a/compiler/utils/arena_bit_vector.cc
+++ b/compiler/utils/arena_bit_vector.cc
@@ -16,11 +16,12 @@
#include "arena_allocator.h"
#include "arena_bit_vector.h"
+#include "base/allocator.h"
namespace art {
template <typename ArenaAlloc>
-class ArenaBitVectorAllocator : public Allocator {
+class ArenaBitVectorAllocator FINAL : public Allocator {
public:
explicit ArenaBitVectorAllocator(ArenaAlloc* arena) : arena_(arena) {}
~ArenaBitVectorAllocator() {}
@@ -37,7 +38,7 @@
static void operator delete(void* p) {} // Nop.
private:
- ArenaAlloc* arena_;
+ ArenaAlloc* const arena_;
DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator);
};
diff --git a/compiler/utils/arena_bit_vector.h b/compiler/utils/arena_bit_vector.h
index 485ed76..c92658f 100644
--- a/compiler/utils/arena_bit_vector.h
+++ b/compiler/utils/arena_bit_vector.h
@@ -51,23 +51,23 @@
* A BitVector implementation that uses Arena allocation.
*/
class ArenaBitVector : public BitVector {
- public:
- ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
- OatBitMapKind kind = kBitMapMisc);
- ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable,
- OatBitMapKind kind = kBitMapMisc);
- ~ArenaBitVector() {}
+ public:
+ ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
+ OatBitMapKind kind = kBitMapMisc);
+ ArenaBitVector(ScopedArenaAllocator* arena, uint32_t start_bits, bool expandable,
+ OatBitMapKind kind = kBitMapMisc);
+ ~ArenaBitVector() {}
static void* operator new(size_t size, ArenaAllocator* arena) {
- return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
+ return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
}
static void* operator new(size_t size, ScopedArenaAllocator* arena) {
- return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
+ return arena->Alloc(sizeof(ArenaBitVector), kArenaAllocGrowableBitMap);
}
static void operator delete(void* p) {} // Nop.
- private:
- const OatBitMapKind kind_; // for memory use tuning. TODO: currently unused.
+ private:
+ const OatBitMapKind kind_; // for memory use tuning. TODO: currently unused.
};
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 671ccb6..637a1ff 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -130,7 +130,7 @@
return ROR << 4 | static_cast<uint32_t>(rm_);
} else {
uint32_t imm3 = immed_ >> 2;
- uint32_t imm2 = immed_ & 0b11;
+ uint32_t imm2 = immed_ & 3U /* 0b11 */;
return imm3 << 12 | imm2 << 6 | shift_ << 4 |
static_cast<uint32_t>(rm_);
@@ -229,8 +229,8 @@
uint32_t PUW = am >> 21; // Move down to bottom of word.
PUW = (PUW >> 1) | (PUW & 1); // Bits 3, 2 and 0.
// If P is 0 then W must be 1 (Different from ARM).
- if ((PUW & 0b100) == 0) {
- PUW |= 0b1;
+ if ((PUW & 4U /* 0b100 */) == 0) {
+ PUW |= 1U /* 0b1 */;
}
encoding |= B11 | PUW << 8 | offset;
} else {
@@ -267,17 +267,17 @@
uint32_t am = am_;
// If P is 0 then W must be 1 (Different from ARM).
uint32_t PU1W = am_ >> 21; // Move down to bottom of word.
- if ((PU1W & 0b1000) == 0) {
+ if ((PU1W & 8U /* 0b1000 */) == 0) {
am |= 1 << 21; // Set W bit.
}
if (offset_ < 0) {
int32_t off = -offset_;
CHECK_LT(off, 1024);
- CHECK_EQ((off & 0b11), 0); // Must be multiple of 4.
+ CHECK_EQ((off & 3 /* 0b11 */), 0); // Must be multiple of 4.
encoding = (am ^ (1 << kUShift)) | off >> 2; // Flip U to adjust sign.
} else {
CHECK_LT(offset_, 1024);
- CHECK_EQ((offset_ & 0b11), 0); // Must be multiple of 4.
+ CHECK_EQ((offset_ & 3 /* 0b11 */), 0); // Must be multiple of 4.
encoding = am | offset_ >> 2;
}
encoding |= static_cast<uint32_t>(rn_) << 16;
@@ -886,8 +886,8 @@
/* Put it all together */
uint32_t v = 8 + z_leading;
- uint32_t i = (v & 0b10000) >> 4;
- uint32_t imm3 = (v >> 1) & 0b111;
+ uint32_t i = (v & 16U /* 0b10000 */) >> 4;
+ uint32_t imm3 = (v >> 1) & 7U /* 0b111 */;
uint32_t a = v & 1;
return value | i << 26 | imm3 << 12 | a << 7;
}
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 267bba8..6af69c8 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -955,11 +955,11 @@
if (dbl) {
// Encoded as D:Vd.
D = (reg >> 4) & 1;
- Vd = reg & 0b1111;
+ Vd = reg & 15U /* 0b1111 */;
} else {
// Encoded as Vd:D.
D = reg & 1;
- Vd = (reg >> 1) & 0b1111;
+ Vd = (reg >> 1) & 15U /* 0b1111 */;
}
int32_t encoding = B27 | B26 | B21 | B19 | B18 | B16 |
B11 | B9 |
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 4904428..7968a77 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -159,8 +159,8 @@
Emit16(encoding);
} else {
// 32 bit.
- uint32_t op1 = 0b000;
- uint32_t op2 = 0b00;
+ uint32_t op1 = 0U /* 0b000 */;
+ uint32_t op2 = 0U /* 0b00 */;
int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
op1 << 20 |
B15 | B14 | B13 | B12 |
@@ -176,8 +176,8 @@
void Thumb2Assembler::mla(Register rd, Register rn, Register rm, Register ra,
Condition cond) {
- uint32_t op1 = 0b000;
- uint32_t op2 = 0b00;
+ uint32_t op1 = 0U /* 0b000 */;
+ uint32_t op2 = 0U /* 0b00 */;
int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
op1 << 20 |
op2 << 4 |
@@ -192,8 +192,8 @@
void Thumb2Assembler::mls(Register rd, Register rn, Register rm, Register ra,
Condition cond) {
- uint32_t op1 = 0b000;
- uint32_t op2 = 0b01;
+ uint32_t op1 = 0U /* 0b000 */;
+ uint32_t op2 = 01 /* 0b01 */;
int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 |
op1 << 20 |
op2 << 4 |
@@ -208,8 +208,8 @@
void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn,
Register rm, Condition cond) {
- uint32_t op1 = 0b010;
- uint32_t op2 = 0b0000;
+ uint32_t op1 = 2U /* 0b010; */;
+ uint32_t op2 = 0U /* 0b0000 */;
int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 |
op1 << 20 |
op2 << 4 |
@@ -223,8 +223,8 @@
void Thumb2Assembler::sdiv(Register rd, Register rn, Register rm, Condition cond) {
- uint32_t op1 = 0b001;
- uint32_t op2 = 0b1111;
+ uint32_t op1 = 1U /* 0b001 */;
+ uint32_t op2 = 15U /* 0b1111 */;
int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B20 |
op1 << 20 |
op2 << 4 |
@@ -238,8 +238,8 @@
void Thumb2Assembler::udiv(Register rd, Register rn, Register rm, Condition cond) {
- uint32_t op1 = 0b001;
- uint32_t op2 = 0b1111;
+ uint32_t op1 = 1U /* 0b001 */;
+ uint32_t op2 = 15U /* 0b1111 */;
int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B21 | B20 |
op1 << 20 |
op2 << 4 |
@@ -733,29 +733,29 @@
Register rn,
Register rd,
const ShifterOperand& so) {
- uint8_t thumb_opcode = 0b11111111;
+ uint8_t thumb_opcode = 255U /* 0b11111111 */;
switch (opcode) {
- case AND: thumb_opcode = 0b0000; break;
- case EOR: thumb_opcode = 0b0100; break;
- case SUB: thumb_opcode = 0b1101; break;
- case RSB: thumb_opcode = 0b1110; break;
- case ADD: thumb_opcode = 0b1000; break;
- case ADC: thumb_opcode = 0b1010; break;
- case SBC: thumb_opcode = 0b1011; break;
+ case AND: thumb_opcode = 0U /* 0b0000 */; break;
+ case EOR: thumb_opcode = 4U /* 0b0100 */; break;
+ case SUB: thumb_opcode = 13U /* 0b1101 */; break;
+ case RSB: thumb_opcode = 14U /* 0b1110 */; break;
+ case ADD: thumb_opcode = 8U /* 0b1000 */; break;
+ case ADC: thumb_opcode = 10U /* 0b1010 */; break;
+ case SBC: thumb_opcode = 11U /* 0b1011 */; break;
case RSC: break;
- case TST: thumb_opcode = 0b0000; set_cc = true; rd = PC; break;
- case TEQ: thumb_opcode = 0b0100; set_cc = true; rd = PC; break;
- case CMP: thumb_opcode = 0b1101; set_cc = true; rd = PC; break;
- case CMN: thumb_opcode = 0b1000; set_cc = true; rd = PC; break;
- case ORR: thumb_opcode = 0b0010; break;
- case MOV: thumb_opcode = 0b0010; rn = PC; break;
- case BIC: thumb_opcode = 0b0001; break;
- case MVN: thumb_opcode = 0b0011; rn = PC; break;
+ case TST: thumb_opcode = 0U /* 0b0000 */; set_cc = true; rd = PC; break;
+ case TEQ: thumb_opcode = 4U /* 0b0100 */; set_cc = true; rd = PC; break;
+ case CMP: thumb_opcode = 13U /* 0b1101 */; set_cc = true; rd = PC; break;
+ case CMN: thumb_opcode = 8U /* 0b1000 */; set_cc = true; rd = PC; break;
+ case ORR: thumb_opcode = 2U /* 0b0010 */; break;
+ case MOV: thumb_opcode = 2U /* 0b0010 */; rn = PC; break;
+ case BIC: thumb_opcode = 1U /* 0b0001 */; break;
+ case MVN: thumb_opcode = 3U /* 0b0011 */; rn = PC; break;
default:
break;
}
- if (thumb_opcode == 0b11111111) {
+ if (thumb_opcode == 255U /* 0b11111111 */) {
LOG(FATAL) << "Invalid thumb2 opcode " << opcode;
}
@@ -764,14 +764,14 @@
// Check special cases.
if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12))) {
if (opcode == SUB) {
- thumb_opcode = 0b0101;
+ thumb_opcode = 5U /* 0b0101 */;
} else {
thumb_opcode = 0;
}
uint32_t imm = so.GetImmediate();
uint32_t i = (imm >> 11) & 1;
- uint32_t imm3 = (imm >> 8) & 0b111;
+ uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */;
uint32_t imm8 = imm & 0xff;
encoding = B31 | B30 | B29 | B28 | B25 |
@@ -817,9 +817,9 @@
Emit16BitAddSub(cond, opcode, set_cc, rn, rd, so);
return;
}
- uint8_t thumb_opcode = 0b11111111;
+ uint8_t thumb_opcode = 255U /* 0b11111111 */;
// Thumb1.
- uint8_t dp_opcode = 0b01;
+ uint8_t dp_opcode = 1U /* 0b01 */;
uint8_t opcode_shift = 6;
uint8_t rd_shift = 0;
uint8_t rn_shift = 3;
@@ -839,13 +839,13 @@
rn = so.GetRegister();
switch (so.GetShift()) {
- case LSL: thumb_opcode = 0b00; break;
- case LSR: thumb_opcode = 0b01; break;
- case ASR: thumb_opcode = 0b10; break;
+ case LSL: thumb_opcode = 0U /* 0b00 */; break;
+ case LSR: thumb_opcode = 1U /* 0b01 */; break;
+ case ASR: thumb_opcode = 2U /* 0b10 */; break;
case ROR:
// ROR doesn't allow immediates.
- thumb_opcode = 0b111;
- dp_opcode = 0b01;
+ thumb_opcode = 7U /* 0b111 */;
+ dp_opcode = 1U /* 0b01 */;
opcode_shift = 6;
use_immediate = false;
break;
@@ -860,68 +860,68 @@
}
switch (opcode) {
- case AND: thumb_opcode = 0b0000; break;
- case EOR: thumb_opcode = 0b0001; break;
+ case AND: thumb_opcode = 0U /* 0b0000 */; break;
+ case EOR: thumb_opcode = 1U /* 0b0001 */; break;
case SUB: break;
- case RSB: thumb_opcode = 0b1001; break;
+ case RSB: thumb_opcode = 9U /* 0b1001 */; break;
case ADD: break;
- case ADC: thumb_opcode = 0b0101; break;
- case SBC: thumb_opcode = 0b0110; break;
+ case ADC: thumb_opcode = 5U /* 0b0101 */; break;
+ case SBC: thumb_opcode = 6U /* 0b0110 */; break;
case RSC: break;
- case TST: thumb_opcode = 0b1000; rn = so.GetRegister(); break;
+ case TST: thumb_opcode = 8U /* 0b1000 */; rn = so.GetRegister(); break;
case TEQ: break;
case CMP:
if (use_immediate) {
// T2 encoding.
dp_opcode = 0;
opcode_shift = 11;
- thumb_opcode = 0b101;
+ thumb_opcode = 5U /* 0b101 */;
rd_shift = 8;
rn_shift = 8;
} else {
- thumb_opcode = 0b1010;
+ thumb_opcode = 10U /* 0b1010 */;
rd = rn;
rn = so.GetRegister();
}
break;
case CMN: {
- thumb_opcode = 0b1011;
+ thumb_opcode = 11U /* 0b1011 */;
rd = rn;
rn = so.GetRegister();
break;
}
- case ORR: thumb_opcode = 0b1100; break;
+ case ORR: thumb_opcode = 12U /* 0b1100 */; break;
case MOV:
dp_opcode = 0;
if (use_immediate) {
// T2 encoding.
opcode_shift = 11;
- thumb_opcode = 0b100;
+ thumb_opcode = 4U /* 0b100 */;
rd_shift = 8;
rn_shift = 8;
} else {
rn = so.GetRegister();
if (IsHighRegister(rn) || IsHighRegister(rd)) {
// Special mov for high registers.
- dp_opcode = 0b01;
+ dp_opcode = 1U /* 0b01 */;
opcode_shift = 7;
// Put the top bit of rd into the bottom bit of the opcode.
- thumb_opcode = 0b0001100 | static_cast<uint32_t>(rd) >> 3;
- rd = static_cast<Register>(static_cast<uint32_t>(rd) & 0b111);
+ thumb_opcode = 12U /* 0b0001100 */ | static_cast<uint32_t>(rd) >> 3;
+ rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
} else {
thumb_opcode = 0;
}
}
break;
- case BIC: thumb_opcode = 0b1110; break;
- case MVN: thumb_opcode = 0b1111; rn = so.GetRegister(); break;
+ case BIC: thumb_opcode = 14U /* 0b1110 */; break;
+ case MVN: thumb_opcode = 15U /* 0b1111 */; rn = so.GetRegister(); break;
default:
break;
}
}
- if (thumb_opcode == 0b11111111) {
+ if (thumb_opcode == 255U /* 0b11111111 */) {
LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
}
@@ -962,17 +962,17 @@
Register rm = so.GetRegister();
if (rn == rd) {
// Can use T2 encoding (allows 4 bit registers)
- dp_opcode = 0b01;
+ dp_opcode = 1U /* 0b01 */;
opcode_shift = 10;
- thumb_opcode = 0b0001;
+ thumb_opcode = 1U /* 0b0001 */;
// Make Rn also contain the top bit of rd.
rn = static_cast<Register>(static_cast<uint32_t>(rm) |
- (static_cast<uint32_t>(rd) & 0b1000) << 1);
- rd = static_cast<Register>(static_cast<uint32_t>(rd) & 0b111);
+ (static_cast<uint32_t>(rd) & 8U /* 0b1000 */) << 1);
+ rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */);
} else {
// T1.
opcode_shift = 9;
- thumb_opcode = 0b01100;
+ thumb_opcode = 12U /* 0b01100 */;
immediate = static_cast<uint32_t>(so.GetRegister());
use_immediate = true;
immediate_shift = 6;
@@ -981,11 +981,11 @@
// Immediate.
if (rd == SP && rn == SP) {
// ADD sp, sp, #imm
- dp_opcode = 0b10;
- thumb_opcode = 0b11;
+ dp_opcode = 2U /* 0b10 */;
+ thumb_opcode = 3U /* 0b11 */;
opcode_shift = 12;
CHECK_LT(immediate, (1 << 9));
- CHECK_EQ((immediate & 0b11), 0);
+ CHECK_EQ((immediate & 3 /* 0b11 */), 0);
// Remove rd and rn from instruction by orring it with immed and clearing bits.
rn = R0;
@@ -995,11 +995,11 @@
immediate >>= 2;
} else if (rd != SP && rn == SP) {
// ADD rd, SP, #imm
- dp_opcode = 0b10;
- thumb_opcode = 0b101;
+ dp_opcode = 2U /* 0b10 */;
+ thumb_opcode = 5U /* 0b101 */;
opcode_shift = 11;
CHECK_LT(immediate, (1 << 10));
- CHECK_EQ((immediate & 0b11), 0);
+ CHECK_EQ((immediate & 3 /* 0b11 */), 0);
// Remove rn from instruction.
rn = R0;
@@ -1009,12 +1009,12 @@
} else if (rn != rd) {
// Must use T1.
opcode_shift = 9;
- thumb_opcode = 0b01110;
+ thumb_opcode = 14U /* 0b01110 */;
immediate_shift = 6;
} else {
// T2 encoding.
opcode_shift = 11;
- thumb_opcode = 0b110;
+ thumb_opcode = 6U /* 0b110 */;
rd_shift = 8;
rn_shift = 8;
}
@@ -1025,18 +1025,18 @@
if (so.IsRegister()) {
// T1.
opcode_shift = 9;
- thumb_opcode = 0b01101;
+ thumb_opcode = 13U /* 0b01101 */;
immediate = static_cast<uint32_t>(so.GetRegister());
use_immediate = true;
immediate_shift = 6;
} else {
if (rd == SP && rn == SP) {
// SUB sp, sp, #imm
- dp_opcode = 0b10;
- thumb_opcode = 0b1100001;
+ dp_opcode = 2U /* 0b10 */;
+ thumb_opcode = 0x61 /* 0b1100001 */;
opcode_shift = 7;
CHECK_LT(immediate, (1 << 9));
- CHECK_EQ((immediate & 0b11), 0);
+ CHECK_EQ((immediate & 3 /* 0b11 */), 0);
// Remove rd and rn from instruction by orring it with immed and clearing bits.
rn = R0;
@@ -1047,12 +1047,12 @@
} else if (rn != rd) {
// Must use T1.
opcode_shift = 9;
- thumb_opcode = 0b01111;
+ thumb_opcode = 15U /* 0b01111 */;
immediate_shift = 6;
} else {
// T2 encoding.
opcode_shift = 11;
- thumb_opcode = 0b111;
+ thumb_opcode = 7U /* 0b111 */;
rd_shift = 8;
rn_shift = 8;
}
@@ -1094,11 +1094,11 @@
if (IsHighRegister(rd) || IsHighRegister(rm) || shift == ROR || shift == RRX) {
uint16_t opcode = 0;
switch (shift) {
- case LSL: opcode = 0b00; break;
- case LSR: opcode = 0b01; break;
- case ASR: opcode = 0b10; break;
- case ROR: opcode = 0b11; break;
- case RRX: opcode = 0b11; amount = 0; break;
+ case LSL: opcode = 0U /* 0b00 */; break;
+ case LSR: opcode = 1U /* 0b01 */; break;
+ case ASR: opcode = 2U /* 0b10 */; break;
+ case ROR: opcode = 3U /* 0b11 */; break;
+ case RRX: opcode = 3U /* 0b11 */; amount = 0; break;
default:
LOG(FATAL) << "Unsupported thumb2 shift opcode";
}
@@ -1106,7 +1106,7 @@
int32_t encoding = B31 | B30 | B29 | B27 | B25 | B22 |
0xf << 16 | (setcc ? B20 : 0);
uint32_t imm3 = amount >> 2;
- uint32_t imm2 = amount & 0b11;
+ uint32_t imm2 = amount & 3U /* 0b11 */;
encoding |= imm3 << 12 | imm2 << 6 | static_cast<int16_t>(rm) |
static_cast<int16_t>(rd) << 8 | opcode << 4;
Emit32(encoding);
@@ -1114,9 +1114,9 @@
// 16 bit shift
uint16_t opcode = 0;
switch (shift) {
- case LSL: opcode = 0b00; break;
- case LSR: opcode = 0b01; break;
- case ASR: opcode = 0b10; break;
+ case LSL: opcode = 0U /* 0b00 */; break;
+ case LSR: opcode = 1U /* 0b01 */; break;
+ case ASR: opcode = 2U /* 0b10 */; break;
default:
LOG(FATAL) << "Unsupported thumb2 shift opcode";
}
@@ -1136,10 +1136,10 @@
if (must_be_32bit) {
uint16_t opcode = 0;
switch (shift) {
- case LSL: opcode = 0b00; break;
- case LSR: opcode = 0b01; break;
- case ASR: opcode = 0b10; break;
- case ROR: opcode = 0b11; break;
+ case LSL: opcode = 0U /* 0b00 */; break;
+ case LSR: opcode = 1U /* 0b01 */; break;
+ case ASR: opcode = 2U /* 0b10 */; break;
+ case ROR: opcode = 3U /* 0b11 */; break;
default:
LOG(FATAL) << "Unsupported thumb2 shift opcode";
}
@@ -1152,9 +1152,9 @@
} else {
uint16_t opcode = 0;
switch (shift) {
- case LSL: opcode = 0b0010; break;
- case LSR: opcode = 0b0011; break;
- case ASR: opcode = 0b0100; break;
+ case LSL: opcode = 2U /* 0b0010 */; break;
+ case LSR: opcode = 3U /* 0b0011 */; break;
+ case ASR: opcode = 4U /* 0b0100 */; break;
default:
LOG(FATAL) << "Unsupported thumb2 shift opcode";
}
@@ -1204,7 +1204,7 @@
if (IsCompareAndBranch()) {
offset -= 4;
uint16_t i = (offset >> 6) & 1;
- uint16_t imm5 = (offset >> 1) & 0b11111;
+ uint16_t imm5 = (offset >> 1) & 31U /* 0b11111 */;
int16_t encoding = B15 | B13 | B12 |
(type_ == kCompareAndBranchNonZero ? B11 : 0) |
static_cast<uint32_t>(rn_) |
@@ -1304,15 +1304,15 @@
bool sp_relative = false;
if (byte) {
- opA = 0b0111;
+ opA = 7U /* 0b0111 */;
} else if (half) {
- opA = 0b1000;
+ opA = 8U /* 0b1000 */;
} else {
if (rn == SP) {
- opA = 0b1001;
+ opA = 9U /* 0b1001 */;
sp_relative = true;
} else {
- opA = 0b0110;
+ opA = 6U /* 0b0110 */;
}
}
int16_t encoding = opA << 12 |
@@ -1322,7 +1322,7 @@
if (sp_relative) {
// SP relative, 10 bit offset.
CHECK_LT(offset, (1 << 10));
- CHECK_EQ((offset & 0b11), 0);
+ CHECK_EQ((offset & 3 /* 0b11 */), 0);
encoding |= rd << 8 | offset >> 2;
} else {
// No SP relative. The offset is shifted right depending on
@@ -1335,12 +1335,12 @@
} else if (half) {
// 6 bit offset, shifted by 1.
CHECK_LT(offset, (1 << 6));
- CHECK_EQ((offset & 0b1), 0);
+ CHECK_EQ((offset & 1 /* 0b1 */), 0);
offset >>= 1;
} else {
// 7 bit offset, shifted by 2.
CHECK_LT(offset, (1 << 7));
- CHECK_EQ((offset & 0b11), 0);
+ CHECK_EQ((offset & 3 /* 0b11 */), 0);
offset >>= 2;
}
encoding |= rn << 3 | offset << 6;
@@ -1428,11 +1428,11 @@
switch (am) {
case IA:
case IA_W:
- op = 0b01;
+ op = 1U /* 0b01 */;
break;
case DB:
case DB_W:
- op = 0b10;
+ op = 2U /* 0b10 */;
break;
case DA:
case IB:
@@ -1534,9 +1534,9 @@
if (must_be_32bit) {
// Use encoding T3.
- uint32_t imm4 = (imm16 >> 12) & 0b1111;
- uint32_t i = (imm16 >> 11) & 0b1;
- uint32_t imm3 = (imm16 >> 8) & 0b111;
+ uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */;
+ uint32_t i = (imm16 >> 11) & 1U /* 0b1 */;
+ uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */;
uint32_t imm8 = imm16 & 0xff;
int32_t encoding = B31 | B30 | B29 | B28 |
B25 | B22 |
@@ -1557,9 +1557,9 @@
void Thumb2Assembler::movt(Register rd, uint16_t imm16, Condition cond) {
CheckCondition(cond);
// Always 32 bits.
- uint32_t imm4 = (imm16 >> 12) & 0b1111;
- uint32_t i = (imm16 >> 11) & 0b1;
- uint32_t imm3 = (imm16 >> 8) & 0b111;
+ uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */;
+ uint32_t i = (imm16 >> 11) & 1U /* 0b1 */;
+ uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */;
uint32_t imm8 = imm16 & 0xff;
int32_t encoding = B31 | B30 | B29 | B28 |
B25 | B23 | B22 |
@@ -1638,9 +1638,9 @@
void Thumb2Assembler::nop(Condition cond) {
CheckCondition(cond);
- int16_t encoding = B15 | B13 | B12 |
+ uint16_t encoding = B15 | B13 | B12 |
B11 | B10 | B9 | B8;
- Emit16(encoding);
+ Emit16(static_cast<int16_t>(encoding));
}
@@ -1840,17 +1840,17 @@
if (dbl) {
// Encoded as D:Vd.
D = (reg >> 4) & 1;
- Vd = reg & 0b1111;
+ Vd = reg & 15U /* 0b1111 */;
} else {
// Encoded as Vd:D.
D = reg & 1;
- Vd = (reg >> 1) & 0b1111;
+ Vd = (reg >> 1) & 15U /* 0b1111 */;
}
int32_t encoding = B27 | B26 | B21 | B19 | B18 | B16 |
B11 | B9 |
(dbl ? B8 : 0) |
(push ? B24 : (B23 | B20)) |
- 0b1110 << 28 |
+ 14U /* 0b1110 */ << 28 |
nregs << (dbl ? 1 : 0) |
D << 22 |
Vd << 12;
@@ -1992,7 +1992,7 @@
mask |= ToItMask(i3, firstcond0, 1);
SetItCondition(i3, firstcond, 3);
if (i3 != kItOmitted) {
- mask |= 0b0001;
+ mask |= 1U /* 0b0001 */;
}
}
}
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 3f90f21..c82b4f0 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -21,6 +21,8 @@
#include "thread.h"
#include "utils.h"
+using namespace vixl; // NOLINT(build/namespaces)
+
namespace art {
namespace arm64 {
@@ -75,7 +77,7 @@
void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value,
Condition cond) {
- if ((cond == AL) || (cond == NV)) {
+ if ((cond == al) || (cond == nv)) {
// VIXL macro-assembler handles all variants.
___ Add(reg_x(rd), reg_x(rn), value);
} else {
@@ -85,7 +87,7 @@
temps.Exclude(reg_x(rd), reg_x(rn));
vixl::Register temp = temps.AcquireX();
___ Add(temp, reg_x(rn), value);
- ___ Csel(reg_x(rd), temp, reg_x(rd), COND_OP(cond));
+ ___ Csel(reg_x(rd), temp, reg_x(rd), cond);
}
}
@@ -195,7 +197,7 @@
// Load routines.
void Arm64Assembler::LoadImmediate(Register dest, int32_t value,
Condition cond) {
- if ((cond == AL) || (cond == NV)) {
+ if ((cond == al) || (cond == nv)) {
___ Mov(reg_x(dest), value);
} else {
// temp = value
@@ -205,9 +207,9 @@
temps.Exclude(reg_x(dest));
vixl::Register temp = temps.AcquireX();
___ Mov(temp, value);
- ___ Csel(reg_x(dest), temp, reg_x(dest), COND_OP(cond));
+ ___ Csel(reg_x(dest), temp, reg_x(dest), cond);
} else {
- ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), COND_OP(cond));
+ ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), cond);
}
}
}
@@ -297,6 +299,10 @@
CHECK(dst.IsCoreRegister() && base.IsCoreRegister());
LoadWFromOffset(kLoadWord, dst.AsOverlappingCoreRegisterLow(), base.AsCoreRegister(),
offs.Int32Value());
+ if (kPoisonHeapReferences) {
+ WRegister ref_reg = dst.AsOverlappingCoreRegisterLow();
+ ___ Neg(reg_w(ref_reg), vixl::Operand(reg_w(ref_reg)));
+ }
}
void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) {
@@ -557,11 +563,11 @@
}
___ Cmp(reg_w(in_reg.AsOverlappingCoreRegisterLow()), 0);
if (!out_reg.Equals(in_reg)) {
- LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+ LoadImmediate(out_reg.AsCoreRegister(), 0, eq);
}
- AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), NE);
+ AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), ne);
} else {
- AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), AL);
+ AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offs.Int32Value(), al);
}
}
@@ -577,9 +583,9 @@
// e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
___ Cmp(reg_w(scratch.AsOverlappingCoreRegisterLow()), 0);
// Move this logic in add constants with flags.
- AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
+ AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), ne);
} else {
- AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
+ AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), al);
}
StoreToOffset(scratch.AsCoreRegister(), SP, out_off.Int32Value());
}
@@ -593,7 +599,7 @@
vixl::Label exit;
if (!out_reg.Equals(in_reg)) {
// FIXME: Who sets the flags here?
- LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
+ LoadImmediate(out_reg.AsCoreRegister(), 0, eq);
}
___ Cbz(reg_x(in_reg.AsCoreRegister()), &exit);
LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0);
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index ab4999a..bf89d24 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -33,29 +33,7 @@
namespace art {
namespace arm64 {
-#define MEM_OP(x...) vixl::MemOperand(x)
-#define COND_OP(x) static_cast<vixl::Condition>(x)
-
-enum Condition {
- kNoCondition = -1,
- EQ = 0,
- NE = 1,
- HS = 2,
- LO = 3,
- MI = 4,
- PL = 5,
- VS = 6,
- VC = 7,
- HI = 8,
- LS = 9,
- GE = 10,
- LT = 11,
- GT = 12,
- LE = 13,
- AL = 14, // Always.
- NV = 15, // Behaves as always/al.
- kMaxCondition = 16,
-};
+#define MEM_OP(...) vixl::MemOperand(__VA_ARGS__)
enum LoadOperandType {
kLoadSignedByte,
@@ -225,15 +203,15 @@
void StoreSToOffset(SRegister source, Register base, int32_t offset);
void StoreDToOffset(DRegister source, Register base, int32_t offset);
- void LoadImmediate(Register dest, int32_t value, Condition cond = AL);
+ void LoadImmediate(Register dest, int32_t value, vixl::Condition cond = vixl::al);
void Load(Arm64ManagedRegister dst, Register src, int32_t src_offset, size_t size);
void LoadWFromOffset(LoadOperandType type, WRegister dest,
Register base, int32_t offset);
void LoadFromOffset(Register dest, Register base, int32_t offset);
void LoadSFromOffset(SRegister dest, Register base, int32_t offset);
void LoadDFromOffset(DRegister dest, Register base, int32_t offset);
- void AddConstant(Register rd, int32_t value, Condition cond = AL);
- void AddConstant(Register rd, Register rn, int32_t value, Condition cond = AL);
+ void AddConstant(Register rd, int32_t value, vixl::Condition cond = vixl::al);
+ void AddConstant(Register rd, Register rn, int32_t value, vixl::Condition cond = vixl::al);
// Vixl buffer.
byte* vixl_buf_;
diff --git a/compiler/utils/array_ref.h b/compiler/utils/array_ref.h
index 2d70b7d..e6b4a6a 100644
--- a/compiler/utils/array_ref.h
+++ b/compiler/utils/array_ref.h
@@ -82,12 +82,13 @@
: array_(array), size_(size) {
}
- explicit ArrayRef(std::vector<T>& v)
+ template <typename Alloc>
+ explicit ArrayRef(std::vector<T, Alloc>& v)
: array_(v.data()), size_(v.size()) {
}
- template <typename U>
- ArrayRef(const std::vector<U>& v,
+ template <typename U, typename Alloc>
+ ArrayRef(const std::vector<U, Alloc>& v,
typename std::enable_if<std::is_same<T, const U>::value, tag>::tag t = tag())
: array_(v.data()), size_(v.size()) {
}
@@ -167,6 +168,16 @@
size_t size_;
};
+template <typename T>
+bool operator==(const ArrayRef<T>& lhs, const ArrayRef<T>& rhs) {
+ return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+}
+
+template <typename T>
+bool operator!=(const ArrayRef<T>& lhs, const ArrayRef<T>& rhs) {
+ return !(lhs == rhs);
+}
+
} // namespace art
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index 68b784a..e3045e1 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -92,6 +92,7 @@
// Compute the relocation delta and switch to the new contents area.
ptrdiff_t delta = new_contents - contents_;
+ delete[] contents_;
contents_ = new_contents;
// Update the cursor and recompute the limit.
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index f72f5e5..4addfa0 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -499,6 +499,10 @@
// and branch to a ExceptionSlowPath if it is.
virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) = 0;
+ virtual void InitializeFrameDescriptionEntry() {}
+ virtual void FinalizeFrameDescriptionEntry() {}
+ virtual std::vector<uint8_t>* GetFrameDescriptionEntry() { return nullptr; }
+
virtual ~Assembler() {}
protected:
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 891a287..e3a9580 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -116,6 +116,19 @@
std::string subdir = toolsdir + std::string("/") + std::string(entry->d_name);
size_t eabi = subdir.find(TOOL_PREFIX);
if (eabi != std::string::npos) {
+ // Check if "bin/{as,objcopy,objdump}" exist under this folder.
+ struct stat exec_st;
+ std::string exec_path;
+ exec_path = subdir + "/bin/" + TOOL_PREFIX + "as";
+ if (stat(exec_path.c_str(), &exec_st) != 0)
+ continue;
+ exec_path = subdir + "/bin/" + TOOL_PREFIX + "objcopy";
+ if (stat(exec_path.c_str(), &exec_st) != 0)
+ continue;
+ exec_path = subdir + "/bin/" + TOOL_PREFIX + "objdump";
+ if (stat(exec_path.c_str(), &exec_st) != 0)
+ continue;
+
std::string suffix = subdir.substr(eabi + strlen(TOOL_PREFIX));
double version = strtod(suffix.c_str(), nullptr);
if (version > maxversion) {
diff --git a/compiler/utils/dwarf_cfi.cc b/compiler/utils/dwarf_cfi.cc
new file mode 100644
index 0000000..83e5f5a
--- /dev/null
+++ b/compiler/utils/dwarf_cfi.cc
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#include "leb128.h"
+#include "utils.h"
+
+#include "dwarf_cfi.h"
+
+namespace art {
+
+void DW_CFA_advance_loc(std::vector<uint8_t>* buf, uint32_t increment) {
+ if (increment < 64) {
+ // Encoding in opcode.
+ buf->push_back(0x1 << 6 | increment);
+ } else if (increment < 256) {
+ // Single byte delta.
+ buf->push_back(0x02);
+ buf->push_back(increment);
+ } else if (increment < 256 * 256) {
+ // Two byte delta.
+ buf->push_back(0x03);
+ buf->push_back(increment & 0xff);
+ buf->push_back((increment >> 8) & 0xff);
+ } else {
+ // Four byte delta.
+ buf->push_back(0x04);
+ PushWord(buf, increment);
+ }
+}
+
+void DW_CFA_offset_extended_sf(std::vector<uint8_t>* buf, int reg, int32_t offset) {
+ buf->push_back(0x11);
+ EncodeUnsignedLeb128(reg, buf);
+ EncodeSignedLeb128(offset, buf);
+}
+
+void DW_CFA_offset(std::vector<uint8_t>* buf, int reg, uint32_t offset) {
+ buf->push_back((0x2 << 6) | reg);
+ EncodeUnsignedLeb128(offset, buf);
+}
+
+void DW_CFA_def_cfa_offset(std::vector<uint8_t>* buf, int32_t offset) {
+ buf->push_back(0x0e);
+ EncodeUnsignedLeb128(offset, buf);
+}
+
+void DW_CFA_remember_state(std::vector<uint8_t>* buf) {
+ buf->push_back(0x0a);
+}
+
+void DW_CFA_restore_state(std::vector<uint8_t>* buf) {
+ buf->push_back(0x0b);
+}
+
+void WriteFDEHeader(std::vector<uint8_t>* buf, bool is_64bit) {
+ // 'length' (filled in by other functions).
+ if (is_64bit) {
+ PushWord(buf, 0xffffffff); // Indicates 64bit
+ PushWord(buf, 0);
+ PushWord(buf, 0);
+ } else {
+ PushWord(buf, 0);
+ }
+
+ // 'CIE_pointer' (filled in by linker).
+ if (is_64bit) {
+ PushWord(buf, 0);
+ PushWord(buf, 0);
+ } else {
+ PushWord(buf, 0);
+ }
+
+ // 'initial_location' (filled in by linker).
+ if (is_64bit) {
+ PushWord(buf, 0);
+ PushWord(buf, 0);
+ } else {
+ PushWord(buf, 0);
+ }
+
+ // 'address_range' (filled in by other functions).
+ if (is_64bit) {
+ PushWord(buf, 0);
+ PushWord(buf, 0);
+ } else {
+ PushWord(buf, 0);
+ }
+
+ // Augmentation length: 0
+ buf->push_back(0);
+}
+
+void WriteFDEAddressRange(std::vector<uint8_t>* buf, uint64_t data, bool is_64bit) {
+ const size_t kOffsetOfAddressRange = is_64bit? 28 : 12;
+ CHECK(buf->size() >= kOffsetOfAddressRange + (is_64bit? 8 : 4));
+
+ uint8_t *p = buf->data() + kOffsetOfAddressRange;
+ if (is_64bit) {
+ p[0] = data;
+ p[1] = data >> 8;
+ p[2] = data >> 16;
+ p[3] = data >> 24;
+ p[4] = data >> 32;
+ p[5] = data >> 40;
+ p[6] = data >> 48;
+ p[7] = data >> 56;
+ } else {
+ p[0] = data;
+ p[1] = data >> 8;
+ p[2] = data >> 16;
+ p[3] = data >> 24;
+ }
+}
+
+void WriteCFILength(std::vector<uint8_t>* buf, bool is_64bit) {
+ uint64_t length = is_64bit ? buf->size() - 12 : buf->size() - 4;
+ DCHECK_EQ((length & 0x3), 0U);
+
+ uint8_t *p = is_64bit? buf->data() + 4 : buf->data();
+ if (is_64bit) {
+ p[0] = length;
+ p[1] = length >> 8;
+ p[2] = length >> 16;
+ p[3] = length >> 24;
+ p[4] = length >> 32;
+ p[5] = length >> 40;
+ p[6] = length >> 48;
+ p[7] = length >> 56;
+ } else {
+ p[0] = length;
+ p[1] = length >> 8;
+ p[2] = length >> 16;
+ p[3] = length >> 24;
+ }
+}
+
+void PadCFI(std::vector<uint8_t>* buf) {
+ while (buf->size() & 0x3) {
+ buf->push_back(0);
+ }
+}
+
+} // namespace art
diff --git a/compiler/utils/dwarf_cfi.h b/compiler/utils/dwarf_cfi.h
new file mode 100644
index 0000000..0c8b151
--- /dev/null
+++ b/compiler/utils/dwarf_cfi.h
@@ -0,0 +1,95 @@
+/*
+ * 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_COMPILER_UTILS_DWARF_CFI_H_
+#define ART_COMPILER_UTILS_DWARF_CFI_H_
+
+#include <vector>
+
+namespace art {
+
+/**
+ * @brief Enter a 'DW_CFA_advance_loc' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param increment Amount by which to increase the current location.
+ */
+void DW_CFA_advance_loc(std::vector<uint8_t>* buf, uint32_t increment);
+
+/**
+ * @brief Enter a 'DW_CFA_offset_extended_sf' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param reg Register number.
+ * @param offset Offset of register address from CFA.
+ */
+void DW_CFA_offset_extended_sf(std::vector<uint8_t>* buf, int reg, int32_t offset);
+
+/**
+ * @brief Enter a 'DW_CFA_offset' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param reg Register number.
+ * @param offset Offset of register address from CFA.
+ */
+void DW_CFA_offset(std::vector<uint8_t>* buf, int reg, uint32_t offset);
+
+/**
+ * @brief Enter a 'DW_CFA_def_cfa_offset' into an FDE buffer
+ * @param buf FDE buffer.
+ * @param offset New offset of CFA.
+ */
+void DW_CFA_def_cfa_offset(std::vector<uint8_t>* buf, int32_t offset);
+
+/**
+ * @brief Enter a 'DW_CFA_remember_state' into an FDE buffer
+ * @param buf FDE buffer.
+ */
+void DW_CFA_remember_state(std::vector<uint8_t>* buf);
+
+/**
+ * @brief Enter a 'DW_CFA_restore_state' into an FDE buffer
+ * @param buf FDE buffer.
+ */
+void DW_CFA_restore_state(std::vector<uint8_t>* buf);
+
+/**
+ * @brief Write FDE header into an FDE buffer
+ * @param buf FDE buffer.
+ * @param is_64bit If FDE is for 64bit application.
+ */
+void WriteFDEHeader(std::vector<uint8_t>* buf, bool is_64bit);
+
+/**
+ * @brief Set 'address_range' field of an FDE buffer
+ * @param buf FDE buffer.
+ * @param data Data value.
+ * @param is_64bit If FDE is for 64bit application.
+ */
+void WriteFDEAddressRange(std::vector<uint8_t>* buf, uint64_t data, bool is_64bit);
+
+/**
+ * @brief Set 'length' field of an FDE buffer
+ * @param buf FDE buffer.
+ * @param is_64bit If FDE is for 64bit application.
+ */
+void WriteCFILength(std::vector<uint8_t>* buf, bool is_64bit);
+
+/**
+ * @brief Pad an FDE buffer with 0 until its size is a multiple of 4
+ * @param buf FDE buffer.
+ */
+void PadCFI(std::vector<uint8_t>* buf);
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_DWARF_CFI_H_
diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h
index a1a3312..61e420c 100644
--- a/compiler/utils/growable_array.h
+++ b/compiler/utils/growable_array.h
@@ -26,61 +26,14 @@
// Type of growable list for memory tuning.
enum OatListKind {
kGrowableArrayMisc = 0,
- kGrowableArrayBlockList,
- kGrowableArraySSAtoDalvikMap,
- kGrowableArrayDfsOrder,
- kGrowableArrayDfsPostOrder,
- kGrowableArrayDomPostOrderTraversal,
- kGrowableArraySwitchTables,
- kGrowableArrayFillArrayData,
- kGrowableArraySuccessorBlocks,
- kGrowableArrayPredecessors,
- kGrowableArraySlowPaths,
kGNumListKinds
};
+// Deprecated
+// TODO: Replace all uses with ArenaVector<T>.
template<typename T>
class GrowableArray {
public:
- class Iterator {
- public:
- explicit Iterator(GrowableArray* g_list)
- : idx_(0),
- g_list_(g_list) {}
-
- explicit Iterator()
- : idx_(0),
- g_list_(nullptr) {}
-
- // NOTE: returns 0/NULL when no next.
- // TODO: redo to make usage consistent with other iterators.
- T Next() {
- DCHECK(g_list_ != nullptr);
- if (idx_ >= g_list_->Size()) {
- return 0;
- } else {
- return g_list_->Get(idx_++);
- }
- }
-
- void Reset() {
- idx_ = 0;
- }
-
- void Reset(GrowableArray* g_list) {
- idx_ = 0;
- g_list_ = g_list;
- }
-
- size_t GetIndex() const {
- return idx_;
- }
-
- private:
- size_t idx_;
- GrowableArray* g_list_;
- };
-
GrowableArray(ArenaAllocator* arena, size_t init_length, OatListKind kind = kGrowableArrayMisc)
: arena_(arena),
num_allocated_(init_length),
@@ -88,7 +41,7 @@
kind_(kind) {
elem_list_ = static_cast<T*>(arena_->Alloc(sizeof(T) * init_length,
kArenaAllocGrowableArray));
- };
+ }
// Expand the list size to at least new length.
@@ -105,7 +58,7 @@
memcpy(new_array, elem_list_, sizeof(T) * num_allocated_);
num_allocated_ = target_length;
elem_list_ = new_array;
- };
+ }
// NOTE: does not return storage, just resets use count.
void Reset() {
@@ -136,7 +89,7 @@
T Get(size_t index) const {
DCHECK_LT(index, num_used_);
return elem_list_[index];
- };
+ }
// Overwrite existing element at position index. List must be large enough.
void Put(size_t index, T elem) {
@@ -167,14 +120,14 @@
// We should either have found the element, or it was the last (unscanned) element.
DCHECK(found || (element == elem_list_[num_used_ - 1]));
num_used_--;
- };
+ }
void DeleteAt(size_t index) {
for (size_t i = index; i < num_used_ - 1; i++) {
elem_list_[i] = elem_list_[i + 1];
}
num_used_--;
- };
+ }
size_t GetNumAllocated() const { return num_allocated_; }
@@ -201,7 +154,7 @@
static void* operator new(size_t size, ArenaAllocator* arena) {
return arena->Alloc(sizeof(GrowableArray<T>), kArenaAllocGrowableArray);
- };
+ }
static void operator delete(void* p) {} // Nop.
private:
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index b6a5c20..f888d46 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -20,6 +20,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "memory_region.h"
#include "thread.h"
+#include "utils/dwarf_cfi.h"
namespace art {
namespace x86 {
@@ -745,6 +746,7 @@
EmitRegisterOperand(dst, src);
}
+
void X86Assembler::xchgl(Register reg, const Address& address) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x87);
@@ -752,6 +754,13 @@
}
+void X86Assembler::cmpw(const Address& address, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitComplex(7, address, imm);
+}
+
+
void X86Assembler::cmpl(Register reg, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitComplex(7, Operand(reg), imm);
@@ -1407,20 +1416,61 @@
EmitOperand(reg_or_opcode, Operand(operand));
}
+void X86Assembler::InitializeFrameDescriptionEntry() {
+ WriteFDEHeader(&cfi_info_, false /* is_64bit */);
+}
+
+void X86Assembler::FinalizeFrameDescriptionEntry() {
+ WriteFDEAddressRange(&cfi_info_, buffer_.Size(), false /* is_64bit */);
+ PadCFI(&cfi_info_);
+ WriteCFILength(&cfi_info_, false /* is_64bit */);
+}
+
constexpr size_t kFramePointerSize = 4;
void X86Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
const std::vector<ManagedRegister>& spill_regs,
const ManagedRegisterEntrySpills& entry_spills) {
+ cfi_cfa_offset_ = kFramePointerSize; // Only return address on stack
+ cfi_pc_ = buffer_.Size(); // Nothing emitted yet
+ DCHECK_EQ(cfi_pc_, 0U);
+
+ uint32_t reg_offset = 1;
CHECK_ALIGNED(frame_size, kStackAlignment);
for (int i = spill_regs.size() - 1; i >= 0; --i) {
pushl(spill_regs.at(i).AsX86().AsCpuRegister());
+
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += kFramePointerSize;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+ // DW_CFA_offset reg offset
+ reg_offset++;
+ DW_CFA_offset(&cfi_info_, spill_regs.at(i).AsX86().DWARFRegId(), reg_offset);
}
+
// return address then method on stack
- addl(ESP, Immediate(-frame_size + (spill_regs.size() * kFramePointerSize) +
- sizeof(StackReference<mirror::ArtMethod>) /*method*/ +
- kFramePointerSize /*return address*/));
+ int32_t adjust = frame_size - (spill_regs.size() * kFramePointerSize) -
+ sizeof(StackReference<mirror::ArtMethod>) /*method*/ -
+ kFramePointerSize /*return address*/;
+ addl(ESP, Immediate(-adjust));
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += adjust;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+
pushl(method_reg.AsX86().AsCpuRegister());
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += kFramePointerSize;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+
for (size_t i = 0; i < entry_spills.size(); ++i) {
movl(Address(ESP, frame_size + sizeof(StackReference<mirror::ArtMethod>) +
(i * kFramePointerSize)),
@@ -1442,6 +1492,12 @@
void X86Assembler::IncreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
addl(ESP, Immediate(-adjust));
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += adjust;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
}
void X86Assembler::DecreaseFrameSize(size_t adjust) {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index ce20768..ec983d9 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -337,6 +337,8 @@
void xchgl(Register dst, Register src);
void xchgl(Register reg, const Address& address);
+ void cmpw(const Address& address, const Immediate& imm);
+
void cmpl(Register reg, const Immediate& imm);
void cmpl(Register reg0, Register reg1);
void cmpl(Register reg, const Address& address);
@@ -571,6 +573,12 @@
// and branch to a ExceptionSlowPath if it is.
void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
+ void InitializeFrameDescriptionEntry() OVERRIDE;
+ void FinalizeFrameDescriptionEntry() OVERRIDE;
+ std::vector<uint8_t>* GetFrameDescriptionEntry() OVERRIDE {
+ return &cfi_info_;
+ }
+
private:
inline void EmitUint8(uint8_t value);
inline void EmitInt32(int32_t value);
@@ -589,6 +597,9 @@
void EmitGenericShift(int rm, Register reg, const Immediate& imm);
void EmitGenericShift(int rm, Register operand, Register shifter);
+ std::vector<uint8_t> cfi_info_;
+ uint32_t cfi_cfa_offset_, cfi_pc_;
+
DISALLOW_COPY_AND_ASSIGN(X86Assembler);
};
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index 09d2b49..5d46ee2 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -88,6 +88,14 @@
// There is a one-to-one mapping between ManagedRegister and register id.
class X86ManagedRegister : public ManagedRegister {
public:
+ int DWARFRegId() const {
+ CHECK(IsCpuRegister());
+ // For all the X86 registers we care about:
+ // EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
+ // DWARF register id is the same as id_.
+ return static_cast<int>(id_);
+ }
+
ByteRegister AsByteRegister() const {
CHECK(IsCpuRegister());
CHECK_LT(AsCpuRegister(), ESP); // ESP, EBP, ESI and EDI cannot be encoded as byte registers.
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 7684271..1dcd4b3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -20,6 +20,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "memory_region.h"
#include "thread.h"
+#include "utils/dwarf_cfi.h"
namespace art {
namespace x86_64 {
@@ -838,6 +839,14 @@
}
+void X86_64Assembler::cmpw(const Address& address, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitOptionalRex32(address);
+ EmitUint8(0x66);
+ EmitComplex(7, address, imm);
+}
+
+
void X86_64Assembler::cmpl(CpuRegister reg, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(reg);
@@ -1714,11 +1723,26 @@
}
}
+void X86_64Assembler::InitializeFrameDescriptionEntry() {
+ WriteFDEHeader(&cfi_info_, true /* is_64bit */);
+}
+
+void X86_64Assembler::FinalizeFrameDescriptionEntry() {
+ WriteFDEAddressRange(&cfi_info_, buffer_.Size(), true /* is_64bit */);
+ PadCFI(&cfi_info_);
+ WriteCFILength(&cfi_info_, true /* is_64bit */);
+}
+
constexpr size_t kFramePointerSize = 8;
void X86_64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
const std::vector<ManagedRegister>& spill_regs,
const ManagedRegisterEntrySpills& entry_spills) {
+ cfi_cfa_offset_ = kFramePointerSize; // Only return address on stack
+ cfi_pc_ = buffer_.Size(); // Nothing emitted yet
+ DCHECK_EQ(cfi_pc_, 0U);
+
+ uint32_t reg_offset = 1;
CHECK_ALIGNED(frame_size, kStackAlignment);
int gpr_count = 0;
for (int i = spill_regs.size() - 1; i >= 0; --i) {
@@ -1726,6 +1750,16 @@
if (spill.IsCpuRegister()) {
pushq(spill.AsCpuRegister());
gpr_count++;
+
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += kFramePointerSize;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+ // DW_CFA_offset reg offset
+ reg_offset++;
+ DW_CFA_offset(&cfi_info_, spill.DWARFRegId(), reg_offset);
}
}
// return address then method on stack
@@ -1733,6 +1767,13 @@
- (gpr_count * kFramePointerSize)
- kFramePointerSize /*return address*/;
subq(CpuRegister(RSP), Immediate(rest_of_frame));
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += rest_of_frame;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
+
// spill xmms
int64_t offset = rest_of_frame;
for (int i = spill_regs.size() - 1; i >= 0; --i) {
@@ -1796,6 +1837,12 @@
void X86_64Assembler::IncreaseFrameSize(size_t adjust) {
CHECK_ALIGNED(adjust, kStackAlignment);
addq(CpuRegister(RSP), Immediate(-static_cast<int64_t>(adjust)));
+ // DW_CFA_advance_loc
+ DW_CFA_advance_loc(&cfi_info_, buffer_.Size() - cfi_pc_);
+ cfi_pc_ = buffer_.Size();
+ // DW_CFA_def_cfa_offset
+ cfi_cfa_offset_ += adjust;
+ DW_CFA_def_cfa_offset(&cfi_info_, cfi_cfa_offset_);
}
void X86_64Assembler::DecreaseFrameSize(size_t adjust) {
@@ -1945,6 +1992,9 @@
X86_64ManagedRegister dest = mdest.AsX86_64();
CHECK(dest.IsCpuRegister() && dest.IsCpuRegister());
movq(dest.AsCpuRegister(), Address(base.AsX86_64().AsCpuRegister(), offs));
+ if (kPoisonHeapReferences) {
+ negl(dest.AsCpuRegister());
+ }
}
void X86_64Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
@@ -2229,4 +2279,3 @@
} // namespace x86_64
} // namespace art
-
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 9a6df1c..1fd65c2 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -253,7 +253,7 @@
class X86_64Assembler FINAL : public Assembler {
public:
- X86_64Assembler() {}
+ X86_64Assembler() : cfi_cfa_offset_(0), cfi_pc_(0) {}
virtual ~X86_64Assembler() {}
/*
@@ -378,6 +378,8 @@
void xchgq(CpuRegister dst, CpuRegister src);
void xchgl(CpuRegister reg, const Address& address);
+ void cmpw(const Address& address, const Immediate& imm);
+
void cmpl(CpuRegister reg, const Immediate& imm);
void cmpl(CpuRegister reg0, CpuRegister reg1);
void cmpl(CpuRegister reg, const Address& address);
@@ -614,6 +616,12 @@
// and branch to a ExceptionSlowPath if it is.
void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) OVERRIDE;
+ void InitializeFrameDescriptionEntry() OVERRIDE;
+ void FinalizeFrameDescriptionEntry() OVERRIDE;
+ std::vector<uint8_t>* GetFrameDescriptionEntry() OVERRIDE {
+ return &cfi_info_;
+ }
+
private:
void EmitUint8(uint8_t value);
void EmitInt32(int32_t value);
@@ -655,6 +663,9 @@
void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, CpuRegister src);
void EmitOptionalByteRegNormalizingRex32(CpuRegister dst, const Operand& operand);
+ std::vector<uint8_t> cfi_info_;
+ uint32_t cfi_cfa_offset_, cfi_pc_;
+
DISALLOW_COPY_AND_ASSIGN(X86_64Assembler);
};
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index 4ed7b20..7a48b63 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -16,6 +16,7 @@
#include "assembler_x86_64.h"
+#include "base/stl_util.h"
#include "utils/assembler_test.h"
namespace art {
@@ -62,6 +63,11 @@
}
}
+ void TearDown() OVERRIDE {
+ AssemblerTest::TearDown();
+ STLDeleteElements(®isters_);
+ }
+
std::vector<x86_64::CpuRegister*> GetRegisters() OVERRIDE {
return registers_;
}
@@ -219,6 +225,7 @@
}
}
+ STLDeleteElements(®isters);
return str.str();
}
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 822659f..3a96ad0 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -87,6 +87,21 @@
// There is a one-to-one mapping between ManagedRegister and register id.
class X86_64ManagedRegister : public ManagedRegister {
public:
+ int DWARFRegId() const {
+ CHECK(IsCpuRegister());
+ switch (id_) {
+ case RAX: return 0;
+ case RDX: return 1;
+ case RCX: return 2;
+ case RBX: return 3;
+ case RSI: return 4;
+ case RDI: return 5;
+ case RBP: return 6;
+ case RSP: return 7;
+ default: return static_cast<int>(id_); // R8 ~ R15
+ }
+ }
+
CpuRegister AsCpuRegister() const {
CHECK(IsCpuRegister());
return CpuRegister(static_cast<Register>(id_));
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 28db711..2ef826b 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -37,9 +37,9 @@
endif
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler,art/compiler,host,ndebug))
endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler,art/compiler,host,debug))
endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 05a940c..09825e2 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -45,7 +45,6 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "elf_fixup.h"
-#include "elf_patcher.h"
#include "elf_stripper.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -160,7 +159,14 @@
UsageError(" Example: --compiler-backend=Portable");
UsageError(" Default: Quick");
UsageError("");
- UsageError(" --compiler-filter=(verify-none|interpret-only|space|balanced|speed|everything):");
+ UsageError(" --compiler-filter="
+ "(verify-none"
+ "|interpret-only"
+ "|space"
+ "|balanced"
+ "|speed"
+ "|everything"
+ "|time):");
UsageError(" select compiler filter.");
UsageError(" Example: --compiler-filter=everything");
#if ART_SMALL_MODE
@@ -226,6 +232,15 @@
UsageError(" --disable-passes=<pass-names>: disable one or more passes separated by comma.");
UsageError(" Example: --disable-passes=UseCount,BBOptimizations");
UsageError("");
+ UsageError(" --print-pass-options: print a list of passes that have configurable options along "
+ "with the setting.");
+ UsageError(" Will print default if no overridden setting exists.");
+ UsageError("");
+ UsageError(" --pass-options=Pass1Name:Pass1OptionName:Pass1Option#,"
+ "Pass2Name:Pass2OptionName:Pass2Option#");
+ UsageError(" Used to specify a pass specific option. The setting itself must be integer.");
+ UsageError(" Separator used between options is a comma.");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -245,12 +260,12 @@
CHECK(verification_results != nullptr);
CHECK(method_inliner_map != nullptr);
std::unique_ptr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
- compiler_kind,
- instruction_set,
- instruction_set_features,
- verification_results,
- method_inliner_map,
- thread_count));
+ compiler_kind,
+ instruction_set,
+ instruction_set_features,
+ verification_results,
+ method_inliner_map,
+ thread_count));
if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
*p_dex2oat = nullptr;
return false;
@@ -325,39 +340,16 @@
return ReadImageClasses(image_classes_stream);
}
- bool PatchOatCode(const CompilerDriver* compiler_driver, File* oat_file,
- const std::string& oat_location, std::string* error_msg) {
- // We asked to include patch information but we are not making an image. We need to fix
- // everything up manually.
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(oat_file, PROT_READ|PROT_WRITE,
- MAP_SHARED, error_msg));
- if (elf_file.get() == NULL) {
- LOG(ERROR) << error_msg;
- return false;
- }
- {
- ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
- return ElfPatcher::Patch(compiler_driver, elf_file.get(), oat_location, error_msg);
- }
- }
-
- const CompilerDriver* CreateOatFile(const std::string& boot_image_option,
- const std::string& android_root,
- bool is_host,
- const std::vector<const DexFile*>& dex_files,
- File* oat_file,
- const std::string& oat_location,
- const std::string& bitcode_filename,
- bool image,
- std::unique_ptr<std::set<std::string>>& image_classes,
- bool dump_stats,
- bool dump_passes,
- TimingLogger& timings,
- CumulativeLogger& compiler_phases_timings,
- std::string profile_file,
- SafeMap<std::string, std::string>* key_value_store) {
- CHECK(key_value_store != nullptr);
-
+ void Compile(const std::string& boot_image_option,
+ const std::vector<const DexFile*>& dex_files,
+ const std::string& bitcode_filename,
+ bool image,
+ std::unique_ptr<std::set<std::string>>& image_classes,
+ bool dump_stats,
+ bool dump_passes,
+ TimingLogger* timings,
+ CumulativeLogger* compiler_phases_timings,
+ const std::string& profile_file) {
// Handle and ClassLoader creation needs to come after Runtime::Create
jobject class_loader = nullptr;
Thread* self = Thread::Current();
@@ -376,31 +368,45 @@
Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
}
- std::unique_ptr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
- verification_results_,
- method_inliner_map_,
- compiler_kind_,
- instruction_set_,
- instruction_set_features_,
- image,
- image_classes.release(),
- thread_count_,
- dump_stats,
- dump_passes,
- &compiler_phases_timings,
- profile_file));
+ driver_.reset(new CompilerDriver(compiler_options_,
+ verification_results_,
+ method_inliner_map_,
+ compiler_kind_,
+ instruction_set_,
+ instruction_set_features_,
+ image,
+ image_classes.release(),
+ thread_count_,
+ dump_stats,
+ dump_passes,
+ compiler_phases_timings,
+ profile_file));
- driver->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename);
+ driver_->GetCompiler()->SetBitcodeFileName(*driver_, bitcode_filename);
- driver->CompileAll(class_loader, dex_files, &timings);
+ driver_->CompileAll(class_loader, dex_files, timings);
+ }
- TimingLogger::ScopedTiming t2("dex2oat OatWriter", &timings);
+ void PrepareImageWriter(uintptr_t image_base) {
+ image_writer_.reset(new ImageWriter(*driver_, image_base));
+ }
+
+ bool CreateOatFile(const std::vector<const DexFile*>& dex_files,
+ const std::string& android_root,
+ bool is_host,
+ File* oat_file,
+ const std::string& oat_location,
+ TimingLogger* timings,
+ SafeMap<std::string, std::string>* key_value_store) {
+ CHECK(key_value_store != nullptr);
+
+ TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings);
std::string image_file_location;
uint32_t image_file_location_oat_checksum = 0;
uintptr_t image_file_location_oat_data_begin = 0;
int32_t image_patch_delta = 0;
- if (!driver->IsImage()) {
- TimingLogger::ScopedTiming t3("Loading image checksum", &timings);
+ if (!driver_->IsImage()) {
+ TimingLogger::ScopedTiming t3("Loading image checksum", timings);
gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
image_file_location_oat_data_begin =
@@ -416,49 +422,50 @@
OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
image_file_location_oat_data_begin,
image_patch_delta,
- driver.get(),
- &timings,
+ driver_.get(),
+ image_writer_.get(),
+ timings,
key_value_store);
- t2.NewTiming("Writing ELF");
- if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
- LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
- return nullptr;
- }
-
- // Flush result to disk. Patching code will re-open the file (mmap), so ensure that our view
- // of the file already made it there and won't be re-ordered with writes from PatchOat or
- // image patching.
- oat_file->Flush();
-
- if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
- t2.NewTiming("Patching ELF");
- std::string error_msg;
- if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
- LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath() << ": " << error_msg;
- return nullptr;
+ if (driver_->IsImage()) {
+ // The OatWriter constructor has already updated offsets in methods and we need to
+ // prepare method offsets in the image address space for direct method patching.
+ t2.NewTiming("Preparing image address space");
+ if (!image_writer_->PrepareImageAddressSpace()) {
+ LOG(ERROR) << "Failed to prepare image address space.";
+ return false;
}
}
- return driver.release();
+ t2.NewTiming("Writing ELF");
+ if (!driver_->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
+ LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
+ return false;
+ }
+
+ // Flush result to disk.
+ t2.NewTiming("Flushing ELF");
+ if (oat_file->Flush() != 0) {
+ LOG(ERROR) << "Failed to flush ELF file " << oat_file->GetPath();
+ return false;
+ }
+
+ return true;
}
bool CreateImageFile(const std::string& image_filename,
- uintptr_t image_base,
const std::string& oat_filename,
- const std::string& oat_location,
- const CompilerDriver& compiler)
+ const std::string& oat_location)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
- uintptr_t oat_data_begin;
- {
- // ImageWriter is scoped so it can free memory before doing FixupElf
- ImageWriter image_writer(compiler);
- if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
- LOG(ERROR) << "Failed to create image file " << image_filename;
- return false;
- }
- oat_data_begin = image_writer.GetOatDataBegin();
+ CHECK(image_writer_ != nullptr);
+ if (!image_writer_->Write(image_filename, oat_filename, oat_location)) {
+ LOG(ERROR) << "Failed to create image file " << image_filename;
+ return false;
}
+ uintptr_t oat_data_begin = image_writer_->GetOatDataBegin();
+
+ // Destroy ImageWriter before doing FixupElf.
+ image_writer_.reset();
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
if (oat_file.get() == nullptr) {
@@ -488,7 +495,9 @@
method_inliner_map_(method_inliner_map),
runtime_(nullptr),
thread_count_(thread_count),
- start_ns_(NanoTime()) {
+ start_ns_(NanoTime()),
+ driver_(nullptr),
+ image_writer_(nullptr) {
CHECK(compiler_options != nullptr);
CHECK(verification_results != nullptr);
CHECK(method_inliner_map != nullptr);
@@ -555,6 +564,8 @@
Runtime* runtime_;
size_t thread_count_;
uint64_t start_ns_;
+ std::unique_ptr<CompilerDriver> driver_;
+ std::unique_ptr<ImageWriter> image_writer_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
@@ -858,6 +869,7 @@
bool dump_stats = false;
bool dump_timing = false;
bool dump_passes = false;
+ bool print_pass_options = false;
bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
bool include_debug_symbols = kIsDebugBuild;
bool dump_slow_timing = kIsDebugBuild;
@@ -1045,6 +1057,11 @@
} else if (option.starts_with("--dump-cfg-passes=")) {
std::string dump_passes = option.substr(strlen("--dump-cfg-passes=")).data();
PassDriverMEOpts::SetDumpPassList(dump_passes);
+ } else if (option == "--print-pass-options") {
+ print_pass_options = true;
+ } else if (option.starts_with("--pass-options=")) {
+ std::string options = option.substr(strlen("--pass-options=")).data();
+ PassDriverMEOpts::SetOverriddenPassOptions(options);
} else if (option == "--include-patch-information") {
include_patch_information = true;
} else if (option == "--no-include-patch-information") {
@@ -1171,6 +1188,8 @@
compiler_filter = CompilerOptions::kSpeed;
} else if (strcmp(compiler_filter_string, "everything") == 0) {
compiler_filter = CompilerOptions::kEverything;
+ } else if (strcmp(compiler_filter_string, "time") == 0) {
+ compiler_filter = CompilerOptions::kTime;
} else {
Usage("Unknown --compiler-filter value %s", compiler_filter_string);
}
@@ -1191,6 +1210,10 @@
break;
}
+ if (print_pass_options) {
+ PassDriverMEOpts::PrintPassOptions();
+ }
+
std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter,
huge_method_threshold,
large_method_threshold,
@@ -1363,7 +1386,7 @@
* If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
* Don't bother to check if we're doing the image.
*/
- if (!image && compiler_options->IsCompilationEnabled()) {
+ if (!image && compiler_options->IsCompilationEnabled() && compiler_kind == Compiler::kQuick) {
size_t num_methods = 0;
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
@@ -1393,22 +1416,28 @@
oss << kRuntimeISA;
key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
- std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
- android_root,
- is_host,
- dex_files,
- oat_file.get(),
- oat_location,
- bitcode_filename,
- image,
- image_classes,
- dump_stats,
- dump_passes,
- timings,
- compiler_phases_timings,
- profile_file,
- key_value_store.get()));
- if (compiler.get() == nullptr) {
+ dex2oat->Compile(boot_image_option,
+ dex_files,
+ bitcode_filename,
+ image,
+ image_classes,
+ dump_stats,
+ dump_passes,
+ &timings,
+ &compiler_phases_timings,
+ profile_file);
+
+ if (image) {
+ dex2oat->PrepareImageWriter(image_base);
+ }
+
+ if (!dex2oat->CreateOatFile(dex_files,
+ android_root,
+ is_host,
+ oat_file.get(),
+ oat_location,
+ &timings,
+ key_value_store.get())) {
LOG(ERROR) << "Failed to create oat file: " << oat_location;
return EXIT_FAILURE;
}
@@ -1444,34 +1473,43 @@
//
// To get this all correct, we go through several steps.
//
- // 1. We have already created that oat file above with
- // CreateOatFile. Originally this was just our own proprietary file
- // but now it is contained within an ELF dynamic object (aka an .so
- // file). The Compiler returned by CreateOatFile provides
- // PatchInformation for references to oat code and Methods that need
- // to be update once we know where the oat file will be located
- // after the image.
+ // 1. We prepare offsets for all data in the oat file and calculate
+ // the oat data size and code size. During this stage, we also set
+ // oat code offsets in methods for use by the image writer.
//
- // 2. We create the image file. It needs to know where the oat file
+ // 2. We prepare offsets for the objects in the image and calculate
+ // the image size.
+ //
+ // 3. We create the oat file. Originally this was just our own proprietary
+ // file but now it is contained within an ELF dynamic object (aka an .so
+ // file). Since we know the image size and oat data size and code size we
+ // can prepare the ELF headers and we then know the ELF memory segment
+ // layout and we can now resolve all references. The compiler provides
+ // LinkerPatch information in each CompiledMethod and we resolve these,
+ // using the layout information and image object locations provided by
+ // image writer, as we're writing the method code.
+ //
+ // 4. We create the image file. It needs to know where the oat file
// will be loaded after itself. Originally when oat file was simply
// memory mapped so we could predict where its contents were based
// on the file size. Now that it is an ELF file, we need to inspect
// the ELF file to understand the in memory segment layout including
- // where the oat header is located within. ElfPatcher's Patch method
- // uses the PatchInformation from the Compiler to touch up absolute
- // references in the oat file.
+ // where the oat header is located within.
+ // TODO: We could just remember this information from step 3.
//
- // 3. We fixup the ELF program headers so that dlopen will try to
+ // 5. We fixup the ELF program headers so that dlopen will try to
// load the .so at the desired location at runtime by offsetting the
// Elf32_Phdr.p_vaddr values by the desired base address.
+ // TODO: Do this in step 3. We already know the layout there.
+ //
+ // Steps 1.-3. are done by the CreateOatFile() above, steps 4.-5.
+ // are done by the CreateImageFile() below.
//
if (image) {
TimingLogger::ScopedTiming t("dex2oat ImageWriter", &timings);
bool image_creation_success = dex2oat->CreateImageFile(image_filename,
- image_base,
oat_unstripped,
- oat_location,
- *compiler.get());
+ oat_location);
if (!image_creation_success) {
return EXIT_FAILURE;
}
@@ -1484,7 +1522,7 @@
LOG(INFO) << Dumpable<TimingLogger>(timings);
}
if (dump_passes) {
- LOG(INFO) << Dumpable<CumulativeLogger>(*compiler.get()->GetTimingsLogger());
+ LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
}
return EXIT_SUCCESS;
}
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index a0abc9e..d67c169 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -99,9 +99,9 @@
$(eval $(call build-libart-disassembler,target,debug))
endif
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
$(eval $(call build-libart-disassembler,host,ndebug))
endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
$(eval $(call build-libart-disassembler,host,debug))
endif
diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 54e7761..6f8e08b 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -82,14 +82,14 @@
void DisassemblerArm::DumpMemoryDomain(std::ostream& os, uint32_t domain) {
switch (domain) {
- case 0b1111: os << "sy"; break;
- case 0b1110: os << "st"; break;
- case 0b1011: os << "ish"; break;
- case 0b1010: os << "ishst"; break;
- case 0b0111: os << "nsh"; break;
- case 0b0110: os << "nshst"; break;
- case 0b0011: os << "osh"; break;
- case 0b0010: os << "oshst"; break;
+ case 15U /* 0b1111 */: os << "sy"; break;
+ case 14U /* 0b1110 */: os << "st"; break;
+ case 11U /* 0b1011 */: os << "ish"; break;
+ case 10U /* 0b1010 */: os << "ishst"; break;
+ case 7U /* 0b0111 */: os << "nsh"; break;
+ case 6U /* 0b0110 */: os << "nshst"; break;
+ case 3U /* 0b0011 */: os << "osh"; break;
+ case 2U /* 0b0010 */: os << "oshst"; break;
}
}
@@ -269,7 +269,7 @@
uint32_t op = (instruction >> 21) & 0xf;
opcode = kDataProcessingOperations[op];
bool implicit_s = ((op & ~3) == 8); // TST, TEQ, CMP, and CMN.
- bool is_mov = op == 0b1101 || op == 0b1111;
+ bool is_mov = op == 13U /* 0b1101 */ || op == 15U /* 0b1111 */;
if (is_mov) {
// Show only Rd and Rm.
if (s) {
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 4708498..195c45f 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -58,10 +58,10 @@
};
// 64-bit opcode REX modifier.
-constexpr uint8_t REX_W = 0b1000;
-constexpr uint8_t REX_R = 0b0100;
-constexpr uint8_t REX_X = 0b0010;
-constexpr uint8_t REX_B = 0b0001;
+constexpr uint8_t REX_W = 8U /* 0b1000 */;
+constexpr uint8_t REX_R = 4U /* 0b0100 */;
+constexpr uint8_t REX_X = 2U /* 0b0010 */;
+constexpr uint8_t REX_B = 1U /* 0b0001 */;
static void DumpReg0(std::ostream& os, uint8_t rex, size_t reg,
bool byte_operand, uint8_t size_override) {
@@ -558,14 +558,19 @@
has_modrm = true;
src_reg_file = dst_reg_file = SSE;
break;
- case 0x62:
+ case 0x60: case 0x61: case 0x62: case 0x6C:
if (prefix[2] == 0x66) {
src_reg_file = dst_reg_file = SSE;
prefix[2] = 0; // Clear prefix now. It has served its purpose as part of the opcode.
} else {
src_reg_file = dst_reg_file = MMX;
}
- opcode << "punpckldq";
+ switch (*instr) {
+ case 0x60: opcode << "punpcklbw"; break;
+ case 0x61: opcode << "punpcklwd"; break;
+ case 0x62: opcode << "punpckldq"; break;
+ case 0x6c: opcode << "punpcklqdq"; break;
+ }
load = true;
has_modrm = true;
break;
@@ -650,7 +655,7 @@
} else {
dst_reg_file = MMX;
}
- static const char* x73_opcodes[] = {"unknown-73", "unknown-73", "psrlq", "unknown-73", "unknown-73", "unknown-73", "psllq", "unknown-73"};
+ static const char* x73_opcodes[] = {"unknown-73", "unknown-73", "psrlq", "psrldq", "unknown-73", "unknown-73", "psllq", "unknown-73"};
modrm_opcodes = x73_opcodes;
reg_is_opcode = true;
has_modrm = true;
@@ -702,12 +707,24 @@
load = true;
immediate_bytes = 1;
break;
+ case 0xA5:
+ opcode << "shld";
+ has_modrm = true;
+ load = true;
+ cx = true;
+ break;
case 0xAC:
opcode << "shrd";
has_modrm = true;
load = true;
immediate_bytes = 1;
break;
+ case 0xAD:
+ opcode << "shrd";
+ has_modrm = true;
+ load = true;
+ cx = true;
+ break;
case 0xAE:
if (prefix[0] == 0xF3) {
prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode
@@ -750,8 +767,9 @@
case 0xB1: opcode << "cmpxchg"; has_modrm = true; store = true; break;
case 0xB6: opcode << "movzxb"; has_modrm = true; load = true; byte_second_operand = true; break;
case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break;
- case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; byte_second_operand = true; rex |= (rex == 0 ? 0 : 0b1000); break;
+ case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; byte_second_operand = true; rex |= (rex == 0 ? 0 : REX_W); break;
case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
+ case 0xC3: opcode << "movnti"; store = true; has_modrm = true; break;
case 0xC5:
if (prefix[2] == 0x66) {
opcode << "pextrw";
@@ -787,6 +805,18 @@
opcode << "bswap";
reg_in_opcode = true;
break;
+ case 0xD4:
+ if (prefix[2] == 0x66) {
+ src_reg_file = dst_reg_file = SSE;
+ prefix[2] = 0;
+ } else {
+ src_reg_file = dst_reg_file = MMX;
+ }
+ opcode << "paddq";
+ prefix[2] = 0;
+ has_modrm = true;
+ load = true;
+ break;
case 0xDB:
if (prefix[2] == 0x66) {
src_reg_file = dst_reg_file = SSE;
@@ -834,66 +864,14 @@
has_modrm = true;
load = true;
break;
+ case 0xF4:
+ case 0xF6:
case 0xF8:
- if (prefix[2] == 0x66) {
- src_reg_file = dst_reg_file = SSE;
- prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
- } else {
- src_reg_file = dst_reg_file = MMX;
- }
- opcode << "psubb";
- prefix[2] = 0;
- has_modrm = true;
- load = true;
- break;
case 0xF9:
- if (prefix[2] == 0x66) {
- src_reg_file = dst_reg_file = SSE;
- prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
- } else {
- src_reg_file = dst_reg_file = MMX;
- }
- opcode << "psubw";
- prefix[2] = 0;
- has_modrm = true;
- load = true;
- break;
case 0xFA:
- if (prefix[2] == 0x66) {
- src_reg_file = dst_reg_file = SSE;
- prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
- } else {
- src_reg_file = dst_reg_file = MMX;
- }
- opcode << "psubd";
- prefix[2] = 0;
- has_modrm = true;
- load = true;
- break;
+ case 0xFB:
case 0xFC:
- if (prefix[2] == 0x66) {
- src_reg_file = dst_reg_file = SSE;
- prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
- } else {
- src_reg_file = dst_reg_file = MMX;
- }
- opcode << "paddb";
- prefix[2] = 0;
- has_modrm = true;
- load = true;
- break;
case 0xFD:
- if (prefix[2] == 0x66) {
- src_reg_file = dst_reg_file = SSE;
- prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode
- } else {
- src_reg_file = dst_reg_file = MMX;
- }
- opcode << "paddw";
- prefix[2] = 0;
- has_modrm = true;
- load = true;
- break;
case 0xFE:
if (prefix[2] == 0x66) {
src_reg_file = dst_reg_file = SSE;
@@ -901,7 +879,17 @@
} else {
src_reg_file = dst_reg_file = MMX;
}
- opcode << "paddd";
+ switch (*instr) {
+ case 0xF4: opcode << "pmuludq"; break;
+ case 0xF6: opcode << "psadbw"; break;
+ case 0xF8: opcode << "psubb"; break;
+ case 0xF9: opcode << "psubw"; break;
+ case 0xFA: opcode << "psubd"; break;
+ case 0xFB: opcode << "psubq"; break;
+ case 0xFC: opcode << "paddb"; break;
+ case 0xFD: opcode << "paddw"; break;
+ case 0xFE: opcode << "paddd"; break;
+ }
prefix[2] = 0;
has_modrm = true;
load = true;
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index c35ff85..25d10bd 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@
oatdump.cc
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler libart-compiler,art/disassembler art/compiler,target,ndebug))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler libart-compiler,art/disassembler art/compiler,target,debug))
endif
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler libart-compiler,art/disassembler art/compiler,host,ndebug))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler libart-compiler,art/disassembler art/compiler,host,debug))
endif
########################################################################
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7607bf0..d5e766f 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -20,6 +20,7 @@
#include <fstream>
#include <iostream>
#include <string>
+#include <unordered_map>
#include <vector>
#include "base/stringpiece.h"
@@ -29,6 +30,7 @@
#include "dex_file-inl.h"
#include "dex_instruction.h"
#include "disassembler.h"
+#include "elf_builder.h"
#include "field_helper.h"
#include "gc_map.h"
#include "gc/space/image_space.h"
@@ -47,13 +49,16 @@
#include "oat.h"
#include "oat_file-inl.h"
#include "os.h"
+#include "output_stream.h"
#include "runtime.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
+#include "ScopedLocalRef.h"
#include "thread_list.h"
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
#include "vmap_table.h"
+#include "well_known_classes.h"
namespace art {
@@ -102,7 +107,6 @@
" --no-disassemble may be used to disable disassembly.\n"
" Example: --no-disassemble\n"
"\n");
- exit(EXIT_FAILURE);
}
const char* image_roots_descriptions_[] = {
@@ -116,24 +120,250 @@
"kClassRoots",
};
+class OatSymbolizer : public CodeOutput {
+ public:
+ explicit OatSymbolizer(const OatFile* oat_file, std::string& output_name) :
+ oat_file_(oat_file), builder_(nullptr), elf_output_(nullptr), output_name_(output_name) {}
+
+ bool Init() {
+ Elf32_Word oat_data_size = oat_file_->GetOatHeader().GetExecutableOffset();
+
+ uint32_t diff = static_cast<uint32_t>(oat_file_->End() - oat_file_->Begin());
+ uint32_t oat_exec_size = diff - oat_data_size;
+
+ if (output_name_.empty()) {
+ output_name_ = "symbolized.oat";
+ }
+ elf_output_ = OS::CreateEmptyFile(output_name_.c_str());
+
+ builder_.reset(new ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(
+ this,
+ elf_output_,
+ oat_file_->GetOatHeader().GetInstructionSet(),
+ 0,
+ oat_data_size,
+ oat_data_size,
+ oat_exec_size,
+ true,
+ false));
+
+ if (!builder_->Init()) {
+ builder_.reset(nullptr);
+ return false;
+ }
+
+ return true;
+ }
+
+ typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&,
+ uint32_t,
+ const OatFile::OatMethod&,
+ const DexFile&,
+ uint32_t,
+ const DexFile::CodeItem*,
+ uint32_t);
+
+ bool Symbolize() {
+ if (builder_.get() == nullptr) {
+ return false;
+ }
+
+ Walk(&art::OatSymbolizer::RegisterForDedup);
+
+ NormalizeState();
+
+ Walk(&art::OatSymbolizer::AddSymbol);
+
+ bool result = builder_->Write();
+
+ elf_output_->Flush();
+ elf_output_->Close();
+
+ return result;
+ }
+
+ void Walk(Callback callback) {
+ std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
+ for (size_t i = 0; i < oat_dex_files.size(); i++) {
+ const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+ CHECK(oat_dex_file != NULL);
+ WalkOatDexFile(oat_dex_file, callback);
+ }
+ }
+
+ void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) {
+ std::string error_msg;
+ std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ return;
+ }
+ for (size_t class_def_index = 0;
+ class_def_index < dex_file->NumClassDefs();
+ class_def_index++) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
+ OatClassType type = oat_class.GetType();
+ switch (type) {
+ case kOatClassAllCompiled:
+ case kOatClassSomeCompiled:
+ WalkOatClass(oat_class, *dex_file.get(), class_def, callback);
+ break;
+
+ case kOatClassNoneCompiled:
+ case kOatClassMax:
+ // Ignore.
+ break;
+ }
+ }
+ }
+
+ void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file,
+ const DexFile::ClassDef& class_def, Callback callback) {
+ const byte* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) { // empty class such as a marker interface?
+ return;
+ }
+ // Note: even if this is an interface or a native class, we still have to walk it, as there
+ // might be a static initializer.
+ ClassDataItemIterator it(dex_file, class_data);
+ SkipAllFields(&it);
+ uint32_t class_method_idx = 0;
+ while (it.HasNextDirectMethod()) {
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+ it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback);
+ class_method_idx++;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(),
+ it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback);
+ class_method_idx++;
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+
+ void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags, Callback callback) {
+ if ((method_access_flags & kAccAbstract) != 0) {
+ // Abstract method, no code.
+ return;
+ }
+ if (oat_method.GetCodeOffset() == 0) {
+ // No code.
+ return;
+ }
+
+ (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item,
+ method_access_flags);
+ }
+
+ void RegisterForDedup(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ state_[oat_method.GetCodeOffset()]++;
+ }
+
+ void NormalizeState() {
+ for (auto& x : state_) {
+ if (x.second == 1) {
+ state_[x.first] = 0;
+ }
+ }
+ }
+
+ enum class DedupState { // private
+ kNotDeduplicated,
+ kDeduplicatedFirst,
+ kDeduplicatedOther
+ };
+ DedupState IsDuplicated(uint32_t offset) {
+ if (state_[offset] == 0) {
+ return DedupState::kNotDeduplicated;
+ }
+ if (state_[offset] == 1) {
+ return DedupState::kDeduplicatedOther;
+ }
+ state_[offset] = 1;
+ return DedupState::kDeduplicatedFirst;
+ }
+
+ void AddSymbol(const DexFile::ClassDef& class_def, uint32_t class_method_index,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ DedupState dedup = IsDuplicated(oat_method.GetCodeOffset());
+ if (dedup != DedupState::kDeduplicatedOther) {
+ std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true);
+
+ if (dedup == DedupState::kDeduplicatedFirst) {
+ pretty_name = "[Dedup]" + pretty_name;
+ }
+
+ ElfSymtabBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr,
+ Elf32_Sym, Elf32_Shdr>* symtab = &builder_->symtab_builder_;
+
+ symtab->AddSymbol(pretty_name, &builder_->text_builder_, oat_method.GetCodeOffset() -
+ oat_file_->GetOatHeader().GetExecutableOffset(), true,
+ oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC);
+ }
+ }
+
+ // Set oat data offset. Required by ElfBuilder/CodeOutput.
+ void SetCodeOffset(size_t offset) {
+ // Nothing to do.
+ }
+
+ // Write oat code. Required by ElfBuilder/CodeOutput.
+ bool Write(OutputStream* out) {
+ return out->WriteFully(oat_file_->Begin(), oat_file_->End() - oat_file_->Begin());
+ }
+
+ private:
+ static void SkipAllFields(ClassDataItemIterator* it) {
+ while (it->HasNextStaticField()) {
+ it->Next();
+ }
+ while (it->HasNextInstanceField()) {
+ it->Next();
+ }
+ }
+
+ const OatFile* oat_file_;
+ std::unique_ptr<ElfBuilder<Elf32_Word, Elf32_Sword, Elf32_Addr, Elf32_Dyn,
+ Elf32_Sym, Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr> > builder_;
+ File* elf_output_;
+ std::unordered_map<uint32_t, uint32_t> state_;
+ std::string output_name_;
+};
+
class OatDumperOptions {
public:
OatDumperOptions(bool dump_raw_mapping_table,
bool dump_raw_gc_map,
bool dump_vmap,
bool disassemble_code,
- bool absolute_addresses)
+ bool absolute_addresses,
+ Handle<mirror::ClassLoader>* class_loader)
: dump_raw_mapping_table_(dump_raw_mapping_table),
dump_raw_gc_map_(dump_raw_gc_map),
dump_vmap_(dump_vmap),
disassemble_code_(disassemble_code),
- absolute_addresses_(absolute_addresses) {}
+ absolute_addresses_(absolute_addresses),
+ class_loader_(class_loader) {}
const bool dump_raw_mapping_table_;
const bool dump_raw_gc_map_;
const bool dump_vmap_;
const bool disassemble_code_;
const bool absolute_addresses_;
+ Handle<mirror::ClassLoader>* class_loader_;
};
class OatDumper {
@@ -145,6 +375,7 @@
disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(),
new DisassemblerOptions(options_->absolute_addresses_,
oat_file.Begin()))) {
+ CHECK(options_->class_loader_ != nullptr);
AddAllOffsets();
}
@@ -574,6 +805,12 @@
*indent2_os << "\n";
}
{
+ // Based on spill masks from QuickMethodFrameInfo so placed
+ // after it is dumped, but useful for understanding quick
+ // code, so dumped here.
+ DumpVregLocations(*indent2_os, oat_method, code_item);
+ }
+ {
*indent1_os << "CODE: ";
uint32_t code_size_offset = oat_method.GetQuickCodeSizeOffset();
if (code_size_offset > oat_file_.Size()) {
@@ -664,6 +901,12 @@
}
void DumpVmap(std::ostream& os, const OatFile::OatMethod& oat_method) {
+ // If the native GC map is null, then this method has been compiled with the
+ // optimizing compiler. The optimizing compiler currently outputs its stack map
+ // in the vmap table, and the code below does not work with such a stack map.
+ if (oat_method.GetNativeGcMap() == nullptr) {
+ return;
+ }
const uint8_t* raw_table = oat_method.GetVmapTable();
if (raw_table != nullptr) {
const VmapTable vmap_table(raw_table);
@@ -690,6 +933,45 @@
}
}
+ void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method,
+ const DexFile::CodeItem* code_item) {
+ if (code_item != nullptr) {
+ size_t num_locals_ins = code_item->registers_size_;
+ size_t num_ins = code_item->ins_size_;
+ size_t num_locals = num_locals_ins - num_ins;
+ size_t num_outs = code_item->outs_size_;
+
+ os << "vr_stack_locations:";
+ for (size_t reg = 0; reg <= num_locals_ins; reg++) {
+ // For readability, delimit the different kinds of VRs.
+ if (reg == num_locals_ins) {
+ os << "\n\tmethod*:";
+ } else if (reg == num_locals && num_ins > 0) {
+ os << "\n\tins:";
+ } else if (reg == 0 && num_locals > 0) {
+ os << "\n\tlocals:";
+ }
+
+ uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
+ oat_method.GetFpSpillMask(),
+ oat_method.GetFrameSizeInBytes(), reg,
+ GetInstructionSet());
+ os << " v" << reg << "[sp + #" << offset << "]";
+ }
+
+ for (size_t out_reg = 0; out_reg < num_outs; out_reg++) {
+ if (out_reg == 0) {
+ os << "\n\touts:";
+ }
+
+ uint32_t offset = StackVisitor::GetOutVROffset(out_reg, GetInstructionSet());
+ os << " v" << out_reg << "[sp + #" << offset << "]";
+ }
+
+ os << "\n";
+ }
+ }
+
void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
const uint8_t* raw_table = oat_method.GetVmapTable();
@@ -898,13 +1180,16 @@
uint32_t method_access_flags) {
if ((method_access_flags & kAccNative) == 0) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
+ StackHandleScope<1> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(
hs.NewHandle(Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file)));
- auto class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
- return verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache,
- class_loader, &class_def, code_item,
- nullptr, method_access_flags);
+ DCHECK(options_->class_loader_ != nullptr);
+ return verifier::MethodVerifier::VerifyMethodAndDump(soa.Self(), os, dex_method_idx, dex_file,
+ dex_cache,
+ *options_->class_loader_,
+ &class_def, code_item,
+ NullHandle<mirror::ArtMethod>(),
+ method_access_flags);
}
return nullptr;
@@ -1146,15 +1431,26 @@
StackHandleScope<1> hs(Thread::Current());
FieldHelper fh(hs.NewHandle(field));
mirror::Class* type = fh.GetType();
+ DCHECK(type->IsPrimitive());
if (type->IsPrimitiveLong()) {
os << StringPrintf("%" PRId64 " (0x%" PRIx64 ")\n", field->Get64(obj), field->Get64(obj));
} else if (type->IsPrimitiveDouble()) {
os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
} else if (type->IsPrimitiveFloat()) {
os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
- } else {
- DCHECK(type->IsPrimitive());
+ } else if (type->IsPrimitiveInt()) {
os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
+ } else if (type->IsPrimitiveChar()) {
+ os << StringPrintf("%u (0x%x)\n", field->GetChar(obj), field->GetChar(obj));
+ } else if (type->IsPrimitiveShort()) {
+ os << StringPrintf("%d (0x%x)\n", field->GetShort(obj), field->GetShort(obj));
+ } else if (type->IsPrimitiveBoolean()) {
+ os << StringPrintf("%s (0x%x)\n", field->GetBoolean(obj)? "true" : "false",
+ field->GetBoolean(obj));
+ } else if (type->IsPrimitiveByte()) {
+ os << StringPrintf("%d (0x%x)\n", field->GetByte(obj), field->GetByte(obj));
+ } else {
+ LOG(FATAL) << "Unknown type: " << PrettyClass(type);
}
} else {
// Get the value, don't compute the type unless it is non-null as we don't want
@@ -1673,103 +1969,10 @@
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
-static int oatdump(int argc, char** argv) {
- InitLogging(argv);
+static NoopCompilerCallbacks callbacks;
- // Skip over argv[0].
- argv++;
- argc--;
-
- if (argc == 0) {
- fprintf(stderr, "No arguments specified\n");
- usage();
- }
-
- const char* oat_filename = nullptr;
- const char* image_location = nullptr;
- const char* boot_image_location = nullptr;
- InstructionSet instruction_set = kRuntimeISA;
- std::string elf_filename_prefix;
- std::ostream* os = &std::cout;
- std::unique_ptr<std::ofstream> out;
- bool dump_raw_mapping_table = false;
- bool dump_raw_gc_map = false;
- bool dump_vmap = true;
- bool disassemble_code = true;
-
- for (int i = 0; i < argc; i++) {
- const StringPiece option(argv[i]);
- if (option.starts_with("--oat-file=")) {
- oat_filename = option.substr(strlen("--oat-file=")).data();
- } else if (option.starts_with("--image=")) {
- image_location = option.substr(strlen("--image=")).data();
- } else if (option.starts_with("--boot-image=")) {
- boot_image_location = option.substr(strlen("--boot-image=")).data();
- } else if (option.starts_with("--instruction-set=")) {
- StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
- if (instruction_set_str == "arm") {
- instruction_set = kThumb2;
- } else if (instruction_set_str == "arm64") {
- instruction_set = kArm64;
- } else if (instruction_set_str == "mips") {
- instruction_set = kMips;
- } else if (instruction_set_str == "x86") {
- instruction_set = kX86;
- } else if (instruction_set_str == "x86_64") {
- instruction_set = kX86_64;
- }
- } else if (option =="--dump:raw_mapping_table") {
- dump_raw_mapping_table = true;
- } else if (option == "--dump:raw_gc_map") {
- dump_raw_gc_map = true;
- } else if (option == "--no-dump:vmap") {
- dump_vmap = false;
- } else if (option == "--no-disassemble") {
- disassemble_code = false;
- } else if (option.starts_with("--output=")) {
- const char* filename = option.substr(strlen("--output=")).data();
- out.reset(new std::ofstream(filename));
- if (!out->good()) {
- fprintf(stderr, "Failed to open output filename %s\n", filename);
- usage();
- }
- os = out.get();
- } else {
- fprintf(stderr, "Unknown argument %s\n", option.data());
- usage();
- }
- }
-
- if (image_location == nullptr && oat_filename == nullptr) {
- fprintf(stderr, "Either --image or --oat must be specified\n");
- return EXIT_FAILURE;
- }
-
- if (image_location != nullptr && oat_filename != nullptr) {
- fprintf(stderr, "Either --image or --oat must be specified but not both\n");
- return EXIT_FAILURE;
- }
-
- // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
- bool absolute_addresses = (oat_filename == nullptr);
- std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(dump_raw_mapping_table,
- dump_raw_gc_map,
- dump_vmap,
- disassemble_code,
- absolute_addresses));
- if (oat_filename != nullptr) {
- std::string error_msg;
- OatFile* oat_file =
- OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
- if (oat_file == nullptr) {
- fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
- return EXIT_FAILURE;
- }
- OatDumper oat_dumper(*oat_file, oat_dumper_options.release());
- bool success = oat_dumper.Dump(*os);
- return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
- }
-
+static Runtime* StartRuntime(const char* boot_image_location, const char* image_location,
+ InstructionSet instruction_set) {
RuntimeOptions options;
std::string image_option;
std::string oat_option;
@@ -1777,7 +1980,6 @@
std::string boot_oat_option;
// We are more like a compiler than a run-time. We don't want to execute code.
- NoopCompilerCallbacks callbacks;
options.push_back(std::make_pair("compilercallbacks", &callbacks));
if (boot_image_location != nullptr) {
@@ -1796,14 +1998,24 @@
if (!Runtime::Create(options, false)) {
fprintf(stderr, "Failed to create runtime\n");
- return EXIT_FAILURE;
+ return nullptr;
}
- std::unique_ptr<Runtime> runtime(Runtime::Current());
+
// Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
// give it away now and then switch to a more manageable ScopedObjectAccess.
Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+ return Runtime::Current();
+}
+
+static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options,
+ std::ostream* os) {
+ // Dumping the image, no explicit class loader.
+ NullHandle<mirror::ClassLoader> null_class_loader;
+ options->class_loader_ = &null_class_loader;
+
ScopedObjectAccess soa(Thread::Current());
- gc::Heap* heap = Runtime::Current()->GetHeap();
+ gc::Heap* heap = runtime->GetHeap();
gc::space::ImageSpace* image_space = heap->GetImageSpace();
CHECK(image_space != nullptr);
const ImageHeader& image_header = image_space->GetImageHeader();
@@ -1811,11 +2023,227 @@
fprintf(stderr, "Invalid image header %s\n", image_location);
return EXIT_FAILURE;
}
- ImageDumper image_dumper(os, *image_space, image_header, oat_dumper_options.release());
+ ImageDumper image_dumper(os, *image_space, image_header, options);
bool success = image_dumper.Dump();
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}
+static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOptions* options,
+ std::ostream* os) {
+ CHECK(runtime != nullptr && oat_file != nullptr && options != nullptr);
+
+ Thread* self = Thread::Current();
+ CHECK(self != nullptr);
+ // Need well-known-classes.
+ WellKnownClasses::Init(self->GetJniEnv());
+
+ // Need to register dex files to get a working dex cache.
+ ScopedObjectAccess soa(self);
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ class_linker->RegisterOatFile(oat_file);
+ std::vector<const DexFile*> dex_files;
+ for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
+ std::string error_msg;
+ const DexFile* dex_file = odf->OpenDexFile(&error_msg);
+ CHECK(dex_file != nullptr) << error_msg;
+ class_linker->RegisterDexFile(*dex_file);
+ dex_files.push_back(dex_file);
+ }
+
+ // Need a class loader.
+ soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
+ ScopedLocalRef<jobject> class_loader_local(soa.Env(),
+ soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+ jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
+ // Fake that we're a compiler.
+ runtime->SetCompileTimeClassPath(class_loader, dex_files);
+
+ // Use the class loader while dumping.
+ StackHandleScope<1> scope(self);
+ Handle<mirror::ClassLoader> loader_handle = scope.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(class_loader));
+ options->class_loader_ = &loader_handle;
+
+ OatDumper oat_dumper(*oat_file, options);
+ bool success = oat_dumper.Dump(*os);
+ return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, std::ostream* os) {
+ // No image = no class loader.
+ NullHandle<mirror::ClassLoader> null_class_loader;
+ options->class_loader_ = &null_class_loader;
+
+ OatDumper oat_dumper(*oat_file, options);
+ bool success = oat_dumper.Dump(*os);
+ return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
+ std::ostream* os) {
+ std::string error_msg;
+ OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+ if (oat_file == nullptr) {
+ fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+ return EXIT_FAILURE;
+ }
+
+ if (runtime != nullptr) {
+ return DumpOatWithRuntime(runtime, oat_file, options, os);
+ } else {
+ return DumpOatWithoutRuntime(oat_file, options, os);
+ }
+}
+
+static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
+ std::string error_msg;
+ OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg);
+ if (oat_file == nullptr) {
+ fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
+ return EXIT_FAILURE;
+ }
+
+ OatSymbolizer oat_symbolizer(oat_file, output_name);
+ if (!oat_symbolizer.Init()) {
+ fprintf(stderr, "Failed to initialize symbolizer\n");
+ return EXIT_FAILURE;
+ }
+ if (!oat_symbolizer.Symbolize()) {
+ fprintf(stderr, "Failed to symbolize\n");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+struct OatdumpArgs {
+ bool Parse(int argc, char** argv) {
+ // Skip over argv[0].
+ argv++;
+ argc--;
+
+ if (argc == 0) {
+ fprintf(stderr, "No arguments specified\n");
+ usage();
+ return false;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ const StringPiece option(argv[i]);
+ if (option.starts_with("--oat-file=")) {
+ oat_filename_ = option.substr(strlen("--oat-file=")).data();
+ } else if (option.starts_with("--image=")) {
+ image_location_ = option.substr(strlen("--image=")).data();
+ } else if (option.starts_with("--boot-image=")) {
+ boot_image_location_ = option.substr(strlen("--boot-image=")).data();
+ } else if (option.starts_with("--instruction-set=")) {
+ StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
+ instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
+ if (instruction_set_ == kNone) {
+ fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
+ usage();
+ return false;
+ }
+ } else if (option =="--dump:raw_mapping_table") {
+ dump_raw_mapping_table_ = true;
+ } else if (option == "--dump:raw_gc_map") {
+ dump_raw_gc_map_ = true;
+ } else if (option == "--no-dump:vmap") {
+ dump_vmap_ = false;
+ } else if (option == "--no-disassemble") {
+ disassemble_code_ = false;
+ } else if (option.starts_with("--output=")) {
+ output_name_ = option.substr(strlen("--output=")).ToString();
+ const char* filename = output_name_.c_str();
+ out_.reset(new std::ofstream(filename));
+ if (!out_->good()) {
+ fprintf(stderr, "Failed to open output filename %s\n", filename);
+ usage();
+ return false;
+ }
+ os_ = out_.get();
+ } else if (option.starts_with("--symbolize=")) {
+ oat_filename_ = option.substr(strlen("--symbolize=")).data();
+ symbolize_ = true;
+ } else {
+ fprintf(stderr, "Unknown argument %s\n", option.data());
+ usage();
+ return false;
+ }
+ }
+
+ if (image_location_ == nullptr && oat_filename_ == nullptr) {
+ fprintf(stderr, "Either --image or --oat must be specified\n");
+ return false;
+ }
+
+ if (image_location_ != nullptr && oat_filename_ != nullptr) {
+ fprintf(stderr, "Either --image or --oat must be specified but not both\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ const char* oat_filename_ = nullptr;
+ const char* image_location_ = nullptr;
+ const char* boot_image_location_ = nullptr;
+ InstructionSet instruction_set_ = kRuntimeISA;
+ std::string elf_filename_prefix_;
+ std::ostream* os_ = &std::cout;
+ std::unique_ptr<std::ofstream> out_;
+ std::string output_name_;
+ bool dump_raw_mapping_table_ = false;
+ bool dump_raw_gc_map_ = false;
+ bool dump_vmap_ = true;
+ bool disassemble_code_ = true;
+ bool symbolize_ = false;
+};
+
+static int oatdump(int argc, char** argv) {
+ InitLogging(argv);
+
+ OatdumpArgs args;
+ if (!args.Parse(argc, argv)) {
+ return EXIT_FAILURE;
+ }
+
+ // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
+ bool absolute_addresses = (args.oat_filename_ == nullptr);
+
+ std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(
+ args.dump_raw_mapping_table_,
+ args.dump_raw_gc_map_,
+ args.dump_vmap_,
+ args.disassemble_code_,
+ absolute_addresses,
+ nullptr));
+
+ std::unique_ptr<Runtime> runtime;
+ if ((args.boot_image_location_ != nullptr || args.image_location_ != nullptr) &&
+ !args.symbolize_) {
+ // If we have a boot image option, try to start the runtime; except when just symbolizing.
+ runtime.reset(StartRuntime(args.boot_image_location_,
+ args.image_location_,
+ args.instruction_set_));
+ }
+
+ if (args.oat_filename_ != nullptr) {
+ if (args.symbolize_) {
+ return SymbolizeOat(args.oat_filename_, args.output_name_);
+ } else {
+ return DumpOat(runtime.get(), args.oat_filename_, oat_dumper_options.release(), args.os_);
+ }
+ }
+
+ if (runtime.get() == nullptr) {
+ // We need the runtime when printing an image.
+ return EXIT_FAILURE;
+ }
+
+ return DumpImage(runtime.get(), args.image_location_, oat_dumper_options.release(), args.os_);
+}
+
} // namespace art
int main(int argc, char** argv) {
diff --git a/patchoat/Android.mk b/patchoat/Android.mk
index 8b6b9ad..1e16096 100644
--- a/patchoat/Android.mk
+++ b/patchoat/Android.mk
@@ -37,9 +37,9 @@
endif
# We always build patchoat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
$(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug))
endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
$(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug))
endif
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a9b2a43..50b4ece 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -411,13 +411,11 @@
void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) {
// Just update the entry points if it looks like we should.
// TODO: sanity check all the pointers' values
-#if defined(ART_USE_PORTABLE_COMPILER)
uintptr_t portable = reinterpret_cast<uintptr_t>(
object->GetEntryPointFromPortableCompiledCode<kVerifyNone>());
if (portable != 0) {
copy->SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(portable + delta_));
}
-#endif
uintptr_t quick= reinterpret_cast<uintptr_t>(
object->GetEntryPointFromQuickCompiledCode<kVerifyNone>());
if (quick != 0) {
@@ -469,16 +467,13 @@
return true;
}
-bool PatchOat::CheckOatFile() {
- Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
- if (patches_sec == nullptr) {
+template <typename ptr_t>
+bool PatchOat::CheckOatFile(const Elf32_Shdr& patches_sec) {
+ if (patches_sec.sh_type != SHT_OAT_PATCH) {
return false;
}
- if (patches_sec->sh_type != SHT_OAT_PATCH) {
- return false;
- }
- uintptr_t* patches = reinterpret_cast<uintptr_t*>(oat_file_->Begin() + patches_sec->sh_offset);
- uintptr_t* patches_end = patches + (patches_sec->sh_size/sizeof(uintptr_t));
+ ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file_->Begin() + patches_sec.sh_offset);
+ ptr_t* patches_end = patches + (patches_sec.sh_size / sizeof(ptr_t));
Elf32_Shdr* oat_data_sec = oat_file_->FindSectionByName(".rodata");
Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text");
if (oat_data_sec == nullptr) {
@@ -577,6 +572,11 @@
}
}
+ t.NewTiming("Fixup Debug Sections");
+ if (!oat_file_->FixupDebugSections(delta_)) {
+ return false;
+ }
+
return true;
}
@@ -604,10 +604,28 @@
LOG(ERROR) << ".oat_patches section not found. Aborting patch";
return false;
}
- DCHECK(CheckOatFile()) << "Oat file invalid";
- CHECK_EQ(patches_sec->sh_type, SHT_OAT_PATCH) << "Unexpected type of .oat_patches";
- uintptr_t* patches = reinterpret_cast<uintptr_t*>(oat_file_->Begin() + patches_sec->sh_offset);
- uintptr_t* patches_end = patches + (patches_sec->sh_size/sizeof(uintptr_t));
+ if (patches_sec->sh_type != SHT_OAT_PATCH) {
+ LOG(ERROR) << "Unexpected type of .oat_patches";
+ return false;
+ }
+
+ switch (patches_sec->sh_entsize) {
+ case sizeof(uint32_t):
+ return PatchTextSection<uint32_t>(*patches_sec);
+ case sizeof(uint64_t):
+ return PatchTextSection<uint64_t>(*patches_sec);
+ default:
+ LOG(ERROR) << ".oat_patches Entsize of " << patches_sec->sh_entsize << "bits "
+ << "is not valid";
+ return false;
+ }
+}
+
+template <typename ptr_t>
+bool PatchOat::PatchTextSection(const Elf32_Shdr& patches_sec) {
+ DCHECK(CheckOatFile<ptr_t>(patches_sec)) << "Oat file invalid";
+ ptr_t* patches = reinterpret_cast<ptr_t*>(oat_file_->Begin() + patches_sec.sh_offset);
+ ptr_t* patches_end = patches + (patches_sec.sh_size / sizeof(ptr_t));
Elf32_Shdr* oat_text_sec = oat_file_->FindSectionByName(".text");
CHECK(oat_text_sec != nullptr);
byte* to_patch = oat_file_->Begin() + oat_text_sec->sh_offset;
@@ -619,7 +637,6 @@
CHECK_LT(reinterpret_cast<uintptr_t>(patch_loc), to_patch_end);
*patch_loc += delta_;
}
-
return true;
}
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 6960d3b..9086d58 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -36,7 +36,7 @@
class Reference;
class Class;
class ArtMethod;
-}; // namespace mirror
+} // namespace mirror
class PatchOat {
public:
@@ -74,11 +74,12 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool InHeap(mirror::Object*);
- bool CheckOatFile();
-
// Patches oat in place, modifying the oat_file given to the constructor.
bool PatchElf();
bool PatchTextSection();
+ // Templatized version to actually do the patching with the right sized offsets.
+ template <typename ptr_t> bool PatchTextSection(const Elf32_Shdr& patches_sec);
+ template <typename ptr_t> bool CheckOatFile(const Elf32_Shdr& patches_sec);
bool PatchOatHeader();
bool PatchSymbols(Elf32_Shdr* section);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 17ee8ab..e954476 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -80,6 +80,7 @@
interpreter/interpreter.cc \
interpreter/interpreter_common.cc \
interpreter/interpreter_switch_impl.cc \
+ java_vm_ext.cc \
jdwp/jdwp_event.cc \
jdwp/jdwp_expand_buf.cc \
jdwp/jdwp_handler.cc \
@@ -87,6 +88,7 @@
jdwp/jdwp_request.cc \
jdwp/jdwp_socket.cc \
jdwp/object_registry.cc \
+ jni_env_ext.cc \
jni_internal.cc \
jobject_comparator.cc \
mem_map.cc \
@@ -326,10 +328,6 @@
LIBART_CFLAGS += -DUSE_JEMALLOC
endif
-ifeq ($(ART_USE_HSPACE_COMPACT),true)
- LIBART_CFLAGS += -DART_USE_HSPACE_COMPACT
-endif
-
# $(1): target or host
# $(2): ndebug or debug
define build-libart
@@ -424,7 +422,7 @@
include external/libcxx/libcxx.mk
LOCAL_SHARED_LIBRARIES += libbacktrace_libc++
ifeq ($$(art_target_or_host),target)
- LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils libsigchain
+ LOCAL_SHARED_LIBRARIES += libcutils libdl libutils libsigchain
LOCAL_STATIC_LIBRARIES := libziparchive libz
else # host
LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils
@@ -471,10 +469,10 @@
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since
# they are used to cross compile for the target.
-ifeq ($(ART_BUILD_NDEBUG),true)
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
$(eval $(call build-libart,host,ndebug))
endif
-ifeq ($(ART_BUILD_DEBUG),true)
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
$(eval $(call build-libart,host,debug))
endif
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 8c6afd6..2780d1b 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -48,12 +48,24 @@
extern "C" void* art_quick_resolve_string(void*, uint32_t);
// Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
extern "C" int art_quick_set32_static(uint32_t, int32_t);
extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
extern "C" int art_quick_set64_static(uint32_t, int64_t);
extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
extern "C" int32_t art_quick_get32_static(uint32_t);
extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -154,15 +166,27 @@
qpoints->pResolveString = art_quick_resolve_string;
// Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
qpoints->pSet32Instance = art_quick_set32_instance;
qpoints->pSet32Static = art_quick_set32_static;
qpoints->pSet64Instance = art_quick_set64_instance;
qpoints->pSet64Static = art_quick_set64_static;
qpoints->pSetObjInstance = art_quick_set_obj_instance;
qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
qpoints->pGet32Instance = art_quick_get32_instance;
qpoints->pGet64Instance = art_quick_get64_instance;
qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
qpoints->pGet32Static = art_quick_get32_static;
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
@@ -232,6 +256,6 @@
qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-};
+}
} // namespace art
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1b30c9c..51bcd3c 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -203,6 +203,77 @@
END \c_name
.endm
+.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_REG reg
+ ldr \reg, [r9, #THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz \reg, 1f
+ bx lr
+1:
+ DELIVER_PENDING_EXCEPTION
+.endm
+
+.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r1
+.endm
+
+.macro RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_ZERO
+ DELIVER_PENDING_EXCEPTION
+.endm
+
+// Macros taking opportunity of code similarities for downcalls with referrer for non-wide fields.
+.macro ONE_ARG_REF_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ ldr r1, [sp, #32] @ pass referrer
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ bl \entrypoint @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ \return
+END \name
+.endm
+
+.macro TWO_ARG_REF_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ ldr r2, [sp, #32] @ pass referrer
+ mov r3, r9 @ pass Thread::Current
+ mov r12, sp
+ str r12, [sp, #-16]! @ expand the frame and pass SP
+ .pad #16
+ .cfi_adjust_cfa_offset 16
+ bl \entrypoint @ (field_idx, Object*, referrer, Thread*, SP)
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ \return
+END \name
+.endm
+
+.macro THREE_ARG_REF_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ ldr r3, [sp, #32] @ pass referrer
+ mov r12, sp @ save SP
+ sub sp, #8 @ grow frame for alignment with stack args
+ .pad #8
+ .cfi_adjust_cfa_offset 8
+ push {r9, r12} @ pass Thread::Current and SP
+ .save {r9, r12}
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset r9, 0
+ .cfi_rel_offset r12, 4
+ bl \entrypoint @ (field_idx, Object*, new_val, referrer, Thread*, SP)
+ add sp, #16 @ release out args
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here
+ \return
+END \name
+.endm
+
/*
* Called by managed code, saves callee saves and then calls artThrowException
* that will place a mock Method* at the bottom of the stack. Arg1 holds the exception.
@@ -601,23 +672,14 @@
END art_quick_initialize_type_and_verify_access
/*
- * Called by managed code to resolve a static field and load a 32-bit primitive value.
+ * Called by managed code to resolve a static field and load a non-wide value.
*/
- .extern artGet32StaticFromCode
-ENTRY art_quick_get32_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r1, [sp, #32] @ pass referrer
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- bl artGet32StaticFromCode @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
- ldr r1, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- cbnz r1, 1f @ success if no exception pending
- bx lr @ return on success
-1:
- DELIVER_PENDING_EXCEPTION
-END art_quick_get32_static
-
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
/*
* Called by managed code to resolve a static field and load a 64-bit primitive value.
*/
@@ -637,43 +699,14 @@
END art_quick_get64_static
/*
- * Called by managed code to resolve a static field and load an object reference.
+ * Called by managed code to resolve an instance field and load a non-wide value.
*/
- .extern artGetObjStaticFromCode
-ENTRY art_quick_get_obj_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r1, [sp, #32] @ pass referrer
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- bl artGetObjStaticFromCode @ (uint32_t field_idx, const Method* referrer, Thread*, SP)
- ldr r1, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- cbnz r1, 1f @ success if no exception pending
- bx lr @ return on success
-1:
- DELIVER_PENDING_EXCEPTION
-END art_quick_get_obj_static
-
- /*
- * Called by managed code to resolve an instance field and load a 32-bit primitive value.
- */
- .extern artGet32InstanceFromCode
-ENTRY art_quick_get32_instance
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r2, [sp, #32] @ pass referrer
- mov r3, r9 @ pass Thread::Current
- mov r12, sp
- str r12, [sp, #-16]! @ expand the frame and pass SP
- bl artGet32InstanceFromCode @ (field_idx, Object*, referrer, Thread*, SP)
- add sp, #16 @ strip the extra frame
- ldr r1, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- cbnz r1, 1f @ success if no exception pending
- bx lr @ return on success
-1:
- DELIVER_PENDING_EXCEPTION
-END art_quick_get32_instance
-
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
/*
* Called by managed code to resolve an instance field and load a 64-bit primitive value.
*/
@@ -698,48 +731,12 @@
END art_quick_get64_instance
/*
- * Called by managed code to resolve an instance field and load an object reference.
+ * Called by managed code to resolve a static field and store a non-wide value.
*/
- .extern artGetObjInstanceFromCode
-ENTRY art_quick_get_obj_instance
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r2, [sp, #32] @ pass referrer
- mov r3, r9 @ pass Thread::Current
- mov r12, sp
- str r12, [sp, #-16]! @ expand the frame and pass SP
- .pad #16
- .cfi_adjust_cfa_offset 16
- bl artGetObjInstanceFromCode @ (field_idx, Object*, referrer, Thread*, SP)
- add sp, #16 @ strip the extra frame
- .cfi_adjust_cfa_offset -16
- ldr r1, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- cbnz r1, 1f @ success if no exception pending
- bx lr @ return on success
-1:
- DELIVER_PENDING_EXCEPTION
-END art_quick_get_obj_instance
-
- /*
- * Called by managed code to resolve a static field and store a 32-bit primitive value.
- */
- .extern artSet32StaticFromCode
-ENTRY art_quick_set32_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r2, [sp, #32] @ pass referrer
- mov r3, r9 @ pass Thread::Current
- mov r12, sp
- str r12, [sp, #-16]! @ expand the frame and pass SP
- .pad #16
- .cfi_adjust_cfa_offset 16
- bl artSet32StaticFromCode @ (field_idx, new_val, referrer, Thread*, SP)
- add sp, #16 @ strip the extra frame
- .cfi_adjust_cfa_offset -16
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_set32_static
-
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
/*
* Called by managed code to resolve a static field and store a 64-bit primitive value.
* On entry r0 holds field index, r1:r2 hold new_val
@@ -767,53 +764,16 @@
END art_quick_set64_static
/*
- * Called by managed code to resolve a static field and store an object reference.
+ * Called by managed code to resolve an instance field and store a non-wide value.
*/
- .extern artSetObjStaticFromCode
-ENTRY art_quick_set_obj_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r2, [sp, #32] @ pass referrer
- mov r3, r9 @ pass Thread::Current
- mov r12, sp
- str r12, [sp, #-16]! @ expand the frame and pass SP
- .pad #16
- .cfi_adjust_cfa_offset 16
- bl artSetObjStaticFromCode @ (field_idx, new_val, referrer, Thread*, SP)
- add sp, #16 @ strip the extra frame
- .cfi_adjust_cfa_offset -16
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_set_obj_static
-
- /*
- * Called by managed code to resolve an instance field and store a 32-bit primitive value.
- */
- .extern artSet32InstanceFromCode
-ENTRY art_quick_set32_instance
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r3, [sp, #32] @ pass referrer
- mov r12, sp @ save SP
- sub sp, #8 @ grow frame for alignment with stack args
- .pad #8
- .cfi_adjust_cfa_offset 8
- push {r9, r12} @ pass Thread::Current and SP
- .save {r9, r12}
- .cfi_adjust_cfa_offset 8
- .cfi_rel_offset r9, 0
- .cfi_rel_offset r12, 4
- bl artSet32InstanceFromCode @ (field_idx, Object*, new_val, referrer, Thread*, SP)
- add sp, #16 @ release out args
- .cfi_adjust_cfa_offset -16
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_set32_instance
-
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
/*
* Called by managed code to resolve an instance field and store a 64-bit primitive value.
*/
- .extern artSet32InstanceFromCode
+ .extern artSet64InstanceFromCode
ENTRY art_quick_set64_instance
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
mov r12, sp @ save SP
@@ -833,29 +793,6 @@
END art_quick_set64_instance
/*
- * Called by managed code to resolve an instance field and store an object reference.
- */
- .extern artSetObjInstanceFromCode
-ENTRY art_quick_set_obj_instance
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
- ldr r3, [sp, #32] @ pass referrer
- mov r12, sp @ save SP
- sub sp, #8 @ grow frame for alignment with stack args
- .pad #8
- .cfi_adjust_cfa_offset 8
- push {r9, r12} @ pass Thread::Current and SP
- .save {r9, r12}
- .cfi_adjust_cfa_offset 8
- .cfi_rel_offset r9, 0
- bl artSetObjInstanceFromCode @ (field_idx, Object*, new_val, referrer, Thread*, SP)
- add sp, #16 @ release out args
- .cfi_adjust_cfa_offset -16
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_set_obj_instance
-
- /*
* Entry from managed code to resolve a string, this stub will allocate a String and deliver an
* exception on error. On success the String is returned. R0 holds the referring method,
* R1 holds the string index. The fast path check for hit in strings cache has already been
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 0c33d9c..70e93b3 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -47,12 +47,24 @@
extern "C" void* art_quick_resolve_string(void*, uint32_t);
// Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
extern "C" int art_quick_set32_static(uint32_t, int32_t);
extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
extern "C" int art_quick_set64_static(uint32_t, int64_t);
extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
extern "C" int32_t art_quick_get32_static(uint32_t);
extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -136,15 +148,27 @@
qpoints->pResolveString = art_quick_resolve_string;
// Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
qpoints->pSet32Instance = art_quick_set32_instance;
qpoints->pSet32Static = art_quick_set32_static;
qpoints->pSet64Instance = art_quick_set64_instance;
qpoints->pSet64Static = art_quick_set64_static;
qpoints->pSetObjInstance = art_quick_set_obj_instance;
qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
qpoints->pGet32Instance = art_quick_get32_instance;
qpoints->pGet64Instance = art_quick_get64_instance;
qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
qpoints->pGet32Static = art_quick_get32_static;
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 2a19e27..606816a 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1266,17 +1266,29 @@
TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 4db5ea6..6add93b 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -22,9 +22,9 @@
// Offset of field Thread::tls32_.state_and_flags verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
// Offset of field Thread::tlsPtr_.card_table verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
// Offset of field Thread::tlsPtr_.exception verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 116
+#define THREAD_EXCEPTION_OFFSET 124
#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 64
#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 64
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index d3e7d5e..25e911d 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -49,12 +49,24 @@
extern "C" void* art_quick_resolve_string(void*, uint32_t);
// Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
extern "C" int art_quick_set32_static(uint32_t, int32_t);
extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
extern "C" int art_quick_set64_static(uint32_t, int64_t);
extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
extern "C" int32_t art_quick_get32_static(uint32_t);
extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -159,15 +171,27 @@
qpoints->pResolveString = art_quick_resolve_string;
// Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
qpoints->pSet32Instance = art_quick_set32_instance;
qpoints->pSet32Static = art_quick_set32_static;
qpoints->pSet64Instance = art_quick_set64_instance;
qpoints->pSet64Static = art_quick_set64_static;
qpoints->pSetObjInstance = art_quick_set_obj_instance;
qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
qpoints->pGet32Instance = art_quick_get32_instance;
qpoints->pGet64Instance = art_quick_get64_instance;
qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
qpoints->pGet32Static = art_quick_get32_static;
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 8786222..9e9e523 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -739,6 +739,59 @@
move $a3, $sp # pass $sp
RETURN_IF_RESULT_IS_NON_ZERO
END art_quick_initialize_type_and_verify_access
+ /*
+ * Called by managed code to resolve a static field and load a boolean primitive value.
+ */
+ .extern artGetBooleanStaticFromCode
+ENTRY art_quick_get_boolean_static
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a1, 64($sp) # pass referrer's Method*
+ move $a2, rSELF # pass Thread::Current
+ jal artGetBooleanStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_boolean_static
+ /*
+ * Called by managed code to resolve a static field and load a byte primitive value.
+ */
+ .extern artGetByteStaticFromCode
+ENTRY art_quick_get_byte_static
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a1, 64($sp) # pass referrer's Method*
+ move $a2, rSELF # pass Thread::Current
+ jal artGetByteStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_byte_static
+
+ /*
+ * Called by managed code to resolve a static field and load a char primitive value.
+ */
+ .extern artGetCharStaticFromCode
+ENTRY art_quick_get_char_static
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a1, 64($sp) # pass referrer's Method*
+ move $a2, rSELF # pass Thread::Current
+ jal artGetCharStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_char_static
+ /*
+ * Called by managed code to resolve a static field and load a short primitive value.
+ */
+ .extern artGetShortStaticFromCode
+ENTRY art_quick_get_short_static
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a1, 64($sp) # pass referrer's Method*
+ move $a2, rSELF # pass Thread::Current
+ jal artGetShortStaticFromCode # (uint32_t field_idx, const Method* referrer, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_short_static
/*
* Called by managed code to resolve a static field and load a 32-bit primitive value.
@@ -783,6 +836,60 @@
END art_quick_get_obj_static
/*
+ * Called by managed code to resolve an instance field and load a boolean primitive value.
+ */
+ .extern artGetBooleanInstanceFromCode
+ENTRY art_quick_get_boolean_instance
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a2, 64($sp) # pass referrer's Method*
+ move $a3, rSELF # pass Thread::Current
+ jal artGetBooleanInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp)
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_boolean_instance
+ /*
+ * Called by managed code to resolve an instance field and load a byte primitive value.
+ */
+ .extern artGetByteInstanceFromCode
+ENTRY art_quick_get_byte_instance
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a2, 64($sp) # pass referrer's Method*
+ move $a3, rSELF # pass Thread::Current
+ jal artGetByteInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp)
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_byte_instance
+
+ /*
+ * Called by managed code to resolve an instance field and load a char primitive value.
+ */
+ .extern artGetCharInstanceFromCode
+ENTRY art_quick_get_char_instance
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a2, 64($sp) # pass referrer's Method*
+ move $a3, rSELF # pass Thread::Current
+ jal artGetCharInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp)
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_char_instance
+ /*
+ * Called by managed code to resolve an instance field and load a short primitive value.
+ */
+ .extern artGetShortInstanceFromCode
+ENTRY art_quick_get_short_instance
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a2, 64($sp) # pass referrer's Method*
+ move $a3, rSELF # pass Thread::Current
+ jal artGetShortInstanceFromCode # (field_idx, Object*, referrer, Thread*, $sp)
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NO_EXCEPTION
+END art_quick_get_short_instance
+
+ /*
* Called by managed code to resolve an instance field and load a 32-bit primitive value.
*/
.extern artGet32InstanceFromCode
@@ -825,6 +932,34 @@
END art_quick_get_obj_instance
/*
+ * Called by managed code to resolve a static field and store a 8-bit primitive value.
+ */
+ .extern artSet8StaticFromCode
+ENTRY art_quick_set8_static
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a2, 64($sp) # pass referrer's Method*
+ move $a3, rSELF # pass Thread::Current
+ jal artSet8StaticFromCode # (field_idx, new_val, referrer, Thread*, $sp)
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_ZERO
+END art_quick_set8_static
+
+ /*
+ * Called by managed code to resolve a static field and store a 16-bit primitive value.
+ */
+ .extern artSet16StaticFromCode
+ENTRY art_quick_set16_static
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a2, 64($sp) # pass referrer's Method*
+ move $a3, rSELF # pass Thread::Current
+ jal artSet16StaticFromCode # (field_idx, new_val, referrer, Thread*, $sp)
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_ZERO
+END art_quick_set16_static
+
+ /*
* Called by managed code to resolve a static field and store a 32-bit primitive value.
*/
.extern artSet32StaticFromCode
@@ -841,7 +976,7 @@
/*
* Called by managed code to resolve a static field and store a 64-bit primitive value.
*/
- .extern artSet32StaticFromCode
+ .extern artSet64StaticFromCode
ENTRY art_quick_set64_static
GENERATE_GLOBAL_POINTER
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
@@ -867,6 +1002,34 @@
END art_quick_set_obj_static
/*
+ * Called by managed code to resolve an instance field and store a 8-bit primitive value.
+ */
+ .extern artSet8InstanceFromCode
+ENTRY art_quick_set8_instance
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a3, 64($sp) # pass referrer's Method*
+ sw rSELF, 16($sp) # pass Thread::Current
+ jal artSet8InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*, $sp)
+ sw $sp, 20($sp) # pass $sp
+ RETURN_IF_ZERO
+END art_quick_set8_instance
+
+ /*
+ * Called by managed code to resolve an instance field and store a 16-bit primitive value.
+ */
+ .extern artSet16InstanceFromCode
+ENTRY art_quick_set16_instance
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ lw $a3, 64($sp) # pass referrer's Method*
+ sw rSELF, 16($sp) # pass Thread::Current
+ jal artSet16InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*, $sp)
+ sw $sp, 20($sp) # pass $sp
+ RETURN_IF_ZERO
+END art_quick_set16_instance
+
+ /*
* Called by managed code to resolve an instance field and store a 32-bit primitive value.
*/
.extern artSet32InstanceFromCode
@@ -883,7 +1046,7 @@
/*
* Called by managed code to resolve an instance field and store a 64-bit primitive value.
*/
- .extern artSet32InstanceFromCode
+ .extern artSet64InstanceFromCode
ENTRY art_quick_set64_instance
GENERATE_GLOBAL_POINTER
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 864e3f7..6b74a1b 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -309,7 +309,7 @@
"addl $16, %%esp" // Pop referrer
: "=a" (result)
// Use the result from eax
- : "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"m"(referrer), [hidden]"r"(hidden)
+ : "a"(arg0), "c"(arg1), "d"(arg2), "D"(code), [referrer]"r"(referrer), [hidden]"m"(hidden)
// This places code into edi, arg0 into eax, arg1 into ecx, and arg2 into edx
: "memory"); // clobber.
// TODO: Should we clobber the other registers? EBX gets clobbered by some of the stubs,
@@ -398,7 +398,7 @@
// Load call params into the right registers.
"ldp x0, x1, [sp]\n\t"
"ldp x2, x3, [sp, #16]\n\t"
- "ldp x18, x12, [sp, #32]\n\t"
+ "ldp x18, x17, [sp, #32]\n\t"
"add sp, sp, #48\n\t"
".cfi_adjust_cfa_offset -48\n\t"
@@ -489,19 +489,17 @@
// Note: Uses the native convention
// TODO: Set the thread?
__asm__ __volatile__(
- "movq %[hidden], %%r9\n\t" // No need to save r9, listed as clobbered
- "movd %%r9, %%xmm0\n\t"
"pushq %[referrer]\n\t" // Push referrer
"pushq (%%rsp)\n\t" // & 16B alignment padding
".cfi_adjust_cfa_offset 16\n\t"
- "call *%%rax\n\t" // Call the stub
+ "call *%%rbx\n\t" // Call the stub
"addq $16, %%rsp\n\t" // Pop nullptr and padding
".cfi_adjust_cfa_offset -16\n\t"
: "=a" (result)
// Use the result from rax
- : "D"(arg0), "S"(arg1), "d"(arg2), "a"(code), [referrer] "m"(referrer), [hidden] "m"(hidden)
+ : "D"(arg0), "S"(arg1), "d"(arg2), "b"(code), [referrer] "c"(referrer), [hidden] "a"(hidden)
// This places arg0 into rdi, arg1 into rsi, arg2 into rdx, and code into rax
- : "rbx", "rcx", "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ : "rbp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"memory"); // clobber all
// TODO: Should we clobber the other registers?
#else
@@ -1306,6 +1304,259 @@
}
+static void GetSetBooleanStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+ mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 5;
+ uint8_t values[num_values] = { 0, 1, 2, 128, 0xFF };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ static_cast<size_t>(values[i]),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickSet8Static),
+ self,
+ referrer);
+
+ size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ 0U, 0U,
+ StubTest::GetEntrypoint(self, kQuickGetBooleanStatic),
+ self,
+ referrer);
+ // Boolean currently stores bools as uint8_t, be more zealous about asserting correct writes/gets.
+ EXPECT_EQ(values[i], static_cast<uint8_t>(res)) << "Iteration " << i;
+ }
+#else
+ LOG(INFO) << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_boolean_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetByteStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+ mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 5;
+ int8_t values[num_values] = { -128, -64, 0, 64, 127 };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ static_cast<size_t>(values[i]),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickSet8Static),
+ self,
+ referrer);
+
+ size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ 0U, 0U,
+ StubTest::GetEntrypoint(self, kQuickGetByteStatic),
+ self,
+ referrer);
+ EXPECT_EQ(values[i], static_cast<int8_t>(res)) << "Iteration " << i;
+ }
+#else
+ LOG(INFO) << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_byte_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+
+static void GetSetBooleanInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+ Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 5;
+ uint8_t values[num_values] = { 0, true, 2, 128, 0xFF };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ static_cast<size_t>(values[i]),
+ StubTest::GetEntrypoint(self, kQuickSet8Instance),
+ self,
+ referrer);
+
+ uint8_t res = f->Get()->GetBoolean(obj->Get());
+ EXPECT_EQ(values[i], res) << "Iteration " << i;
+
+ f->Get()->SetBoolean<false>(obj->Get(), res);
+
+ size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickGetBooleanInstance),
+ self,
+ referrer);
+ EXPECT_EQ(res, static_cast<uint8_t>(res2));
+ }
+#else
+ LOG(INFO) << "Skipping set_boolean_instance as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_boolean_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetByteInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+ Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 5;
+ int8_t values[num_values] = { -128, -64, 0, 64, 127 };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ static_cast<size_t>(values[i]),
+ StubTest::GetEntrypoint(self, kQuickSet8Instance),
+ self,
+ referrer);
+
+ int8_t res = f->Get()->GetByte(obj->Get());
+ EXPECT_EQ(res, values[i]) << "Iteration " << i;
+ f->Get()->SetByte<false>(obj->Get(), ++res);
+
+ size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickGetByteInstance),
+ self,
+ referrer);
+ EXPECT_EQ(res, static_cast<int8_t>(res2));
+ }
+#else
+ LOG(INFO) << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_byte_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+static void GetSetCharStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+ mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 6;
+ uint16_t values[num_values] = { 0, 1, 2, 255, 32768, 0xFFFF };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ static_cast<size_t>(values[i]),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickSet16Static),
+ self,
+ referrer);
+
+ size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ 0U, 0U,
+ StubTest::GetEntrypoint(self, kQuickGetCharStatic),
+ self,
+ referrer);
+
+ EXPECT_EQ(values[i], static_cast<uint16_t>(res)) << "Iteration " << i;
+ }
+#else
+ LOG(INFO) << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_char_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetShortStatic(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
+ mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 6;
+ int16_t values[num_values] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ static_cast<size_t>(values[i]),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickSet16Static),
+ self,
+ referrer);
+
+ size_t res = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ 0U, 0U,
+ StubTest::GetEntrypoint(self, kQuickGetShortStatic),
+ self,
+ referrer);
+
+ EXPECT_EQ(static_cast<int16_t>(res), values[i]) << "Iteration " << i;
+ }
+#else
+ LOG(INFO) << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_short_static as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
+static void GetSetCharInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+ Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 6;
+ uint16_t values[num_values] = { 0, 1, 2, 255, 32768, 0xFFFF };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ static_cast<size_t>(values[i]),
+ StubTest::GetEntrypoint(self, kQuickSet16Instance),
+ self,
+ referrer);
+
+ uint16_t res = f->Get()->GetChar(obj->Get());
+ EXPECT_EQ(res, values[i]) << "Iteration " << i;
+ f->Get()->SetChar<false>(obj->Get(), ++res);
+
+ size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickGetCharInstance),
+ self,
+ referrer);
+ EXPECT_EQ(res, static_cast<uint16_t>(res2));
+ }
+#else
+ LOG(INFO) << "Skipping set_char_instance as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_char_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+static void GetSetShortInstance(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f,
+ Thread* self, mirror::ArtMethod* referrer, StubTest* test)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
+ constexpr size_t num_values = 6;
+ int16_t values[num_values] = { -0x7FFF, -32768, 0, 255, 32767, 0x7FFE };
+
+ for (size_t i = 0; i < num_values; ++i) {
+ test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ static_cast<size_t>(values[i]),
+ StubTest::GetEntrypoint(self, kQuickSet16Instance),
+ self,
+ referrer);
+
+ int16_t res = f->Get()->GetShort(obj->Get());
+ EXPECT_EQ(res, values[i]) << "Iteration " << i;
+ f->Get()->SetShort<false>(obj->Get(), ++res);
+
+ size_t res2 = test->Invoke3WithReferrer(static_cast<size_t>((*f)->GetDexFieldIndex()),
+ reinterpret_cast<size_t>(obj->Get()),
+ 0U,
+ StubTest::GetEntrypoint(self, kQuickGetShortInstance),
+ self,
+ referrer);
+ EXPECT_EQ(res, static_cast<int16_t>(res2));
+ }
+#else
+ LOG(INFO) << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping set_short_instance as I don't know how to do that on " << kRuntimeISA << std::endl;
+#endif
+}
+
static void GetSet32Static(Handle<mirror::Object>* obj, Handle<mirror::ArtField>* f, Thread* self,
mirror::ArtMethod* referrer, StubTest* test)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -1555,6 +1806,26 @@
Primitive::Type type = f->GetTypeAsPrimitiveType();
switch (type) {
+ case Primitive::Type::kPrimBoolean:
+ if (test_type == type) {
+ GetSetBooleanStatic(&obj, &f, self, m.Get(), test);
+ }
+ break;
+ case Primitive::Type::kPrimByte:
+ if (test_type == type) {
+ GetSetByteStatic(&obj, &f, self, m.Get(), test);
+ }
+ break;
+ case Primitive::Type::kPrimChar:
+ if (test_type == type) {
+ GetSetCharStatic(&obj, &f, self, m.Get(), test);
+ }
+ break;
+ case Primitive::Type::kPrimShort:
+ if (test_type == type) {
+ GetSetShortStatic(&obj, &f, self, m.Get(), test);
+ }
+ break;
case Primitive::Type::kPrimInt:
if (test_type == type) {
GetSet32Static(&obj, &f, self, m.Get(), test);
@@ -1590,6 +1861,26 @@
Primitive::Type type = f->GetTypeAsPrimitiveType();
switch (type) {
+ case Primitive::Type::kPrimBoolean:
+ if (test_type == type) {
+ GetSetBooleanInstance(&obj, &f, self, m.Get(), test);
+ }
+ break;
+ case Primitive::Type::kPrimByte:
+ if (test_type == type) {
+ GetSetByteInstance(&obj, &f, self, m.Get(), test);
+ }
+ break;
+ case Primitive::Type::kPrimChar:
+ if (test_type == type) {
+ GetSetCharInstance(&obj, &f, self, m.Get(), test);
+ }
+ break;
+ case Primitive::Type::kPrimShort:
+ if (test_type == type) {
+ GetSetShortInstance(&obj, &f, self, m.Get(), test);
+ }
+ break;
case Primitive::Type::kPrimInt:
if (test_type == type) {
GetSet32Instance(&obj, &f, self, m.Get(), test);
@@ -1618,6 +1909,33 @@
// TODO: Deallocate things.
}
+TEST_F(StubTest, Fields8) {
+ TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+
+ Thread* self = Thread::Current();
+
+ self->TransitionFromSuspendedToRunnable();
+ LoadDex("AllFields");
+ bool started = runtime_->Start();
+ CHECK(started);
+
+ TestFields(self, this, Primitive::Type::kPrimBoolean);
+ TestFields(self, this, Primitive::Type::kPrimByte);
+}
+
+TEST_F(StubTest, Fields16) {
+ TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
+
+ Thread* self = Thread::Current();
+
+ self->TransitionFromSuspendedToRunnable();
+ LoadDex("AllFields");
+ bool started = runtime_->Start();
+ CHECK(started);
+
+ TestFields(self, this, Primitive::Type::kPrimChar);
+ TestFields(self, this, Primitive::Type::kPrimShort);
+}
TEST_F(StubTest, Fields32) {
TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
@@ -1658,7 +1976,6 @@
TestFields(self, this, Primitive::Type::kPrimLong);
}
-
TEST_F(StubTest, IMT) {
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__))
TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING();
@@ -1709,19 +2026,6 @@
jmethodID obj_constructor = env->GetMethodID(obj_jclass, "<init>", "()V");
ASSERT_NE(nullptr, obj_constructor);
- // Sanity check: check that there is a conflict for List.contains in ArrayList.
-
- mirror::Class* arraylist_class = soa.Decode<mirror::Class*>(arraylist_jclass);
- mirror::ArtMethod* m = arraylist_class->GetEmbeddedImTableEntry(
- inf_contains->GetDexMethodIndex() % mirror::Class::kImtSize);
-
- if (!m->IsImtConflictMethod()) {
- LOG(WARNING) << "Test is meaningless, no IMT conflict in setup: " <<
- PrettyMethod(m, true);
- LOG(WARNING) << "Please update StubTest.IMT.";
- return;
- }
-
// Create instances.
jobject jarray_list = env->NewObject(arraylist_jclass, arraylist_constructor);
@@ -1732,7 +2036,11 @@
ASSERT_NE(nullptr, jobj);
Handle<mirror::Object> obj(hs.NewHandle(soa.Decode<mirror::Object*>(jobj)));
- // Invoke.
+ // Invocation tests.
+
+ // 1. imt_conflict
+
+ // Contains.
size_t result =
Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
@@ -1750,7 +2058,7 @@
ASSERT_FALSE(self->IsExceptionPending()) << PrettyTypeOf(self->GetException(nullptr));
- // Invoke again.
+ // Contains.
result = Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
reinterpret_cast<size_t>(obj.Get()),
@@ -1760,6 +2068,28 @@
ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+
+ // 2. regular interface trampoline
+
+ result = Invoke3WithReferrer(static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ reinterpret_cast<size_t>(obj.Get()),
+ StubTest::GetEntrypoint(self,
+ kQuickInvokeInterfaceTrampolineWithAccessCheck),
+ self, contains_amethod.Get());
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+
+ result = Invoke3WithReferrer(static_cast<size_t>(inf_contains.Get()->GetDexMethodIndex()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ reinterpret_cast<size_t>(array_list.Get()),
+ StubTest::GetEntrypoint(self,
+ kQuickInvokeInterfaceTrampolineWithAccessCheck),
+ self, contains_amethod.Get());
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result);
#else
LOG(INFO) << "Skipping imt as I don't know how to do that on " << kRuntimeISA;
// Force-print to std::cout so it's also outside the logcat.
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index a578023..efbbfb3 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -181,13 +181,4 @@
#endif
END_MACRO
-MACRO0(SETUP_GOT)
- PUSH ebx
- SETUP_GOT_NOSAVE
-END_MACRO
-
-MACRO0(UNDO_SETUP_GOT)
- POP ebx
-END_MACRO
-
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_S_
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index a072996..682c502 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -47,12 +47,24 @@
extern "C" void* art_quick_resolve_string(void*, uint32_t);
// Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
extern "C" int art_quick_set32_static(uint32_t, int32_t);
extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
extern "C" int art_quick_set64_static(uint32_t, int64_t);
extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
extern "C" int32_t art_quick_get32_static(uint32_t);
extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -137,15 +149,27 @@
qpoints->pResolveString = art_quick_resolve_string;
// Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
qpoints->pSet32Instance = art_quick_set32_instance;
qpoints->pSet32Static = art_quick_set32_static;
qpoints->pSet64Instance = art_quick_set64_instance;
qpoints->pSet64Static = art_quick_set64_static;
qpoints->pSetObjInstance = art_quick_set_obj_instance;
qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
qpoints->pGet32Instance = art_quick_get32_instance;
qpoints->pGet64Instance = art_quick_get64_instance;
qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
qpoints->pGet32Static = art_quick_get32_static;
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index fb26f5f..17310b6 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -104,11 +104,17 @@
bool two_byte = false;
uint32_t displacement_size = 0;
uint32_t immediate_size = 0;
+ bool operand_size_prefix = false;
// Prefixes.
while (true) {
bool prefix_present = false;
switch (opcode) {
+ // Group 3
+ case 0x66:
+ operand_size_prefix = true;
+ // fallthrough
+
// Group 1
case 0xf0:
case 0xf2:
@@ -122,9 +128,6 @@
case 0x64:
case 0x65:
- // Group 3
- case 0x66:
-
// Group 4
case 0x67:
opcode = *pc++;
@@ -150,8 +153,8 @@
if (two_byte) {
switch (opcode) {
- case 0x10: // vmovsd/ss
- case 0x11: // vmovsd/ss
+ case 0x10: // vmovsd/ss
+ case 0x11: // vmovsd/ss
case 0xb6: // movzx
case 0xb7:
case 0xbe: // movsx
@@ -165,7 +168,8 @@
}
} else {
switch (opcode) {
- case 0x89: // mov
+ case 0x88: // mov byte
+ case 0x89: // mov
case 0x8b:
case 0x38: // cmp with memory.
case 0x39:
@@ -188,7 +192,7 @@
case 0x81: // group 1, word immediate.
modrm = *pc++;
has_modrm = true;
- immediate_size = 4;
+ immediate_size = operand_size_prefix ? 2 : 4;
break;
default:
@@ -203,18 +207,18 @@
}
if (has_modrm) {
- uint8_t mod = (modrm >> 6) & 0b11;
+ uint8_t mod = (modrm >> 6) & 3U /* 0b11 */;
// Check for SIB.
- if (mod != 0b11 && (modrm & 0b111) == 4) {
+ if (mod != 3U /* 0b11 */ && (modrm & 7U /* 0b111 */) == 4) {
++pc; // SIB
}
switch (mod) {
- case 0b00: break;
- case 0b01: displacement_size = 1; break;
- case 0b10: displacement_size = 4; break;
- case 0b11:
+ case 0U /* 0b00 */: break;
+ case 1U /* 0b01 */: displacement_size = 1; break;
+ case 2U /* 0b10 */: displacement_size = 4; break;
+ case 3U /* 0b11 */:
break;
}
}
diff --git a/runtime/arch/x86/jni_entrypoints_x86.S b/runtime/arch/x86/jni_entrypoints_x86.S
index 997a259..5d27e47 100644
--- a/runtime/arch/x86/jni_entrypoints_x86.S
+++ b/runtime/arch/x86/jni_entrypoints_x86.S
@@ -20,18 +20,13 @@
* Jni dlsym lookup stub.
*/
DEFINE_FUNCTION art_jni_dlsym_lookup_stub
- subl LITERAL(4), %esp // align stack
- CFI_ADJUST_CFA_OFFSET(4)
- SETUP_GOT // pushes ebx
+ subl LITERAL(8), %esp // align stack
+ CFI_ADJUST_CFA_OFFSET(8)
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- call PLT_SYMBOL(artFindNativeMethod) // (Thread*)
- addl LITERAL(4), %esp // remove argument
- CFI_ADJUST_CFA_OFFSET(-4)
- UNDO_SETUP_GOT // pop ebx
- addl LITERAL(4), %esp // remove padding
- CFI_ADJUST_CFA_OFFSET(-4)
- testl %eax, %eax // check if returned method code is null
+ call SYMBOL(artFindNativeMethod) // (Thread*)
+ addl LITERAL(12), %esp // remove argument & padding
+ CFI_ADJUST_CFA_OFFSET(-12)
+ testl %eax, %eax // check if returned method code is null
jz .Lno_native_code_found // if null, jump to return to handle
jmp *%eax // otherwise, tail call to intended method
.Lno_native_code_found:
diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S
index 9365795..f5fe869 100644
--- a/runtime/arch/x86/portable_entrypoints_x86.S
+++ b/runtime/arch/x86/portable_entrypoints_x86.S
@@ -70,8 +70,7 @@
PUSH ebp // Set up frame.
movl %esp, %ebp
CFI_DEF_CFA_REGISTER(%ebp)
- subl LITERAL(4), %esp // Align stack
- SETUP_GOT // pushes ebx
+ subl LITERAL(8), %esp // Align stack
leal 8(%ebp), %edx // %edx = ArtMethod** called_addr
movl 12(%ebp), %ecx // %ecx = receiver
movl 0(%edx), %eax // %eax = ArtMethod* called
@@ -79,8 +78,7 @@
pushl %fs:THREAD_SELF_OFFSET // Pass thread.
pushl %ecx // Pass receiver.
pushl %eax // Pass called.
- call PLT_SYMBOL(artPortableProxyInvokeHandler) // (called, receiver, Thread*, &called)
- UNDO_SETUP_GOT
+ call SYMBOL(artPortableProxyInvokeHandler) // (called, receiver, Thread*, &called)
leave
CFI_RESTORE(%ebp)
CFI_DEF_CFA(%esp, 4)
@@ -94,8 +92,7 @@
PUSH ebp // Set up frame.
movl %esp, %ebp
CFI_DEF_CFA_REGISTER(%ebp)
- subl LITERAL(4), %esp // Align stack
- SETUP_GOT // pushes ebx
+ subl LITERAL(8), %esp // Align stack
leal 8(%ebp), %edx // %edx = ArtMethod** called_addr
movl 12(%ebp), %ecx // %ecx = receiver
movl 0(%edx), %eax // %eax = ArtMethod* called
@@ -103,8 +100,7 @@
pushl %fs:THREAD_SELF_OFFSET // Pass thread.
pushl %ecx // Pass receiver.
pushl %eax // Pass called.
- call PLT_SYMBOL(artPortableResolutionTrampoline) // (called, receiver, Thread*, &called)
- UNDO_SETUP_GOT
+ call SYMBOL(artPortableResolutionTrampoline) // (called, receiver, Thread*, &called)
leave
CFI_RESTORE(%ebp)
CFI_DEF_CFA(%esp, 4)
@@ -119,15 +115,13 @@
PUSH ebp // Set up frame.
movl %esp, %ebp
CFI_DEF_CFA_REGISTER(%ebp)
- subl LITERAL(8), %esp // Align stack
- SETUP_GOT
+ subl LITERAL(12), %esp // Align stack
leal 8(%ebp), %edx // %edx = ArtMethod** called_addr
movl 0(%edx), %eax // %eax = ArtMethod* called
pushl %edx // Pass called_addr.
pushl %fs:THREAD_SELF_OFFSET // Pass thread.
pushl %eax // Pass called.
- call PLT_SYMBOL(artPortableToInterpreterBridge) // (called, Thread*, &called)
- UNDO_SETUP_GOT
+ call SYMBOL(artPortableToInterpreterBridge) // (called, Thread*, &called)
leave
CFI_RESTORE(%ebp)
CFI_DEF_CFA(%esp, 4)
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 75c8646..4155b7e 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -105,7 +105,6 @@
PUSH ecx // pass SP
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call SYMBOL(artDeliverPendingExceptionFromCode) // artDeliverPendingExceptionFromCode(Thread*, SP)
int3 // unreached
END_MACRO
@@ -120,7 +119,6 @@
PUSH ecx // pass SP
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call VAR(cxx_name, 1) // cxx_name(Thread*, SP)
int3 // unreached
END_FUNCTION RAW_VAR(c_name, 0)
@@ -136,7 +134,6 @@
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
PUSH eax // pass arg1
- SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call VAR(cxx_name, 1) // cxx_name(arg1, Thread*, SP)
int3 // unreached
END_FUNCTION RAW_VAR(c_name, 0)
@@ -152,7 +149,6 @@
CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // pass arg2
PUSH eax // pass arg1
- SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
call VAR(cxx_name, 1) // cxx_name(arg1, arg2, Thread*, SP)
int3 // unreached
END_FUNCTION RAW_VAR(c_name, 0)
@@ -219,7 +215,6 @@
PUSH eax // <-- callee save Method* to go here
movl %esp, %edx // remember SP
// Outgoing argument set up
- SETUP_GOT_NOSAVE
subl MACRO_LITERAL(12), %esp // alignment padding
CFI_ADJUST_CFA_OFFSET(12)
PUSH edx // pass SP
@@ -318,7 +313,6 @@
DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
- SETUP_GOT_NOSAVE // clobbers ebx (harmless here)
// Outgoing argument set up
subl MACRO_LITERAL(8), %esp // push padding
CFI_ADJUST_CFA_OFFSET(8)
@@ -337,7 +331,6 @@
DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
- SETUP_GOT_NOSAVE // clobbers EBX
// Outgoing argument set up
PUSH eax // push padding
PUSH edx // pass SP
@@ -356,7 +349,6 @@
DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
- SETUP_GOT_NOSAVE // clobbers EBX
// Outgoing argument set up
PUSH edx // pass SP
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
@@ -384,7 +376,6 @@
PUSH edx // pass arg3
PUSH ecx // pass arg2
PUSH eax // pass arg1
- SETUP_GOT_NOSAVE // clobbers EBX
call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, Thread*, SP)
addl MACRO_LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -393,6 +384,48 @@
END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
+MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %edx // remember SP
+ mov 32(%esp), %ecx // get referrer
+ // Outgoing argument set up
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH ecx // pass referrer
+ PUSH eax // pass arg1
+ call VAR(cxx_name, 1) // cxx_name(arg1, referrer, Thread*, SP)
+ addl MACRO_LITERAL(16), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-16)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ CALL_MACRO(return_macro, 2) // return or deliver exception
+ END_FUNCTION RAW_VAR(c_name, 0)
+END_MACRO
+
+MACRO3(TWO_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %ebx // remember SP
+ mov 32(%esp), %edx // get referrer
+ subl MACRO_LITERAL(12), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(12)
+ PUSH ebx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ // Outgoing argument set up
+ PUSH edx // pass referrer
+ PUSH ecx // pass arg2
+ PUSH eax // pass arg1
+ call VAR(cxx_name, 1) // cxx_name(arg1, arg2, referrer, Thread*, SP)
+ addl MACRO_LITERAL(32), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-32)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ CALL_MACRO(return_macro, 2) // return or deliver exception
+ END_FUNCTION RAW_VAR(c_name, 0)
+END_MACRO
+
+
MACRO0(RETURN_IF_RESULT_IS_NON_ZERO)
testl %eax, %eax // eax == 0 ?
jz 1f // if eax == 0 goto 1
@@ -559,7 +592,6 @@
.Lslow_lock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
- SETUP_GOT_NOSAVE // clobbers EBX
// Outgoing argument set up
PUSH eax // push padding
PUSH edx // pass SP
@@ -593,7 +625,6 @@
.Lslow_unlock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %edx // remember SP
- SETUP_GOT_NOSAVE // clobbers EBX
// Outgoing argument set up
PUSH eax // push padding
PUSH edx // pass SP
@@ -608,7 +639,6 @@
END_FUNCTION art_quick_unlock_object
DEFINE_FUNCTION art_quick_is_assignable
- SETUP_GOT_NOSAVE // clobbers EBX
PUSH eax // alignment padding
PUSH ecx // pass arg2 - obj->klass
PUSH eax // pass arg1 - checked class
@@ -619,7 +649,6 @@
END_FUNCTION art_quick_is_assignable
DEFINE_FUNCTION art_quick_check_cast
- SETUP_GOT_NOSAVE // clobbers EBX
PUSH eax // alignment padding
PUSH ecx // pass arg2 - obj->klass
PUSH eax // pass arg1 - checked class
@@ -691,7 +720,6 @@
pushl CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored
CFI_ADJUST_CFA_OFFSET(4)
PUSH ebx // pass arg1 - component type of the array
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
@@ -738,7 +766,6 @@
PUSH eax // alignment padding
PUSH ecx // pass arg2 a.hi
PUSH eax // pass arg1 a.lo
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(art_d2l) // (jdouble a)
addl LITERAL(12), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-12)
@@ -748,7 +775,6 @@
DEFINE_FUNCTION art_quick_f2l
subl LITERAL(8), %esp // alignment padding
CFI_ADJUST_CFA_OFFSET(8)
- SETUP_GOT_NOSAVE // clobbers EBX
PUSH eax // pass arg1 a
call SYMBOL(art_f2l) // (jfloat a)
addl LITERAL(12), %esp // pop arguments
@@ -763,7 +789,6 @@
PUSH edx // pass arg3 b.lo
PUSH ecx // pass arg2 a.hi
PUSH eax // pass arg1 a.lo
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artLdiv) // (jlong a, jlong b)
addl LITERAL(28), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-28)
@@ -777,7 +802,6 @@
PUSH edx // pass arg3 b.lo
PUSH ecx // pass arg2 a.hi
PUSH eax // pass arg1 a.lo
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artLmod) // (jlong a, jlong b)
addl LITERAL(28), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-28)
@@ -832,6 +856,46 @@
ret
END_FUNCTION art_quick_lushr
+DEFINE_FUNCTION art_quick_set8_instance
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %ebx // remember SP
+ subl LITERAL(8), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(8)
+ PUSH ebx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ mov 32(%ebx), %ebx // get referrer
+ PUSH ebx // pass referrer
+ PUSH edx // pass new_val
+ PUSH ecx // pass object
+ PUSH eax // pass field_idx
+ call PLT_SYMBOL(artSet8InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
+ addl LITERAL(32), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-32)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO // return or deliver exception
+END_FUNCTION art_quick_set8_instance
+
+DEFINE_FUNCTION art_quick_set16_instance
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %ebx // remember SP
+ subl LITERAL(8), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(8)
+ PUSH ebx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ mov 32(%ebx), %ebx // get referrer
+ PUSH ebx // pass referrer
+ PUSH edx // pass new_val
+ PUSH ecx // pass object
+ PUSH eax // pass field_idx
+ call PLT_SYMBOL(artSet16InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
+ addl LITERAL(32), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-32)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO // return or deliver exception
+END_FUNCTION art_quick_set16_instance
+
DEFINE_FUNCTION art_quick_set32_instance
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %ebx // remember SP
@@ -845,7 +909,6 @@
PUSH edx // pass new_val
PUSH ecx // pass object
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artSet32InstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -865,7 +928,6 @@
PUSH edx // pass low half of new_val
PUSH ecx // pass object
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artSet64InstanceFromCode) // (field_idx, Object*, new_val, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -886,7 +948,6 @@
PUSH edx // pass new_val
PUSH ecx // pass object
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artSetObjInstanceFromCode) // (field_idx, Object*, new_val, referrer, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -894,25 +955,12 @@
RETURN_IF_EAX_ZERO // return or deliver exception
END_FUNCTION art_quick_set_obj_instance
-DEFINE_FUNCTION art_quick_get32_instance
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
- mov %esp, %ebx // remember SP
- mov 32(%esp), %edx // get referrer
- subl LITERAL(12), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(12)
- PUSH ebx // pass SP
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH edx // pass referrer
- PUSH ecx // pass object
- PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
- call SYMBOL(artGet32InstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP)
- addl LITERAL(32), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-32)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
- RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
-END_FUNCTION art_quick_get32_instance
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
DEFINE_FUNCTION art_quick_get64_instance
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
@@ -926,7 +974,6 @@
PUSH edx // pass referrer
PUSH ecx // pass object
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artGet64InstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -934,7 +981,7 @@
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_get64_instance
-DEFINE_FUNCTION art_quick_get_obj_instance
+DEFINE_FUNCTION art_quick_set8_static
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
mov %esp, %ebx // remember SP
mov 32(%esp), %edx // get referrer
@@ -944,15 +991,33 @@
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
PUSH edx // pass referrer
- PUSH ecx // pass object
+ PUSH ecx // pass new_val
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
- call SYMBOL(artGetObjInstanceFromCode) // (field_idx, Object*, referrer, Thread*, SP)
+ call SYMBOL(artSet8StaticFromCode) // (field_idx, new_val, referrer, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
- RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
-END_FUNCTION art_quick_get_obj_instance
+ RETURN_IF_EAX_ZERO // return or deliver exception
+END_FUNCTION art_quick_set8_static
+
+DEFINE_FUNCTION art_quick_set16_static
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %ebx // remember SP
+ mov 32(%esp), %edx // get referrer
+ subl LITERAL(12), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(12)
+ PUSH ebx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH edx // pass referrer
+ PUSH ecx // pass new_val
+ PUSH eax // pass field_idx
+ call SYMBOL(artSet16StaticFromCode) // (field_idx, new_val, referrer, Thread*, SP)
+ addl LITERAL(32), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-32)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO // return or deliver exception
+END_FUNCTION art_quick_set16_static
DEFINE_FUNCTION art_quick_set32_static
SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
@@ -966,7 +1031,6 @@
PUSH edx // pass referrer
PUSH ecx // pass new_val
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artSet32StaticFromCode) // (field_idx, new_val, referrer, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -987,7 +1051,6 @@
PUSH ecx // pass low half of new_val
PUSH ebx // pass referrer
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artSet64StaticFromCode) // (field_idx, referrer, new_val, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
@@ -1007,63 +1070,19 @@
PUSH edx // pass referrer
PUSH ecx // pass new_val
PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artSetObjStaticFromCode) // (field_idx, new_val, referrer, Thread*, SP)
addl LITERAL(32), %esp // pop arguments
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
RETURN_IF_EAX_ZERO // return or deliver exception
END_FUNCTION art_quick_set_obj_static
-DEFINE_FUNCTION art_quick_get32_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
- mov %esp, %edx // remember SP
- mov 32(%esp), %ecx // get referrer
- PUSH edx // pass SP
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH ecx // pass referrer
- PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
- call SYMBOL(artGet32StaticFromCode) // (field_idx, referrer, Thread*, SP)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
- RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
-END_FUNCTION art_quick_get32_static
-
-DEFINE_FUNCTION art_quick_get64_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
- mov %esp, %edx // remember SP
- mov 32(%esp), %ecx // get referrer
- PUSH edx // pass SP
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH ecx // pass referrer
- PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
- call SYMBOL(artGet64StaticFromCode) // (field_idx, referrer, Thread*, SP)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
- RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
-END_FUNCTION art_quick_get64_static
-
-DEFINE_FUNCTION art_quick_get_obj_static
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
- mov %esp, %edx // remember SP
- mov 32(%esp), %ecx // get referrer
- PUSH edx // pass SP
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH ecx // pass referrer
- PUSH eax // pass field_idx
- SETUP_GOT_NOSAVE // clobbers EBX
- call SYMBOL(artGetObjStaticFromCode) // (field_idx, referrer, Thread*, SP)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
- RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
-END_FUNCTION art_quick_get_obj_static
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
DEFINE_FUNCTION art_quick_proxy_invoke_handler
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // save frame and Method*
@@ -1072,7 +1091,6 @@
CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // pass receiver
PUSH eax // pass proxy method
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artQuickProxyInvokeHandler) // (proxy method, receiver, Thread*, SP)
movd %eax, %xmm0 // place return value also into floating point return value
movd %edx, %xmm1
@@ -1104,7 +1122,6 @@
CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // pass receiver
PUSH eax // pass method
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artQuickResolutionTrampoline) // (Method* called, receiver, Thread*, SP)
movl %eax, %edi // remember code pointer in EDI
addl LITERAL(16), %esp // pop arguments
@@ -1139,7 +1156,6 @@
subl LITERAL(8), %esp // Padding for 16B alignment.
pushl %ebp // Pass SP (to ArtMethod).
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
- SETUP_GOT_NOSAVE // Clobbers ebx.
call SYMBOL(artQuickGenericJniTrampoline) // (Thread*, sp)
// The C call will have registered the complete save-frame on success.
@@ -1212,7 +1228,6 @@
pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
PUSH eax // pass method
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artQuickToInterpreterBridge) // (method, Thread*, SP)
movd %eax, %xmm0 // place return value also into floating point return value
movd %edx, %xmm1
@@ -1239,8 +1254,8 @@
CFI_ADJUST_CFA_OFFSET(4)
PUSH ecx // Pass receiver.
PUSH eax // Pass Method*.
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, SP, LR)
+ SETUP_GOT_NOSAVE
addl LITERAL(28), %esp // Pop arguments upto saved Method*.
movl 28(%esp), %edi // Restore edi.
movl %eax, 28(%esp) // Place code* over edi, just under return pc.
@@ -1274,7 +1289,6 @@
PUSH ecx // Pass SP.
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current.
CFI_ADJUST_CFA_OFFSET(4)
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artInstrumentationMethodExitFromCode) // (Thread*, SP, gpr_result, fpr_result)
mov %eax, %ecx // Move returned link register.
addl LITERAL(32), %esp // Pop arguments.
@@ -1304,7 +1318,6 @@
PUSH ecx // Pass SP.
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
CFI_ADJUST_CFA_OFFSET(4)
- SETUP_GOT_NOSAVE // clobbers EBX
call SYMBOL(artDeoptimize) // artDeoptimize(Thread*, SP)
int3 // Unreachable.
END_FUNCTION art_quick_deoptimize
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index f7acbdb..4ae61a2 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -119,6 +119,8 @@
.balign 16
END_MACRO
+// TODO: we might need to use SYMBOL() here to add the underscore prefix
+// for mac builds.
MACRO1(DEFINE_FUNCTION, c_name)
FUNCTION_TYPE(\c_name, 0)
ASM_HIDDEN VAR(c_name, 0)
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 35a0cf4..c9028e1 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -48,12 +48,24 @@
extern "C" void* art_quick_resolve_string(void*, uint32_t);
// Field entrypoints.
+extern "C" int art_quick_set8_instance(uint32_t, void*, int8_t);
+extern "C" int art_quick_set8_static(uint32_t, int8_t);
+extern "C" int art_quick_set16_instance(uint32_t, void*, int16_t);
+extern "C" int art_quick_set16_static(uint32_t, int16_t);
extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t);
extern "C" int art_quick_set32_static(uint32_t, int32_t);
extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t);
extern "C" int art_quick_set64_static(uint32_t, int64_t);
extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*);
extern "C" int art_quick_set_obj_static(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_instance(uint32_t, void*);
+extern "C" uint8_t art_quick_get_boolean_instance(uint32_t, void*);
+extern "C" int8_t art_quick_get_byte_static(uint32_t);
+extern "C" uint8_t art_quick_get_boolean_static(uint32_t);
+extern "C" int16_t art_quick_get_short_instance(uint32_t, void*);
+extern "C" uint16_t art_quick_get_char_instance(uint32_t, void*);
+extern "C" int16_t art_quick_get_short_static(uint32_t);
+extern "C" uint16_t art_quick_get_char_static(uint32_t);
extern "C" int32_t art_quick_get32_instance(uint32_t, void*);
extern "C" int32_t art_quick_get32_static(uint32_t);
extern "C" int64_t art_quick_get64_instance(uint32_t, void*);
@@ -141,15 +153,27 @@
qpoints->pResolveString = art_quick_resolve_string;
// Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
qpoints->pSet32Instance = art_quick_set32_instance;
qpoints->pSet32Static = art_quick_set32_static;
qpoints->pSet64Instance = art_quick_set64_instance;
qpoints->pSet64Static = art_quick_set64_static;
qpoints->pSetObjInstance = art_quick_set_obj_instance;
qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
qpoints->pGet32Instance = art_quick_get32_instance;
qpoints->pGet64Instance = art_quick_get64_instance;
qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
qpoints->pGet32Static = art_quick_get32_static;
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 5798092..e68cfbc 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1076,17 +1076,29 @@
UNIMPLEMENTED art_quick_lshr
UNIMPLEMENTED art_quick_lushr
+THREE_ARG_REF_DOWNCALL art_quick_set8_instance, artSet8InstanceFromCode, RETURN_IF_EAX_ZERO
+THREE_ARG_REF_DOWNCALL art_quick_set16_instance, artSet16InstanceFromCode, RETURN_IF_EAX_ZERO
THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCode, RETURN_IF_EAX_ZERO
THREE_ARG_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCode, RETURN_IF_EAX_ZERO
THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RETURN_IF_EAX_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCode, RETURN_IF_EAX_ZERO
+TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_IF_EAX_ZERO
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5978443..62f3593 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -27,7 +27,7 @@
#define CLASS_OFFSET 0
#define LOCK_WORD_OFFSET 4
-#if !defined(USE_BAKER_OR_BROOKS_READ_BARRIER)
+#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER
// Offsets within java.lang.Class.
#define CLASS_COMPONENT_TYPE_OFFSET 12
@@ -44,13 +44,8 @@
// Offsets within java.lang.Method.
#define METHOD_DEX_CACHE_METHODS_OFFSET 12
-#if defined(ART_USE_PORTABLE_COMPILER)
#define METHOD_PORTABLE_CODE_OFFSET 40
#define METHOD_QUICK_CODE_OFFSET 48
-#else
-#define METHOD_PORTABLE_CODE_OFFSET 40
-#define METHOD_QUICK_CODE_OFFSET 40
-#endif // ART_USE_PORTABLE_COMPILER
#else
@@ -72,6 +67,6 @@
#define METHOD_PORTABLE_CODE_OFFSET 48
#define METHOD_QUICK_CODE_OFFSET 56
-#endif // USE_BAKER_OR_BROOKS_READ_BARRIER
+#endif
#endif // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 5f43bec..b8edad3 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -57,17 +57,19 @@
}
}
-void Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
+bool Barrier::Increment(Thread* self, int delta, uint32_t timeout_ms) {
MutexLock mu(self, lock_);
SetCountLocked(self, count_ + delta);
+ bool timed_out = false;
if (count_ != 0) {
- condition_.TimedWait(self, timeout_ms, 0);
+ timed_out = condition_.TimedWait(self, timeout_ms, 0);
}
+ return timed_out;
}
void Barrier::SetCountLocked(Thread* self, int count) {
count_ = count;
- if (count_ == 0) {
+ if (count == 0) {
condition_.Broadcast(self);
}
}
diff --git a/runtime/barrier.h b/runtime/barrier.h
index a433cac..167e1d6 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -38,10 +38,11 @@
void Init(Thread* self, int count);
// Increment the count by delta, wait on condition if count is non zero.
- void Increment(Thread* self, int delta);
+ void Increment(Thread* self, int delta) LOCKS_EXCLUDED(lock_);
- // Increment the count by delta, wait on condition if count is non zero, with a timeout
- void Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
+ // Increment the count by delta, wait on condition if count is non zero, with a timeout. Returns
+ // true if time out occurred.
+ bool Increment(Thread* self, int delta, uint32_t timeout_ms) LOCKS_EXCLUDED(lock_);
private:
void SetCountLocked(Thread* self, int count) EXCLUSIVE_LOCKS_REQUIRED(lock_);
diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc
index 39d51a5..64cdbbf 100644
--- a/runtime/base/allocator.cc
+++ b/runtime/base/allocator.cc
@@ -29,7 +29,7 @@
Atomic<uint64_t> TrackedAllocators::max_bytes_used_[kAllocatorTagCount];
Atomic<uint64_t> TrackedAllocators::total_bytes_used_[kAllocatorTagCount];
-class MallocAllocator : public Allocator {
+class MallocAllocator FINAL : public Allocator {
public:
explicit MallocAllocator() {}
~MallocAllocator() {}
@@ -48,7 +48,7 @@
MallocAllocator g_malloc_allocator;
-class NoopAllocator : public Allocator {
+class NoopAllocator FINAL : public Allocator {
public:
explicit NoopAllocator() {}
~NoopAllocator() {}
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index a7adb02..2c3e966 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -66,6 +66,7 @@
kAllocatorTagCompileTimeClassPath,
kAllocatorTagOatFile,
kAllocatorTagDexFileVerifier,
+ kAllocatorTagRosAlloc,
kAllocatorTagCount, // Must always be last element.
};
std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag);
@@ -149,6 +150,10 @@
Key, T, Compare, TrackingAllocator<std::pair<Key, T>, kTag>> {
};
+template<class Key, AllocatorTag kTag, class Compare = std::less<Key>>
+class AllocationTrackingSet : public std::set<Key, Compare, TrackingAllocator<Key, kTag>> {
+};
+
} // namespace art
#endif // ART_RUNTIME_BASE_ALLOCATOR_H_
diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h
new file mode 100644
index 0000000..dc13dd5
--- /dev/null
+++ b/runtime/base/bit_vector-inl.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 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_BASE_BIT_VECTOR_INL_H_
+#define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
+
+#include "bit_vector.h"
+#include "logging.h"
+#include "utils.h"
+
+namespace art {
+
+inline bool BitVector::IndexIterator::operator==(const IndexIterator& other) const {
+ DCHECK(bit_storage_ == other.bit_storage_);
+ DCHECK_EQ(storage_size_, other.storage_size_);
+ return bit_index_ == other.bit_index_;
+}
+
+inline int BitVector::IndexIterator::operator*() const {
+ DCHECK_LT(bit_index_, BitSize());
+ return bit_index_;
+}
+
+inline BitVector::IndexIterator& BitVector::IndexIterator::operator++() {
+ DCHECK_LT(bit_index_, BitSize());
+ bit_index_ = FindIndex(bit_index_ + 1u);
+ return *this;
+}
+
+inline BitVector::IndexIterator BitVector::IndexIterator::operator++(int) {
+ IndexIterator result(*this);
+ ++*this;
+ return result;
+}
+
+inline uint32_t BitVector::IndexIterator::FindIndex(uint32_t start_index) const {
+ DCHECK_LE(start_index, BitSize());
+ uint32_t word_index = start_index / kWordBits;
+ if (UNLIKELY(word_index == storage_size_)) {
+ return start_index;
+ }
+ uint32_t word = bit_storage_[word_index];
+ // Mask out any bits in the first word we've already considered.
+ word &= static_cast<uint32_t>(-1) << (start_index & 0x1f);
+ while (word == 0u) {
+ ++word_index;
+ if (UNLIKELY(word_index == storage_size_)) {
+ return BitSize();
+ }
+ word = bit_storage_[word_index];
+ }
+ return word_index * 32u + CTZ(word);
+}
+
+inline void BitVector::ClearAllBits() {
+ memset(storage_, 0, storage_size_ * kWordBytes);
+}
+
+inline bool BitVector::Equal(const BitVector* src) const {
+ return (storage_size_ == src->GetStorageSize()) &&
+ (expandable_ == src->IsExpandable()) &&
+ (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_VECTOR_INL_H_
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 1b9022e..3d2f0de 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -16,20 +16,14 @@
#include "bit_vector.h"
+#include "allocator.h"
+#include "bit_vector-inl.h"
+
namespace art {
-// TODO: profile to make sure this is still a win relative to just using shifted masks.
-static uint32_t check_masks[32] = {
- 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
- 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
- 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
- 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
- 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
- 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
- 0x40000000, 0x80000000 };
-
-static inline uint32_t BitsToWords(uint32_t bits) {
- return (bits + 31) >> 5;
+// The number of words necessary to encode bits.
+static constexpr uint32_t BitsToWords(uint32_t bits) {
+ return RoundUp(bits, 32) / 32;
}
// TODO: replace excessive argument defaulting when we are at gcc 4.7
@@ -40,10 +34,10 @@
Allocator* allocator,
uint32_t storage_size,
uint32_t* storage)
- : allocator_(allocator),
- expandable_(expandable),
+ : storage_(storage),
storage_size_(storage_size),
- storage_(storage) {
+ allocator_(allocator),
+ expandable_(expandable) {
COMPILE_ASSERT(sizeof(*storage_) == kWordBytes, check_word_bytes);
COMPILE_ASSERT(sizeof(*storage_) * 8u == kWordBits, check_word_bits);
if (storage_ == nullptr) {
@@ -56,59 +50,7 @@
allocator_->Free(storage_);
}
-/*
- * Determine whether or not the specified bit is set.
- */
-bool BitVector::IsBitSet(uint32_t num) const {
- // If the index is over the size:
- if (num >= storage_size_ * kWordBits) {
- // Whether it is expandable or not, this bit does not exist: thus it is not set.
- return false;
- }
-
- return IsBitSet(storage_, num);
-}
-
-// Mark all bits bit as "clear".
-void BitVector::ClearAllBits() {
- memset(storage_, 0, storage_size_ * kWordBytes);
-}
-
-// Mark the specified bit as "set".
-/*
- * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're
- * not using it badly or change resize mechanism.
- */
-void BitVector::SetBit(uint32_t num) {
- if (num >= storage_size_ * kWordBits) {
- DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
-
- /* Round up to word boundaries for "num+1" bits */
- uint32_t new_size = BitsToWords(num + 1);
- DCHECK_GT(new_size, storage_size_);
- uint32_t *new_storage =
- static_cast<uint32_t*>(allocator_->Alloc(new_size * kWordBytes));
- memcpy(new_storage, storage_, storage_size_ * kWordBytes);
- // Zero out the new storage words.
- memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * kWordBytes);
- // TOTO: collect stats on space wasted because of resize.
- storage_ = new_storage;
- storage_size_ = new_size;
- }
-
- storage_[num >> 5] |= check_masks[num & 0x1f];
-}
-
-// Mark the specified bit as "unset".
-void BitVector::ClearBit(uint32_t num) {
- // If the index is over the size, we don't have to do anything, it is cleared.
- if (num < storage_size_ * kWordBits) {
- // Otherwise, go ahead and clear it.
- storage_[num >> 5] &= ~check_masks[num & 0x1f];
- }
-}
-
-bool BitVector::SameBitsSet(const BitVector *src) {
+bool BitVector::SameBitsSet(const BitVector *src) const {
int our_highest = GetHighestBitSet();
int src_highest = src->GetHighestBitSet();
@@ -134,7 +76,6 @@
return (memcmp(storage_, src->GetRawStorage(), our_highest_index * kWordBytes) == 0);
}
-// Intersect with another bit vector.
void BitVector::Intersect(const BitVector* src) {
uint32_t src_storage_size = src->storage_size_;
@@ -155,9 +96,6 @@
}
}
-/*
- * Union with another bit vector.
- */
bool BitVector::Union(const BitVector* src) {
// Get the highest bit to determine how much we need to expand.
int highest_bit = src->GetHighestBitSet();
@@ -175,8 +113,7 @@
if (storage_size_ < src_size) {
changed = true;
- // Set it to reallocate.
- SetBit(highest_bit);
+ EnsureSize(highest_bit);
// Paranoid: storage size should be big enough to hold this bit now.
DCHECK_LT(static_cast<uint32_t> (highest_bit), storage_size_ * kWordBits);
@@ -242,21 +179,20 @@
}
void BitVector::Subtract(const BitVector *src) {
- uint32_t src_size = src->storage_size_;
+ uint32_t src_size = src->storage_size_;
- // We only need to operate on bytes up to the smaller of the sizes of the two operands.
- unsigned int min_size = (storage_size_ > src_size) ? src_size : storage_size_;
+ // We only need to operate on bytes up to the smaller of the sizes of the two operands.
+ unsigned int min_size = (storage_size_ > src_size) ? src_size : storage_size_;
- // Difference until max, we know both accept it:
- // There is no need to do more:
- // If we are bigger than src, the upper bits are unchanged.
- // If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
- for (uint32_t idx = 0; idx < min_size; idx++) {
- storage_[idx] &= (~(src->GetRawStorageWord(idx)));
- }
+ // Difference until max, we know both accept it:
+ // There is no need to do more:
+ // If we are bigger than src, the upper bits are unchanged.
+ // If we are smaller than src, the non-existant upper bits are 0 and thus can't get subtracted.
+ for (uint32_t idx = 0; idx < min_size; idx++) {
+ storage_[idx] &= (~(src->GetRawStorageWord(idx)));
+ }
}
-// Count the number of bits that are set.
uint32_t BitVector::NumSetBits() const {
uint32_t count = 0;
for (uint32_t word = 0; word < storage_size_; word++) {
@@ -265,17 +201,11 @@
return count;
}
-// Count the number of bits that are set in range [0, end).
uint32_t BitVector::NumSetBits(uint32_t end) const {
DCHECK_LE(end, storage_size_ * kWordBits);
return NumSetBits(storage_, end);
}
-/*
- * Mark specified number of bits as "set". Cannot set all bits like ClearAll
- * since there might be unused bits - setting those to one will confuse the
- * iterator.
- */
void BitVector::SetInitialBits(uint32_t num_bits) {
// If num_bits is 0, clear everything.
if (num_bits == 0) {
@@ -288,7 +218,7 @@
uint32_t idx;
// We can set every storage element with -1.
- for (idx = 0; idx < (num_bits >> 5); idx++) {
+ for (idx = 0; idx < WordIndex(num_bits); idx++) {
storage_[idx] = -1;
}
@@ -312,20 +242,8 @@
uint32_t value = storage_[idx];
if (value != 0) {
- // Shift right for the counting.
- value /= 2;
-
- int cnt = 0;
-
- // Count the bits.
- while (value > 0) {
- value /= 2;
- cnt++;
- }
-
- // Return cnt + how many storage units still remain * the number of bits per unit.
- int res = cnt + (idx * kWordBits);
- return res;
+ // Return highest bit set in value plus bits from previous storage indexes.
+ return 31 - CLZ(value) + (idx * kWordBits);
}
}
@@ -333,23 +251,6 @@
return -1;
}
-bool BitVector::EnsureSizeAndClear(unsigned int num) {
- // Check if the bitvector is expandable.
- if (IsExpandable() == false) {
- return false;
- }
-
- if (num > 0) {
- // Now try to expand by setting the last bit.
- SetBit(num - 1);
- }
-
- // We must clear all bits as per our specification.
- ClearAllBits();
-
- return true;
-}
-
void BitVector::Copy(const BitVector *src) {
// Get highest bit set, we only need to copy till then.
int highest_bit = src->GetHighestBitSet();
@@ -375,13 +276,8 @@
}
}
-bool BitVector::IsBitSet(const uint32_t* storage, uint32_t num) {
- uint32_t val = storage[num >> 5] & check_masks[num & 0x1f];
- return (val != 0);
-}
-
uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
- uint32_t word_end = end >> 5;
+ uint32_t word_end = WordIndex(end);
uint32_t partial_word_bits = end & 0x1f;
uint32_t count = 0u;
@@ -400,45 +296,6 @@
os << buffer.str() << std::endl;
}
-
-void BitVector::DumpDotHelper(bool last_entry, FILE* file, std::ostringstream& buffer) const {
- // Now print it to the file.
- fprintf(file, " {%s}", buffer.str().c_str());
-
- // If it isn't the last entry, add a |.
- if (last_entry == false) {
- fprintf(file, "|");
- }
-
- // Add the \n.
- fprintf(file, "\\\n");
-}
-
-void BitVector::DumpDot(FILE* file, const char* prefix, bool last_entry) const {
- std::ostringstream buffer;
- DumpHelper(prefix, buffer);
- DumpDotHelper(last_entry, file, buffer);
-}
-
-void BitVector::DumpIndicesDot(FILE* file, const char* prefix, bool last_entry) const {
- std::ostringstream buffer;
- DumpIndicesHelper(prefix, buffer);
- DumpDotHelper(last_entry, file, buffer);
-}
-
-void BitVector::DumpIndicesHelper(const char* prefix, std::ostringstream& buffer) const {
- // Initialize it.
- if (prefix != nullptr) {
- buffer << prefix;
- }
-
- for (size_t i = 0; i < storage_size_ * kWordBits; i++) {
- if (IsBitSet(i)) {
- buffer << i << " ";
- }
- }
-}
-
void BitVector::DumpHelper(const char* prefix, std::ostringstream& buffer) const {
// Initialize it.
if (prefix != nullptr) {
@@ -452,4 +309,22 @@
buffer << ')';
}
+void BitVector::EnsureSize(uint32_t idx) {
+ if (idx >= storage_size_ * kWordBits) {
+ DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << idx;
+
+ /* Round up to word boundaries for "idx+1" bits */
+ uint32_t new_size = BitsToWords(idx + 1);
+ DCHECK_GT(new_size, storage_size_);
+ uint32_t *new_storage =
+ static_cast<uint32_t*>(allocator_->Alloc(new_size * kWordBytes));
+ memcpy(new_storage, storage_, storage_size_ * kWordBytes);
+ // Zero out the new storage words.
+ memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * kWordBytes);
+ // TOTO: collect stats on space wasted because of resize.
+ storage_ = new_storage;
+ storage_size_ = new_size;
+ }
+}
+
} // namespace art
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index fb1646f..1e28a27 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -18,235 +18,237 @@
#define ART_RUNTIME_BASE_BIT_VECTOR_H_
#include <stdint.h>
-#include <stddef.h>
-
-#include "allocator.h"
-#include "base/logging.h"
-#include "utils.h"
+#include <iterator>
namespace art {
+class Allocator;
+
/*
* Expanding bitmap, used for tracking resources. Bits are numbered starting
* from zero. All operations on a BitVector are unsynchronized.
*/
class BitVector {
- public:
- class IndexContainer;
+ public:
+ class IndexContainer;
- /**
- * @brief Convenient iterator across the indexes of the BitVector's set bits.
- *
- * @details IndexIterator is a Forward iterator (C++11: 24.2.5) from the lowest
- * to the highest index of the BitVector's set bits. Instances can be retrieved
- * only through BitVector::Indexes() which returns an IndexContainer wrapper
- * object with begin() and end() suitable for range-based loops:
- * for (uint32_t idx : bit_vector.Indexes()) {
- * // Use idx.
- * }
- */
- class IndexIterator
- : std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
- public:
- bool operator==(const IndexIterator& other) const {
- DCHECK(bit_storage_ == other.bit_storage_);
- DCHECK_EQ(storage_size_, other.storage_size_);
- return bit_index_ == other.bit_index_;
- }
+ /**
+ * @brief Convenient iterator across the indexes of the BitVector's set bits.
+ *
+ * @details IndexIterator is a Forward iterator (C++11: 24.2.5) from the lowest
+ * to the highest index of the BitVector's set bits. Instances can be retrieved
+ * only through BitVector::Indexes() which returns an IndexContainer wrapper
+ * object with begin() and end() suitable for range-based loops:
+ * for (uint32_t idx : bit_vector.Indexes()) {
+ * // Use idx.
+ * }
+ */
+ class IndexIterator :
+ std::iterator<std::forward_iterator_tag, uint32_t, ptrdiff_t, void, uint32_t> {
+ public:
+ bool operator==(const IndexIterator& other) const;
- bool operator!=(const IndexIterator& other) const {
- return !(*this == other);
- }
-
- int operator*() const {
- DCHECK_LT(bit_index_, BitSize());
- return bit_index_;
- }
-
- IndexIterator& operator++() {
- DCHECK_LT(bit_index_, BitSize());
- bit_index_ = FindIndex(bit_index_ + 1u);
- return *this;
- }
-
- IndexIterator operator++(int) {
- IndexIterator result(*this);
- ++*this;
- return result;
- }
-
- // Helper function to check for end without comparing with bit_vector.Indexes().end().
- bool Done() const {
- return bit_index_ == BitSize();
- }
-
- private:
- struct begin_tag { };
- struct end_tag { };
-
- IndexIterator(const BitVector* bit_vector, begin_tag)
- : bit_storage_(bit_vector->GetRawStorage()),
- storage_size_(bit_vector->storage_size_),
- bit_index_(FindIndex(0u)) { }
-
- IndexIterator(const BitVector* bit_vector, end_tag)
- : bit_storage_(bit_vector->GetRawStorage()),
- storage_size_(bit_vector->storage_size_),
- bit_index_(BitSize()) { }
-
- uint32_t BitSize() const {
- return storage_size_ * kWordBits;
- }
-
- uint32_t FindIndex(uint32_t start_index) const {
- DCHECK_LE(start_index, BitSize());
- uint32_t word_index = start_index / kWordBits;
- if (UNLIKELY(word_index == storage_size_)) {
- return start_index;
- }
- uint32_t word = bit_storage_[word_index];
- // Mask out any bits in the first word we've already considered.
- word &= static_cast<uint32_t>(-1) << (start_index & 0x1f);
- while (word == 0u) {
- ++word_index;
- if (UNLIKELY(word_index == storage_size_)) {
- return BitSize();
- }
- word = bit_storage_[word_index];
- }
- return word_index * 32u + CTZ(word);
- }
-
- const uint32_t* const bit_storage_;
- const uint32_t storage_size_; // Size of vector in words.
- uint32_t bit_index_; // Current index (size in bits).
-
- friend class BitVector::IndexContainer;
- };
-
- /**
- * @brief BitVector wrapper class for iteration across indexes of set bits.
- */
- class IndexContainer {
- public:
- explicit IndexContainer(const BitVector* bit_vector) : bit_vector_(bit_vector) { }
-
- IndexIterator begin() const {
- return IndexIterator(bit_vector_, IndexIterator::begin_tag());
- }
-
- IndexIterator end() const {
- return IndexIterator(bit_vector_, IndexIterator::end_tag());
- }
-
- private:
- const BitVector* const bit_vector_;
- };
-
- BitVector(uint32_t start_bits,
- bool expandable,
- Allocator* allocator,
- uint32_t storage_size = 0,
- uint32_t* storage = nullptr);
-
- virtual ~BitVector();
-
- void SetBit(uint32_t num);
- void ClearBit(uint32_t num);
- bool IsBitSet(uint32_t num) const;
- void ClearAllBits();
- void SetInitialBits(uint32_t num_bits);
-
- void Copy(const BitVector* src);
- void Intersect(const BitVector* src2);
- bool Union(const BitVector* src);
-
- // Set bits of union_with that are not in not_in.
- bool UnionIfNotIn(const BitVector* union_with, const BitVector* not_in);
-
- void Subtract(const BitVector* src);
- // Are we equal to another bit vector? Note: expandability attributes must also match.
- bool Equal(const BitVector* src) {
- return (storage_size_ == src->GetStorageSize()) &&
- (expandable_ == src->IsExpandable()) &&
- (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+ bool operator!=(const IndexIterator& other) const {
+ return !(*this == other);
}
- /**
- * @brief Are all the bits set the same?
- * @details expandability and size can differ as long as the same bits are set.
- */
- bool SameBitsSet(const BitVector *src);
+ int operator*() const;
- uint32_t NumSetBits() const;
+ IndexIterator& operator++();
- // Number of bits set in range [0, end).
- uint32_t NumSetBits(uint32_t end) const;
+ IndexIterator operator++(int);
- IndexContainer Indexes() const {
- return IndexContainer(this);
+ // Helper function to check for end without comparing with bit_vector.Indexes().end().
+ bool Done() const {
+ return bit_index_ == BitSize();
}
- uint32_t GetStorageSize() const { return storage_size_; }
- bool IsExpandable() const { return expandable_; }
- uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; }
- uint32_t* GetRawStorage() { return storage_; }
- const uint32_t* GetRawStorage() const { return storage_; }
- size_t GetSizeOf() const { return storage_size_ * kWordBytes; }
+ private:
+ struct begin_tag { };
+ struct end_tag { };
- /**
- * @return the highest bit set, -1 if none are set
+ IndexIterator(const BitVector* bit_vector, begin_tag)
+ : bit_storage_(bit_vector->GetRawStorage()),
+ storage_size_(bit_vector->storage_size_),
+ bit_index_(FindIndex(0u)) { }
+
+ IndexIterator(const BitVector* bit_vector, end_tag)
+ : bit_storage_(bit_vector->GetRawStorage()),
+ storage_size_(bit_vector->storage_size_),
+ bit_index_(BitSize()) { }
+
+ uint32_t BitSize() const {
+ return storage_size_ * kWordBits;
+ }
+
+ uint32_t FindIndex(uint32_t start_index) const;
+ const uint32_t* const bit_storage_;
+ const uint32_t storage_size_; // Size of vector in words.
+ uint32_t bit_index_; // Current index (size in bits).
+
+ friend class BitVector::IndexContainer;
+ };
+
+ /**
+ * @brief BitVector wrapper class for iteration across indexes of set bits.
+ */
+ class IndexContainer {
+ public:
+ explicit IndexContainer(const BitVector* bit_vector) : bit_vector_(bit_vector) { }
+
+ IndexIterator begin() const {
+ return IndexIterator(bit_vector_, IndexIterator::begin_tag());
+ }
+
+ IndexIterator end() const {
+ return IndexIterator(bit_vector_, IndexIterator::end_tag());
+ }
+
+ private:
+ const BitVector* const bit_vector_;
+ };
+
+ BitVector(uint32_t start_bits,
+ bool expandable,
+ Allocator* allocator,
+ uint32_t storage_size = 0,
+ uint32_t* storage = nullptr);
+
+ virtual ~BitVector();
+
+ // Mark the specified bit as "set".
+ void SetBit(uint32_t idx) {
+ /*
+ * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're
+ * not using it badly or change resize mechanism.
*/
- int GetHighestBitSet() const;
+ if (idx >= storage_size_ * kWordBits) {
+ EnsureSize(idx);
+ }
+ storage_[WordIndex(idx)] |= BitMask(idx);
+ }
- // Is bit set in storage. (No range check.)
- static bool IsBitSet(const uint32_t* storage, uint32_t num);
- // Number of bits set in range [0, end) in storage. (No range check.)
- static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+ // Mark the specified bit as "unset".
+ void ClearBit(uint32_t idx) {
+ // If the index is over the size, we don't have to do anything, it is cleared.
+ if (idx < storage_size_ * kWordBits) {
+ // Otherwise, go ahead and clear it.
+ storage_[WordIndex(idx)] &= ~BitMask(idx);
+ }
+ }
- bool EnsureSizeAndClear(unsigned int num);
+ // Determine whether or not the specified bit is set.
+ bool IsBitSet(uint32_t idx) const {
+ // If the index is over the size, whether it is expandable or not, this bit does not exist:
+ // thus it is not set.
+ return (idx < (storage_size_ * kWordBits)) && IsBitSet(storage_, idx);
+ }
- void Dump(std::ostream& os, const char* prefix) const;
+ // Mark all bits bit as "clear".
+ void ClearAllBits();
- /**
- * @brief last_entry is this the last entry for the dot dumping
- * @details if not, a "|" is appended to the dump.
- */
- void DumpDot(FILE* file, const char* prefix, bool last_entry = false) const;
+ // Mark specified number of bits as "set". Cannot set all bits like ClearAll since there might
+ // be unused bits - setting those to one will confuse the iterator.
+ void SetInitialBits(uint32_t num_bits);
- /**
- * @brief last_entry is this the last entry for the dot dumping
- * @details if not, a "|" is appended to the dump.
- */
- void DumpIndicesDot(FILE* file, const char* prefix, bool last_entry = false) const;
+ void Copy(const BitVector* src);
- protected:
- /**
- * @brief Dump the bitvector into buffer in a 00101..01 format.
- * @param buffer the ostringstream used to dump the bitvector into.
- */
- void DumpHelper(const char* prefix, std::ostringstream& buffer) const;
+ // Intersect with another bit vector.
+ void Intersect(const BitVector* src2);
- /**
- * @brief Dump the bitvector in a 1 2 5 8 format, where the numbers are the bit set.
- * @param buffer the ostringstream used to dump the bitvector into.
- */
- void DumpIndicesHelper(const char* prefix, std::ostringstream& buffer) const;
+ // Union with another bit vector.
+ bool Union(const BitVector* src);
- /**
- * @brief Wrapper to perform the bitvector dumping with the .dot format.
- * @param buffer the ostringstream used to dump the bitvector into.
- */
- void DumpDotHelper(bool last_entry, FILE* file, std::ostringstream& buffer) const;
+ // Set bits of union_with that are not in not_in.
+ bool UnionIfNotIn(const BitVector* union_with, const BitVector* not_in);
- private:
- static constexpr uint32_t kWordBytes = sizeof(uint32_t);
- static constexpr uint32_t kWordBits = kWordBytes * 8;
+ void Subtract(const BitVector* src);
- Allocator* const allocator_;
- const bool expandable_; // expand bitmap if we run out?
- uint32_t storage_size_; // current size, in 32-bit words.
- uint32_t* storage_;
+ // Are we equal to another bit vector? Note: expandability attributes must also match.
+ bool Equal(const BitVector* src) const;
+
+ /**
+ * @brief Are all the bits set the same?
+ * @details expandability and size can differ as long as the same bits are set.
+ */
+ bool SameBitsSet(const BitVector *src) const;
+
+ // Count the number of bits that are set.
+ uint32_t NumSetBits() const;
+
+ // Count the number of bits that are set in range [0, end).
+ uint32_t NumSetBits(uint32_t end) const;
+
+ IndexContainer Indexes() const {
+ return IndexContainer(this);
+ }
+
+ uint32_t GetStorageSize() const {
+ return storage_size_;
+ }
+
+ bool IsExpandable() const {
+ return expandable_;
+ }
+
+ uint32_t GetRawStorageWord(size_t idx) const {
+ return storage_[idx];
+ }
+
+ uint32_t* GetRawStorage() {
+ return storage_;
+ }
+
+ const uint32_t* GetRawStorage() const {
+ return storage_;
+ }
+
+ size_t GetSizeOf() const {
+ return storage_size_ * kWordBytes;
+ }
+
+ /**
+ * @return the highest bit set, -1 if none are set
+ */
+ int GetHighestBitSet() const;
+
+ // Is bit set in storage. (No range check.)
+ static bool IsBitSet(const uint32_t* storage, uint32_t idx) {
+ return (storage[WordIndex(idx)] & BitMask(idx)) != 0;
+ }
+
+ // Number of bits set in range [0, end) in storage. (No range check.)
+ static uint32_t NumSetBits(const uint32_t* storage, uint32_t end);
+
+ void Dump(std::ostream& os, const char* prefix) const;
+
+ private:
+ /**
+ * @brief Dump the bitvector into buffer in a 00101..01 format.
+ * @param buffer the ostringstream used to dump the bitvector into.
+ */
+ void DumpHelper(const char* prefix, std::ostringstream& buffer) const;
+
+ // Ensure there is space for a bit at idx.
+ void EnsureSize(uint32_t idx);
+
+ // The index of the word within storage.
+ static constexpr uint32_t WordIndex(uint32_t idx) {
+ return idx >> 5;
+ }
+
+ // A bit mask to extract the bit for the given index.
+ static constexpr uint32_t BitMask(uint32_t idx) {
+ return 1 << (idx & 0x1f);
+ }
+
+ static constexpr uint32_t kWordBytes = sizeof(uint32_t);
+ static constexpr uint32_t kWordBits = kWordBytes * 8;
+
+ uint32_t* storage_; // The storage for the bit vector.
+ uint32_t storage_size_; // Current size, in 32-bit words.
+ Allocator* const allocator_; // Allocator if expandable.
+ const bool expandable_; // Should the bitmap expand if too small?
};
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index 1403f50..df5d79d 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -16,7 +16,8 @@
#include <memory>
-#include "bit_vector.h"
+#include "allocator.h"
+#include "bit_vector-inl.h"
#include "gtest/gtest.h"
namespace art {
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index caeb946..cf3e763 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -151,7 +151,7 @@
template <typename LHS, typename RHS>
struct EagerEvaluator {
- EagerEvaluator(LHS lhs, RHS rhs) : lhs(lhs), rhs(rhs) { }
+ EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) { }
LHS lhs;
RHS rhs;
};
@@ -163,9 +163,9 @@
// protect you against combinations not explicitly listed below.
#define EAGER_PTR_EVALUATOR(T1, T2) \
template <> struct EagerEvaluator<T1, T2> { \
- EagerEvaluator(T1 lhs, T2 rhs) \
- : lhs(reinterpret_cast<const void*>(lhs)), \
- rhs(reinterpret_cast<const void*>(rhs)) { } \
+ EagerEvaluator(T1 l, T2 r) \
+ : lhs(reinterpret_cast<const void*>(l)), \
+ rhs(reinterpret_cast<const void*>(r)) { } \
const void* lhs; \
const void* rhs; \
}
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index fae9271..b66d528 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -179,6 +179,7 @@
#define WARN_UNUSED __attribute__((warn_unused_result))
template<typename T> void UNUSED(const T&) {}
+#define UNREACHABLE __builtin_unreachable
// Annotalysis thread-safety analysis support.
#if defined(__SUPPORT_TS_ANNOTATION__) || defined(__clang__)
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 9b97411..4383a7c 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -39,6 +39,7 @@
ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
Mutex* Locks::mem_maps_lock_ = nullptr;
Mutex* Locks::modify_ldt_lock_ = nullptr;
@@ -779,8 +780,9 @@
guard_.recursion_count_ = old_recursion_count;
}
-void ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) {
+bool ConditionVariable::TimedWait(Thread* self, int64_t ms, int32_t ns) {
DCHECK(self == NULL || self == Thread::Current());
+ bool timed_out = false;
guard_.AssertExclusiveHeld(self);
guard_.CheckSafeToWait(self);
unsigned int old_recursion_count = guard_.recursion_count_;
@@ -796,6 +798,7 @@
if (futex(sequence_.Address(), FUTEX_WAIT, cur_sequence, &rel_ts, NULL, 0) != 0) {
if (errno == ETIMEDOUT) {
// Timed out we're done.
+ timed_out = true;
} else if ((errno == EAGAIN) || (errno == EINTR)) {
// A signal or ConditionVariable::Signal/Broadcast has come in.
} else {
@@ -820,13 +823,16 @@
timespec ts;
InitTimeSpec(true, clock, ms, ns, &ts);
int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &guard_.mutex_, &ts));
- if (rc != 0 && rc != ETIMEDOUT) {
+ if (rc == ETIMEDOUT) {
+ timed_out = true;
+ } else if (rc != 0) {
errno = rc;
PLOG(FATAL) << "TimedWait failed for " << name_;
}
guard_.exclusive_owner_ = old_owner;
#endif
guard_.recursion_count_ = old_recursion_count;
+ return timed_out;
}
void Locks::Init() {
@@ -846,6 +852,7 @@
DCHECK(deoptimization_lock_ != nullptr);
DCHECK(heap_bitmap_lock_ != nullptr);
DCHECK(intern_table_lock_ != nullptr);
+ DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
DCHECK(profiler_lock_ != nullptr);
@@ -906,6 +913,10 @@
DCHECK(thread_list_lock_ == nullptr);
thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock);
+ DCHECK(jni_libraries_lock_ == nullptr);
+ jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock);
DCHECK(breakpoint_lock_ == nullptr);
breakpoint_lock_ = new ReaderWriterMutex("breakpoint lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 1847f6b..516fa07 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -79,7 +79,6 @@
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
kPinTableLock,
- kLoadLibraryLock,
kJdwpObjectRegistryLock,
kModifyLdtLock,
kAllocatedThreadIdsLock,
@@ -88,6 +87,7 @@
kBreakpointLock,
kMonitorLock,
kMonitorListLock,
+ kJniLoadLibraryLock,
kThreadListLock,
kBreakpointInvokeLock,
kAllocTrackerLock,
@@ -388,7 +388,7 @@
// TODO: No thread safety analysis on Wait and TimedWait as they call mutex operations via their
// pointer copy, thereby defeating annotalysis.
void Wait(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
- void TimedWait(Thread* self, int64_t ms, int32_t ns) NO_THREAD_SAFETY_ANALYSIS;
+ bool TimedWait(Thread* self, int64_t ms, int32_t ns) NO_THREAD_SAFETY_ANALYSIS;
// Variant of Wait that should be used with caution. Doesn't validate that no mutexes are held
// when waiting.
// TODO: remove this.
@@ -579,8 +579,11 @@
// attaching and detaching.
static Mutex* thread_list_lock_ ACQUIRED_AFTER(deoptimization_lock_);
+ // Guards maintaining loading library data structures.
+ static Mutex* jni_libraries_lock_ ACQUIRED_AFTER(thread_list_lock_);
+
// Guards breakpoints.
- static ReaderWriterMutex* breakpoint_lock_ ACQUIRED_AFTER(trace_lock_);
+ static ReaderWriterMutex* breakpoint_lock_ ACQUIRED_AFTER(jni_libraries_lock_);
// Guards lists of classes within the class linker.
static ReaderWriterMutex* classlinker_classes_lock_ ACQUIRED_AFTER(breakpoint_lock_);
diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc
index 47140e3..824ee48 100644
--- a/runtime/base/stringpiece.cc
+++ b/runtime/base/stringpiece.cc
@@ -19,26 +19,34 @@
#include <iostream>
#include <utility>
+#include "logging.h"
+
namespace art {
+#if !defined(NDEBUG)
+char StringPiece::operator[](size_type i) const {
+ CHECK_LT(i, length_);
+ return ptr_[i];
+}
+#endif
+
void StringPiece::CopyToString(std::string* target) const {
target->assign(ptr_, length_);
}
-int StringPiece::copy(char* buf, size_type n, size_type pos) const {
- int ret = std::min(length_ - pos, n);
+StringPiece::size_type StringPiece::copy(char* buf, size_type n, size_type pos) const {
+ size_type ret = std::min(length_ - pos, n);
memcpy(buf, ptr_ + pos, ret);
return ret;
}
StringPiece::size_type StringPiece::find(const StringPiece& s, size_type pos) const {
- if (length_ < 0 || pos > static_cast<size_type>(length_))
+ if (length_ == 0 || pos > static_cast<size_type>(length_)) {
return npos;
-
- const char* result = std::search(ptr_ + pos, ptr_ + length_,
- s.ptr_, s.ptr_ + s.length_);
+ }
+ const char* result = std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_);
const size_type xpos = result - ptr_;
- return xpos + s.length_ <= static_cast<size_type>(length_) ? xpos : npos;
+ return xpos + s.length_ <= length_ ? xpos : npos;
}
int StringPiece::compare(const StringPiece& x) const {
@@ -51,7 +59,7 @@
}
StringPiece::size_type StringPiece::find(char c, size_type pos) const {
- if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
+ if (length_ == 0 || pos >= length_) {
return npos;
}
const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
@@ -69,7 +77,7 @@
}
StringPiece::size_type StringPiece::rfind(char c, size_type pos) const {
- if (length_ <= 0) return npos;
+ if (length_ == 0) return npos;
for (int i = std::min(pos, static_cast<size_type>(length_ - 1));
i >= 0; --i) {
if (ptr_[i] == c) {
@@ -85,8 +93,6 @@
return StringPiece(ptr_ + pos, n);
}
-const StringPiece::size_type StringPiece::npos = size_type(-1);
-
std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
o.write(piece.data(), piece.size());
return o;
diff --git a/runtime/base/stringpiece.h b/runtime/base/stringpiece.h
index 91b83f6..b8de308 100644
--- a/runtime/base/stringpiece.h
+++ b/runtime/base/stringpiece.h
@@ -14,6 +14,14 @@
* limitations under the License.
*/
+#ifndef ART_RUNTIME_BASE_STRINGPIECE_H_
+#define ART_RUNTIME_BASE_STRINGPIECE_H_
+
+#include <string.h>
+#include <string>
+
+namespace art {
+
// A string-like object that points to a sized piece of memory.
//
// Functions or methods may use const StringPiece& parameters to accept either
@@ -21,74 +29,75 @@
// a StringPiece. The implicit conversion means that it is often appropriate
// to include this .h file in other files rather than forward-declaring
// StringPiece as would be appropriate for most other Google classes.
-//
-// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
-// conversions from "const char*" to "string" and back again.
-
-#ifndef ART_RUNTIME_BASE_STRINGPIECE_H_
-#define ART_RUNTIME_BASE_STRINGPIECE_H_
-
-#include <string.h>
-#include <algorithm>
-#include <cstddef>
-#include <iosfwd>
-#include <string>
-
-namespace art {
-
class StringPiece {
- private:
- const char* ptr_;
- int length_;
-
public:
+ // standard STL container boilerplate
+ typedef char value_type;
+ typedef const char* pointer;
+ typedef const char& reference;
+ typedef const char& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ static constexpr size_type npos = size_type(-1);
+ typedef const char* const_iterator;
+ typedef const char* iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+
// We provide non-explicit singleton constructors so users can pass
// in a "const char*" or a "string" wherever a "StringPiece" is
// expected.
- StringPiece() : ptr_(NULL), length_(0) { }
- StringPiece(const char* str) // NOLINT
- : ptr_(str), length_((str == NULL) ? 0 : static_cast<int>(strlen(str))) { }
- StringPiece(const std::string& str) // NOLINT
- : ptr_(str.data()), length_(static_cast<int>(str.size())) { }
- StringPiece(const char* offset, int len) : ptr_(offset), length_(len) { }
+ StringPiece() : ptr_(nullptr), length_(0) { }
+ StringPiece(const char* str) // NOLINT implicit constructor desired
+ : ptr_(str), length_((str == nullptr) ? 0 : strlen(str)) { }
+ StringPiece(const std::string& str) // NOLINT implicit constructor desired
+ : ptr_(str.data()), length_(str.size()) { }
+ StringPiece(const char* offset, size_t len) : ptr_(offset), length_(len) { }
// data() may return a pointer to a buffer with embedded NULs, and the
// returned buffer may or may not be null terminated. Therefore it is
// typically a mistake to pass data() to a routine that expects a NUL
// terminated string.
const char* data() const { return ptr_; }
- int size() const { return length_; }
- int length() const { return length_; }
+ size_type size() const { return length_; }
+ size_type length() const { return length_; }
bool empty() const { return length_ == 0; }
void clear() {
- ptr_ = NULL;
+ ptr_ = nullptr;
length_ = 0;
}
- void set(const char* data, int len) {
+ void set(const char* data, size_type len) {
ptr_ = data;
length_ = len;
}
void set(const char* str) {
ptr_ = str;
- if (str != NULL)
- length_ = static_cast<int>(strlen(str));
- else
+ if (str != nullptr) {
+ length_ = strlen(str);
+ } else {
length_ = 0;
+ }
}
- void set(const void* data, int len) {
+ void set(const void* data, size_type len) {
ptr_ = reinterpret_cast<const char*>(data);
length_ = len;
}
- char operator[](int i) const { return ptr_[i]; }
+#if defined(NDEBUG)
+ char operator[](size_type i) const {
+ return ptr_[i];
+ }
+#else
+ char operator[](size_type i) const;
+#endif
- void remove_prefix(int n) {
+ void remove_prefix(size_type n) {
ptr_ += n;
length_ -= n;
}
- void remove_suffix(int n) {
+ void remove_suffix(size_type n) {
length_ -= n;
}
@@ -121,18 +130,6 @@
(memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
}
- // standard STL container boilerplate
- typedef char value_type;
- typedef const char* pointer;
- typedef const char& reference;
- typedef const char& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
- static const size_type npos;
- typedef const char* const_iterator;
- typedef const char* iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- typedef std::reverse_iterator<iterator> reverse_iterator;
iterator begin() const { return ptr_; }
iterator end() const { return ptr_ + length_; }
const_reverse_iterator rbegin() const {
@@ -141,11 +138,8 @@
const_reverse_iterator rend() const {
return const_reverse_iterator(ptr_);
}
- // STLS says return size_type, but Google says return int
- int max_size() const { return length_; }
- int capacity() const { return length_; }
- int copy(char* buf, size_type n, size_type pos = 0) const;
+ size_type copy(char* buf, size_type n, size_type pos = 0) const;
size_type find(const StringPiece& s, size_type pos = 0) const;
size_type find(char c, size_type pos = 0) const;
@@ -153,13 +147,19 @@
size_type rfind(char c, size_type pos = npos) const;
StringPiece substr(size_type pos, size_type n = npos) const;
+
+ private:
+ // Pointer to char data, not necessarily zero terminated.
+ const char* ptr_;
+ // Length of data.
+ size_type length_;
};
// This large function is defined inline so that in a fairly common case where
// one of the arguments is a literal, the compiler can elide a lot of the
// following comparisons.
inline bool operator==(const StringPiece& x, const StringPiece& y) {
- int len = x.size();
+ StringPiece::size_type len = x.size();
if (len != y.size()) {
return false;
}
@@ -169,7 +169,7 @@
if (p1 == p2) {
return true;
}
- if (len <= 0) {
+ if (len == 0) {
return true;
}
@@ -184,10 +184,22 @@
return memcmp(p1, p2, len) == 0;
}
+inline bool operator==(const StringPiece& x, const char* y) {
+ if (y == nullptr) {
+ return x.size() == 0;
+ } else {
+ return strncmp(x.data(), y, x.size()) == 0 && y[x.size()] == '\0';
+ }
+}
+
inline bool operator!=(const StringPiece& x, const StringPiece& y) {
return !(x == y);
}
+inline bool operator!=(const StringPiece& x, const char* y) {
+ return !(x == y);
+}
+
inline bool operator<(const StringPiece& x, const StringPiece& y) {
const int r = memcmp(x.data(), y.data(),
std::min(x.size(), y.size()));
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 9036e3d..bfe44a2 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -25,6 +25,7 @@
#include "dex_file-inl.h"
#include "field_helper.h"
#include "gc/space/space.h"
+#include "java_vm_ext.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
@@ -35,62 +36,16 @@
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
+#include "well_known_classes.h"
namespace art {
-static void JniAbort(const char* jni_function_name, const char* msg) {
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
- mirror::ArtMethod* current_method = self->GetCurrentMethod(nullptr);
-
- std::ostringstream os;
- os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
-
- if (jni_function_name != nullptr) {
- os << "\n in call to " << jni_function_name;
- }
- // TODO: is this useful given that we're about to dump the calling thread's stack?
- if (current_method != nullptr) {
- os << "\n from " << PrettyMethod(current_method);
- }
- os << "\n";
- self->Dump(os);
-
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- if (vm->check_jni_abort_hook != nullptr) {
- vm->check_jni_abort_hook(vm->check_jni_abort_hook_data, os.str());
- } else {
- // Ensure that we get a native stack trace for this thread.
- self->TransitionFromRunnableToSuspended(kNative);
- LOG(FATAL) << os.str();
- self->TransitionFromSuspendedToRunnable(); // Unreachable, keep annotalysis happy.
- }
-}
-
-static void JniAbortV(const char* jni_function_name, const char* fmt, va_list ap) {
- std::string msg;
- StringAppendV(&msg, fmt, ap);
- JniAbort(jni_function_name, msg.c_str());
-}
-
-void JniAbortF(const char* jni_function_name, const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- JniAbortV(jni_function_name, fmt, args);
- va_end(args);
-}
-
/*
* ===========================================================================
* JNI function helpers
* ===========================================================================
*/
-static bool IsHandleScopeLocalRef(JNIEnv* env, jobject localRef) {
- return GetIndirectRefKind(localRef) == kHandleScopeOrInvalid &&
- reinterpret_cast<JNIEnvExt*>(env)->self->HandleScopeContains(localRef);
-}
-
// Flags passed into ScopedCheck.
#define kFlag_Default 0x0000
@@ -109,134 +64,88 @@
#define kFlag_Invocation 0x8000 // Part of the invocation interface (JavaVM*).
#define kFlag_ForceTrace 0x80000000 // Add this to a JNI function's flags if you want to trace every call.
-
-static const char* gBuiltInPrefixes[] = {
- "Landroid/",
- "Lcom/android/",
- "Lcom/google/android/",
- "Ldalvik/",
- "Ljava/",
- "Ljavax/",
- "Llibcore/",
- "Lorg/apache/harmony/",
- nullptr
+/*
+ * Java primitive types:
+ * B - jbyte
+ * C - jchar
+ * D - jdouble
+ * F - jfloat
+ * I - jint
+ * J - jlong
+ * S - jshort
+ * Z - jboolean (shown as true and false)
+ * V - void
+ *
+ * Java reference types:
+ * L - jobject
+ * a - jarray
+ * c - jclass
+ * s - jstring
+ * t - jthrowable
+ *
+ * JNI types:
+ * b - jboolean (shown as JNI_TRUE and JNI_FALSE)
+ * f - jfieldID
+ * i - JNI error value (JNI_OK, JNI_ERR, JNI_EDETACHED, JNI_EVERSION)
+ * m - jmethodID
+ * p - void*
+ * r - jint (for release mode arguments)
+ * u - const char* (Modified UTF-8)
+ * z - jsize (for lengths; use i if negative values are okay)
+ * v - JavaVM*
+ * w - jobjectRefType
+ * E - JNIEnv*
+ * . - no argument; just print "..." (used for varargs JNI calls)
+ *
+ */
+union JniValueType {
+ jarray a;
+ jboolean b;
+ jclass c;
+ jfieldID f;
+ jint i;
+ jmethodID m;
+ const void* p; // Pointer.
+ jint r; // Release mode.
+ jstring s;
+ jthrowable t;
+ const char* u; // Modified UTF-8.
+ JavaVM* v;
+ jobjectRefType w;
+ jsize z;
+ jbyte B;
+ jchar C;
+ jdouble D;
+ JNIEnv* E;
+ jfloat F;
+ jint I;
+ jlong J;
+ jobject L;
+ jshort S;
+ const void* V; // void
+ jboolean Z;
};
-static bool ShouldTrace(JavaVMExt* vm, mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
- // when a native method that matches the -Xjnitrace argument calls a JNI function
- // such as NewByteArray.
- // If -verbose:third-party-jni is on, we want to log any JNI function calls
- // made by a third-party native method.
- std::string class_name(method->GetDeclaringClassDescriptor());
- if (!vm->trace.empty() && class_name.find(vm->trace) != std::string::npos) {
- return true;
- }
- if (VLOG_IS_ON(third_party_jni)) {
- // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
- // like part of Android.
- for (size_t i = 0; gBuiltInPrefixes[i] != nullptr; ++i) {
- if (StartsWith(class_name, gBuiltInPrefixes[i])) {
- return false;
- }
- }
- return true;
- }
- return false;
-}
-
class ScopedCheck {
public:
- // For JNIEnv* functions.
- explicit ScopedCheck(JNIEnv* env, int flags, const char* functionName)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
- : soa_(env) {
- Init(flags, functionName, true);
- CheckThread(flags);
+ explicit ScopedCheck(int flags, const char* functionName, bool has_method = true)
+ : function_name_(functionName), flags_(flags), indent_(0), has_method_(has_method) {
}
- // For JavaVM* functions.
- // TODO: it's not correct that this is a lock function, but making it so aids annotalysis.
- explicit ScopedCheck(JavaVM* vm, bool has_method, const char* functionName)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
- : soa_(vm) {
- Init(kFlag_Invocation, functionName, has_method);
- }
-
- ~ScopedCheck() UNLOCK_FUNCTION(Locks::mutator_lock_) {}
-
- const ScopedObjectAccess& soa() {
- return soa_;
- }
-
- bool ForceCopy() {
- return Runtime::Current()->GetJavaVM()->force_copy;
- }
+ ~ScopedCheck() {}
// Checks that 'class_name' is a valid "fully-qualified" JNI class name, like "java/lang/Thread"
// or "[Ljava/lang/Object;". A ClassLoader can actually normalize class names a couple of
// times, so using "java.lang.Thread" instead of "java/lang/Thread" might work in some
// circumstances, but this is incorrect.
- void CheckClassName(const char* class_name) {
+ bool CheckClassName(const char* class_name) {
if ((class_name == nullptr) || !IsValidJniClassName(class_name)) {
- JniAbortF(function_name_,
- "illegal class name '%s'\n"
- " (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')",
- class_name);
+ AbortF("illegal class name '%s'\n"
+ " (should be of the form 'package/Class', [Lpackage/Class;' or '[[B')",
+ class_name);
+ return false;
}
- }
-
- /*
- * Verify that the field is of the appropriate type. If the field has an
- * object type, "java_object" is the object we're trying to assign into it.
- *
- * Works for both static and instance fields.
- */
- void CheckFieldType(jvalue value, jfieldID fid, char prim, bool isStatic)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::ArtField> f(hs.NewHandle(CheckFieldID(fid)));
- if (f.Get() == nullptr) {
- return;
- }
- mirror::Class* field_type = FieldHelper(f).GetType();
- if (!field_type->IsPrimitive()) {
- jobject java_object = value.l;
- if (java_object != nullptr) {
- mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object);
- // If java_object is a weak global ref whose referent has been cleared,
- // obj will be NULL. Otherwise, obj should always be non-NULL
- // and valid.
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
- Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "field operation on invalid %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
- return;
- } else {
- if (!obj->InstanceOf(field_type)) {
- JniAbortF(function_name_, "attempt to set field %s with value of wrong type: %s",
- PrettyField(f.Get()).c_str(), PrettyTypeOf(obj).c_str());
- return;
- }
- }
- }
- } else if (field_type != Runtime::Current()->GetClassLinker()->FindPrimitiveClass(prim)) {
- JniAbortF(function_name_, "attempt to set field %s with value of wrong type: %c",
- PrettyField(f.Get()).c_str(), prim);
- return;
- }
-
- if (isStatic != f.Get()->IsStatic()) {
- if (isStatic) {
- JniAbortF(function_name_, "accessing non-static field %s as static",
- PrettyField(f.Get()).c_str());
- } else {
- JniAbortF(function_name_, "accessing static field %s as non-static",
- PrettyField(f.Get()).c_str());
- }
- return;
- }
+ return true;
}
/*
@@ -244,59 +153,90 @@
*
* Assumes "jobj" has already been validated.
*/
- void CheckInstanceFieldID(jobject java_object, jfieldID fid)
+ bool CheckInstanceFieldID(ScopedObjectAccess& soa, jobject java_object, jfieldID fid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
- if (o == nullptr || !Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+ mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
+ if (o == nullptr) {
+ AbortF("field operation on NULL object: %p", java_object);
+ return false;
+ }
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "field operation on invalid %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
- return;
+ AbortF("field operation on invalid %s: %p",
+ ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+ java_object);
+ return false;
}
- mirror::ArtField* f = CheckFieldID(fid);
+ mirror::ArtField* f = CheckFieldID(soa, fid);
if (f == nullptr) {
- return;
+ return false;
}
mirror::Class* c = o->GetClass();
if (c->FindInstanceField(f->GetName(), f->GetTypeDescriptor()) == nullptr) {
- JniAbortF(function_name_, "jfieldID %s not valid for an object of class %s",
- PrettyField(f).c_str(), PrettyTypeOf(o).c_str());
+ AbortF("jfieldID %s not valid for an object of class %s",
+ PrettyField(f).c_str(), PrettyTypeOf(o).c_str());
+ return false;
}
+ return true;
}
/*
* Verify that the pointer value is non-NULL.
*/
- void CheckNonNull(const void* ptr) {
- if (ptr == nullptr) {
- JniAbortF(function_name_, "non-nullable argument was NULL");
+ bool CheckNonNull(const void* ptr) {
+ if (UNLIKELY(ptr == nullptr)) {
+ AbortF("non-nullable argument was NULL");
+ return false;
}
+ return true;
}
/*
* Verify that the method's return type matches the type of call.
* 'expectedType' will be "L" for all objects, including arrays.
*/
- void CheckSig(jmethodID mid, const char* expectedType, bool isStatic)
+ bool CheckMethodAndSig(ScopedObjectAccess& soa, jobject jobj, jclass jc,
+ jmethodID mid, Primitive::Type type, InvokeType invoke)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = CheckMethodID(mid);
+ mirror::ArtMethod* m = CheckMethodID(soa, mid);
if (m == nullptr) {
- return;
+ return false;
}
- if (*expectedType != m->GetShorty()[0]) {
- JniAbortF(function_name_, "the return type of %s does not match %s",
- function_name_, PrettyMethod(m).c_str());
+ if (type != Primitive::GetType(m->GetShorty()[0])) {
+ AbortF("the return type of %s does not match %s", function_name_, PrettyMethod(m).c_str());
+ return false;
}
- if (isStatic != m->IsStatic()) {
- if (isStatic) {
- JniAbortF(function_name_, "calling non-static method %s with %s",
- PrettyMethod(m).c_str(), function_name_);
+ bool is_static = (invoke == kStatic);
+ if (is_static != m->IsStatic()) {
+ if (is_static) {
+ AbortF("calling non-static method %s with %s",
+ PrettyMethod(m).c_str(), function_name_);
} else {
- JniAbortF(function_name_, "calling static method %s with %s",
- PrettyMethod(m).c_str(), function_name_);
+ AbortF("calling static method %s with %s",
+ PrettyMethod(m).c_str(), function_name_);
+ }
+ return false;
+ }
+ if (invoke != kVirtual) {
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
+ AbortF("can't call %s %s with class %s", invoke == kStatic ? "static" : "nonvirtual",
+ PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+ return false;
}
}
+ if (invoke != kStatic) {
+ mirror::Object* o = soa.Decode<mirror::Object*>(jobj);
+ if (o == nullptr) {
+ AbortF("can't call %s on null object", PrettyMethod(m).c_str());
+ return false;
+ } else if (!o->InstanceOf(m->GetDeclaringClass())) {
+ AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+ return false;
+ }
+ }
+ return true;
}
/*
@@ -304,17 +244,18 @@
*
* Assumes "java_class" has already been validated.
*/
- void CheckStaticFieldID(jclass java_class, jfieldID fid)
+ bool CheckStaticFieldID(ScopedObjectAccess& soa, jclass java_class, jfieldID fid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
- mirror::ArtField* f = CheckFieldID(fid);
+ mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
+ mirror::ArtField* f = CheckFieldID(soa, fid);
if (f == nullptr) {
- return;
+ return false;
}
if (f->GetDeclaringClass() != c) {
- JniAbortF(function_name_, "static jfieldID %p not valid for class %s",
- fid, PrettyClass(c).c_str());
+ AbortF("static jfieldID %p not valid for class %s", fid, PrettyClass(c).c_str());
+ return false;
}
+ return true;
}
/*
@@ -326,17 +267,18 @@
*
* Instances of "java_class" must be instances of the method's declaring class.
*/
- void CheckStaticMethod(jclass java_class, jmethodID mid)
+ bool CheckStaticMethod(ScopedObjectAccess& soa, jclass java_class, jmethodID mid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = CheckMethodID(mid);
+ mirror::ArtMethod* m = CheckMethodID(soa, mid);
if (m == nullptr) {
- return;
+ return false;
}
- mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
+ mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
- JniAbortF(function_name_, "can't call static %s on class %s",
- PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+ AbortF("can't call static %s on class %s", PrettyMethod(m).c_str(), PrettyClass(c).c_str());
+ return false;
}
+ return true;
}
/*
@@ -346,19 +288,21 @@
* (Note the mid might point to a declaration in an interface; this
* will be handled automatically by the instanceof check.)
*/
- void CheckVirtualMethod(jobject java_object, jmethodID mid)
+ bool CheckVirtualMethod(ScopedObjectAccess& soa, jobject java_object, jmethodID mid)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = CheckMethodID(mid);
+ mirror::ArtMethod* m = CheckMethodID(soa, mid);
if (m == nullptr) {
- return;
+ return false;
}
- mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
+ mirror::Object* o = soa.Decode<mirror::Object*>(java_object);
if (o == nullptr) {
- JniAbortF(function_name_, "can't call %s on null object", PrettyMethod(m).c_str());
+ AbortF("can't call %s on null object", PrettyMethod(m).c_str());
+ return false;
} else if (!o->InstanceOf(m->GetDeclaringClass())) {
- JniAbortF(function_name_, "can't call %s on instance of %s",
- PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+ AbortF("can't call %s on instance of %s", PrettyMethod(m).c_str(), PrettyTypeOf(o).c_str());
+ return false;
}
+ return true;
}
/**
@@ -397,11 +341,10 @@
*
* Use the kFlag_NullableUtf flag where 'u' field(s) are nullable.
*/
- void Check(bool entry, const char* fmt0, ...) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- va_list ap;
-
+ bool Check(ScopedObjectAccess& soa, bool entry, const char* fmt, JniValueType* args)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* traceMethod = nullptr;
- if (has_method_ && (!soa_.Vm()->trace.empty() || VLOG_IS_ON(third_party_jni))) {
+ if (has_method_ && soa.Vm()->IsTracingEnabled()) {
// We need to guard some of the invocation interface's calls: a bad caller might
// use DetachCurrentThread or GetEnv on a thread that's not yet attached.
Thread* self = Thread::Current();
@@ -411,124 +354,14 @@
}
if (((flags_ & kFlag_ForceTrace) != 0) ||
- (traceMethod != nullptr && ShouldTrace(soa_.Vm(), traceMethod))) {
- va_start(ap, fmt0);
+ (traceMethod != nullptr && soa.Vm()->ShouldTrace(traceMethod))) {
std::string msg;
- for (const char* fmt = fmt0; *fmt;) {
- char ch = *fmt++;
- if (ch == 'B') { // jbyte
- jbyte b = va_arg(ap, int);
- if (b >= 0 && b < 10) {
- StringAppendF(&msg, "%d", b);
- } else {
- StringAppendF(&msg, "%#x (%d)", b, b);
- }
- } else if (ch == 'C') { // jchar
- jchar c = va_arg(ap, int);
- if (c < 0x7f && c >= ' ') {
- StringAppendF(&msg, "U+%x ('%c')", c, c);
- } else {
- StringAppendF(&msg, "U+%x", c);
- }
- } else if (ch == 'F' || ch == 'D') { // jfloat, jdouble
- StringAppendF(&msg, "%g", va_arg(ap, double));
- } else if (ch == 'I' || ch == 'S') { // jint, jshort
- StringAppendF(&msg, "%d", va_arg(ap, int));
- } else if (ch == 'J') { // jlong
- StringAppendF(&msg, "%" PRId64, va_arg(ap, jlong));
- } else if (ch == 'Z') { // jboolean
- StringAppendF(&msg, "%s", va_arg(ap, int) ? "true" : "false");
- } else if (ch == 'V') { // void
- msg += "void";
- } else if (ch == 'v') { // JavaVM*
- JavaVM* vm = va_arg(ap, JavaVM*);
- StringAppendF(&msg, "(JavaVM*)%p", vm);
- } else if (ch == 'E') { // JNIEnv*
- JNIEnv* env = va_arg(ap, JNIEnv*);
- StringAppendF(&msg, "(JNIEnv*)%p", env);
- } else if (ch == 'L' || ch == 'a' || ch == 's') { // jobject, jarray, jstring
- // For logging purposes, these are identical.
- jobject o = va_arg(ap, jobject);
- if (o == nullptr) {
- msg += "NULL";
- } else {
- StringAppendF(&msg, "%p", o);
- }
- } else if (ch == 'b') { // jboolean (JNI-style)
- jboolean b = va_arg(ap, int);
- msg += (b ? "JNI_TRUE" : "JNI_FALSE");
- } else if (ch == 'c') { // jclass
- jclass jc = va_arg(ap, jclass);
- mirror::Class* c = reinterpret_cast<mirror::Class*>(Thread::Current()->DecodeJObject(jc));
- if (c == nullptr) {
- msg += "NULL";
- } else if (c == kInvalidIndirectRefObject ||
- !Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
- StringAppendF(&msg, "INVALID POINTER:%p", jc);
- } else if (!c->IsClass()) {
- msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
- } else {
- msg += PrettyClass(c);
- if (!entry) {
- StringAppendF(&msg, " (%p)", jc);
- }
- }
- } else if (ch == 'f') { // jfieldID
- jfieldID fid = va_arg(ap, jfieldID);
- mirror::ArtField* f = reinterpret_cast<mirror::ArtField*>(fid);
- msg += PrettyField(f);
- if (!entry) {
- StringAppendF(&msg, " (%p)", fid);
- }
- } else if (ch == 'z') { // non-negative jsize
- // You might expect jsize to be size_t, but it's not; it's the same as jint.
- // We only treat this specially so we can do the non-negative check.
- // TODO: maybe this wasn't worth it?
- jint i = va_arg(ap, jint);
- StringAppendF(&msg, "%d", i);
- } else if (ch == 'm') { // jmethodID
- jmethodID mid = va_arg(ap, jmethodID);
- mirror::ArtMethod* m = reinterpret_cast<mirror::ArtMethod*>(mid);
- msg += PrettyMethod(m);
- if (!entry) {
- StringAppendF(&msg, " (%p)", mid);
- }
- } else if (ch == 'p') { // void* ("pointer")
- void* p = va_arg(ap, void*);
- if (p == nullptr) {
- msg += "NULL";
- } else {
- StringAppendF(&msg, "(void*) %p", p);
- }
- } else if (ch == 'r') { // jint (release mode)
- jint releaseMode = va_arg(ap, jint);
- if (releaseMode == 0) {
- msg += "0";
- } else if (releaseMode == JNI_ABORT) {
- msg += "JNI_ABORT";
- } else if (releaseMode == JNI_COMMIT) {
- msg += "JNI_COMMIT";
- } else {
- StringAppendF(&msg, "invalid release mode %d", releaseMode);
- }
- } else if (ch == 'u') { // const char* (Modified UTF-8)
- const char* utf = va_arg(ap, const char*);
- if (utf == nullptr) {
- msg += "NULL";
- } else {
- StringAppendF(&msg, "\"%s\"", utf);
- }
- } else if (ch == '.') {
- msg += "...";
- } else {
- JniAbortF(function_name_, "unknown trace format specifier: %c", ch);
- return;
- }
- if (*fmt) {
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ TracePossibleHeapValue(soa, entry, fmt[i], args[i], &msg);
+ if (fmt[i + 1] != '\0') {
StringAppendF(&msg, ", ");
}
}
- va_end(ap);
if ((flags_ & kFlag_ForceTrace) != 0) {
LOG(INFO) << "JNI: call to " << function_name_ << "(" << msg << ")";
@@ -548,43 +381,227 @@
// We always do the thorough checks on entry, and never on exit...
if (entry) {
- va_start(ap, fmt0);
- for (const char* fmt = fmt0; *fmt; ++fmt) {
- char ch = *fmt;
- if (ch == 'a') {
- CheckArray(va_arg(ap, jarray));
- } else if (ch == 'c') {
- CheckInstance(kClass, va_arg(ap, jclass));
- } else if (ch == 'L') {
- CheckObject(va_arg(ap, jobject));
- } else if (ch == 'r') {
- CheckReleaseMode(va_arg(ap, jint));
- } else if (ch == 's') {
- CheckInstance(kString, va_arg(ap, jstring));
- } else if (ch == 'u') {
- if ((flags_ & kFlag_Release) != 0) {
- CheckNonNull(va_arg(ap, const char*));
- } else {
- bool nullable = ((flags_ & kFlag_NullableUtf) != 0);
- CheckUtfString(va_arg(ap, const char*), nullable);
- }
- } else if (ch == 'z') {
- CheckLengthPositive(va_arg(ap, jsize));
- } else if (strchr("BCISZbfmpEv", ch) != nullptr) {
- va_arg(ap, uint32_t); // Skip this argument.
- } else if (ch == 'D' || ch == 'F') {
- va_arg(ap, double); // Skip this argument.
- } else if (ch == 'J') {
- va_arg(ap, uint64_t); // Skip this argument.
- } else if (ch == '.') {
- } else {
- LOG(FATAL) << "Unknown check format specifier: " << ch;
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ if (!CheckPossibleHeapValue(soa, fmt[i], args[i])) {
+ return false;
}
}
- va_end(ap);
}
+ return true;
}
+ bool CheckNonHeap(JavaVMExt* vm, bool entry, const char* fmt, JniValueType* args) {
+ bool should_trace = (flags_ & kFlag_ForceTrace) != 0;
+ if (!should_trace && vm->IsTracingEnabled()) {
+ // We need to guard some of the invocation interface's calls: a bad caller might
+ // use DetachCurrentThread or GetEnv on a thread that's not yet attached.
+ Thread* self = Thread::Current();
+ if ((flags_ & kFlag_Invocation) == 0 || self != nullptr) {
+ ScopedObjectAccess soa(self);
+ mirror::ArtMethod* traceMethod = self->GetCurrentMethod(nullptr);
+ should_trace = (traceMethod != nullptr && vm->ShouldTrace(traceMethod));
+ }
+ }
+ if (should_trace) {
+ std::string msg;
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ TraceNonHeapValue(fmt[i], args[i], &msg);
+ if (fmt[i + 1] != '\0') {
+ StringAppendF(&msg, ", ");
+ }
+ }
+
+ if ((flags_ & kFlag_ForceTrace) != 0) {
+ LOG(INFO) << "JNI: call to " << function_name_ << "(" << msg << ")";
+ } else if (entry) {
+ if (has_method_) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ mirror::ArtMethod* traceMethod = self->GetCurrentMethod(nullptr);
+ std::string methodName(PrettyMethod(traceMethod, false));
+ LOG(INFO) << "JNI: " << methodName << " -> " << function_name_ << "(" << msg << ")";
+ indent_ = methodName.size() + 1;
+ } else {
+ LOG(INFO) << "JNI: -> " << function_name_ << "(" << msg << ")";
+ indent_ = 0;
+ }
+ } else {
+ LOG(INFO) << StringPrintf("JNI: %*s<- %s returned %s", indent_, "", function_name_, msg.c_str());
+ }
+ }
+
+ // We always do the thorough checks on entry, and never on exit...
+ if (entry) {
+ for (size_t i = 0; fmt[i] != '\0'; ++i) {
+ if (!CheckNonHeapValue(fmt[i], args[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ bool CheckReflectedMethod(ScopedObjectAccess& soa, jobject jmethod)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* method = soa.Decode<mirror::Object*>(jmethod);
+ if (method == nullptr) {
+ AbortF("expected non-null method");
+ return false;
+ }
+ mirror::Class* c = method->GetClass();
+ if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Method) != c &&
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Constructor) != c) {
+ AbortF("expected java.lang.reflect.Method or "
+ "java.lang.reflect.Constructor but got object of type %s: %p",
+ PrettyTypeOf(method).c_str(), jmethod);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckConstructor(ScopedObjectAccess& soa, jmethodID mid)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = soa.DecodeMethod(mid);
+ if (method == nullptr) {
+ AbortF("expected non-null constructor");
+ return false;
+ }
+ if (!method->IsConstructor() || method->IsStatic()) {
+ AbortF("expected a constructor but %s: %p", PrettyTypeOf(method).c_str(), mid);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckReflectedField(ScopedObjectAccess& soa, jobject jfield)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* field = soa.Decode<mirror::Object*>(jfield);
+ if (field == nullptr) {
+ AbortF("expected non-null java.lang.reflect.Field");
+ return false;
+ }
+ mirror::Class* c = field->GetClass();
+ if (soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Field) != c) {
+ AbortF("expected java.lang.reflect.Field but got object of type %s: %p",
+ PrettyTypeOf(field).c_str(), jfield);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckThrowable(ScopedObjectAccess& soa, jthrowable jobj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* obj = soa.Decode<mirror::Object*>(jobj);
+ if (!obj->GetClass()->IsThrowableClass()) {
+ AbortF("expected java.lang.Throwable but got object of type "
+ "%s: %p", PrettyTypeOf(obj).c_str(), obj);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckThrowableClass(ScopedObjectAccess& soa, jclass jc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (!c->IsThrowableClass()) {
+ AbortF("expected java.lang.Throwable class but got object of "
+ "type %s: %p", PrettyDescriptor(c).c_str(), c);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckReferenceKind(IndirectRefKind expected_kind, JavaVMExt* vm, Thread* self, jobject obj) {
+ IndirectRefKind found_kind;
+ if (expected_kind == kLocal) {
+ found_kind = GetIndirectRefKind(obj);
+ if (found_kind == kHandleScopeOrInvalid && self->HandleScopeContains(obj)) {
+ found_kind = kLocal;
+ }
+ } else {
+ found_kind = GetIndirectRefKind(obj);
+ }
+ if (obj != nullptr && found_kind != expected_kind) {
+ AbortF("expected reference of kind %s but found %s: %p",
+ ToStr<IndirectRefKind>(expected_kind).c_str(),
+ ToStr<IndirectRefKind>(GetIndirectRefKind(obj)).c_str(),
+ obj);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckInstantiableNonArray(ScopedObjectAccess& soa, jclass jc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (!c->IsInstantiableNonArray()) {
+ AbortF("can't make objects of type %s: %p", PrettyDescriptor(c).c_str(), c);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckPrimitiveArrayType(ScopedObjectAccess& soa, jarray array, Primitive::Type type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (!CheckArray(soa, array)) {
+ return false;
+ }
+ mirror::Array* a = soa.Decode<mirror::Array*>(array);
+ if (a->GetClass()->GetComponentType()->GetPrimitiveType() != type) {
+ AbortF("incompatible array type %s expected %s[]: %p",
+ PrettyDescriptor(a->GetClass()).c_str(), PrettyDescriptor(type).c_str(), array);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckFieldAccess(ScopedObjectAccess& soa, jobject obj, jfieldID fid, bool is_static,
+ Primitive::Type type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (is_static && !CheckStaticFieldID(soa, down_cast<jclass>(obj), fid)) {
+ return false;
+ }
+ if (!is_static && !CheckInstanceFieldID(soa, obj, fid)) {
+ return false;
+ }
+ mirror::ArtField* field = soa.DecodeField(fid);
+ DCHECK(field != nullptr); // Already checked by Check.
+ if (is_static != field->IsStatic()) {
+ AbortF("attempt to access %s field %s: %p",
+ field->IsStatic() ? "static" : "non-static", PrettyField(field).c_str(), fid);
+ return false;
+ }
+ if (type != field->GetTypeAsPrimitiveType()) {
+ AbortF("attempt to access field %s of type %s with the wrong type %s: %p",
+ PrettyField(field).c_str(), PrettyDescriptor(field->GetTypeDescriptor()).c_str(),
+ PrettyDescriptor(type).c_str(), fid);
+ return false;
+ }
+ if (is_static) {
+ mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+ if (o == nullptr || !o->IsClass()) {
+ AbortF("attempt to access static field %s with a class argument of type %s: %p",
+ PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
+ return false;
+ }
+ mirror::Class* c = o->AsClass();
+ if (field->GetDeclaringClass() != c) {
+ AbortF("attempt to access static field %s with an incompatible class argument of %s: %p",
+ PrettyField(field).c_str(), PrettyDescriptor(c).c_str(), fid);
+ return false;
+ }
+ } else {
+ mirror::Object* o = soa.Decode<mirror::Object*>(obj);
+ if (o == nullptr || !field->GetDeclaringClass()->IsAssignableFrom(o->GetClass())) {
+ AbortF("attempt to access field %s from an object argument of type %s: %p",
+ PrettyField(field).c_str(), PrettyTypeOf(o).c_str(), fid);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
enum InstanceKind {
kClass,
kDirectByteBuffer,
@@ -600,7 +617,7 @@
* Because we're looking at an object on the GC heap, we have to switch
* to "running" mode before doing the checks.
*/
- bool CheckInstance(InstanceKind kind, jobject java_object)
+ bool CheckInstance(ScopedObjectAccess& soa, InstanceKind kind, jobject java_object, bool null_ok)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const char* what = nullptr;
switch (kind) {
@@ -624,15 +641,38 @@
}
if (java_object == nullptr) {
- JniAbortF(function_name_, "%s received null %s", function_name_, what);
- return false;
+ if (null_ok) {
+ return true;
+ } else {
+ AbortF("%s received NULL %s", function_name_, what);
+ return false;
+ }
}
- mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object);
+ mirror::Object* obj = soa.Decode<mirror::Object*>(java_object);
+ if (obj == nullptr) {
+ // Either java_object is invalid or is a cleared weak.
+ IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
+ bool okay;
+ if (GetIndirectRefKind(ref) != kWeakGlobal) {
+ okay = false;
+ } else {
+ obj = soa.Vm()->DecodeWeakGlobal(soa.Self(), ref);
+ okay = Runtime::Current()->IsClearedJniWeakGlobal(obj);
+ }
+ if (!okay) {
+ AbortF("%s is an invalid %s: %p (%p)",
+ what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+ java_object, obj);
+ return false;
+ }
+ }
+
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "%s is an invalid %s: %p (%p)",
- what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object, obj);
+ AbortF("%s is an invalid %s: %p (%p)",
+ what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
+ java_object, obj);
return false;
}
@@ -654,114 +694,332 @@
break;
}
if (!okay) {
- JniAbortF(function_name_, "%s has wrong type: %s", what, PrettyTypeOf(obj).c_str());
+ AbortF("%s has wrong type: %s", what, PrettyTypeOf(obj).c_str());
return false;
}
return true;
}
- private:
- // Set "has_method" to true if we have a valid thread with a method pointer.
- // We won't have one before attaching a thread, after detaching a thread, or
- // when shutting down the runtime.
- void Init(int flags, const char* functionName, bool has_method) {
- flags_ = flags;
- function_name_ = functionName;
- has_method_ = has_method;
+ /*
+ * Verify that the "mode" argument passed to a primitive array Release
+ * function is one of the valid values.
+ */
+ bool CheckReleaseMode(jint mode) {
+ if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
+ AbortF("unknown value for release mode: %d", mode);
+ return false;
+ }
+ return true;
}
+ bool CheckPossibleHeapValue(ScopedObjectAccess& soa, char fmt, JniValueType arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ switch (fmt) {
+ case 'a': // jarray
+ return CheckArray(soa, arg.a);
+ case 'c': // jclass
+ return CheckInstance(soa, kClass, arg.c, false);
+ case 'f': // jfieldID
+ return CheckFieldID(soa, arg.f) != nullptr;
+ case 'm': // jmethodID
+ return CheckMethodID(soa, arg.m) != nullptr;
+ case 'r': // release int
+ return CheckReleaseMode(arg.r);
+ case 's': // jstring
+ return CheckInstance(soa, kString, arg.s, false);
+ case 't': // jthrowable
+ return CheckInstance(soa, kThrowable, arg.t, false);
+ case 'E': // JNIEnv*
+ return CheckThread(arg.E);
+ case 'L': // jobject
+ return CheckInstance(soa, kObject, arg.L, true);
+ default:
+ return CheckNonHeapValue(fmt, arg);
+ }
+ }
+
+ bool CheckNonHeapValue(char fmt, JniValueType arg) {
+ switch (fmt) {
+ case '.': // ...
+ case 'p': // TODO: pointer - null or readable?
+ case 'v': // JavaVM*
+ case 'B': // jbyte
+ case 'C': // jchar
+ case 'D': // jdouble
+ case 'F': // jfloat
+ case 'I': // jint
+ case 'J': // jlong
+ case 'S': // jshort
+ break; // Ignored.
+ case 'b': // jboolean, why two? Fall-through.
+ case 'Z':
+ return CheckBoolean(arg.Z);
+ case 'u': // utf8
+ if ((flags_ & kFlag_Release) != 0) {
+ return CheckNonNull(arg.u);
+ } else {
+ bool nullable = ((flags_ & kFlag_NullableUtf) != 0);
+ return CheckUtfString(arg.u, nullable);
+ }
+ case 'w': // jobjectRefType
+ switch (arg.w) {
+ case JNIInvalidRefType:
+ case JNILocalRefType:
+ case JNIGlobalRefType:
+ case JNIWeakGlobalRefType:
+ break;
+ default:
+ AbortF("Unknown reference type");
+ return false;
+ }
+ break;
+ case 'z': // jsize
+ return CheckLengthPositive(arg.z);
+ default:
+ AbortF("unknown format specifier: '%c'", fmt);
+ return false;
+ }
+ return true;
+ }
+
+ void TracePossibleHeapValue(ScopedObjectAccess& soa, bool entry, char fmt, JniValueType arg,
+ std::string* msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ switch (fmt) {
+ case 'L': // jobject fall-through.
+ case 'a': // jarray fall-through.
+ case 's': // jstring fall-through.
+ case 't': // jthrowable fall-through.
+ if (arg.L == nullptr) {
+ *msg += "NULL";
+ } else {
+ StringAppendF(msg, "%p", arg.L);
+ }
+ break;
+ case 'c': { // jclass
+ jclass jc = arg.c;
+ mirror::Class* c = soa.Decode<mirror::Class*>(jc);
+ if (c == nullptr) {
+ *msg += "NULL";
+ } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+ StringAppendF(msg, "INVALID POINTER:%p", jc);
+ } else if (!c->IsClass()) {
+ *msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
+ } else {
+ *msg += PrettyClass(c);
+ if (!entry) {
+ StringAppendF(msg, " (%p)", jc);
+ }
+ }
+ break;
+ }
+ case 'f': { // jfieldID
+ jfieldID fid = arg.f;
+ mirror::ArtField* f = soa.DecodeField(fid);
+ *msg += PrettyField(f);
+ if (!entry) {
+ StringAppendF(msg, " (%p)", fid);
+ }
+ break;
+ }
+ case 'm': { // jmethodID
+ jmethodID mid = arg.m;
+ mirror::ArtMethod* m = soa.DecodeMethod(mid);
+ *msg += PrettyMethod(m);
+ if (!entry) {
+ StringAppendF(msg, " (%p)", mid);
+ }
+ break;
+ }
+ default:
+ TraceNonHeapValue(fmt, arg, msg);
+ break;
+ }
+ }
+
+ void TraceNonHeapValue(char fmt, JniValueType arg, std::string* msg) {
+ switch (fmt) {
+ case 'B': // jbyte
+ if (arg.B >= 0 && arg.B < 10) {
+ StringAppendF(msg, "%d", arg.B);
+ } else {
+ StringAppendF(msg, "%#x (%d)", arg.B, arg.B);
+ }
+ break;
+ case 'C': // jchar
+ if (arg.C < 0x7f && arg.C >= ' ') {
+ StringAppendF(msg, "U+%x ('%c')", arg.C, arg.C);
+ } else {
+ StringAppendF(msg, "U+%x", arg.C);
+ }
+ break;
+ case 'F': // jfloat
+ StringAppendF(msg, "%g", arg.F);
+ break;
+ case 'D': // jdouble
+ StringAppendF(msg, "%g", arg.D);
+ break;
+ case 'S': // jshort
+ StringAppendF(msg, "%d", arg.S);
+ break;
+ case 'i': // jint - fall-through.
+ case 'I': // jint
+ StringAppendF(msg, "%d", arg.I);
+ break;
+ case 'J': // jlong
+ StringAppendF(msg, "%" PRId64, arg.J);
+ break;
+ case 'Z': // jboolean
+ case 'b': // jboolean (JNI-style)
+ *msg += arg.b == JNI_TRUE ? "true" : "false";
+ break;
+ case 'V': // void
+ DCHECK(arg.V == nullptr);
+ *msg += "void";
+ break;
+ case 'v': // JavaVM*
+ StringAppendF(msg, "(JavaVM*)%p", arg.v);
+ break;
+ case 'E':
+ StringAppendF(msg, "(JNIEnv*)%p", arg.E);
+ break;
+ case 'z': // non-negative jsize
+ // You might expect jsize to be size_t, but it's not; it's the same as jint.
+ // We only treat this specially so we can do the non-negative check.
+ // TODO: maybe this wasn't worth it?
+ StringAppendF(msg, "%d", arg.z);
+ break;
+ case 'p': // void* ("pointer")
+ if (arg.p == nullptr) {
+ *msg += "NULL";
+ } else {
+ StringAppendF(msg, "(void*) %p", arg.p);
+ }
+ break;
+ case 'r': { // jint (release mode)
+ jint releaseMode = arg.r;
+ if (releaseMode == 0) {
+ *msg += "0";
+ } else if (releaseMode == JNI_ABORT) {
+ *msg += "JNI_ABORT";
+ } else if (releaseMode == JNI_COMMIT) {
+ *msg += "JNI_COMMIT";
+ } else {
+ StringAppendF(msg, "invalid release mode %d", releaseMode);
+ }
+ break;
+ }
+ case 'u': // const char* (Modified UTF-8)
+ if (arg.u == nullptr) {
+ *msg += "NULL";
+ } else {
+ StringAppendF(msg, "\"%s\"", arg.u);
+ }
+ break;
+ case 'w': // jobjectRefType
+ switch (arg.w) {
+ case JNIInvalidRefType:
+ *msg += "invalid reference type";
+ break;
+ case JNILocalRefType:
+ *msg += "local ref type";
+ break;
+ case JNIGlobalRefType:
+ *msg += "global ref type";
+ break;
+ case JNIWeakGlobalRefType:
+ *msg += "weak global ref type";
+ break;
+ default:
+ *msg += "unknown ref type";
+ break;
+ }
+ break;
+ case '.':
+ *msg += "...";
+ break;
+ default:
+ LOG(FATAL) << function_name_ << ": unknown trace format specifier: '" << fmt << "'";
+ }
+ }
/*
* Verify that "array" is non-NULL and points to an Array object.
*
* Since we're dealing with objects, switch to "running" mode.
*/
- void CheckArray(jarray java_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (java_array == nullptr) {
- JniAbortF(function_name_, "jarray was NULL");
- return;
+ bool CheckArray(ScopedObjectAccess& soa, jarray java_array)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (UNLIKELY(java_array == nullptr)) {
+ AbortF("jarray was NULL");
+ return false;
}
- mirror::Array* a = soa_.Decode<mirror::Array*>(java_array);
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(a)) {
+ mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+ if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "jarray is an invalid %s: %p (%p)",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(), java_array, a);
+ AbortF("jarray is an invalid %s: %p (%p)",
+ ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(),
+ java_array, a);
+ return false;
} else if (!a->IsArrayInstance()) {
- JniAbortF(function_name_, "jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
+ AbortF("jarray argument has non-array type: %s", PrettyTypeOf(a).c_str());
+ return false;
}
+ return true;
}
- void CheckLengthPositive(jsize length) {
+ bool CheckBoolean(jboolean z) {
+ if (z != JNI_TRUE && z != JNI_FALSE) {
+ AbortF("unexpected jboolean value: %d", z);
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckLengthPositive(jsize length) {
if (length < 0) {
- JniAbortF(function_name_, "negative jsize: %d", length);
+ AbortF("negative jsize: %d", length);
+ return false;
}
+ return true;
}
- mirror::ArtField* CheckFieldID(jfieldID fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* CheckFieldID(ScopedObjectAccess& soa, jfieldID fid)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (fid == nullptr) {
- JniAbortF(function_name_, "jfieldID was NULL");
+ AbortF("jfieldID was NULL");
return nullptr;
}
- mirror::ArtField* f = soa_.DecodeField(fid);
+ mirror::ArtField* f = soa.DecodeField(fid);
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f) || !f->IsArtField()) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "invalid jfieldID: %p", fid);
+ AbortF("invalid jfieldID: %p", fid);
return nullptr;
}
return f;
}
- mirror::ArtMethod* CheckMethodID(jmethodID mid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* CheckMethodID(ScopedObjectAccess& soa, jmethodID mid)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (mid == nullptr) {
- JniAbortF(function_name_, "jmethodID was NULL");
+ AbortF("jmethodID was NULL");
return nullptr;
}
- mirror::ArtMethod* m = soa_.DecodeMethod(mid);
+ mirror::ArtMethod* m = soa.DecodeMethod(mid);
if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m) || !m->IsArtMethod()) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- JniAbortF(function_name_, "invalid jmethodID: %p", mid);
+ AbortF("invalid jmethodID: %p", mid);
return nullptr;
}
return m;
}
- /*
- * Verify that "jobj" is a valid object, and that it's an object that JNI
- * is allowed to know about. We allow NULL references.
- *
- * Switches to "running" mode before performing checks.
- */
- void CheckObject(jobject java_object)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (java_object == nullptr) {
- return;
- }
-
- mirror::Object* o = soa_.Decode<mirror::Object*>(java_object);
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
- Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
- // TODO: when we remove work_around_app_jni_bugs, this should be impossible.
- JniAbortF(function_name_, "native code passing in reference to invalid %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object);
- }
- }
-
- /*
- * Verify that the "mode" argument passed to a primitive array Release
- * function is one of the valid values.
- */
- void CheckReleaseMode(jint mode) {
- if (mode != 0 && mode != JNI_COMMIT && mode != JNI_ABORT) {
- JniAbortF(function_name_, "unknown value for release mode: %d", mode);
- }
- }
-
- void CheckThread(int flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool CheckThread(JNIEnv* env) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Thread* self = Thread::Current();
if (self == nullptr) {
- JniAbortF(function_name_, "a thread (tid %d) is making JNI calls without being attached", GetTid());
- return;
+ AbortF("a thread (tid %d) is making JNI calls without being attached", GetTid());
+ return false;
}
// Get the *correct* JNIEnv by going through our TLS pointer.
@@ -769,21 +1027,22 @@
// Verify that the current thread is (a) attached and (b) associated with
// this particular instance of JNIEnv.
- if (soa_.Env() != threadEnv) {
- JniAbortF(function_name_, "thread %s using JNIEnv* from thread %s",
- ToStr<Thread>(*self).c_str(), ToStr<Thread>(*soa_.Self()).c_str());
- return;
+ if (env != threadEnv) {
+ AbortF("thread %s using JNIEnv* from thread %s",
+ ToStr<Thread>(*self).c_str(), ToStr<Thread>(*self).c_str());
+ return false;
}
// Verify that, if this thread previously made a critical "get" call, we
// do the corresponding "release" call before we try anything else.
- switch (flags & kFlag_CritMask) {
+ switch (flags_ & kFlag_CritMask) {
case kFlag_CritOkay: // okay to call this method
break;
case kFlag_CritBad: // not okay to call
if (threadEnv->critical) {
- JniAbortF(function_name_, "thread %s using JNI after critical get", ToStr<Thread>(*self).c_str());
- return;
+ AbortF("thread %s using JNI after critical get",
+ ToStr<Thread>(*self).c_str());
+ return false;
}
break;
case kFlag_CritGet: // this is a "get" call
@@ -793,44 +1052,46 @@
case kFlag_CritRelease: // this is a "release" call
threadEnv->critical--;
if (threadEnv->critical < 0) {
- JniAbortF(function_name_, "thread %s called too many critical releases", ToStr<Thread>(*self).c_str());
- return;
+ AbortF("thread %s called too many critical releases",
+ ToStr<Thread>(*self).c_str());
+ return false;
}
break;
default:
- LOG(FATAL) << "Bad flags (internal error): " << flags;
+ LOG(FATAL) << "Bad flags (internal error): " << flags_;
}
// Verify that, if an exception has been raised, the native code doesn't
// make any JNI calls other than the Exception* methods.
- if ((flags & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
+ if ((flags_ & kFlag_ExcepOkay) == 0 && self->IsExceptionPending()) {
ThrowLocation throw_location;
mirror::Throwable* exception = self->GetException(&throw_location);
std::string type(PrettyTypeOf(exception));
- JniAbortF(function_name_, "JNI %s called with pending exception '%s' thrown in %s",
- function_name_, type.c_str(), throw_location.Dump().c_str());
- return;
+ AbortF("JNI %s called with pending exception '%s' thrown in %s",
+ function_name_, type.c_str(), throw_location.Dump().c_str());
+ return false;
}
+ return true;
}
// Verifies that "bytes" points to valid Modified UTF-8 data.
- void CheckUtfString(const char* bytes, bool nullable) {
+ bool CheckUtfString(const char* bytes, bool nullable) {
if (bytes == nullptr) {
if (!nullable) {
- JniAbortF(function_name_, "non-nullable const char* was NULL");
- return;
+ AbortF("non-nullable const char* was NULL");
+ return false;
}
- return;
+ return true;
}
const char* errorKind = nullptr;
uint8_t utf8 = CheckUtfBytes(bytes, &errorKind);
if (errorKind != nullptr) {
- JniAbortF(function_name_,
- "input is not valid Modified UTF-8: illegal %s byte %#x\n"
- " string: '%s'", errorKind, utf8, bytes);
- return;
+ AbortF("input is not valid Modified UTF-8: illegal %s byte %#x\n"
+ " string: '%s'", errorKind, utf8, bytes);
+ return false;
}
+ return true;
}
static uint8_t CheckUtfBytes(const char* bytes, const char** errorKind) {
@@ -882,92 +1143,120 @@
return 0;
}
- const ScopedObjectAccess soa_;
- const char* function_name_;
- int flags_;
- bool has_method_;
+ void AbortF(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+ va_list args;
+ va_start(args, fmt);
+ Runtime::Current()->GetJavaVM()->JniAbortV(function_name_, fmt, args);
+ va_end(args);
+ }
+
+ // The name of the JNI function being checked.
+ const char* const function_name_;
+
+ const int flags_;
int indent_;
+ const bool has_method_;
+
DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
};
-#define CHECK_JNI_ENTRY(flags, types, args...) \
- ScopedCheck sc(env, flags, __FUNCTION__); \
- sc.Check(true, types, ##args)
-
-#define CHECK_JNI_EXIT(type, exp) ({ \
- auto _rc = (exp); \
- sc.Check(false, type, _rc); \
- _rc; })
-#define CHECK_JNI_EXIT_VOID() \
- sc.Check(false, "V")
-
/*
* ===========================================================================
* Guarded arrays
* ===========================================================================
*/
-#define kGuardLen 512 /* must be multiple of 2 */
-#define kGuardPattern 0xd5e3 /* uncommon values; d5e3d5e3 invalid addr */
-#define kGuardMagic 0xffd5aa96
-
/* this gets tucked in at the start of the buffer; struct size must be even */
-struct GuardedCopy {
- uint32_t magic;
- uLong adler;
- size_t original_length;
- const void* original_ptr;
-
- /* find the GuardedCopy given the pointer into the "live" data */
- static inline const GuardedCopy* FromData(const void* dataBuf) {
- return reinterpret_cast<const GuardedCopy*>(ActualBuffer(dataBuf));
- }
-
+class GuardedCopy {
+ public:
/*
* Create an over-sized buffer to hold the contents of "buf". Copy it in,
* filling in the area around it with guard data.
- *
- * We use a 16-bit pattern to make a rogue memset less likely to elude us.
*/
- static void* Create(const void* buf, size_t len, bool modOkay) {
- size_t newLen = ActualLength(len);
- uint8_t* newBuf = DebugAlloc(newLen);
-
- // Fill it in with a pattern.
- uint16_t* pat = reinterpret_cast<uint16_t*>(newBuf);
- for (size_t i = 0; i < newLen / 2; i++) {
- *pat++ = kGuardPattern;
- }
-
- // Copy the data in; note "len" could be zero.
- memcpy(newBuf + kGuardLen / 2, buf, len);
+ static void* Create(const void* original_buf, size_t len, bool mod_okay) {
+ const size_t new_len = LengthIncludingRedZones(len);
+ uint8_t* const new_buf = DebugAlloc(new_len);
// If modification is not expected, grab a checksum.
uLong adler = 0;
- if (!modOkay) {
- adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, reinterpret_cast<const Bytef*>(buf), len);
- *reinterpret_cast<uLong*>(newBuf) = adler;
+ if (!mod_okay) {
+ adler = adler32(adler32(0L, Z_NULL, 0), reinterpret_cast<const Bytef*>(original_buf), len);
}
- GuardedCopy* pExtra = reinterpret_cast<GuardedCopy*>(newBuf);
- pExtra->magic = kGuardMagic;
- pExtra->adler = adler;
- pExtra->original_ptr = buf;
- pExtra->original_length = len;
+ GuardedCopy* copy = new (new_buf) GuardedCopy(original_buf, len, adler);
- return newBuf + kGuardLen / 2;
+ // Fill begin region with canary pattern.
+ const size_t kStartCanaryLength = (GuardedCopy::kRedZoneSize / 2) - sizeof(GuardedCopy);
+ for (size_t i = 0, j = 0; i < kStartCanaryLength; ++i) {
+ const_cast<char*>(copy->StartRedZone())[i] = kCanary[j];
+ if (kCanary[j] == '\0') {
+ j = 0;
+ }
+ }
+
+ // Copy the data in; note "len" could be zero.
+ memcpy(const_cast<uint8_t*>(copy->BufferWithinRedZones()), original_buf, len);
+
+ // Fill end region with canary pattern.
+ for (size_t i = 0, j = 0; i < kEndCanaryLength; ++i) {
+ const_cast<char*>(copy->EndRedZone())[i] = kCanary[j];
+ if (kCanary[j] == '\0') {
+ j = 0;
+ }
+ }
+
+ return const_cast<uint8_t*>(copy->BufferWithinRedZones());
}
/*
+ * Create a guarded copy of a primitive array. Modifications to the copied
+ * data are allowed. Returns a pointer to the copied data.
+ */
+ static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy) {
+ ScopedObjectAccess soa(env);
+
+ mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+ size_t component_size = a->GetClass()->GetComponentSize();
+ size_t byte_count = a->GetLength() * component_size;
+ void* result = Create(a->GetRawData(component_size, 0), byte_count, true);
+ if (is_copy != nullptr) {
+ *is_copy = JNI_TRUE;
+ }
+ return result;
+ }
+
+ /*
+ * Perform the array "release" operation, which may or may not copy data
+ * back into the managed heap, and may or may not release the underlying storage.
+ */
+ static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env, jarray java_array,
+ void* embedded_buf, int mode) {
+ ScopedObjectAccess soa(env);
+ mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
+
+ if (!GuardedCopy::Check(function_name, embedded_buf, true)) {
+ return nullptr;
+ }
+ if (mode != JNI_ABORT) {
+ size_t len = FromEmbedded(embedded_buf)->original_length_;
+ memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), embedded_buf, len);
+ }
+ if (mode != JNI_COMMIT) {
+ return Destroy(embedded_buf);
+ }
+ return embedded_buf;
+ }
+
+
+ /*
* Free up the guard buffer, scrub it, and return the original pointer.
*/
- static void* Destroy(void* dataBuf) {
- const GuardedCopy* pExtra = GuardedCopy::FromData(dataBuf);
- void* original_ptr = const_cast<void*>(pExtra->original_ptr);
- size_t len = pExtra->original_length;
- DebugFree(dataBuf, len);
+ static void* Destroy(void* embedded_buf) {
+ GuardedCopy* copy = FromEmbedded(embedded_buf);
+ void* original_ptr = const_cast<void*>(copy->original_ptr_);
+ size_t len = LengthIncludingRedZones(copy->original_length_);
+ DebugFree(copy, len);
return original_ptr;
}
@@ -977,67 +1266,16 @@
*
* The caller has already checked that "dataBuf" is non-NULL.
*/
- static void Check(const char* functionName, const void* dataBuf, bool modOkay) {
- static const uint32_t kMagicCmp = kGuardMagic;
- const uint8_t* fullBuf = ActualBuffer(dataBuf);
- const GuardedCopy* pExtra = GuardedCopy::FromData(dataBuf);
-
- // Before we do anything with "pExtra", check the magic number. We
- // do the check with memcmp rather than "==" in case the pointer is
- // unaligned. If it points to completely bogus memory we're going
- // to crash, but there's no easy way around that.
- if (memcmp(&pExtra->magic, &kMagicCmp, 4) != 0) {
- uint8_t buf[4];
- memcpy(buf, &pExtra->magic, 4);
- JniAbortF(functionName,
- "guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
- buf[3], buf[2], buf[1], buf[0], dataBuf); // Assumes little-endian.
- }
-
- size_t len = pExtra->original_length;
-
- // Check bottom half of guard; skip over optional checksum storage.
- const uint16_t* pat = reinterpret_cast<const uint16_t*>(fullBuf);
- for (size_t i = sizeof(GuardedCopy) / 2; i < (kGuardLen / 2 - sizeof(GuardedCopy)) / 2; i++) {
- if (pat[i] != kGuardPattern) {
- JniAbortF(functionName, "guard pattern(1) disturbed at %p +%zd", fullBuf, i*2);
- }
- }
-
- int offset = kGuardLen / 2 + len;
- if (offset & 0x01) {
- // Odd byte; expected value depends on endian.
- const uint16_t patSample = kGuardPattern;
- uint8_t expected_byte = reinterpret_cast<const uint8_t*>(&patSample)[1];
- if (fullBuf[offset] != expected_byte) {
- JniAbortF(functionName, "guard pattern disturbed in odd byte after %p +%d 0x%02x 0x%02x",
- fullBuf, offset, fullBuf[offset], expected_byte);
- }
- offset++;
- }
-
- // Check top half of guard.
- pat = reinterpret_cast<const uint16_t*>(fullBuf + offset);
- for (size_t i = 0; i < kGuardLen / 4; i++) {
- if (pat[i] != kGuardPattern) {
- JniAbortF(functionName, "guard pattern(2) disturbed at %p +%zd", fullBuf, offset + i*2);
- }
- }
-
- // If modification is not expected, verify checksum. Strictly speaking
- // this is wrong: if we told the client that we made a copy, there's no
- // reason they can't alter the buffer.
- if (!modOkay) {
- uLong adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, (const Bytef*)dataBuf, len);
- if (pExtra->adler != adler) {
- JniAbortF(functionName, "buffer modified (0x%08lx vs 0x%08lx) at address %p",
- pExtra->adler, adler, dataBuf);
- }
- }
+ static bool Check(const char* function_name, const void* embedded_buf, bool mod_okay) {
+ const GuardedCopy* copy = FromEmbedded(embedded_buf);
+ return copy->CheckHeader(function_name, mod_okay) && copy->CheckRedZones(function_name);
}
private:
+ GuardedCopy(const void* original_buf, size_t len, uLong adler) :
+ magic_(kGuardMagic), adler_(adler), original_ptr_(original_buf), original_length_(len) {
+ }
+
static uint8_t* DebugAlloc(size_t len) {
void* result = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (result == MAP_FAILED) {
@@ -1046,68 +1284,126 @@
return reinterpret_cast<uint8_t*>(result);
}
- static void DebugFree(void* dataBuf, size_t len) {
- uint8_t* fullBuf = ActualBuffer(dataBuf);
- size_t totalByteCount = ActualLength(len);
- // TODO: we could mprotect instead, and keep the allocation around for a while.
- // This would be even more expensive, but it might catch more errors.
- // if (mprotect(fullBuf, totalByteCount, PROT_NONE) != 0) {
- // PLOG(WARNING) << "mprotect(PROT_NONE) failed";
- // }
- if (munmap(fullBuf, totalByteCount) != 0) {
- PLOG(FATAL) << "munmap(" << reinterpret_cast<void*>(fullBuf) << ", " << totalByteCount << ") failed";
+ static void DebugFree(void* buf, size_t len) {
+ if (munmap(buf, len) != 0) {
+ PLOG(FATAL) << "munmap(" << buf << ", " << len << ") failed";
}
}
- static const uint8_t* ActualBuffer(const void* dataBuf) {
- return reinterpret_cast<const uint8_t*>(dataBuf) - kGuardLen / 2;
+ static size_t LengthIncludingRedZones(size_t len) {
+ return len + kRedZoneSize;
}
- static uint8_t* ActualBuffer(void* dataBuf) {
- return reinterpret_cast<uint8_t*>(dataBuf) - kGuardLen / 2;
+ // Get the GuardedCopy from the interior pointer.
+ static GuardedCopy* FromEmbedded(void* embedded_buf) {
+ return reinterpret_cast<GuardedCopy*>(
+ reinterpret_cast<uint8_t*>(embedded_buf) - (kRedZoneSize / 2));
}
- // Underlying length of a user allocation of 'length' bytes.
- static size_t ActualLength(size_t length) {
- return (length + kGuardLen + 1) & ~0x01;
+ static const GuardedCopy* FromEmbedded(const void* embedded_buf) {
+ return reinterpret_cast<const GuardedCopy*>(
+ reinterpret_cast<const uint8_t*>(embedded_buf) - (kRedZoneSize / 2));
}
+
+ static void AbortF(const char* jni_function_name, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ Runtime::Current()->GetJavaVM()->JniAbortV(jni_function_name, fmt, args);
+ va_end(args);
+ }
+
+ bool CheckHeader(const char* function_name, bool mod_okay) const {
+ static const uint32_t kMagicCmp = kGuardMagic;
+
+ // Before we do anything with "pExtra", check the magic number. We
+ // do the check with memcmp rather than "==" in case the pointer is
+ // unaligned. If it points to completely bogus memory we're going
+ // to crash, but there's no easy way around that.
+ if (UNLIKELY(memcmp(&magic_, &kMagicCmp, 4) != 0)) {
+ uint8_t buf[4];
+ memcpy(buf, &magic_, 4);
+ AbortF(function_name,
+ "guard magic does not match (found 0x%02x%02x%02x%02x) -- incorrect data pointer %p?",
+ buf[3], buf[2], buf[1], buf[0], this); // Assumes little-endian.
+ return false;
+ }
+
+ // If modification is not expected, verify checksum. Strictly speaking this is wrong: if we
+ // told the client that we made a copy, there's no reason they can't alter the buffer.
+ if (!mod_okay) {
+ uLong computed_adler =
+ adler32(adler32(0L, Z_NULL, 0), BufferWithinRedZones(), original_length_);
+ if (computed_adler != adler_) {
+ AbortF(function_name, "buffer modified (0x%08lx vs 0x%08lx) at address %p",
+ computed_adler, adler_, this);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool CheckRedZones(const char* function_name) const {
+ // Check the begin red zone.
+ const size_t kStartCanaryLength = (GuardedCopy::kRedZoneSize / 2) - sizeof(GuardedCopy);
+ for (size_t i = 0, j = 0; i < kStartCanaryLength; ++i) {
+ if (UNLIKELY(StartRedZone()[i] != kCanary[j])) {
+ AbortF(function_name, "guard pattern before buffer disturbed at %p +%zd", this, i);
+ return false;
+ }
+ if (kCanary[j] == '\0') {
+ j = 0;
+ }
+ }
+
+ // Check end region.
+ for (size_t i = 0, j = 0; i < kEndCanaryLength; ++i) {
+ if (UNLIKELY(EndRedZone()[i] != kCanary[j])) {
+ size_t offset_from_buffer_start =
+ &(EndRedZone()[i]) - &(StartRedZone()[kStartCanaryLength]);
+ AbortF(function_name, "guard pattern after buffer disturbed at %p +%zd", this,
+ offset_from_buffer_start);
+ return false;
+ }
+ if (kCanary[j] == '\0') {
+ j = 0;
+ }
+ }
+ return true;
+ }
+
+ // Location that canary value will be written before the guarded region.
+ const char* StartRedZone() const {
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(this);
+ return reinterpret_cast<const char*>(buf + sizeof(GuardedCopy));
+ }
+
+ // Return the interior embedded buffer.
+ const uint8_t* BufferWithinRedZones() const {
+ const uint8_t* embedded_buf = reinterpret_cast<const uint8_t*>(this) + (kRedZoneSize / 2);
+ return embedded_buf;
+ }
+
+ // Location that canary value will be written after the guarded region.
+ const char* EndRedZone() const {
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(this);
+ size_t buf_len = LengthIncludingRedZones(original_length_);
+ return reinterpret_cast<const char*>(buf + (buf_len - (kRedZoneSize / 2)));
+ }
+
+ static constexpr size_t kRedZoneSize = 512;
+ static constexpr size_t kEndCanaryLength = kRedZoneSize / 2;
+
+ // Value written before and after the guarded array.
+ static const char* const kCanary;
+
+ static constexpr uint32_t kGuardMagic = 0xffd5aa96;
+
+ const uint32_t magic_;
+ const uLong adler_;
+ const void* const original_ptr_;
+ const size_t original_length_;
};
-
-/*
- * Create a guarded copy of a primitive array. Modifications to the copied
- * data are allowed. Returns a pointer to the copied data.
- */
-static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* isCopy) {
- ScopedObjectAccess soa(env);
-
- mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
- size_t component_size = a->GetClass()->GetComponentSize();
- size_t byte_count = a->GetLength() * component_size;
- void* result = GuardedCopy::Create(a->GetRawData(component_size, 0), byte_count, true);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
- }
- return result;
-}
-
-/*
- * Perform the array "release" operation, which may or may not copy data
- * back into the managed heap, and may or may not release the underlying storage.
- */
-static void ReleaseGuardedPACopy(JNIEnv* env, jarray java_array, void* dataBuf, int mode) {
- ScopedObjectAccess soa(env);
- mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
-
- GuardedCopy::Check(__FUNCTION__, dataBuf, true);
-
- if (mode != JNI_ABORT) {
- size_t len = GuardedCopy::FromData(dataBuf)->original_length;
- memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), dataBuf, len);
- }
- if (mode != JNI_COMMIT) {
- GuardedCopy::Destroy(dataBuf);
- }
-}
+const char* const GuardedCopy::kCanary = "JNI BUFFER RED ZONE";
/*
* ===========================================================================
@@ -1118,668 +1414,1955 @@
class CheckJNI {
public:
static jint GetVersion(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_Default, "E", env);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetVersion(env));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[1] = {{.E = env }};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ result.I = baseEnv(env)->GetVersion(env);
+ if (sc.Check(soa, false, "I", &result)) {
+ return result.I;
+ }
+ }
+ return JNI_ERR;
}
- static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf, jsize bufLen) {
- CHECK_JNI_ENTRY(kFlag_Default, "EuLpz", env, name, loader, buf, bufLen);
- sc.CheckClassName(name);
- return CHECK_JNI_EXIT("c", baseEnv(env)->DefineClass(env, name, loader, buf, bufLen));
+ static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env }, {.p = vm}};
+ if (sc.Check(soa, true, "Ep", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->GetJavaVM(env, vm);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env }, {.c = c}, {.p = methods}, {.I = nMethods}};
+ if (sc.Check(soa, true, "EcpI", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->RegisterNatives(env, c, methods, nMethods);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jint UnregisterNatives(JNIEnv* env, jclass c) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env }, {.c = c}};
+ if (sc.Check(soa, true, "Ec", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->UnregisterNatives(env, c);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
+ // Note: we use "EL" here but "Ep" has been used in the past on the basis that we'd like to
+ // know the object is invalid. The spec says that passing invalid objects or even ones that
+ // are deleted isn't supported.
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env }, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.w = baseEnv(env)->GetObjectRefType(env, obj);
+ if (sc.Check(soa, false, "w", &result)) {
+ return result.w;
+ }
+ }
+ return JNIInvalidRefType;
+ }
+
+ static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf,
+ jsize bufLen) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.u = name}, {.L = loader}, {.p = buf}, {.z = bufLen}};
+ if (sc.Check(soa, true, "EuLpz", args) && sc.CheckClassName(name)) {
+ JniValueType result;
+ result.c = baseEnv(env)->DefineClass(env, name, loader, buf, bufLen);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jclass FindClass(JNIEnv* env, const char* name) {
- CHECK_JNI_ENTRY(kFlag_Default, "Eu", env, name);
- sc.CheckClassName(name);
- return CHECK_JNI_EXIT("c", baseEnv(env)->FindClass(env, name));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.u = name}};
+ if (sc.Check(soa, true, "Eu", args) && sc.CheckClassName(name)) {
+ JniValueType result;
+ result.c = baseEnv(env)->FindClass(env, name);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jclass GetSuperclass(JNIEnv* env, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
- return CHECK_JNI_EXIT("c", baseEnv(env)->GetSuperclass(env, c));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.c = c}};
+ if (sc.Check(soa, true, "Ec", args)) {
+ JniValueType result;
+ result.c = baseEnv(env)->GetSuperclass(env, c);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
}
static jboolean IsAssignableFrom(JNIEnv* env, jclass c1, jclass c2) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecc", env, c1, c2);
- return CHECK_JNI_EXIT("b", baseEnv(env)->IsAssignableFrom(env, c1, c2));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.c = c1}, {.c = c2}};
+ if (sc.Check(soa, true, "Ecc", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->IsAssignableFrom(env, c1, c2);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
}
static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, method);
- // TODO: check that 'field' is a java.lang.reflect.Method.
- return CHECK_JNI_EXIT("m", baseEnv(env)->FromReflectedMethod(env, method));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = method}};
+ if (sc.Check(soa, true, "EL", args) && sc.CheckReflectedMethod(soa, method)) {
+ JniValueType result;
+ result.m = baseEnv(env)->FromReflectedMethod(env, method);
+ if (sc.Check(soa, false, "m", &result)) {
+ return result.m;
+ }
+ }
+ return nullptr;
}
static jfieldID FromReflectedField(JNIEnv* env, jobject field) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, field);
- // TODO: check that 'field' is a java.lang.reflect.Field.
- return CHECK_JNI_EXIT("f", baseEnv(env)->FromReflectedField(env, field));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = field}};
+ if (sc.Check(soa, true, "EL", args) && sc.CheckReflectedField(soa, field)) {
+ JniValueType result;
+ result.f = baseEnv(env)->FromReflectedField(env, field);
+ if (sc.Check(soa, false, "f", &result)) {
+ return result.f;
+ }
+ }
+ return nullptr;
}
static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecmb", env, cls, mid, isStatic);
- return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.b = isStatic}};
+ if (sc.Check(soa, true, "Ecmb", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic);
+ if (sc.Check(soa, false, "L", &result) && (result.L != nullptr)) {
+ DCHECK(sc.CheckReflectedMethod(soa, result.L));
+ return result.L;
+ }
+ }
+ return nullptr;
}
static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecfb", env, cls, fid, isStatic);
- return CHECK_JNI_EXIT("L", baseEnv(env)->ToReflectedField(env, cls, fid, isStatic));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.b = isStatic}};
+ if (sc.Check(soa, true, "Ecfb", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->ToReflectedField(env, cls, fid, isStatic);
+ if (sc.Check(soa, false, "L", &result) && (result.L != nullptr)) {
+ DCHECK(sc.CheckReflectedField(soa, result.L));
+ return result.L;
+ }
+ }
+ return nullptr;
}
static jint Throw(JNIEnv* env, jthrowable obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- // TODO: check that 'obj' is a java.lang.Throwable.
- return CHECK_JNI_EXIT("I", baseEnv(env)->Throw(env, obj));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.t = obj}};
+ if (sc.Check(soa, true, "Et", args) && sc.CheckThrowable(soa, obj)) {
+ JniValueType result;
+ result.i = baseEnv(env)->Throw(env, obj);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jint ThrowNew(JNIEnv* env, jclass c, const char* message) {
- CHECK_JNI_ENTRY(kFlag_NullableUtf, "Ecu", env, c, message);
- return CHECK_JNI_EXIT("I", baseEnv(env)->ThrowNew(env, c, message));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.c = c}, {.u = message}};
+ if (sc.Check(soa, true, "Ecu", args) && sc.CheckThrowableClass(soa, c)) {
+ JniValueType result;
+ result.i = baseEnv(env)->ThrowNew(env, c, message);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jthrowable ExceptionOccurred(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
- return CHECK_JNI_EXIT("L", baseEnv(env)->ExceptionOccurred(env));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ result.t = baseEnv(env)->ExceptionOccurred(env);
+ if (sc.Check(soa, false, "t", &result)) {
+ return result.t;
+ }
+ }
+ return nullptr;
}
static void ExceptionDescribe(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
- baseEnv(env)->ExceptionDescribe(env);
- CHECK_JNI_EXIT_VOID();
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ baseEnv(env)->ExceptionDescribe(env);
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
static void ExceptionClear(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay, "E", env);
- baseEnv(env)->ExceptionClear(env);
- CHECK_JNI_EXIT_VOID();
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ baseEnv(env)->ExceptionClear(env);
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static jboolean ExceptionCheck(JNIEnv* env) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay | kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[1] = {{.E = env}};
+ if (sc.Check(soa, true, "E", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->ExceptionCheck(env);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
}
static void FatalError(JNIEnv* env, const char* msg) {
// The JNI specification doesn't say it's okay to call FatalError with a pending exception,
// but you're about to abort anyway, and it's quite likely that you have a pending exception,
// and it's not unimaginable that you don't know that you do. So we allow it.
- CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_NullableUtf, "Eu", env, msg);
- baseEnv(env)->FatalError(env, msg);
- CHECK_JNI_EXIT_VOID();
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay | kFlag_NullableUtf, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.u = msg}};
+ if (sc.Check(soa, true, "Eu", args)) {
+ JniValueType result;
+ baseEnv(env)->FatalError(env, msg);
+ // Unreachable.
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
static jint PushLocalFrame(JNIEnv* env, jint capacity) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EI", env, capacity);
- return CHECK_JNI_EXIT("I", baseEnv(env)->PushLocalFrame(env, capacity));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.I = capacity}};
+ if (sc.Check(soa, true, "EI", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->PushLocalFrame(env, capacity);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
}
static jobject PopLocalFrame(JNIEnv* env, jobject res) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, res);
- return CHECK_JNI_EXIT("L", baseEnv(env)->PopLocalFrame(env, res));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = res}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->PopLocalFrame(env, res);
+ sc.Check(soa, false, "L", &result);
+ return result.L;
+ }
+ return nullptr;
}
static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewGlobalRef(env, obj));
+ return NewRef(__FUNCTION__, env, obj, kGlobal);
}
- static jobject NewLocalRef(JNIEnv* env, jobject ref) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, ref);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewLocalRef(env, ref));
- }
-
- static void DeleteGlobalRef(JNIEnv* env, jobject globalRef) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, globalRef);
- if (globalRef != nullptr && GetIndirectRefKind(globalRef) != kGlobal) {
- JniAbortF(__FUNCTION__, "DeleteGlobalRef on %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(globalRef)).c_str(), globalRef);
- } else {
- baseEnv(env)->DeleteGlobalRef(env, globalRef);
- CHECK_JNI_EXIT_VOID();
- }
- }
-
- static void DeleteWeakGlobalRef(JNIEnv* env, jweak weakGlobalRef) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, weakGlobalRef);
- if (weakGlobalRef != nullptr && GetIndirectRefKind(weakGlobalRef) != kWeakGlobal) {
- JniAbortF(__FUNCTION__, "DeleteWeakGlobalRef on %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(weakGlobalRef)).c_str(), weakGlobalRef);
- } else {
- baseEnv(env)->DeleteWeakGlobalRef(env, weakGlobalRef);
- CHECK_JNI_EXIT_VOID();
- }
- }
-
- static void DeleteLocalRef(JNIEnv* env, jobject localRef) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, localRef);
- if (localRef != nullptr && GetIndirectRefKind(localRef) != kLocal && !IsHandleScopeLocalRef(env, localRef)) {
- JniAbortF(__FUNCTION__, "DeleteLocalRef on %s: %p",
- ToStr<IndirectRefKind>(GetIndirectRefKind(localRef)).c_str(), localRef);
- } else {
- baseEnv(env)->DeleteLocalRef(env, localRef);
- CHECK_JNI_EXIT_VOID();
- }
- }
-
- static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) {
- CHECK_JNI_ENTRY(kFlag_Default, "EI", env, capacity);
- return CHECK_JNI_EXIT("I", baseEnv(env)->EnsureLocalCapacity(env, capacity));
- }
-
- static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
- CHECK_JNI_ENTRY(kFlag_Default, "ELL", env, ref1, ref2);
- return CHECK_JNI_EXIT("b", baseEnv(env)->IsSameObject(env, ref1, ref2));
- }
-
- static jobject AllocObject(JNIEnv* env, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
- return CHECK_JNI_EXIT("L", baseEnv(env)->AllocObject(env, c));
- }
-
- static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
- va_list args;
- va_start(args, mid);
- jobject result = baseEnv(env)->NewObjectV(env, c, mid, args);
- va_end(args);
- return CHECK_JNI_EXIT("L", result);
- }
-
- static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list args) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectV(env, c, mid, args));
- }
-
- static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* args) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewObjectA(env, c, mid, args));
- }
-
- static jclass GetObjectClass(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- return CHECK_JNI_EXIT("c", baseEnv(env)->GetObjectClass(env, obj));
- }
-
- static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "ELc", env, obj, c);
- return CHECK_JNI_EXIT("b", baseEnv(env)->IsInstanceOf(env, obj, c));
- }
-
- static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("m", baseEnv(env)->GetMethodID(env, c, name, sig));
- }
-
- static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("f", baseEnv(env)->GetFieldID(env, c, name, sig));
- }
-
- static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("m", baseEnv(env)->GetStaticMethodID(env, c, name, sig));
- }
-
- static jfieldID GetStaticFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ecuu", env, c, name, sig);
- return CHECK_JNI_EXIT("f", baseEnv(env)->GetStaticFieldID(env, c, name, sig));
- }
-
-#define FIELD_ACCESSORS(_ctype, _jname, _jvalue_type, _type) \
- static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass c, jfieldID fid) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecf", env, c, fid); \
- sc.CheckStaticFieldID(c, fid); \
- return CHECK_JNI_EXIT(_type, baseEnv(env)->GetStatic##_jname##Field(env, c, fid)); \
- } \
- static _ctype Get##_jname##Field(JNIEnv* env, jobject obj, jfieldID fid) { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELf", env, obj, fid); \
- sc.CheckInstanceFieldID(obj, fid); \
- return CHECK_JNI_EXIT(_type, baseEnv(env)->Get##_jname##Field(env, obj, fid)); \
- } \
- static void SetStatic##_jname##Field(JNIEnv* env, jclass c, jfieldID fid, _ctype value) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecf" _type, env, c, fid, value); \
- sc.CheckStaticFieldID(c, fid); \
- /* "value" arg only used when type == ref */ \
- jvalue java_type_value; \
- java_type_value._jvalue_type = value; \
- sc.CheckFieldType(java_type_value, fid, _type[0], true); \
- baseEnv(env)->SetStatic##_jname##Field(env, c, fid, value); \
- CHECK_JNI_EXIT_VOID(); \
- } \
- static void Set##_jname##Field(JNIEnv* env, jobject obj, jfieldID fid, _ctype value) { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELf" _type, env, obj, fid, value); \
- sc.CheckInstanceFieldID(obj, fid); \
- /* "value" arg only used when type == ref */ \
- jvalue java_type_value; \
- java_type_value._jvalue_type = value; \
- sc.CheckFieldType(java_type_value, fid, _type[0], false); \
- baseEnv(env)->Set##_jname##Field(env, obj, fid, value); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-FIELD_ACCESSORS(jobject, Object, l, "L");
-FIELD_ACCESSORS(jboolean, Boolean, z, "Z");
-FIELD_ACCESSORS(jbyte, Byte, b, "B");
-FIELD_ACCESSORS(jchar, Char, c, "C");
-FIELD_ACCESSORS(jshort, Short, s, "S");
-FIELD_ACCESSORS(jint, Int, i, "I");
-FIELD_ACCESSORS(jlong, Long, j, "J");
-FIELD_ACCESSORS(jfloat, Float, f, "F");
-FIELD_ACCESSORS(jdouble, Double, d, "D");
-
-#define CALL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \
- /* Virtual... */ \
- static _ctype Call##_jname##Method(JNIEnv* env, jobject obj, \
- jmethodID mid, ...) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- va_list args; \
- va_start(args, mid); \
- _retasgn(baseEnv(env)->Call##_jname##MethodV(env, obj, mid, args)); \
- va_end(args); \
- _retok; \
- } \
- static _ctype Call##_jname##MethodV(JNIEnv* env, jobject obj, \
- jmethodID mid, va_list args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->Call##_jname##MethodV(env, obj, mid, args)); \
- _retok; \
- } \
- static _ctype Call##_jname##MethodA(JNIEnv* env, jobject obj, \
- jmethodID mid, jvalue* args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELm.", env, obj, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->Call##_jname##MethodA(env, obj, mid, args)); \
- _retok; \
- } \
- /* Non-virtual... */ \
- static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, \
- jobject obj, jclass c, jmethodID mid, ...) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- va_list args; \
- va_start(args, mid); \
- _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, c, mid, args)); \
- va_end(args); \
- _retok; \
- } \
- static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, \
- jobject obj, jclass c, jmethodID mid, va_list args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodV(env, obj, c, mid, args)); \
- _retok; \
- } \
- static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, \
- jobject obj, jclass c, jmethodID mid, jvalue* args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "ELcm.", env, obj, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, false); \
- sc.CheckVirtualMethod(obj, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallNonvirtual##_jname##MethodA(env, obj, c, mid, args)); \
- _retok; \
- } \
- /* Static... */ \
- static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass c, jmethodID mid, ...) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, true); \
- sc.CheckStaticMethod(c, mid); \
- _retdecl; \
- va_list args; \
- va_start(args, mid); \
- _retasgn(baseEnv(env)->CallStatic##_jname##MethodV(env, c, mid, args)); \
- va_end(args); \
- _retok; \
- } \
- static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass c, jmethodID mid, va_list args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, true); \
- sc.CheckStaticMethod(c, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallStatic##_jname##MethodV(env, c, mid, args)); \
- _retok; \
- } \
- static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* args) \
- { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ecm.", env, c, mid); /* TODO: args! */ \
- sc.CheckSig(mid, _retsig, true); \
- sc.CheckStaticMethod(c, mid); \
- _retdecl; \
- _retasgn(baseEnv(env)->CallStatic##_jname##MethodA(env, c, mid, args)); \
- _retok; \
- }
-
-#define NON_VOID_RETURN(_retsig, _ctype) return CHECK_JNI_EXIT(_retsig, (_ctype) result)
-#define VOID_RETURN CHECK_JNI_EXIT_VOID()
-
-CALL(jobject, Object, mirror::Object* result, result = reinterpret_cast<mirror::Object*>, NON_VOID_RETURN("L", jobject), "L");
-CALL(jboolean, Boolean, jboolean result, result =, NON_VOID_RETURN("Z", jboolean), "Z");
-CALL(jbyte, Byte, jbyte result, result =, NON_VOID_RETURN("B", jbyte), "B");
-CALL(jchar, Char, jchar result, result =, NON_VOID_RETURN("C", jchar), "C");
-CALL(jshort, Short, jshort result, result =, NON_VOID_RETURN("S", jshort), "S");
-CALL(jint, Int, jint result, result =, NON_VOID_RETURN("I", jint), "I");
-CALL(jlong, Long, jlong result, result =, NON_VOID_RETURN("J", jlong), "J");
-CALL(jfloat, Float, jfloat result, result =, NON_VOID_RETURN("F", jfloat), "F");
-CALL(jdouble, Double, jdouble result, result =, NON_VOID_RETURN("D", jdouble), "D");
-CALL(void, Void, , , VOID_RETURN, "V");
-
- static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
- CHECK_JNI_ENTRY(kFlag_Default, "Epz", env, unicodeChars, len);
- return CHECK_JNI_EXIT("s", baseEnv(env)->NewString(env, unicodeChars, len));
- }
-
- static jsize GetStringLength(JNIEnv* env, jstring string) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringLength(env, string));
- }
-
- static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, java_string, isCopy);
- const jchar* result = baseEnv(env)->GetStringChars(env, java_string, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- mirror::String* s = sc.soa().Decode<mirror::String*>(java_string);
- int byteCount = s->GetLength() * 2;
- result = (const jchar*) GuardedCopy::Create(result, byteCount, false);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
- }
- }
- return CHECK_JNI_EXIT("p", result);
- }
-
- static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Esp", env, string, chars);
- sc.CheckNonNull(chars);
- if (sc.ForceCopy()) {
- GuardedCopy::Check(__FUNCTION__, chars, false);
- chars = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<jchar*>(chars)));
- }
- baseEnv(env)->ReleaseStringChars(env, string, chars);
- CHECK_JNI_EXIT_VOID();
- }
-
- static jstring NewStringUTF(JNIEnv* env, const char* bytes) {
- CHECK_JNI_ENTRY(kFlag_NullableUtf, "Eu", env, bytes); // TODO: show pointer and truncate string.
- return CHECK_JNI_EXIT("s", baseEnv(env)->NewStringUTF(env, bytes));
- }
-
- static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Es", env, string);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetStringUTFLength(env, string));
- }
-
- static const char* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Esp", env, string, isCopy);
- const char* result = baseEnv(env)->GetStringUTFChars(env, string, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- result = (const char*) GuardedCopy::Create(result, strlen(result) + 1, false);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
- }
- }
- return CHECK_JNI_EXIT("u", result); // TODO: show pointer and truncate string.
- }
-
- static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
- CHECK_JNI_ENTRY(kFlag_ExcepOkay | kFlag_Release, "Esu", env, string, utf); // TODO: show pointer and truncate string.
- if (sc.ForceCopy()) {
- GuardedCopy::Check(__FUNCTION__, utf, false);
- utf = reinterpret_cast<const char*>(GuardedCopy::Destroy(const_cast<char*>(utf)));
- }
- baseEnv(env)->ReleaseStringUTFChars(env, string, utf);
- CHECK_JNI_EXIT_VOID();
- }
-
- static jsize GetArrayLength(JNIEnv* env, jarray array) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "Ea", env, array);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetArrayLength(env, array));
- }
-
- static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass elementClass, jobject initialElement) {
- CHECK_JNI_ENTRY(kFlag_Default, "EzcL", env, length, elementClass, initialElement);
- return CHECK_JNI_EXIT("a", baseEnv(env)->NewObjectArray(env, length, elementClass, initialElement));
- }
-
- static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
- CHECK_JNI_ENTRY(kFlag_Default, "EaI", env, array, index);
- return CHECK_JNI_EXIT("L", baseEnv(env)->GetObjectArrayElement(env, array, index));
- }
-
- static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) {
- CHECK_JNI_ENTRY(kFlag_Default, "EaIL", env, array, index, value);
- baseEnv(env)->SetObjectArrayElement(env, array, index, value);
- CHECK_JNI_EXIT_VOID();
- }
-
-#define NEW_PRIMITIVE_ARRAY(_artype, _jname) \
- static _artype New##_jname##Array(JNIEnv* env, jsize length) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Ez", env, length); \
- return CHECK_JNI_EXIT("a", baseEnv(env)->New##_jname##Array(env, length)); \
- }
-NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean);
-NEW_PRIMITIVE_ARRAY(jbyteArray, Byte);
-NEW_PRIMITIVE_ARRAY(jcharArray, Char);
-NEW_PRIMITIVE_ARRAY(jshortArray, Short);
-NEW_PRIMITIVE_ARRAY(jintArray, Int);
-NEW_PRIMITIVE_ARRAY(jlongArray, Long);
-NEW_PRIMITIVE_ARRAY(jfloatArray, Float);
-NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
-
-struct ForceCopyGetChecker {
- public:
- ForceCopyGetChecker(ScopedCheck& sc, jboolean* isCopy) {
- force_copy = sc.ForceCopy();
- no_copy = 0;
- if (force_copy && isCopy != nullptr) {
- // Capture this before the base call tramples on it.
- no_copy = *reinterpret_cast<uint32_t*>(isCopy);
- }
- }
-
- template<typename ResultT>
- ResultT Check(JNIEnv* env, jarray array, jboolean* isCopy, ResultT result) {
- if (force_copy && result != nullptr) {
- result = reinterpret_cast<ResultT>(CreateGuardedPACopy(env, array, isCopy));
- }
- return result;
- }
-
- uint32_t no_copy;
- bool force_copy;
-};
-
-#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
- static _ctype* Get##_jname##ArrayElements(JNIEnv* env, _ctype##Array array, jboolean* isCopy) { \
- CHECK_JNI_ENTRY(kFlag_Default, "Eap", env, array, isCopy); \
- _ctype* result = ForceCopyGetChecker(sc, isCopy).Check(env, array, isCopy, baseEnv(env)->Get##_jname##ArrayElements(env, array, isCopy)); \
- return CHECK_JNI_EXIT("p", result); \
- }
-
-#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
- static void Release##_jname##ArrayElements(JNIEnv* env, _ctype##Array array, _ctype* elems, jint mode) { \
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "Eapr", env, array, elems, mode); \
- sc.CheckNonNull(elems); \
- if (sc.ForceCopy()) { \
- ReleaseGuardedPACopy(env, array, elems, mode); \
- } \
- baseEnv(env)->Release##_jname##ArrayElements(env, array, elems, mode); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
- static void Get##_jname##ArrayRegion(JNIEnv* env, _ctype##Array array, jsize start, jsize len, _ctype* buf) { \
- CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
- baseEnv(env)->Get##_jname##ArrayRegion(env, array, start, len, buf); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
- static void Set##_jname##ArrayRegion(JNIEnv* env, _ctype##Array array, jsize start, jsize len, const _ctype* buf) { \
- CHECK_JNI_ENTRY(kFlag_Default, "EaIIp", env, array, start, len, buf); \
- baseEnv(env)->Set##_jname##ArrayRegion(env, array, start, len, buf); \
- CHECK_JNI_EXIT_VOID(); \
- }
-
-#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname, _typechar) \
- GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
- RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
- GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
- SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
-
-// TODO: verify primitive array type matches call type.
-PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, 'Z');
-PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, 'B');
-PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, 'C');
-PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, 'S');
-PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, 'I');
-PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, 'J');
-PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, 'F');
-PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, 'D');
-
- static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
- CHECK_JNI_ENTRY(kFlag_Default, "EcpI", env, c, methods, nMethods);
- return CHECK_JNI_EXIT("I", baseEnv(env)->RegisterNatives(env, c, methods, nMethods));
- }
-
- static jint UnregisterNatives(JNIEnv* env, jclass c) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ec", env, c);
- return CHECK_JNI_EXIT("I", baseEnv(env)->UnregisterNatives(env, c));
- }
-
- static jint MonitorEnter(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- if (!sc.CheckInstance(ScopedCheck::kObject, obj)) {
- return JNI_ERR; // Only for jni_internal_test. Real code will have aborted already.
- }
- return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorEnter(env, obj));
- }
-
- static jint MonitorExit(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default | kFlag_ExcepOkay, "EL", env, obj);
- if (!sc.CheckInstance(ScopedCheck::kObject, obj)) {
- return JNI_ERR; // Only for jni_internal_test. Real code will have aborted already.
- }
- return CHECK_JNI_EXIT("I", baseEnv(env)->MonitorExit(env, obj));
- }
-
- static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
- CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, vm);
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetJavaVM(env, vm));
- }
-
- static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, jchar* buf) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
- baseEnv(env)->GetStringRegion(env, str, start, len, buf);
- CHECK_JNI_EXIT_VOID();
- }
-
- static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, jsize len, char* buf) {
- CHECK_JNI_ENTRY(kFlag_CritOkay, "EsIIp", env, str, start, len, buf);
- baseEnv(env)->GetStringUTFRegion(env, str, start, len, buf);
- CHECK_JNI_EXIT_VOID();
- }
-
- static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritGet, "Eap", env, array, isCopy);
- void* result = baseEnv(env)->GetPrimitiveArrayCritical(env, array, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- result = CreateGuardedPACopy(env, array, isCopy);
- }
- return CHECK_JNI_EXIT("p", result);
- }
-
- static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
- CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Eapr", env, array, carray, mode);
- sc.CheckNonNull(carray);
- if (sc.ForceCopy()) {
- ReleaseGuardedPACopy(env, array, carray, mode);
- }
- baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
- CHECK_JNI_EXIT_VOID();
- }
-
- static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* isCopy) {
- CHECK_JNI_ENTRY(kFlag_CritGet, "Esp", env, java_string, isCopy);
- const jchar* result = baseEnv(env)->GetStringCritical(env, java_string, isCopy);
- if (sc.ForceCopy() && result != nullptr) {
- mirror::String* s = sc.soa().Decode<mirror::String*>(java_string);
- int byteCount = s->GetLength() * 2;
- result = (const jchar*) GuardedCopy::Create(result, byteCount, false);
- if (isCopy != nullptr) {
- *isCopy = JNI_TRUE;
- }
- }
- return CHECK_JNI_EXIT("p", result);
- }
-
- static void ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* carray) {
- CHECK_JNI_ENTRY(kFlag_CritRelease | kFlag_ExcepOkay, "Esp", env, string, carray);
- sc.CheckNonNull(carray);
- if (sc.ForceCopy()) {
- GuardedCopy::Check(__FUNCTION__, carray, false);
- carray = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<jchar*>(carray)));
- }
- baseEnv(env)->ReleaseStringCritical(env, string, carray);
- CHECK_JNI_EXIT_VOID();
+ static jobject NewLocalRef(JNIEnv* env, jobject obj) {
+ return NewRef(__FUNCTION__, env, obj, kLocal);
}
static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, obj);
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewWeakGlobalRef(env, obj));
+ return NewRef(__FUNCTION__, env, obj, kWeakGlobal);
}
- static jboolean ExceptionCheck(JNIEnv* env) {
- CHECK_JNI_ENTRY(kFlag_CritOkay | kFlag_ExcepOkay, "E", env);
- return CHECK_JNI_EXIT("b", baseEnv(env)->ExceptionCheck(env));
+ static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
+ DeleteRef(__FUNCTION__, env, obj, kGlobal);
}
- static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
- // Note: we use "Ep" rather than "EL" because this is the one JNI function
- // that it's okay to pass an invalid reference to.
- CHECK_JNI_ENTRY(kFlag_Default, "Ep", env, obj);
- // TODO: proper decoding of jobjectRefType!
- return CHECK_JNI_EXIT("I", baseEnv(env)->GetObjectRefType(env, obj));
+ static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
+ DeleteRef(__FUNCTION__, env, obj, kWeakGlobal);
+ }
+
+ static void DeleteLocalRef(JNIEnv* env, jobject obj) {
+ DeleteRef(__FUNCTION__, env, obj, kLocal);
+ }
+
+ static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.I = capacity}};
+ if (sc.Check(soa, true, "EI", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->EnsureLocalCapacity(env, capacity);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.L = ref1}, {.L = ref2}};
+ if (sc.Check(soa, true, "ELL", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->IsSameObject(env, ref1, ref2);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
+ }
+
+ static jobject AllocObject(JNIEnv* env, jclass c) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.c = c}};
+ if (sc.Check(soa, true, "Ec", args) && sc.CheckInstantiableNonArray(soa, c)) {
+ JniValueType result;
+ result.L = baseEnv(env)->AllocObject(env, c);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
+ }
+
+ static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+ if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) &&
+ sc.CheckConstructor(soa, mid)) {
+ JniValueType result;
+ result.L = baseEnv(env)->NewObjectV(env, c, mid, vargs);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
+ }
+
+ static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) {
+ va_list args;
+ va_start(args, mid);
+ jobject result = NewObjectV(env, c, mid, args);
+ va_end(args);
+ return result;
+ }
+
+ static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+ if (sc.Check(soa, true, "Ecm.", args) && sc.CheckInstantiableNonArray(soa, c) &&
+ sc.CheckConstructor(soa, mid)) {
+ JniValueType result;
+ result.L = baseEnv(env)->NewObjectA(env, c, mid, vargs);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
+ }
+
+ static jclass GetObjectClass(JNIEnv* env, jobject obj) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.c = baseEnv(env)->GetObjectClass(env, obj);
+ if (sc.Check(soa, false, "c", &result)) {
+ return result.c;
+ }
+ }
+ return nullptr;
+ }
+
+ static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.L = obj}, {.c = c}};
+ if (sc.Check(soa, true, "ELc", args)) {
+ JniValueType result;
+ result.b = baseEnv(env)->IsInstanceOf(env, obj, c);
+ if (sc.Check(soa, false, "b", &result)) {
+ return result.b;
+ }
+ }
+ return JNI_FALSE;
+ }
+
+ static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, false);
+ }
+
+ static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, true);
+ }
+
+ static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, false);
+ }
+
+ static jfieldID GetStaticFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, true);
+ }
+
+#define FIELD_ACCESSORS(jtype, name, ptype, shorty) \
+ static jtype GetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid) { \
+ return GetField(__FUNCTION__, env, c, fid, true, ptype).shorty; \
+ } \
+ \
+ static jtype Get##name##Field(JNIEnv* env, jobject obj, jfieldID fid) { \
+ return GetField(__FUNCTION__, env, obj, fid, false, ptype).shorty; \
+ } \
+ \
+ static void SetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid, jtype v) { \
+ JniValueType value; \
+ value.shorty = v; \
+ SetField(__FUNCTION__, env, c, fid, true, ptype, value); \
+ } \
+ \
+ static void Set##name##Field(JNIEnv* env, jobject obj, jfieldID fid, jtype v) { \
+ JniValueType value; \
+ value.shorty = v; \
+ SetField(__FUNCTION__, env, obj, fid, false, ptype, value); \
+ }
+
+ FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L)
+ FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z)
+ FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B)
+ FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C)
+ FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S)
+ FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I)
+ FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J)
+ FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F)
+ FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D)
+#undef FIELD_ACCESSORS
+
+ static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) {
+ CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+ }
+
+ static void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid,
+ jvalue* vargs) {
+ CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+ }
+
+ static void CallStaticVoidMethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+ CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+ }
+
+ static void CallVoidMethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list vargs) {
+ CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+ }
+
+ static void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass c, jmethodID mid,
+ va_list vargs) {
+ CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+ }
+
+ static void CallStaticVoidMethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+ CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+ }
+
+ static void CallVoidMethod(JNIEnv* env, jobject obj, jmethodID mid, ...) {
+ va_list vargs;
+ va_start(vargs, mid);
+ CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, Primitive::kPrimVoid, kVirtual);
+ va_end(vargs);
+ }
+
+ static void CallNonvirtualVoidMethod(JNIEnv* env, jobject obj, jclass c, jmethodID mid, ...) {
+ va_list vargs;
+ va_start(vargs, mid);
+ CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
+ va_end(vargs);
+ }
+
+ static void CallStaticVoidMethod(JNIEnv* env, jclass c, jmethodID mid, ...) {
+ va_list vargs;
+ va_start(vargs, mid);
+ CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
+ va_end(vargs);
+ }
+
+#define CALL(rtype, name, ptype, shorty) \
+ static rtype Call##name##MethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) { \
+ return CallMethodA(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+ } \
+ \
+ static rtype CallNonvirtual##name##MethodA(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+ jvalue* vargs) { \
+ return CallMethodA(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+ } \
+ \
+ static rtype CallStatic##name##MethodA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) { \
+ return CallMethodA(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+ } \
+ \
+ static rtype Call##name##MethodV(JNIEnv* env, jobject obj, jmethodID mid, va_list vargs) { \
+ return CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+ } \
+ \
+ static rtype CallNonvirtual##name##MethodV(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+ va_list vargs) { \
+ return CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+ } \
+ \
+ static rtype CallStatic##name##MethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) { \
+ return CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+ } \
+ \
+ static rtype Call##name##Method(JNIEnv* env, jobject obj, jmethodID mid, ...) { \
+ va_list vargs; \
+ va_start(vargs, mid); \
+ rtype result = \
+ CallMethodV(__FUNCTION__, env, obj, nullptr, mid, vargs, ptype, kVirtual).shorty; \
+ va_end(vargs); \
+ return result; \
+ } \
+ \
+ static rtype CallNonvirtual##name##Method(JNIEnv* env, jobject obj, jclass c, jmethodID mid, \
+ ...) { \
+ va_list vargs; \
+ va_start(vargs, mid); \
+ rtype result = \
+ CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, ptype, kDirect).shorty; \
+ va_end(vargs); \
+ return result; \
+ } \
+ \
+ static rtype CallStatic##name##Method(JNIEnv* env, jclass c, jmethodID mid, ...) { \
+ va_list vargs; \
+ va_start(vargs, mid); \
+ rtype result = \
+ CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, ptype, kStatic).shorty; \
+ va_end(vargs); \
+ return result; \
+ }
+
+ CALL(jobject, Object, Primitive::kPrimNot, L)
+ CALL(jboolean, Boolean, Primitive::kPrimBoolean, Z)
+ CALL(jbyte, Byte, Primitive::kPrimByte, B)
+ CALL(jchar, Char, Primitive::kPrimChar, C)
+ CALL(jshort, Short, Primitive::kPrimShort, S)
+ CALL(jint, Int, Primitive::kPrimInt, I)
+ CALL(jlong, Long, Primitive::kPrimLong, J)
+ CALL(jfloat, Float, Primitive::kPrimFloat, F)
+ CALL(jdouble, Double, Primitive::kPrimDouble, D)
+#undef CALL
+
+ static jstring NewString(JNIEnv* env, const jchar* unicode_chars, jsize len) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.p = unicode_chars}, {.z = len}};
+ if (sc.Check(soa, true, "Epz", args)) {
+ JniValueType result;
+ result.s = baseEnv(env)->NewString(env, unicode_chars, len);
+ if (sc.Check(soa, false, "s", &result)) {
+ return result.s;
+ }
+ }
+ return nullptr;
+ }
+
+ static jstring NewStringUTF(JNIEnv* env, const char* chars) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.u = chars}};
+ if (sc.Check(soa, true, "Eu", args)) {
+ JniValueType result;
+ // TODO: stale? show pointer and truncate string.
+ result.s = baseEnv(env)->NewStringUTF(env, chars);
+ if (sc.Check(soa, false, "s", &result)) {
+ return result.s;
+ }
+ }
+ return nullptr;
+ }
+
+ static jsize GetStringLength(JNIEnv* env, jstring string) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.s = string}};
+ if (sc.Check(soa, true, "Es", args)) {
+ JniValueType result;
+ result.z = baseEnv(env)->GetStringLength(env, string);
+ if (sc.Check(soa, false, "z", &result)) {
+ return result.z;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.s = string}};
+ if (sc.Check(soa, true, "Es", args)) {
+ JniValueType result;
+ result.z = baseEnv(env)->GetStringUTFLength(env, string);
+ if (sc.Check(soa, false, "z", &result)) {
+ return result.z;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static const jchar* GetStringChars(JNIEnv* env, jstring string, jboolean* is_copy) {
+ return reinterpret_cast<const jchar*>(GetStringCharsInternal(__FUNCTION__, env, string,
+ is_copy, false, false));
+ }
+
+ static const char* GetStringUTFChars(JNIEnv* env, jstring string, jboolean* is_copy) {
+ return reinterpret_cast<const char*>(GetStringCharsInternal(__FUNCTION__, env, string,
+ is_copy, true, false));
+ }
+
+ static const jchar* GetStringCritical(JNIEnv* env, jstring string, jboolean* is_copy) {
+ return reinterpret_cast<const jchar*>(GetStringCharsInternal(__FUNCTION__, env, string,
+ is_copy, false, true));
+ }
+
+ static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) {
+ ReleaseStringCharsInternal(__FUNCTION__, env, string, chars, false, false);
+ }
+
+ static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) {
+ ReleaseStringCharsInternal(__FUNCTION__, env, string, utf, true, false);
+ }
+
+ static void ReleaseStringCritical(JNIEnv* env, jstring string, const jchar* chars) {
+ ReleaseStringCharsInternal(__FUNCTION__, env, string, chars, false, true);
+ }
+
+ static void GetStringRegion(JNIEnv* env, jstring string, jsize start, jsize len, jchar* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EsIIp", args)) {
+ baseEnv(env)->GetStringRegion(env, string, start, len, buf);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static void GetStringUTFRegion(JNIEnv* env, jstring string, jsize start, jsize len, char* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EsIIp", args)) {
+ baseEnv(env)->GetStringUTFRegion(env, string, start, len, buf);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static jsize GetArrayLength(JNIEnv* env, jarray array) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.a = array}};
+ if (sc.Check(soa, true, "Ea", args)) {
+ JniValueType result;
+ result.z = baseEnv(env)->GetArrayLength(env, array);
+ if (sc.Check(soa, false, "z", &result)) {
+ return result.z;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_class,
+ jobject initial_element) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] =
+ {{.E = env}, {.z = length}, {.c = element_class}, {.L = initial_element}};
+ if (sc.Check(soa, true, "EzcL", args)) {
+ JniValueType result;
+ // Note: assignability tests of initial_element are done in the base implementation.
+ result.a = baseEnv(env)->NewObjectArray(env, length, element_class, initial_element);
+ if (sc.Check(soa, false, "a", &result)) {
+ return down_cast<jobjectArray>(result.a);
+ }
+ }
+ return nullptr;
+ }
+
+ static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.a = array}, {.z = index}};
+ if (sc.Check(soa, true, "Eaz", args)) {
+ JniValueType result;
+ result.L = baseEnv(env)->GetObjectArrayElement(env, array, index);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
+ }
+ return nullptr;
+ }
+
+ static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[4] = {{.E = env}, {.a = array}, {.z = index}, {.L = value}};
+ // Note: the index arguments is checked as 'I' rather than 'z' as invalid indices result in
+ // ArrayIndexOutOfBoundsExceptions in the base implementation. Similarly invalid stores result
+ // in ArrayStoreExceptions.
+ if (sc.Check(soa, true, "EaIL", args)) {
+ baseEnv(env)->SetObjectArrayElement(env, array, index, value);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static jbooleanArray NewBooleanArray(JNIEnv* env, jsize length) {
+ return down_cast<jbooleanArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimBoolean));
+ }
+
+ static jbyteArray NewByteArray(JNIEnv* env, jsize length) {
+ return down_cast<jbyteArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimByte));
+ }
+
+ static jcharArray NewCharArray(JNIEnv* env, jsize length) {
+ return down_cast<jcharArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimChar));
+ }
+
+ static jshortArray NewShortArray(JNIEnv* env, jsize length) {
+ return down_cast<jshortArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimShort));
+ }
+
+ static jintArray NewIntArray(JNIEnv* env, jsize length) {
+ return down_cast<jintArray>(NewPrimitiveArray(__FUNCTION__, env, length, Primitive::kPrimInt));
+ }
+
+ static jlongArray NewLongArray(JNIEnv* env, jsize length) {
+ return down_cast<jlongArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimLong));
+ }
+
+ static jfloatArray NewFloatArray(JNIEnv* env, jsize length) {
+ return down_cast<jfloatArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimFloat));
+ }
+
+ static jdoubleArray NewDoubleArray(JNIEnv* env, jsize length) {
+ return down_cast<jdoubleArray>(NewPrimitiveArray(__FUNCTION__, env, length,
+ Primitive::kPrimDouble));
+ }
+
+#define PRIMITIVE_ARRAY_FUNCTIONS(ctype, name, ptype) \
+ static ctype* Get##name##ArrayElements(JNIEnv* env, ctype##Array array, jboolean* is_copy) { \
+ return reinterpret_cast<ctype*>( \
+ GetPrimitiveArrayElements(__FUNCTION__, ptype, env, array, is_copy)); \
+ } \
+ \
+ static void Release##name##ArrayElements(JNIEnv* env, ctype##Array array, ctype* elems, \
+ jint mode) { \
+ ReleasePrimitiveArrayElements(__FUNCTION__, ptype, env, array, elems, mode); \
+ } \
+ \
+ static void Get##name##ArrayRegion(JNIEnv* env, ctype##Array array, jsize start, jsize len, \
+ ctype* buf) { \
+ GetPrimitiveArrayRegion(__FUNCTION__, ptype, env, array, start, len, buf); \
+ } \
+ \
+ static void Set##name##ArrayRegion(JNIEnv* env, ctype##Array array, jsize start, jsize len, \
+ const ctype* buf) { \
+ SetPrimitiveArrayRegion(__FUNCTION__, ptype, env, array, start, len, buf); \
+ }
+
+ PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean, Primitive::kPrimBoolean)
+ PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte, Primitive::kPrimByte)
+ PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char, Primitive::kPrimChar)
+ PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short, Primitive::kPrimShort)
+ PRIMITIVE_ARRAY_FUNCTIONS(jint, Int, Primitive::kPrimInt)
+ PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long, Primitive::kPrimLong)
+ PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float, Primitive::kPrimFloat)
+ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double, Primitive::kPrimDouble)
+#undef PRIMITIVE_ARRAY_FUNCTIONS
+
+ static jint MonitorEnter(JNIEnv* env, jobject obj) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->MonitorEnter(env, obj);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static jint MonitorExit(JNIEnv* env, jobject obj) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ result.i = baseEnv(env)->MonitorExit(env, obj);
+ if (sc.Check(soa, false, "i", &result)) {
+ return result.i;
+ }
+ }
+ return JNI_ERR;
+ }
+
+ static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* is_copy) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritGet, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
+ if (sc.Check(soa, true, "Eap", args)) {
+ JniValueType result;
+ result.p = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
+ if (result.p != nullptr && soa.ForceCopy()) {
+ result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+ }
+ if (sc.Check(soa, false, "p", &result)) {
+ return const_cast<void*>(result.p);
+ }
+ }
+ return nullptr;
+ }
+
+ static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_CritRelease | kFlag_ExcepOkay, __FUNCTION__);
+ sc.CheckNonNull(carray);
+ JniValueType args[4] = {{.E = env}, {.a = array}, {.p = carray}, {.r = mode}};
+ if (sc.Check(soa, true, "Eapr", args)) {
+ if (soa.ForceCopy()) {
+ GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
+ }
+ baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
}
static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
- CHECK_JNI_ENTRY(kFlag_Default, "EpJ", env, address, capacity);
- if (address == nullptr) {
- JniAbortF(__FUNCTION__, "non-nullable address is NULL");
- return nullptr;
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[3] = {{.E = env}, {.p = address}, {.J = capacity}};
+ if (sc.Check(soa, true, "EpJ", args)) {
+ JniValueType result;
+ // Note: the validity of address and capacity are checked in the base implementation.
+ result.L = baseEnv(env)->NewDirectByteBuffer(env, address, capacity);
+ if (sc.Check(soa, false, "L", &result)) {
+ return result.L;
+ }
}
- return CHECK_JNI_EXIT("L", baseEnv(env)->NewDirectByteBuffer(env, address, capacity));
+ return nullptr;
}
static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
- // TODO: check that 'buf' is a java.nio.Buffer.
- return CHECK_JNI_EXIT("p", baseEnv(env)->GetDirectBufferAddress(env, buf));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = buf}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ // Note: this is implemented in the base environment by a GetLongField which will sanity
+ // check the type of buf in GetLongField above.
+ result.p = baseEnv(env)->GetDirectBufferAddress(env, buf);
+ if (sc.Check(soa, false, "p", &result)) {
+ return const_cast<void*>(result.p);
+ }
+ }
+ return nullptr;
}
static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
- CHECK_JNI_ENTRY(kFlag_Default, "EL", env, buf);
- // TODO: check that 'buf' is a java.nio.Buffer.
- return CHECK_JNI_EXIT("J", baseEnv(env)->GetDirectBufferCapacity(env, buf));
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.L = buf}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ // Note: this is implemented in the base environment by a GetIntField which will sanity
+ // check the type of buf in GetIntField above.
+ result.J = baseEnv(env)->GetDirectBufferCapacity(env, buf);
+ if (sc.Check(soa, false, "J", &result)) {
+ return result.J;
+ }
+ }
+ return JNI_ERR;
}
private:
- static inline const JNINativeInterface* baseEnv(JNIEnv* env) {
+ static JavaVMExt* GetJavaVMExt(JNIEnv* env) {
+ return reinterpret_cast<JNIEnvExt*>(env)->vm;
+ }
+
+ static const JNINativeInterface* baseEnv(JNIEnv* env) {
return reinterpret_cast<JNIEnvExt*>(env)->unchecked_functions;
}
+
+ static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ if (sc.Check(soa, true, "EL", args)) {
+ JniValueType result;
+ switch (kind) {
+ case kGlobal:
+ result.L = baseEnv(env)->NewGlobalRef(env, obj);
+ break;
+ case kLocal:
+ result.L = baseEnv(env)->NewLocalRef(env, obj);
+ break;
+ case kWeakGlobal:
+ result.L = baseEnv(env)->NewWeakGlobalRef(env, obj);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected reference kind: " << kind;
+ }
+ if (sc.Check(soa, false, "L", &result)) {
+ DCHECK_EQ(IsSameObject(env, obj, result.L), JNI_TRUE);
+ DCHECK(sc.CheckReferenceKind(kind, soa.Vm(), soa.Self(), result.L));
+ return result.L;
+ }
+ }
+ return nullptr;
+ }
+
+ static void DeleteRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, function_name);
+ JniValueType args[2] = {{.E = env}, {.L = obj}};
+ sc.Check(soa, true, "EL", args);
+ if (sc.CheckReferenceKind(kind, soa.Vm(), soa.Self(), obj)) {
+ JniValueType result;
+ switch (kind) {
+ case kGlobal:
+ baseEnv(env)->DeleteGlobalRef(env, obj);
+ break;
+ case kLocal:
+ baseEnv(env)->DeleteLocalRef(env, obj);
+ break;
+ case kWeakGlobal:
+ baseEnv(env)->DeleteWeakGlobalRef(env, obj);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected reference kind: " << kind;
+ }
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static jmethodID GetMethodIDInternal(const char* function_name, JNIEnv* env, jclass c,
+ const char* name, const char* sig, bool is_static) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
+ if (sc.Check(soa, true, "Ecuu", args)) {
+ JniValueType result;
+ if (is_static) {
+ result.m = baseEnv(env)->GetStaticMethodID(env, c, name, sig);
+ } else {
+ result.m = baseEnv(env)->GetMethodID(env, c, name, sig);
+ }
+ if (sc.Check(soa, false, "m", &result)) {
+ return result.m;
+ }
+ }
+ return nullptr;
+ }
+
+ static jfieldID GetFieldIDInternal(const char* function_name, JNIEnv* env, jclass c,
+ const char* name, const char* sig, bool is_static) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
+ if (sc.Check(soa, true, "Ecuu", args)) {
+ JniValueType result;
+ if (is_static) {
+ result.f = baseEnv(env)->GetStaticFieldID(env, c, name, sig);
+ } else {
+ result.f = baseEnv(env)->GetFieldID(env, c, name, sig);
+ }
+ if (sc.Check(soa, false, "f", &result)) {
+ return result.f;
+ }
+ }
+ return nullptr;
+ }
+
+ static JniValueType GetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
+ bool is_static, Primitive::Type type) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[3] = {{.E = env}, {.L = obj}, {.f = fid}};
+ JniValueType result;
+ if (sc.Check(soa, true, is_static ? "Ecf" : "ELf", args) &&
+ sc.CheckFieldAccess(soa, obj, fid, is_static, type)) {
+ const char* result_check = nullptr;
+ switch (type) {
+ case Primitive::kPrimNot:
+ if (is_static) {
+ result.L = baseEnv(env)->GetStaticObjectField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.L = baseEnv(env)->GetObjectField(env, obj, fid);
+ }
+ result_check = "L";
+ break;
+ case Primitive::kPrimBoolean:
+ if (is_static) {
+ result.Z = baseEnv(env)->GetStaticBooleanField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.Z = baseEnv(env)->GetBooleanField(env, obj, fid);
+ }
+ result_check = "Z";
+ break;
+ case Primitive::kPrimByte:
+ if (is_static) {
+ result.B = baseEnv(env)->GetStaticByteField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.B = baseEnv(env)->GetByteField(env, obj, fid);
+ }
+ result_check = "B";
+ break;
+ case Primitive::kPrimChar:
+ if (is_static) {
+ result.C = baseEnv(env)->GetStaticCharField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.C = baseEnv(env)->GetCharField(env, obj, fid);
+ }
+ result_check = "C";
+ break;
+ case Primitive::kPrimShort:
+ if (is_static) {
+ result.S = baseEnv(env)->GetStaticShortField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.S = baseEnv(env)->GetShortField(env, obj, fid);
+ }
+ result_check = "S";
+ break;
+ case Primitive::kPrimInt:
+ if (is_static) {
+ result.I = baseEnv(env)->GetStaticIntField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.I = baseEnv(env)->GetIntField(env, obj, fid);
+ }
+ result_check = "I";
+ break;
+ case Primitive::kPrimLong:
+ if (is_static) {
+ result.J = baseEnv(env)->GetStaticLongField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.J = baseEnv(env)->GetLongField(env, obj, fid);
+ }
+ result_check = "J";
+ break;
+ case Primitive::kPrimFloat:
+ if (is_static) {
+ result.F = baseEnv(env)->GetStaticFloatField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.F = baseEnv(env)->GetFloatField(env, obj, fid);
+ }
+ result_check = "F";
+ break;
+ case Primitive::kPrimDouble:
+ if (is_static) {
+ result.D = baseEnv(env)->GetStaticDoubleField(env, down_cast<jclass>(obj), fid);
+ } else {
+ result.D = baseEnv(env)->GetDoubleField(env, obj, fid);
+ }
+ result_check = "D";
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type: " << type;
+ break;
+ }
+ if (sc.Check(soa, false, result_check, &result)) {
+ return result;
+ }
+ }
+ result.J = 0;
+ return result;
+ }
+
+ static void SetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
+ bool is_static, Primitive::Type type, JniValueType value) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[4] = {{.E = env}, {.L = obj}, {.f = fid}, value};
+ char sig[5] = { 'E', is_static ? 'c' : 'L', 'f',
+ type == Primitive::kPrimNot ? 'L' : Primitive::Descriptor(type)[0], '\0'};
+ if (sc.Check(soa, true, sig, args) &&
+ sc.CheckFieldAccess(soa, obj, fid, is_static, type)) {
+ switch (type) {
+ case Primitive::kPrimNot:
+ if (is_static) {
+ baseEnv(env)->SetStaticObjectField(env, down_cast<jclass>(obj), fid, value.L);
+ } else {
+ baseEnv(env)->SetObjectField(env, obj, fid, value.L);
+ }
+ break;
+ case Primitive::kPrimBoolean:
+ if (is_static) {
+ baseEnv(env)->SetStaticBooleanField(env, down_cast<jclass>(obj), fid, value.Z);
+ } else {
+ baseEnv(env)->SetBooleanField(env, obj, fid, value.Z);
+ }
+ break;
+ case Primitive::kPrimByte:
+ if (is_static) {
+ baseEnv(env)->SetStaticByteField(env, down_cast<jclass>(obj), fid, value.B);
+ } else {
+ baseEnv(env)->SetByteField(env, obj, fid, value.B);
+ }
+ break;
+ case Primitive::kPrimChar:
+ if (is_static) {
+ baseEnv(env)->SetStaticCharField(env, down_cast<jclass>(obj), fid, value.C);
+ } else {
+ baseEnv(env)->SetCharField(env, obj, fid, value.C);
+ }
+ break;
+ case Primitive::kPrimShort:
+ if (is_static) {
+ baseEnv(env)->SetStaticShortField(env, down_cast<jclass>(obj), fid, value.S);
+ } else {
+ baseEnv(env)->SetShortField(env, obj, fid, value.S);
+ }
+ break;
+ case Primitive::kPrimInt:
+ if (is_static) {
+ baseEnv(env)->SetStaticIntField(env, down_cast<jclass>(obj), fid, value.I);
+ } else {
+ baseEnv(env)->SetIntField(env, obj, fid, value.I);
+ }
+ break;
+ case Primitive::kPrimLong:
+ if (is_static) {
+ baseEnv(env)->SetStaticLongField(env, down_cast<jclass>(obj), fid, value.J);
+ } else {
+ baseEnv(env)->SetLongField(env, obj, fid, value.J);
+ }
+ break;
+ case Primitive::kPrimFloat:
+ if (is_static) {
+ baseEnv(env)->SetStaticFloatField(env, down_cast<jclass>(obj), fid, value.F);
+ } else {
+ baseEnv(env)->SetFloatField(env, obj, fid, value.F);
+ }
+ break;
+ case Primitive::kPrimDouble:
+ if (is_static) {
+ baseEnv(env)->SetStaticDoubleField(env, down_cast<jclass>(obj), fid, value.D);
+ } else {
+ baseEnv(env)->SetDoubleField(env, obj, fid, value.D);
+ }
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unexpected type: " << type;
+ break;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static bool CheckCallArgs(ScopedObjectAccess& soa, ScopedCheck& sc, JNIEnv* env, jobject obj,
+ jclass c, jmethodID mid, InvokeType invoke)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool checked;
+ switch (invoke) {
+ case kVirtual: {
+ DCHECK(c == nullptr);
+ JniValueType args[3] = {{.E = env}, {.L = obj}, {.m = mid}};
+ checked = sc.Check(soa, true, "ELm.", args);
+ break;
+ }
+ case kDirect: {
+ JniValueType args[4] = {{.E = env}, {.L = obj}, {.c = c}, {.m = mid}};
+ checked = sc.Check(soa, true, "ELcm.", args);
+ break;
+ }
+ case kStatic: {
+ DCHECK(obj == nullptr);
+ JniValueType args[3] = {{.E = env}, {.c = c}, {.m = mid}};
+ checked = sc.Check(soa, true, "Ecm.", args);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ checked = false;
+ break;
+ }
+ return checked;
+ }
+
+ static JniValueType CallMethodA(const char* function_name, JNIEnv* env, jobject obj, jclass c,
+ jmethodID mid, jvalue* vargs, Primitive::Type type,
+ InvokeType invoke) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType result;
+ if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) &&
+ sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) {
+ const char* result_check;
+ switch (type) {
+ case Primitive::kPrimNot:
+ result_check = "L";
+ switch (invoke) {
+ case kVirtual:
+ result.L = baseEnv(env)->CallObjectMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.L = baseEnv(env)->CallNonvirtualObjectMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.L = baseEnv(env)->CallStaticObjectMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimBoolean:
+ result_check = "Z";
+ switch (invoke) {
+ case kVirtual:
+ result.Z = baseEnv(env)->CallBooleanMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.Z = baseEnv(env)->CallNonvirtualBooleanMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.Z = baseEnv(env)->CallStaticBooleanMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimByte:
+ result_check = "B";
+ switch (invoke) {
+ case kVirtual:
+ result.B = baseEnv(env)->CallByteMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.B = baseEnv(env)->CallNonvirtualByteMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.B = baseEnv(env)->CallStaticByteMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimChar:
+ result_check = "C";
+ switch (invoke) {
+ case kVirtual:
+ result.C = baseEnv(env)->CallCharMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.C = baseEnv(env)->CallNonvirtualCharMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.C = baseEnv(env)->CallStaticCharMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimShort:
+ result_check = "S";
+ switch (invoke) {
+ case kVirtual:
+ result.S = baseEnv(env)->CallShortMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.S = baseEnv(env)->CallNonvirtualShortMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.S = baseEnv(env)->CallStaticShortMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimInt:
+ result_check = "I";
+ switch (invoke) {
+ case kVirtual:
+ result.I = baseEnv(env)->CallIntMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.I = baseEnv(env)->CallNonvirtualIntMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.I = baseEnv(env)->CallStaticIntMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimLong:
+ result_check = "J";
+ switch (invoke) {
+ case kVirtual:
+ result.J = baseEnv(env)->CallLongMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.J = baseEnv(env)->CallNonvirtualLongMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.J = baseEnv(env)->CallStaticLongMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimFloat:
+ result_check = "F";
+ switch (invoke) {
+ case kVirtual:
+ result.F = baseEnv(env)->CallFloatMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.F = baseEnv(env)->CallNonvirtualFloatMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.F = baseEnv(env)->CallStaticFloatMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimDouble:
+ result_check = "D";
+ switch (invoke) {
+ case kVirtual:
+ result.D = baseEnv(env)->CallDoubleMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.D = baseEnv(env)->CallNonvirtualDoubleMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.D = baseEnv(env)->CallStaticDoubleMethodA(env, c, mid, vargs);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Primitive::kPrimVoid:
+ result_check = "V";
+ result.V = nullptr;
+ switch (invoke) {
+ case kVirtual:
+ baseEnv(env)->CallVoidMethodA(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ baseEnv(env)->CallNonvirtualVoidMethodA(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ baseEnv(env)->CallStaticVoidMethodA(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected return type: " << type;
+ result_check = nullptr;
+ }
+ if (sc.Check(soa, false, result_check, &result)) {
+ return result;
+ }
+ }
+ result.J = 0;
+ return result;
+ }
+
+ static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c,
+ jmethodID mid, va_list vargs, Primitive::Type type,
+ InvokeType invoke) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType result;
+ if (CheckCallArgs(soa, sc, env, obj, c, mid, invoke) &&
+ sc.CheckMethodAndSig(soa, obj, c, mid, type, invoke)) {
+ const char* result_check;
+ switch (type) {
+ case Primitive::kPrimNot:
+ result_check = "L";
+ switch (invoke) {
+ case kVirtual:
+ result.L = baseEnv(env)->CallObjectMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.L = baseEnv(env)->CallNonvirtualObjectMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.L = baseEnv(env)->CallStaticObjectMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimBoolean:
+ result_check = "Z";
+ switch (invoke) {
+ case kVirtual:
+ result.Z = baseEnv(env)->CallBooleanMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.Z = baseEnv(env)->CallNonvirtualBooleanMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.Z = baseEnv(env)->CallStaticBooleanMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimByte:
+ result_check = "B";
+ switch (invoke) {
+ case kVirtual:
+ result.B = baseEnv(env)->CallByteMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.B = baseEnv(env)->CallNonvirtualByteMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.B = baseEnv(env)->CallStaticByteMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimChar:
+ result_check = "C";
+ switch (invoke) {
+ case kVirtual:
+ result.C = baseEnv(env)->CallCharMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.C = baseEnv(env)->CallNonvirtualCharMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.C = baseEnv(env)->CallStaticCharMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimShort:
+ result_check = "S";
+ switch (invoke) {
+ case kVirtual:
+ result.S = baseEnv(env)->CallShortMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.S = baseEnv(env)->CallNonvirtualShortMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.S = baseEnv(env)->CallStaticShortMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimInt:
+ result_check = "I";
+ switch (invoke) {
+ case kVirtual:
+ result.I = baseEnv(env)->CallIntMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.I = baseEnv(env)->CallNonvirtualIntMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.I = baseEnv(env)->CallStaticIntMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimLong:
+ result_check = "J";
+ switch (invoke) {
+ case kVirtual:
+ result.J = baseEnv(env)->CallLongMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.J = baseEnv(env)->CallNonvirtualLongMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.J = baseEnv(env)->CallStaticLongMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimFloat:
+ result_check = "F";
+ switch (invoke) {
+ case kVirtual:
+ result.F = baseEnv(env)->CallFloatMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.F = baseEnv(env)->CallNonvirtualFloatMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.F = baseEnv(env)->CallStaticFloatMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimDouble:
+ result_check = "D";
+ switch (invoke) {
+ case kVirtual:
+ result.D = baseEnv(env)->CallDoubleMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ result.D = baseEnv(env)->CallNonvirtualDoubleMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ result.D = baseEnv(env)->CallStaticDoubleMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ case Primitive::kPrimVoid:
+ result_check = "V";
+ result.V = nullptr;
+ switch (invoke) {
+ case kVirtual:
+ baseEnv(env)->CallVoidMethodV(env, obj, mid, vargs);
+ break;
+ case kDirect:
+ baseEnv(env)->CallNonvirtualVoidMethodV(env, obj, c, mid, vargs);
+ break;
+ case kStatic:
+ baseEnv(env)->CallStaticVoidMethodV(env, c, mid, vargs);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected invoke: " << invoke;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unexpected return type: " << type;
+ result_check = nullptr;
+ }
+ if (sc.Check(soa, false, result_check, &result)) {
+ return result;
+ }
+ }
+ result.J = 0;
+ return result;
+ }
+
+ static const void* GetStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
+ jboolean* is_copy, bool utf, bool critical) {
+ ScopedObjectAccess soa(env);
+ int flags = critical ? kFlag_CritGet : kFlag_CritOkay;
+ ScopedCheck sc(flags, function_name);
+ JniValueType args[3] = {{.E = env}, {.s = string}, {.p = is_copy}};
+ if (sc.Check(soa, true, "Esp", args)) {
+ JniValueType result;
+ if (utf) {
+ CHECK(!critical);
+ result.u = baseEnv(env)->GetStringUTFChars(env, string, is_copy);
+ } else {
+ if (critical) {
+ result.p = baseEnv(env)->GetStringCritical(env, string, is_copy);
+ } else {
+ result.p = baseEnv(env)->GetStringChars(env, string, is_copy);
+ }
+ }
+ // TODO: could we be smarter about not copying when local_is_copy?
+ if (result.p != nullptr && soa.ForceCopy()) {
+ if (utf) {
+ size_t length_in_bytes = strlen(result.u) + 1;
+ result.u =
+ reinterpret_cast<const char*>(GuardedCopy::Create(result.u, length_in_bytes, false));
+ } else {
+ size_t length_in_bytes = baseEnv(env)->GetStringLength(env, string) * 2;
+ result.p =
+ reinterpret_cast<const jchar*>(GuardedCopy::Create(result.p, length_in_bytes, false));
+ }
+ if (is_copy != nullptr) {
+ *is_copy = JNI_TRUE;
+ }
+ }
+ if (sc.Check(soa, false, utf ? "u" : "p", &result)) {
+ return utf ? result.u : result.p;
+ }
+ }
+ return nullptr;
+ }
+
+ static void ReleaseStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
+ const void* chars, bool utf, bool critical) {
+ ScopedObjectAccess soa(env);
+ int flags = kFlag_ExcepOkay | kFlag_Release;
+ if (critical) {
+ flags |= kFlag_CritRelease;
+ }
+ ScopedCheck sc(flags, function_name);
+ sc.CheckNonNull(chars);
+ bool force_copy_ok = !soa.ForceCopy() || GuardedCopy::Check(function_name, chars, false);
+ if (force_copy_ok && soa.ForceCopy()) {
+ chars = reinterpret_cast<const jchar*>(GuardedCopy::Destroy(const_cast<void*>(chars)));
+ }
+ if (force_copy_ok) {
+ JniValueType args[3] = {{.E = env}, {.s = string}, {.p = chars}};
+ if (sc.Check(soa, true, utf ? "Esu" : "Esp", args)) {
+ if (utf) {
+ CHECK(!critical);
+ baseEnv(env)->ReleaseStringUTFChars(env, string, reinterpret_cast<const char*>(chars));
+ } else {
+ if (critical) {
+ baseEnv(env)->ReleaseStringCritical(env, string, reinterpret_cast<const jchar*>(chars));
+ } else {
+ baseEnv(env)->ReleaseStringChars(env, string, reinterpret_cast<const jchar*>(chars));
+ }
+ }
+ JniValueType result;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+ }
+
+ static jarray NewPrimitiveArray(const char* function_name, JNIEnv* env, jsize length,
+ Primitive::Type type) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, __FUNCTION__);
+ JniValueType args[2] = {{.E = env}, {.z = length}};
+ if (sc.Check(soa, true, "Ez", args)) {
+ JniValueType result;
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ result.a = baseEnv(env)->NewBooleanArray(env, length);
+ break;
+ case Primitive::kPrimByte:
+ result.a = baseEnv(env)->NewByteArray(env, length);
+ break;
+ case Primitive::kPrimChar:
+ result.a = baseEnv(env)->NewCharArray(env, length);
+ break;
+ case Primitive::kPrimShort:
+ result.a = baseEnv(env)->NewShortArray(env, length);
+ break;
+ case Primitive::kPrimInt:
+ result.a = baseEnv(env)->NewIntArray(env, length);
+ break;
+ case Primitive::kPrimLong:
+ result.a = baseEnv(env)->NewLongArray(env, length);
+ break;
+ case Primitive::kPrimFloat:
+ result.a = baseEnv(env)->NewFloatArray(env, length);
+ break;
+ case Primitive::kPrimDouble:
+ result.a = baseEnv(env)->NewDoubleArray(env, length);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ if (sc.Check(soa, false, "a", &result)) {
+ return result.a;
+ }
+ }
+ return nullptr;
+ }
+
+ static void* GetPrimitiveArrayElements(const char* function_name, Primitive::Type type,
+ JNIEnv* env, jarray array, jboolean* is_copy) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
+ if (sc.Check(soa, true, "Eap", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ JniValueType result;
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ result.p = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimByte:
+ result.p = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimChar:
+ result.p = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimShort:
+ result.p = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimInt:
+ result.p = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
+ break;
+ case Primitive::kPrimLong:
+ result.p = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimFloat:
+ result.p = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array),
+ is_copy);
+ break;
+ case Primitive::kPrimDouble:
+ result.p = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array),
+ is_copy);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ if (result.p != nullptr && soa.ForceCopy()) {
+ result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+ if (is_copy != nullptr) {
+ *is_copy = JNI_TRUE;
+ }
+ }
+ if (sc.Check(soa, false, "p", &result)) {
+ return const_cast<void*>(result.p);
+ }
+ }
+ return nullptr;
+ }
+
+ static void ReleasePrimitiveArrayElements(const char* function_name, Primitive::Type type,
+ JNIEnv* env, jarray array, void* elems, jint mode) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_ExcepOkay, function_name);
+ if (sc.CheckNonNull(elems) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ if (soa.ForceCopy()) {
+ elems = GuardedCopy::ReleaseGuardedPACopy(function_name, env, array, elems, mode);
+ }
+ if (!soa.ForceCopy() || elems != nullptr) {
+ JniValueType args[4] = {{.E = env}, {.a = array}, {.p = elems}, {.r = mode}};
+ if (sc.Check(soa, true, "Eapr", args)) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ baseEnv(env)->ReleaseBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+ reinterpret_cast<jboolean*>(elems), mode);
+ break;
+ case Primitive::kPrimByte:
+ baseEnv(env)->ReleaseByteArrayElements(env, down_cast<jbyteArray>(array),
+ reinterpret_cast<jbyte*>(elems), mode);
+ break;
+ case Primitive::kPrimChar:
+ baseEnv(env)->ReleaseCharArrayElements(env, down_cast<jcharArray>(array),
+ reinterpret_cast<jchar*>(elems), mode);
+ break;
+ case Primitive::kPrimShort:
+ baseEnv(env)->ReleaseShortArrayElements(env, down_cast<jshortArray>(array),
+ reinterpret_cast<jshort*>(elems), mode);
+ break;
+ case Primitive::kPrimInt:
+ baseEnv(env)->ReleaseIntArrayElements(env, down_cast<jintArray>(array),
+ reinterpret_cast<jint*>(elems), mode);
+ break;
+ case Primitive::kPrimLong:
+ baseEnv(env)->ReleaseLongArrayElements(env, down_cast<jlongArray>(array),
+ reinterpret_cast<jlong*>(elems), mode);
+ break;
+ case Primitive::kPrimFloat:
+ baseEnv(env)->ReleaseFloatArrayElements(env, down_cast<jfloatArray>(array),
+ reinterpret_cast<jfloat*>(elems), mode);
+ break;
+ case Primitive::kPrimDouble:
+ baseEnv(env)->ReleaseDoubleArrayElements(env, down_cast<jdoubleArray>(array),
+ reinterpret_cast<jdouble*>(elems), mode);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+ }
+ }
+
+ static void GetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
+ jarray array, jsize start, jsize len, void* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EaIIp", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ baseEnv(env)->GetBooleanArrayRegion(env, down_cast<jbooleanArray>(array), start, len,
+ reinterpret_cast<jboolean*>(buf));
+ break;
+ case Primitive::kPrimByte:
+ baseEnv(env)->GetByteArrayRegion(env, down_cast<jbyteArray>(array), start, len,
+ reinterpret_cast<jbyte*>(buf));
+ break;
+ case Primitive::kPrimChar:
+ baseEnv(env)->GetCharArrayRegion(env, down_cast<jcharArray>(array), start, len,
+ reinterpret_cast<jchar*>(buf));
+ break;
+ case Primitive::kPrimShort:
+ baseEnv(env)->GetShortArrayRegion(env, down_cast<jshortArray>(array), start, len,
+ reinterpret_cast<jshort*>(buf));
+ break;
+ case Primitive::kPrimInt:
+ baseEnv(env)->GetIntArrayRegion(env, down_cast<jintArray>(array), start, len,
+ reinterpret_cast<jint*>(buf));
+ break;
+ case Primitive::kPrimLong:
+ baseEnv(env)->GetLongArrayRegion(env, down_cast<jlongArray>(array), start, len,
+ reinterpret_cast<jlong*>(buf));
+ break;
+ case Primitive::kPrimFloat:
+ baseEnv(env)->GetFloatArrayRegion(env, down_cast<jfloatArray>(array), start, len,
+ reinterpret_cast<jfloat*>(buf));
+ break;
+ case Primitive::kPrimDouble:
+ baseEnv(env)->GetDoubleArrayRegion(env, down_cast<jdoubleArray>(array), start, len,
+ reinterpret_cast<jdouble*>(buf));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
+
+ static void SetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
+ jarray array, jsize start, jsize len, const void* buf) {
+ ScopedObjectAccess soa(env);
+ ScopedCheck sc(kFlag_Default, function_name);
+ JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
+ // Note: the start and len arguments are checked as 'I' rather than 'z' as invalid indices
+ // result in ArrayIndexOutOfBoundsExceptions in the base implementation.
+ if (sc.Check(soa, true, "EaIIp", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ baseEnv(env)->SetBooleanArrayRegion(env, down_cast<jbooleanArray>(array), start, len,
+ reinterpret_cast<const jboolean*>(buf));
+ break;
+ case Primitive::kPrimByte:
+ baseEnv(env)->SetByteArrayRegion(env, down_cast<jbyteArray>(array), start, len,
+ reinterpret_cast<const jbyte*>(buf));
+ break;
+ case Primitive::kPrimChar:
+ baseEnv(env)->SetCharArrayRegion(env, down_cast<jcharArray>(array), start, len,
+ reinterpret_cast<const jchar*>(buf));
+ break;
+ case Primitive::kPrimShort:
+ baseEnv(env)->SetShortArrayRegion(env, down_cast<jshortArray>(array), start, len,
+ reinterpret_cast<const jshort*>(buf));
+ break;
+ case Primitive::kPrimInt:
+ baseEnv(env)->SetIntArrayRegion(env, down_cast<jintArray>(array), start, len,
+ reinterpret_cast<const jint*>(buf));
+ break;
+ case Primitive::kPrimLong:
+ baseEnv(env)->SetLongArrayRegion(env, down_cast<jlongArray>(array), start, len,
+ reinterpret_cast<const jlong*>(buf));
+ break;
+ case Primitive::kPrimFloat:
+ baseEnv(env)->SetFloatArrayRegion(env, down_cast<jfloatArray>(array), start, len,
+ reinterpret_cast<const jfloat*>(buf));
+ break;
+ case Primitive::kPrimDouble:
+ baseEnv(env)->SetDoubleArrayRegion(env, down_cast<jdoubleArray>(array), start, len,
+ reinterpret_cast<const jdouble*>(buf));
+ break;
+ default:
+ LOG(FATAL) << "Unexpected primitive type: " << type;
+ }
+ JniValueType result;
+ result.V = nullptr;
+ sc.Check(soa, false, "V", &result);
+ }
+ }
};
const JNINativeInterface gCheckNativeInterface = {
@@ -2025,38 +3608,58 @@
class CheckJII {
public:
static jint DestroyJavaVM(JavaVM* vm) {
- ScopedCheck sc(vm, false, __FUNCTION__);
- sc.Check(true, "v", vm);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->DestroyJavaVM(vm));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__, false);
+ JniValueType args[1] = {{.v = vm}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "v", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->DestroyJavaVM(vm);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- ScopedCheck sc(vm, false, __FUNCTION__);
- sc.Check(true, "vpp", vm, p_env, thr_args);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->AttachCurrentThread(vm, p_env, thr_args);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- ScopedCheck sc(vm, false, __FUNCTION__);
- sc.Check(true, "vpp", vm, p_env, thr_args);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.p = thr_args}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpp", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->AttachCurrentThreadAsDaemon(vm, p_env, thr_args);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
static jint DetachCurrentThread(JavaVM* vm) {
- ScopedCheck sc(vm, true, __FUNCTION__);
- sc.Check(true, "v", vm);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->DetachCurrentThread(vm));
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[1] = {{.v = vm}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "v", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->DetachCurrentThread(vm);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
- static jint GetEnv(JavaVM* vm, void** env, jint version) {
- ScopedCheck sc(vm, true, __FUNCTION__);
- sc.Check(true, "vpI", vm);
- return CHECK_JNI_EXIT("I", BaseVm(vm)->GetEnv(vm, env, version));
+ static jint GetEnv(JavaVM* vm, void** p_env, jint version) {
+ ScopedCheck sc(kFlag_Invocation, __FUNCTION__);
+ JniValueType args[3] = {{.v = vm}, {.p = p_env}, {.I = version}};
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), true, "vpI", args);
+ JniValueType result;
+ result.i = BaseVm(vm)->GetEnv(vm, p_env, version);
+ sc.CheckNonHeap(reinterpret_cast<JavaVMExt*>(vm), false, "i", &result);
+ return result.i;
}
private:
- static inline const JNIInvokeInterface* BaseVm(JavaVM* vm) {
- return reinterpret_cast<JavaVMExt*>(vm)->unchecked_functions;
+ static const JNIInvokeInterface* BaseVm(JavaVM* vm) {
+ return reinterpret_cast<JavaVMExt*>(vm)->GetUncheckedFunctions();
}
};
diff --git a/runtime/check_jni.h b/runtime/check_jni.h
new file mode 100644
index 0000000..f41abf8
--- /dev/null
+++ b/runtime/check_jni.h
@@ -0,0 +1,29 @@
+/*
+ * 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_CHECK_JNI_H_
+#define ART_RUNTIME_CHECK_JNI_H_
+
+#include <jni.h>
+
+namespace art {
+
+const JNINativeInterface* GetCheckJniNativeInterface();
+const JNIInvokeInterface* GetCheckJniInvokeInterface();
+
+} // namespace art
+
+#endif // ART_RUNTIME_CHECK_JNI_H_
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
new file mode 100644
index 0000000..1a78d72
--- /dev/null
+++ b/runtime/check_reference_map_visitor.h
@@ -0,0 +1,111 @@
+/*
+ * 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_CHECK_REFERENCE_MAP_VISITOR_H_
+#define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_
+
+#include "gc_map.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack_map.h"
+
+namespace art {
+
+// Helper class for tests checking that the compiler keeps track of dex registers
+// holding references.
+class CheckReferenceMapVisitor : public StackVisitor {
+ public:
+ explicit CheckReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr) {}
+
+ bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* m = GetMethod();
+ if (m->IsCalleeSaveMethod() || m->IsNative()) {
+ CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex);
+ }
+
+ if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) {
+ return true;
+ }
+
+ LOG(INFO) << "At " << PrettyMethod(m, false);
+
+ if (m->IsCalleeSaveMethod()) {
+ LOG(WARNING) << "no PC for " << PrettyMethod(m);
+ return true;
+ }
+
+ return false;
+ }
+
+ void CheckReferences(int* registers, int number_of_references, uint32_t native_pc_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (GetMethod()->IsOptimized()) {
+ CheckOptimizedMethod(registers, number_of_references, native_pc_offset);
+ } else {
+ CheckQuickMethod(registers, number_of_references, native_pc_offset);
+ }
+ }
+
+ private:
+ void CheckOptimizedMethod(int* registers, int number_of_references, uint32_t native_pc_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* m = GetMethod();
+ CodeInfo code_info = m->GetOptimizedCodeInfo();
+ StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, m->GetCodeItem()->registers_size_);
+ MemoryRegion stack_mask = stack_map.GetStackMask();
+ uint32_t register_mask = stack_map.GetRegisterMask();
+ for (int i = 0; i < number_of_references; ++i) {
+ int reg = registers[i];
+ CHECK(reg < m->GetCodeItem()->registers_size_);
+ DexRegisterMap::LocationKind location = dex_register_map.GetLocationKind(reg);
+ switch (location) {
+ case DexRegisterMap::kNone:
+ // Not set, should not be a reference.
+ CHECK(false);
+ break;
+ case DexRegisterMap::kInStack:
+ CHECK(stack_mask.LoadBit(dex_register_map.GetValue(reg) >> 2));
+ break;
+ case DexRegisterMap::kInRegister:
+ CHECK_NE(register_mask & dex_register_map.GetValue(reg), 0u);
+ break;
+ case DexRegisterMap::kConstant:
+ CHECK_EQ(dex_register_map.GetValue(0), 0);
+ break;
+ }
+ }
+ }
+
+ void CheckQuickMethod(int* registers, int number_of_references, uint32_t native_pc_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* m = GetMethod();
+ NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
+ const uint8_t* ref_bitmap = map.FindBitMap(native_pc_offset);
+ CHECK(ref_bitmap);
+ for (int i = 0; i < number_of_references; ++i) {
+ int reg = registers[i];
+ CHECK(reg < m->GetCodeItem()->registers_size_);
+ CHECK((*((ref_bitmap) + reg / 8) >> (reg % 8) ) & 0x01)
+ << "Error: Reg @" << i << " is not in GC map";
+ }
+ }
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index d05f7af..875efbb 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -155,6 +155,11 @@
return resolved_field;
}
+inline mirror::Object* ClassLinker::AllocObject(Thread* self) {
+ return GetClassRoot(kJavaLangObject)->Alloc<true, false>(self,
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
+}
+
template <class T>
inline mirror::ObjectArray<T>* ClassLinker::AllocObjectArray(Thread* self, size_t length) {
return mirror::ObjectArray<T>::Alloc(self, GetClassRoot(kObjectArrayClass), length);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d718367..6ed27bb 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,11 +16,9 @@
#include "class_linker.h"
-#include <fcntl.h>
-#include <sys/file.h>
-#include <sys/stat.h>
#include <deque>
#include <memory>
+#include <queue>
#include <string>
#include <utility>
#include <vector>
@@ -66,7 +64,7 @@
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "utils.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
@@ -91,21 +89,29 @@
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
// the previous error.
- if (!Runtime::Current()->IsCompiler()) { // Give info if this occurs at runtime.
+ Runtime* runtime = Runtime::Current();
+ bool is_compiler = runtime->IsCompiler();
+ if (!is_compiler) { // Give info if this occurs at runtime.
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
}
CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
Thread* self = Thread::Current();
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- if (c->GetVerifyErrorClass() != nullptr) {
- // TODO: change the verifier to store an _instance_, with a useful detail message?
- std::string temp;
- self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
- PrettyDescriptor(c).c_str());
+ if (is_compiler) {
+ // At compile time, accurate errors and NCDFE are disabled to speed compilation.
+ mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
+ self->SetException(ThrowLocation(), pre_allocated);
} else {
- self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
- PrettyDescriptor(c).c_str());
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ if (c->GetVerifyErrorClass() != NULL) {
+ // TODO: change the verifier to store an _instance_, with a useful detail message?
+ std::string temp;
+ self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
+ PrettyDescriptor(c).c_str());
+ } else {
+ self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
+ PrettyDescriptor(c).c_str());
+ }
}
}
@@ -137,6 +143,88 @@
return hash;
}
+// Gap between two fields in object layout.
+struct FieldGap {
+ uint32_t start_offset; // The offset from the start of the object.
+ uint32_t size; // The gap size of 1, 2, or 4 bytes.
+};
+struct FieldGapsComparator {
+ explicit FieldGapsComparator() {
+ }
+ bool operator() (const FieldGap& lhs, const FieldGap& rhs)
+ NO_THREAD_SAFETY_ANALYSIS {
+ // Sort by gap size, largest first.
+ return lhs.size > rhs.size;
+ }
+};
+typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps;
+
+// Adds largest aligned gaps to queue of gaps.
+void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) {
+ DCHECK(gaps != nullptr);
+
+ uint32_t current_offset = gap_start;
+ while (current_offset != gap_end) {
+ size_t remaining = gap_end - current_offset;
+ if (remaining >= sizeof(uint32_t) && IsAligned<4>(current_offset)) {
+ gaps->push(FieldGap {current_offset, sizeof(uint32_t)});
+ current_offset += sizeof(uint32_t);
+ } else if (remaining >= sizeof(uint16_t) && IsAligned<2>(current_offset)) {
+ gaps->push(FieldGap {current_offset, sizeof(uint16_t)});
+ current_offset += sizeof(uint16_t);
+ } else {
+ gaps->push(FieldGap {current_offset, sizeof(uint8_t)});
+ current_offset += sizeof(uint8_t);
+ }
+ DCHECK_LE(current_offset, gap_end) << "Overran gap";
+ }
+}
+// Shuffle fields forward, making use of gaps whenever possible.
+template<int n>
+static void ShuffleForward(const size_t num_fields, size_t* current_field_idx,
+ MemberOffset* field_offset,
+ mirror::ObjectArray<mirror::ArtField>* fields,
+ std::deque<mirror::ArtField*>* grouped_and_sorted_fields,
+ FieldGaps* gaps)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(current_field_idx != nullptr);
+ DCHECK(grouped_and_sorted_fields != nullptr);
+ DCHECK(fields != nullptr || (num_fields == 0 && grouped_and_sorted_fields->empty()));
+ DCHECK(gaps != nullptr);
+ DCHECK(field_offset != nullptr);
+
+ DCHECK(IsPowerOfTwo(n));
+ while (!grouped_and_sorted_fields->empty()) {
+ mirror::ArtField* field = grouped_and_sorted_fields->front();
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ if (Primitive::ComponentSize(type) < n) {
+ break;
+ }
+ if (!IsAligned<n>(field_offset->Uint32Value())) {
+ MemberOffset old_offset = *field_offset;
+ *field_offset = MemberOffset(RoundUp(field_offset->Uint32Value(), n));
+ AddFieldGap(old_offset.Uint32Value(), field_offset->Uint32Value(), gaps);
+ }
+ CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types
+ grouped_and_sorted_fields->pop_front();
+ fields->Set<false>(*current_field_idx, field);
+ if (!gaps->empty() && gaps->top().size >= n) {
+ FieldGap gap = gaps->top();
+ gaps->pop();
+ DCHECK(IsAligned<n>(gap.start_offset));
+ field->SetOffset(MemberOffset(gap.start_offset));
+ if (gap.size > n) {
+ AddFieldGap(gap.start_offset + n, gap.start_offset + gap.size, gaps);
+ }
+ } else {
+ DCHECK(IsAligned<n>(field_offset->Uint32Value()));
+ field->SetOffset(*field_offset);
+ *field_offset = MemberOffset(field_offset->Uint32Value() + n);
+ }
+ ++(*current_field_idx);
+ }
+}
+
const char* ClassLinker::class_roots_descriptors_[] = {
"Ljava/lang/Class;",
"Ljava/lang/Object;",
@@ -226,6 +314,7 @@
java_lang_Class->AssertReadBarrierPointer();
}
java_lang_Class->SetClassSize(mirror::Class::ClassClassSize());
+ java_lang_Class->SetPrimitiveType(Primitive::kPrimNot);
heap->DecrementDisableMovingGC(self);
// AllocClass(mirror::Class*) can now be used
@@ -250,6 +339,12 @@
// Setup the char (primitive) class to be used for char[].
Handle<mirror::Class> char_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Class::PrimitiveClassSize())));
+ // The primitive char class won't be initialized by
+ // InitializePrimitiveClass until line 459, but strings (and
+ // internal char arrays) will be allocated before that and the
+ // component size, which is computed from the primitive type, needs
+ // to be set here.
+ char_class->SetPrimitiveType(Primitive::kPrimChar);
// Setup the char[] class to be used for String.
Handle<mirror::Class> char_array_class(hs.NewHandle(
@@ -359,7 +454,7 @@
for (size_t i = 0; i != boot_class_path.size(); ++i) {
const DexFile* dex_file = boot_class_path[i];
CHECK(dex_file != nullptr);
- AppendToBootClassPath(*dex_file);
+ AppendToBootClassPath(self, *dex_file);
}
// now we can use FindSystemClass
@@ -587,7 +682,7 @@
if (!c->IsArrayClass() && !c->IsPrimitive()) {
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i))));
- EnsureInitialized(h_class, true, true);
+ EnsureInitialized(self, h_class, true, true);
self->AssertNoPendingException();
}
}
@@ -1134,13 +1229,7 @@
return false;
}
- if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
- oat_file->GetLocation().c_str(),
- oat_dex_file->GetDexFileLocationChecksum(),
- dex_location, dex_location_checksum);
- return false;
- }
+ DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
return true;
}
@@ -1294,7 +1383,6 @@
if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
&odex_checksum_verified,
&odex_error_msg)) {
- error_msgs->push_back(odex_error_msg);
return odex_oat_file.release();
} else {
if (odex_checksum_verified) {
@@ -1317,7 +1405,6 @@
if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
&cache_checksum_verified,
&cache_error_msg)) {
- error_msgs->push_back(cache_error_msg);
return cache_oat_file.release();
} else if (cache_checksum_verified) {
// We can just relocate
@@ -1493,17 +1580,16 @@
bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
bool* checksum_verified,
std::string* error_msg) {
- std::string compound_msg("Oat file failed to verify: ");
Runtime* runtime = Runtime::Current();
- uint32_t real_image_checksum;
- void* real_image_oat_offset;
- int32_t real_patch_delta;
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
if (image_space == nullptr) {
*error_msg = "No image space present";
return false;
}
- if (isa == Runtime::Current()->GetInstructionSet()) {
+ uint32_t real_image_checksum;
+ void* real_image_oat_offset;
+ int32_t real_patch_delta;
+ if (isa == runtime->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
real_image_checksum = image_header.GetOatChecksum();
real_image_oat_offset = image_header.GetOatDataBegin();
@@ -1517,32 +1603,33 @@
}
const OatHeader& oat_header = oat_file->GetOatHeader();
+ std::string compound_msg;
uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
*checksum_verified = oat_image_checksum == real_image_checksum;
if (!*checksum_verified) {
- compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)",
- real_image_checksum, oat_image_checksum);
+ StringAppendF(&compound_msg, " Oat Image Checksum Incorrect (expected 0x%x, received 0x%x)",
+ real_image_checksum, oat_image_checksum);
}
void* oat_image_oat_offset =
reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
if (!offset_verified) {
- compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
- real_image_oat_offset, oat_image_oat_offset);
+ StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
+ real_image_oat_offset, oat_image_oat_offset);
}
int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
bool patch_delta_verified = oat_patch_delta == real_patch_delta;
if (!patch_delta_verified) {
- compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
- real_patch_delta, oat_patch_delta);
+ StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, received 0x%x)",
+ real_patch_delta, oat_patch_delta);
}
bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
- if (ret) {
- *error_msg = compound_msg;
+ if (!ret) {
+ *error_msg = "Oat file failed to verify:" + compound_msg;
}
return ret;
}
@@ -1571,9 +1658,7 @@
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
if (method != Runtime::Current()->GetResolutionMethod()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
}
}
}
@@ -1798,7 +1883,7 @@
} else {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
- Handle<mirror::ObjectArray<mirror::Class>> classes =
+ MutableHandle<mirror::ObjectArray<mirror::Class>> classes =
hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
GetClassesVisitorArrayArg local_arg;
local_arg.classes = &classes;
@@ -1808,7 +1893,7 @@
while (!local_arg.success) {
size_t class_table_size;
{
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
class_table_size = class_table_.size();
}
mirror::Class* class_type = mirror::Class::GetJavaLangClass();
@@ -1952,7 +2037,7 @@
}
CHECK(h_class->IsRetired());
// Get the updated class from class table.
- klass = LookupClass(descriptor, h_class.Get()->GetClassLoader());
+ klass = LookupClass(self, descriptor, h_class.Get()->GetClassLoader());
}
// Wait for the class if it has not already been linked.
@@ -2011,11 +2096,11 @@
ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
// Check if this would be found in the parent boot class loader.
if (pair.second != nullptr) {
- mirror::Class* klass = LookupClass(descriptor, nullptr);
+ mirror::Class* klass = LookupClass(self, descriptor, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
- klass = DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ klass = DefineClass(self, descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
if (klass != nullptr) {
return klass;
@@ -2031,7 +2116,7 @@
hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
Handle<mirror::ArtField> dex_file_field =
hs.NewHandle(
- soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList$Element_dexFile));
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile));
mirror::Object* dex_path_list =
soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
GetObject(class_loader.Get());
@@ -2067,7 +2152,7 @@
if (dex_class_def != nullptr) {
RegisterDexFile(*dex_file);
mirror::Class* klass =
- DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
+ DefineClass(self, descriptor, class_loader, *dex_file, *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
@@ -2095,7 +2180,7 @@
return FindPrimitiveClass(descriptor[0]);
}
// Find the class in the loaded classes table.
- mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
+ mirror::Class* klass = LookupClass(self, descriptor, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
@@ -2106,7 +2191,8 @@
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
if (pair.second != nullptr) {
- return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+ return DefineClass(self, descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
@@ -2118,7 +2204,7 @@
} else if (Runtime::Current()->UseCompileTimeClassPath()) {
// First try with the bootstrap class loader.
if (class_loader.Get() != nullptr) {
- klass = LookupClass(descriptor, nullptr);
+ klass = LookupClass(self, descriptor, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
@@ -2127,7 +2213,8 @@
// a NoClassDefFoundError being allocated.
ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
if (pair.second != nullptr) {
- return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+ return DefineClass(self, descriptor, NullHandle<mirror::ClassLoader>(), *pair.first,
+ *pair.second);
}
// Next try the compile time class path.
const std::vector<const DexFile*>* class_path;
@@ -2139,7 +2226,12 @@
}
pair = FindInClassPath(descriptor, *class_path);
if (pair.second != nullptr) {
- return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
+ return DefineClass(self, descriptor, class_loader, *pair.first, *pair.second);
+ } else {
+ // Use the pre-allocated NCDFE at compile time to avoid wasting time constructing exceptions.
+ mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+ self->SetException(ThrowLocation(), pre_allocated);
+ return nullptr;
}
} else {
ScopedObjectAccessUnchecked soa(self);
@@ -2177,16 +2269,13 @@
return soa.Decode<mirror::Class*>(result.get());
}
}
-
- ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
- return nullptr;
+ UNREACHABLE();
}
-mirror::Class* ClassLinker::DefineClass(const char* descriptor,
+mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
- Thread* self = Thread::Current();
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
bool should_allocate = false;
@@ -2227,7 +2316,7 @@
return nullptr;
}
klass->SetDexCache(FindDexCache(dex_file));
- LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
+ LoadClass(self, dex_file, dex_class_def, klass, class_loader.Get());
ObjectLock<mirror::Class> lock(self, klass);
if (self->IsExceptionPending()) {
// An exception occured during load, set status to erroneous while holding klass' lock in case
@@ -2296,6 +2385,8 @@
const DexFile::ClassDef& dex_class_def) {
const byte* class_data = dex_file.GetClassData(dex_class_def);
size_t num_ref = 0;
+ size_t num_8 = 0;
+ size_t num_16 = 0;
size_t num_32 = 0;
size_t num_64 = 0;
if (class_data != nullptr) {
@@ -2303,29 +2394,45 @@
const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex());
const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id);
char c = descriptor[0];
- if (c == 'L' || c == '[') {
- num_ref++;
- } else if (c == 'J' || c == 'D') {
- num_64++;
- } else {
- num_32++;
+ switch (c) {
+ case 'L':
+ case '[':
+ num_ref++;
+ break;
+ case 'J':
+ case 'D':
+ num_64++;
+ break;
+ case 'I':
+ case 'F':
+ num_32++;
+ break;
+ case 'S':
+ case 'C':
+ num_16++;
+ break;
+ case 'B':
+ case 'Z':
+ num_8++;
+ break;
+ default:
+ LOG(FATAL) << "Unknown descriptor: " << c;
}
}
}
- return mirror::Class::ComputeClassSize(false, 0, num_32, num_64, num_ref);
+ return mirror::Class::ComputeClassSize(false, 0, num_8, num_16, num_32, num_64, num_ref);
}
-bool ClassLinker::FindOatClass(const DexFile& dex_file,
- uint16_t class_def_idx,
- OatFile::OatClass* oat_class) {
- DCHECK(oat_class != nullptr);
+OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
+ bool* found) {
DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file);
if (oat_dex_file == nullptr) {
- return false;
+ *found = false;
+ return OatFile::OatClass::Invalid();
}
- *oat_class = oat_dex_file->GetOatClass(class_def_idx);
- return true;
+ *found = true;
+ return oat_dex_file->GetOatClass(class_def_idx);
}
static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx,
@@ -2362,8 +2469,7 @@
return 0;
}
-bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method) {
- DCHECK(oat_method != nullptr);
+const OatFile::OatMethod ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, bool* found) {
// Although we overwrite the trampoline of non-static methods, we may get here via the resolution
// method for direct methods (or virtual methods made direct).
mirror::Class* declaring_class = method->GetDeclaringClass();
@@ -2392,15 +2498,14 @@
GetOatMethodIndexFromMethodIndex(*declaring_class->GetDexCache()->GetDexFile(),
method->GetDeclaringClass()->GetDexClassDefIndex(),
method->GetDexMethodIndex()));
- OatFile::OatClass oat_class;
- if (!FindOatClass(*declaring_class->GetDexCache()->GetDexFile(),
- declaring_class->GetDexClassDefIndex(),
- &oat_class)) {
- return false;
+ OatFile::OatClass oat_class = FindOatClass(*declaring_class->GetDexCache()->GetDexFile(),
+ declaring_class->GetDexClassDefIndex(),
+ found);
+ if (!found) {
+ return OatFile::OatMethod::Invalid();
}
-
- *oat_method = oat_class.GetOatMethod(oat_method_index);
- return true;
+ *found = true;
+ return oat_class.GetOatMethod(oat_method_index);
}
// Special case to get oat code without overwriting a trampoline.
@@ -2409,9 +2514,10 @@
if (method->IsProxyMethod()) {
return GetQuickProxyInvokeHandler();
}
- OatFile::OatMethod oat_method;
+ bool found;
+ OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
const void* result = nullptr;
- if (FindOatMethodFor(method, &oat_method)) {
+ if (found) {
result = oat_method.GetQuickCode();
}
@@ -2419,11 +2525,9 @@
if (method->IsNative()) {
// No code and native? Use generic trampoline.
result = GetQuickGenericJniTrampoline();
-#if defined(ART_USE_PORTABLE_COMPILER)
} else if (method->IsPortableCompiled()) {
// No code? Do we expect portable code?
result = GetQuickToPortableBridge();
-#endif
} else {
// No code? You must mean to go into the interpreter.
result = GetQuickToInterpreterBridge();
@@ -2432,7 +2536,6 @@
return result;
}
-#if defined(ART_USE_PORTABLE_COMPILER)
const void* ClassLinker::GetPortableOatCodeFor(mirror::ArtMethod* method,
bool* have_portable_code) {
CHECK(!method->IsAbstract()) << PrettyMethod(method);
@@ -2440,10 +2543,11 @@
if (method->IsProxyMethod()) {
return GetPortableProxyInvokeHandler();
}
- OatFile::OatMethod oat_method;
+ bool found;
+ OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
const void* result = nullptr;
const void* quick_code = nullptr;
- if (FindOatMethodFor(method, &oat_method)) {
+ if (found) {
result = oat_method.GetPortableCode();
quick_code = oat_method.GetQuickCode();
}
@@ -2461,29 +2565,46 @@
}
return result;
}
-#endif
+
+const void* ClassLinker::GetOatMethodQuickCodeFor(mirror::ArtMethod* method) {
+ if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
+ return nullptr;
+ }
+ bool found;
+ OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
+ return found ? oat_method.GetQuickCode() : nullptr;
+}
+
+const void* ClassLinker::GetOatMethodPortableCodeFor(mirror::ArtMethod* method) {
+ if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
+ return nullptr;
+ }
+ bool found;
+ OatFile::OatMethod oat_method = FindOatMethodFor(method, &found);
+ return found ? oat_method.GetPortableCode() : nullptr;
+}
const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
uint32_t method_idx) {
- OatFile::OatClass oat_class;
- if (!FindOatClass(dex_file, class_def_idx, &oat_class)) {
+ bool found;
+ OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
+ if (!found) {
return nullptr;
}
uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
return oat_class.GetOatMethod(oat_method_idx).GetQuickCode();
}
-#if defined(ART_USE_PORTABLE_COMPILER)
const void* ClassLinker::GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx,
uint32_t method_idx) {
- OatFile::OatClass oat_class;
- if (!FindOatClass(dex_file, class_def_idx, &oat_class)) {
+ bool found;
+ OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found);
+ if (!found) {
return nullptr;
}
uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx);
return oat_class.GetOatMethod(oat_method_idx).GetPortableCode();
}
-#endif
// Returns true if the method must run with interpreter, false otherwise.
static bool NeedsInterpreter(
@@ -2534,8 +2655,9 @@
while (it.HasNextInstanceField()) {
it.Next();
}
- OatFile::OatClass oat_class;
- bool has_oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class);
+ bool has_oat_class;
+ OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
+ &has_oat_class);
// Link the code of methods skipped by LinkCode.
for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
mirror::ArtMethod* method = klass->GetDirectMethod(method_index);
@@ -2557,17 +2679,12 @@
// Check whether the method is native, in which case it's generic JNI.
if (quick_code == nullptr && portable_code == nullptr && method->IsNative()) {
quick_code = GetQuickGenericJniTrampoline();
-#if defined(ART_USE_PORTABLE_COMPILER)
portable_code = GetPortableToQuickBridge();
-#endif
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
portable_code = GetPortableToInterpreterBridge();
-#endif
quick_code = GetQuickToInterpreterBridge();
}
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
if (portable_code == nullptr) {
portable_code = GetPortableToQuickBridge();
} else {
@@ -2576,11 +2693,6 @@
if (quick_code == nullptr) {
quick_code = GetQuickToPortableBridge();
}
-#else
- if (quick_code == nullptr) {
- quick_code = GetQuickToInterpreterBridge();
- }
-#endif
}
runtime->GetInstrumentation()->UpdateMethodsCode(method, quick_code, portable_code,
have_portable_code);
@@ -2588,7 +2700,8 @@
// Ignore virtual methods on the iterator.
}
-void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
+void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method,
+ const OatFile::OatClass* oat_class,
const DexFile& dex_file, uint32_t dex_method_index,
uint32_t method_index) {
if (Runtime::Current()->IsCompiler()) {
@@ -2597,9 +2710,7 @@
}
// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
-#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK(method->GetEntryPointFromPortableCompiledCode() == nullptr);
-#endif
if (oat_class != nullptr) {
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
@@ -2610,11 +2721,7 @@
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method.Get(),
method->GetEntryPointFromQuickCompiledCode(),
-#if defined(ART_USE_PORTABLE_COMPILER)
method->GetEntryPointFromPortableCompiledCode());
-#else
- nullptr);
-#endif
if (enter_interpreter && !method->IsNative()) {
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
} else {
@@ -2623,9 +2730,7 @@
if (method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
return;
}
@@ -2635,33 +2740,23 @@
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionTrampoline());
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableResolutionTrampoline());
-#endif
} else if (enter_interpreter) {
if (!method->IsNative()) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToInterpreterBridge());
-#endif
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniTrampoline());
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
-#endif
}
-#if defined(ART_USE_PORTABLE_COMPILER)
} else if (method->GetEntryPointFromPortableCompiledCode() != nullptr) {
DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
have_portable_code = true;
method->SetEntryPointFromQuickCompiledCode(GetQuickToPortableBridge());
-#endif
} else {
DCHECK(method->GetEntryPointFromQuickCompiledCode() != nullptr);
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableToQuickBridge());
-#endif
}
if (method->IsNative()) {
@@ -2680,15 +2775,13 @@
Runtime* runtime = Runtime::Current();
runtime->GetInstrumentation()->UpdateMethodsCode(method.Get(),
method->GetEntryPointFromQuickCompiledCode(),
-#if defined(ART_USE_PORTABLE_COMPILER)
method->GetEntryPointFromPortableCompiledCode(),
-#else
- nullptr,
-#endif
have_portable_code);
}
-void ClassLinker::LoadClass(const DexFile& dex_file,
+
+
+void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def,
Handle<mirror::Class> klass,
mirror::ClassLoader* class_loader) {
@@ -2717,24 +2810,27 @@
return; // no fields or methods - for example a marker interface
}
- OatFile::OatClass oat_class;
- if (Runtime::Current()->IsStarted()
- && !Runtime::Current()->UseCompileTimeClassPath()
- && FindOatClass(dex_file, klass->GetDexClassDefIndex(), &oat_class)) {
- LoadClassMembers(dex_file, class_data, klass, class_loader, &oat_class);
- } else {
- LoadClassMembers(dex_file, class_data, klass, class_loader, nullptr);
+
+ bool has_oat_class = false;
+ if (Runtime::Current()->IsStarted() && !Runtime::Current()->UseCompileTimeClassPath()) {
+ OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(),
+ &has_oat_class);
+ if (has_oat_class) {
+ LoadClassMembers(self, dex_file, class_data, klass, class_loader, &oat_class);
+ }
+ }
+ if (!has_oat_class) {
+ LoadClassMembers(self, dex_file, class_data, klass, class_loader, nullptr);
}
}
-void ClassLinker::LoadClassMembers(const DexFile& dex_file,
+void ClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file,
const byte* class_data,
Handle<mirror::Class> klass,
mirror::ClassLoader* class_loader,
const OatFile::OatClass* oat_class) {
// Load fields.
ClassDataItemIterator it(dex_file, class_data);
- Thread* self = Thread::Current();
if (it.NumStaticFields() != 0) {
mirror::ObjectArray<mirror::ArtField>* statics = AllocArtFieldArray(self, it.NumStaticFields());
if (UNLIKELY(statics == nullptr)) {
@@ -2753,6 +2849,7 @@
klass->SetIFields(fields);
}
for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
+ self->AllowThreadSuspension();
StackHandleScope<1> hs(self);
Handle<mirror::ArtField> sfield(hs.NewHandle(AllocArtField(self)));
if (UNLIKELY(sfield.Get() == nullptr)) {
@@ -2763,6 +2860,7 @@
LoadField(dex_file, it, klass, sfield);
}
for (size_t i = 0; it.HasNextInstanceField(); i++, it.Next()) {
+ self->AllowThreadSuspension();
StackHandleScope<1> hs(self);
Handle<mirror::ArtField> ifield(hs.NewHandle(AllocArtField(self)));
if (UNLIKELY(ifield.Get() == nullptr)) {
@@ -2798,6 +2896,7 @@
uint32_t last_dex_method_index = DexFile::kDexNoIndex;
size_t last_class_def_method_index = 0;
for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
+ self->AllowThreadSuspension();
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
if (UNLIKELY(method.Get() == nullptr)) {
@@ -2818,6 +2917,7 @@
class_def_method_index++;
}
for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
+ self->AllowThreadSuspension();
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> method(hs.NewHandle(LoadMethod(self, dex_file, it, klass)));
if (UNLIKELY(method.Get() == nullptr)) {
@@ -2833,7 +2933,8 @@
}
void ClassLinker::LoadField(const DexFile& /*dex_file*/, const ClassDataItemIterator& it,
- Handle<mirror::Class> klass, Handle<mirror::ArtField> dst) {
+ Handle<mirror::Class> klass,
+ Handle<mirror::ArtField> dst) {
uint32_t field_idx = it.GetMemberIndex();
dst->SetDexFieldIndex(field_idx);
dst->SetDeclaringClass(klass.Get());
@@ -2854,7 +2955,7 @@
}
DCHECK(dst->IsArtMethod()) << PrettyDescriptor(dst->GetClass());
- const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod");
+ ScopedAssertNoThreadSuspension ants(self, "LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.Get());
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
@@ -2901,12 +3002,10 @@
}
dst->SetAccessFlags(access_flags);
- self->EndAssertNoThreadSuspension(old_cause);
return dst;
}
-void ClassLinker::AppendToBootClassPath(const DexFile& dex_file) {
- Thread* self = Thread::Current();
+void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) {
StackHandleScope<1> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for "
@@ -3058,11 +3157,12 @@
// Identify the underlying component type
CHECK_EQ('[', descriptor[0]);
StackHandleScope<2> hs(self);
- Handle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1, class_loader)));
+ MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1,
+ class_loader)));
if (component_type.Get() == nullptr) {
DCHECK(self->IsExceptionPending());
// We need to accept erroneous classes as component types.
- component_type.Assign(LookupClass(descriptor + 1, class_loader.Get()));
+ component_type.Assign(LookupClass(self, descriptor + 1, class_loader.Get()));
if (component_type.Get() == nullptr) {
DCHECK(self->IsExceptionPending());
return nullptr;
@@ -3092,7 +3192,7 @@
// class to the hash table --- necessary because of possible races with
// other threads.)
if (class_loader.Get() != component_type->GetClassLoader()) {
- mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader());
+ mirror::Class* new_class = LookupClass(self, descriptor, component_type->GetClassLoader());
if (new_class != nullptr) {
return new_class;
}
@@ -3311,11 +3411,11 @@
return false;
}
-mirror::Class* ClassLinker::LookupClass(const char* descriptor,
+mirror::Class* ClassLinker::LookupClass(Thread* self, const char* descriptor,
const mirror::ClassLoader* class_loader) {
size_t hash = Hash(descriptor);
{
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
mirror::Class* result = LookupClassFromTableLocked(descriptor, class_loader, hash);
if (result != nullptr) {
return result;
@@ -3378,8 +3478,7 @@
if (!dex_cache_image_class_lookup_required_) {
return; // All dex cache classes are already in the class table.
}
- const char* old_no_suspend_cause =
- self->StartAssertNoThreadSuspension("Moving image classes to class table");
+ ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table");
mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
std::string temp;
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
@@ -3405,13 +3504,10 @@
}
}
dex_cache_image_class_lookup_required_ = false;
- self->EndAssertNoThreadSuspension(old_no_suspend_cause);
}
mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
- Thread* self = Thread::Current();
- const char* old_no_suspend_cause =
- self->StartAssertNoThreadSuspension("Image class lookup");
+ ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup");
mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
@@ -3425,13 +3521,11 @@
uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
if (klass != nullptr) {
- self->EndAssertNoThreadSuspension(old_no_suspend_cause);
return klass;
}
}
}
}
- self->EndAssertNoThreadSuspension(old_no_suspend_cause);
return nullptr;
}
@@ -3451,9 +3545,8 @@
}
}
-void ClassLinker::VerifyClass(Handle<mirror::Class> klass) {
+void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
// TODO: assert that the monitor on the Class is held
- Thread* self = Thread::Current();
ObjectLock<mirror::Class> lock(self, klass);
// Don't attempt to re-verify if already sufficiently verified.
@@ -3496,7 +3589,7 @@
ObjectLock<mirror::Class> lock(self, super);
if (!super->IsVerified() && !super->IsErroneous()) {
- VerifyClass(super);
+ VerifyClass(self, super);
}
if (!super->IsCompileTimeVerified()) {
std::string error_msg(
@@ -3537,7 +3630,7 @@
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
std::string error_msg;
if (!preverified) {
- verifier_failure = verifier::MethodVerifier::VerifyClass(klass.Get(),
+ verifier_failure = verifier::MethodVerifier::VerifyClass(self, klass.Get(),
Runtime::Current()->IsCompiler(),
&error_msg);
}
@@ -3572,7 +3665,7 @@
klass->SetStatus(mirror::Class::kStatusVerified, self);
// As this is a fake verified status, make sure the methods are _not_ marked preverified
// later.
- klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified);
+ klass->SetPreverified();
}
}
} else {
@@ -3595,9 +3688,9 @@
}
void ClassLinker::EnsurePreverifiedMethods(Handle<mirror::Class> klass) {
- if ((klass->GetAccessFlags() & kAccPreverified) == 0) {
+ if (!klass->IsPreverified()) {
klass->SetPreverifiedFlagOnAllMethods();
- klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified);
+ klass->SetPreverified();
}
}
@@ -3721,7 +3814,7 @@
jobjectArray methods, jobjectArray throws) {
Thread* self = soa.Self();
StackHandleScope<8> hs(self);
- Handle<mirror::Class> klass(hs.NewHandle(
+ MutableHandle<mirror::Class> klass(hs.NewHandle(
AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class))));
if (klass.Get() == nullptr) {
CHECK(self->IsExceptionPending()); // OOME.
@@ -3965,15 +4058,14 @@
// At runtime the method looks like a reference and argument saving method, clone the code
// related parameters from this method.
method->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableProxyInvokeHandler());
-#endif
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
return method;
}
-static void CheckProxyMethod(Handle<mirror::ArtMethod> method, Handle<mirror::ArtMethod> prototype)
+static void CheckProxyMethod(Handle<mirror::ArtMethod> method,
+ Handle<mirror::ArtMethod> prototype)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Basic sanity
CHECK(!prototype->IsFinal());
@@ -3987,8 +4079,9 @@
CHECK(prototype->HasSameDexCacheResolvedTypes(method.Get()));
CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
- MethodHelper mh(method);
- MethodHelper mh2(prototype);
+ StackHandleScope<2> hs(Thread::Current());
+ MethodHelper mh(hs.NewHandle(method.Get()));
+ MethodHelper mh2(hs.NewHandle(prototype.Get()));
CHECK_STREQ(method->GetName(), prototype->GetName());
CHECK_STREQ(method->GetShorty(), prototype->GetShorty());
// More complex sanity - via dex cache
@@ -4029,12 +4122,8 @@
return true;
}
-bool ClassLinker::IsInitialized() const {
- return init_done_;
-}
-
-bool ClassLinker::InitializeClass(Handle<mirror::Class> klass, bool can_init_statics,
- bool can_init_parents) {
+bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
+ bool can_init_statics, bool can_init_parents) {
// see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
// Are we already initialized and therefore done?
@@ -4049,7 +4138,7 @@
return false;
}
- Thread* self = Thread::Current();
+ self->AllowThreadSuspension();
uint64_t t0;
{
ObjectLock<mirror::Class> lock(self, klass);
@@ -4068,7 +4157,7 @@
CHECK(klass->IsResolved()) << PrettyClass(klass.Get()) << ": state=" << klass->GetStatus();
if (!klass->IsVerified()) {
- VerifyClass(klass);
+ VerifyClass(self, klass);
if (!klass->IsVerified()) {
// We failed to verify, expect either the klass to be erroneous or verification failed at
// compile time.
@@ -4107,6 +4196,7 @@
klass->SetStatus(mirror::Class::kStatusError, self);
return false;
}
+ self->AllowThreadSuspension();
CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass.Get());
@@ -4126,7 +4216,7 @@
CHECK(can_init_parents);
StackHandleScope<1> hs(self);
Handle<mirror::Class> handle_scope_super(hs.NewHandle(super_class));
- bool super_initialized = InitializeClass(handle_scope_super, can_init_statics, true);
+ bool super_initialized = InitializeClass(self, handle_scope_super, can_init_statics, true);
if (!super_initialized) {
// The super class was verified ahead of entering initializing, we should only be here if
// the super class became erroneous due to initialization.
@@ -4148,23 +4238,26 @@
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
CHECK(dex_class_def != nullptr);
const DexFile& dex_file = klass->GetDexFile();
- StackHandleScope<2> hs(self);
+ StackHandleScope<3> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
- EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader,
- this, *dex_class_def);
- if (it.HasNext()) {
+ EncodedStaticFieldValueIterator value_it(dex_file, &dex_cache, &class_loader,
+ this, *dex_class_def);
+ const byte* class_data = dex_file.GetClassData(*dex_class_def);
+ ClassDataItemIterator field_it(dex_file, class_data);
+ if (value_it.HasNext()) {
+ DCHECK(field_it.HasNextStaticField());
CHECK(can_init_statics);
- // We reordered the fields, so we need to be able to map the
- // field indexes to the right fields.
- SafeMap<uint32_t, mirror::ArtField*> field_map;
- ConstructFieldMap(dex_file, *dex_class_def, klass.Get(), field_map);
- for (size_t i = 0; it.HasNext(); i++, it.Next()) {
+ for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ArtField> field(hs.NewHandle(
+ ResolveField(dex_file, field_it.GetMemberIndex(), dex_cache, class_loader, true)));
if (Runtime::Current()->IsActiveTransaction()) {
- it.ReadValueToField<true>(field_map.Get(i));
+ value_it.ReadValueToField<true>(field);
} else {
- it.ReadValueToField<false>(field_map.Get(i));
+ value_it.ReadValueToField<false>(field);
}
+ DCHECK(!value_it.HasNext() || field_it.HasNextStaticField());
}
}
}
@@ -4176,6 +4269,7 @@
clinit->Invoke(self, nullptr, 0, &result, "V");
}
+ self->AllowThreadSuspension();
uint64_t t1 = NanoTime();
bool success = true;
@@ -4244,7 +4338,7 @@
LOG(FATAL) << "Unexpected class status. " << PrettyClass(klass.Get()) << " is "
<< klass->GetStatus();
}
- LOG(FATAL) << "Not Reached" << PrettyClass(klass.Get());
+ UNREACHABLE();
}
bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) {
@@ -4253,8 +4347,8 @@
}
// Begin with the methods local to the superclass.
StackHandleScope<2> hs(Thread::Current());
- MethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
if (klass->HasSuperClass() &&
klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) {
for (int i = klass->GetSuperClass()->GetVTableLength() - 1; i >= 0; --i) {
@@ -4292,15 +4386,14 @@
return true;
}
-bool ClassLinker::EnsureInitialized(Handle<mirror::Class> c, bool can_init_fields,
+bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields,
bool can_init_parents) {
DCHECK(c.Get() != nullptr);
if (c->IsInitialized()) {
EnsurePreverifiedMethods(c);
return true;
}
- const bool success = InitializeClass(c, can_init_fields, can_init_parents);
- Thread* self = Thread::Current();
+ const bool success = InitializeClass(self, c, can_init_fields, can_init_parents);
if (!success) {
if (can_init_fields && can_init_parents) {
CHECK(self->IsExceptionPending()) << PrettyClass(c.Get());
@@ -4311,20 +4404,6 @@
return success;
}
-void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
- mirror::Class* c,
- SafeMap<uint32_t, mirror::ArtField*>& field_map) {
- const byte* class_data = dex_file.GetClassData(dex_class_def);
- ClassDataItemIterator it(dex_file, class_data);
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(c->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(c->GetClassLoader()));
- CHECK(!kMovingFields);
- for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) {
- field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true));
- }
-}
-
void ClassLinker::FixupTemporaryDeclaringClass(mirror::Class* temp_class, mirror::Class* new_class) {
mirror::ObjectArray<mirror::ArtField>* fields = new_class->GetIFields();
if (fields != nullptr) {
@@ -4374,15 +4453,14 @@
if (!LinkMethods(self, klass, interfaces)) {
return false;
}
- if (!LinkInstanceFields(klass)) {
+ if (!LinkInstanceFields(self, klass)) {
return false;
}
size_t class_size;
- if (!LinkStaticFields(klass, &class_size)) {
+ if (!LinkStaticFields(self, klass, &class_size)) {
return false;
}
CreateReferenceInstanceOffsets(klass);
- CreateReferenceStaticOffsets(klass);
CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
if (!klass->IsTemp() || (!init_done_ && klass->GetClassSize() == class_size)) {
@@ -4536,6 +4614,7 @@
// Populate the class vtable and itable. Compute return type indices.
bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+ self->AllowThreadSuspension();
if (klass->IsInterface()) {
// No vtable.
size_t count = klass->NumVirtualMethods();
@@ -4563,7 +4642,7 @@
CHECK_LE(actual_count, max_count);
StackHandleScope<4> hs(self);
Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass()));
- Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
+ MutableHandle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
vtable = hs.NewHandle(AllocArtMethodArray(self, max_count));
if (UNLIKELY(vtable.Get() == nullptr)) {
@@ -4584,8 +4663,8 @@
}
// See if any of our virtual methods override the superclass.
- MethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i);
local_mh.ChangeMethod(local_method);
@@ -4700,8 +4779,8 @@
return true;
}
}
- StackHandleScope<4> hs(self);
- Handle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
+ StackHandleScope<5> hs(self);
+ MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
if (UNLIKELY(iftable.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
@@ -4713,6 +4792,7 @@
iftable->SetInterface(i, super_interface);
}
}
+ self->AllowThreadSuspension();
// Flatten the interface inheritance hierarchy.
size_t idx = super_ifcount;
for (size_t i = 0; i < num_interfaces; i++) {
@@ -4756,6 +4836,7 @@
}
}
}
+ self->AllowThreadSuspension();
// Shrink iftable in case duplicates were found
if (idx < ifcount) {
iftable.Assign(down_cast<mirror::IfTable*>(iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
@@ -4773,6 +4854,7 @@
if (klass->IsInterface()) {
return true;
}
+ self->AllowThreadSuspension();
// Allocate imtable
bool imtable_changed = false;
Handle<mirror::ObjectArray<mirror::ArtMethod>> imtable(
@@ -4781,10 +4863,17 @@
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
- MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- MethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
- std::vector<mirror::ArtMethod*> miranda_list;
+ MutableMethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper vtable_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ size_t max_miranda_methods = 0; // The max size of miranda_list.
for (size_t i = 0; i < ifcount; ++i) {
+ max_miranda_methods += iftable->GetInterface(i)->NumVirtualMethods();
+ }
+ Handle<mirror::ObjectArray<mirror::ArtMethod>>
+ miranda_list(hs.NewHandle(AllocArtMethodArray(self, max_miranda_methods)));
+ size_t miranda_list_size = 0; // The current size of miranda_list.
+ for (size_t i = 0; i < ifcount; ++i) {
+ self->AllowThreadSuspension();
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
StackHandleScope<2> hs(self);
@@ -4798,8 +4887,7 @@
Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
hs.NewHandle(klass->GetVTableDuringLinking()));
for (size_t j = 0; j < num_methods; ++j) {
- mirror::ArtMethod* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j);
- interface_mh.ChangeMethod(interface_method);
+ interface_mh.ChangeMethod(iftable->GetInterface(i)->GetVirtualMethod(j));
int32_t k;
// For each method listed in the interface's method list, find the
// matching method in our class's method list. We want to favor the
@@ -4810,22 +4898,21 @@
// those don't end up in the virtual method table, so it shouldn't
// matter which direction we go. We walk it backward anyway.)
for (k = vtable->GetLength() - 1; k >= 0; --k) {
- mirror::ArtMethod* vtable_method = vtable->Get(k);
- vtable_mh.ChangeMethod(vtable_method);
+ vtable_mh.ChangeMethod(vtable->Get(k));
if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
- if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
+ if (!vtable_mh.Get()->IsAbstract() && !vtable_mh.Get()->IsPublic()) {
ThrowIllegalAccessError(
klass.Get(),
"Method '%s' implementing interface method '%s' is not public",
- PrettyMethod(vtable_method).c_str(),
- PrettyMethod(interface_method).c_str());
+ PrettyMethod(vtable_mh.Get()).c_str(),
+ PrettyMethod(interface_mh.Get()).c_str());
return false;
}
- method_array->Set<false>(j, vtable_method);
+ method_array->Set<false>(j, vtable_mh.Get());
// Place method in imt if entry is empty, place conflict otherwise.
- uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ uint32_t imt_index = interface_mh.Get()->GetDexMethodIndex() % mirror::Class::kImtSize;
if (imtable->Get(imt_index) == nullptr) {
- imtable->Set<false>(imt_index, vtable_method);
+ imtable->Set<false>(imt_index, vtable_mh.Get());
imtable_changed = true;
} else {
imtable->Set<false>(imt_index, runtime->GetImtConflictMethod());
@@ -4836,7 +4923,9 @@
if (k < 0) {
StackHandleScope<1> hs(self);
auto miranda_method = hs.NewHandle<mirror::ArtMethod>(nullptr);
- for (mirror::ArtMethod* mir_method : miranda_list) {
+ for (size_t l = 0; l < miranda_list_size; ++l) {
+ mirror::ArtMethod* mir_method = miranda_list->Get(l);
+ DCHECK(mir_method != nullptr);
vtable_mh.ChangeMethod(mir_method);
if (interface_mh.HasSameNameAndSignature(&vtable_mh)) {
miranda_method.Assign(mir_method);
@@ -4845,13 +4934,13 @@
}
if (miranda_method.Get() == nullptr) {
// Point the interface table at a phantom slot.
- miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_method->Clone(self)));
+ miranda_method.Assign(down_cast<mirror::ArtMethod*>(interface_mh.Get()->Clone(self)));
if (UNLIKELY(miranda_method.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
- // TODO: If a methods move then the miranda_list may hold stale references.
- miranda_list.push_back(miranda_method.Get());
+ DCHECK_LT(miranda_list_size, max_miranda_methods);
+ miranda_list->Set<false>(miranda_list_size++, miranda_method.Get());
}
method_array->Set<false>(j, miranda_method.Get());
}
@@ -4868,9 +4957,9 @@
}
klass->SetImTable(imtable.Get());
}
- if (!miranda_list.empty()) {
+ if (miranda_list_size > 0) {
int old_method_count = klass->NumVirtualMethods();
- int new_method_count = old_method_count + miranda_list.size();
+ int new_method_count = old_method_count + miranda_list_size;
mirror::ObjectArray<mirror::ArtMethod>* virtuals;
if (old_method_count == 0) {
virtuals = AllocArtMethodArray(self, new_method_count);
@@ -4884,18 +4973,18 @@
klass->SetVirtualMethods(virtuals);
StackHandleScope<1> hs(self);
- Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
+ MutableHandle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
hs.NewHandle(klass->GetVTableDuringLinking()));
CHECK(vtable.Get() != nullptr);
int old_vtable_count = vtable->GetLength();
- int new_vtable_count = old_vtable_count + miranda_list.size();
+ int new_vtable_count = old_vtable_count + miranda_list_size;
vtable.Assign(vtable->CopyOf(self, new_vtable_count));
if (UNLIKELY(vtable.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // OOME.
return false;
}
- for (size_t i = 0; i < miranda_list.size(); ++i) {
- mirror::ArtMethod* method = miranda_list[i];
+ for (size_t i = 0; i < miranda_list_size; ++i) {
+ mirror::ArtMethod* method = miranda_list->Get(i);
// Leave the declaring class alone as type indices are relative to it
method->SetAccessFlags(method->GetAccessFlags() | kAccMiranda);
method->SetMethodIndex(0xFFFF & (old_vtable_count + i));
@@ -4911,19 +5000,19 @@
CHECK(vtable->Get(i) != nullptr);
}
-// klass->DumpClass(std::cerr, Class::kDumpClassFullDetail);
+ self->AllowThreadSuspension();
return true;
}
-bool ClassLinker::LinkInstanceFields(Handle<mirror::Class> klass) {
+bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) {
CHECK(klass.Get() != nullptr);
- return LinkFields(klass, false, nullptr);
+ return LinkFields(self, klass, false, nullptr);
}
-bool ClassLinker::LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size) {
+bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) {
CHECK(klass.Get() != nullptr);
- return LinkFields(klass, true, class_size);
+ return LinkFields(self, klass, true, class_size);
}
struct LinkFieldsComparator {
@@ -4932,20 +5021,20 @@
// No thread safety analysis as will be called from STL. Checked lock held in constructor.
bool operator()(mirror::ArtField* field1, mirror::ArtField* field2)
NO_THREAD_SAFETY_ANALYSIS {
- // First come reference fields, then 64-bit, and finally 32-bit
+ // First come reference fields, then 64-bit, then 32-bit, and then 16-bit, then finally 8-bit.
Primitive::Type type1 = field1->GetTypeAsPrimitiveType();
Primitive::Type type2 = field2->GetTypeAsPrimitiveType();
if (type1 != type2) {
bool is_primitive1 = type1 != Primitive::kPrimNot;
bool is_primitive2 = type2 != Primitive::kPrimNot;
- bool is64bit1 = is_primitive1 && (type1 == Primitive::kPrimLong ||
- type1 == Primitive::kPrimDouble);
- bool is64bit2 = is_primitive2 && (type2 == Primitive::kPrimLong ||
- type2 == Primitive::kPrimDouble);
- int order1 = !is_primitive1 ? 0 : (is64bit1 ? 1 : 2);
- int order2 = !is_primitive2 ? 0 : (is64bit2 ? 1 : 2);
- if (order1 != order2) {
- return order1 < order2;
+ if (type1 != type2) {
+ if (is_primitive1 && is_primitive2) {
+ // Larger primitive types go first.
+ return Primitive::ComponentSize(type1) > Primitive::ComponentSize(type2);
+ } else {
+ // Reference always goes first.
+ return !is_primitive1;
+ }
}
}
// same basic group? then sort by string.
@@ -4953,7 +5042,9 @@
}
};
-bool ClassLinker::LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size) {
+bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static,
+ size_t* class_size) {
+ self->AllowThreadSuspension();
size_t num_fields =
is_static ? klass->NumStaticFields() : klass->NumInstanceFields();
@@ -4967,7 +5058,7 @@
if (klass->ShouldHaveEmbeddedImtAndVTable()) {
// Static fields come after the embedded tables.
base = mirror::Class::ComputeClassSize(true, klass->GetVTableDuringLinking()->GetLength(),
- 0, 0, 0);
+ 0, 0, 0, 0, 0);
}
field_offset = MemberOffset(base);
} else {
@@ -4984,6 +5075,8 @@
// we want a relatively stable order so that adding new fields
// minimizes disruption of C++ version such as Class and Method.
std::deque<mirror::ArtField*> grouped_and_sorted_fields;
+ const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension(
+ "Naked ArtField references in deque");
for (size_t i = 0; i < num_fields; i++) {
mirror::ArtField* f = fields->Get(i);
CHECK(f != nullptr) << PrettyClass(klass.Get());
@@ -4995,6 +5088,8 @@
// References should be at the front.
size_t current_field = 0;
size_t num_reference_fields = 0;
+ FieldGaps gaps;
+
for (; current_field < num_fields; current_field++) {
mirror::ArtField* field = grouped_and_sorted_fields.front();
Primitive::Type type = field->GetTypeAsPrimitiveType();
@@ -5002,51 +5097,31 @@
if (isPrimitive) {
break; // past last reference, move on to the next phase
}
+ if (UNLIKELY(!IsAligned<4>(field_offset.Uint32Value()))) {
+ MemberOffset old_offset = field_offset;
+ field_offset = MemberOffset(RoundUp(field_offset.Uint32Value(), 4));
+ AddFieldGap(old_offset.Uint32Value(), field_offset.Uint32Value(), &gaps);
+ }
+ DCHECK(IsAligned<4>(field_offset.Uint32Value()));
grouped_and_sorted_fields.pop_front();
num_reference_fields++;
fields->Set<false>(current_field, field);
field->SetOffset(field_offset);
field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
}
-
- // Now we want to pack all of the double-wide fields together. If
- // we're not aligned, though, we want to shuffle one 32-bit field
- // into place. If we can't find one, we'll have to pad it.
- if (current_field != num_fields && !IsAligned<8>(field_offset.Uint32Value())) {
- for (size_t i = 0; i < grouped_and_sorted_fields.size(); i++) {
- mirror::ArtField* field = grouped_and_sorted_fields[i];
- Primitive::Type type = field->GetTypeAsPrimitiveType();
- CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types
- if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) {
- continue;
- }
- fields->Set<false>(current_field++, field);
- field->SetOffset(field_offset);
- // drop the consumed field
- grouped_and_sorted_fields.erase(grouped_and_sorted_fields.begin() + i);
- break;
- }
- // whether we found a 32-bit field for padding or not, we advance
- field_offset = MemberOffset(field_offset.Uint32Value() + sizeof(uint32_t));
- }
-
- // Alignment is good, shuffle any double-wide fields forward, and
- // finish assigning field offsets to all fields.
- DCHECK(current_field == num_fields || IsAligned<8>(field_offset.Uint32Value()))
- << PrettyClass(klass.Get());
- while (!grouped_and_sorted_fields.empty()) {
- mirror::ArtField* field = grouped_and_sorted_fields.front();
- grouped_and_sorted_fields.pop_front();
- Primitive::Type type = field->GetTypeAsPrimitiveType();
- CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types
- fields->Set<false>(current_field, field);
- field->SetOffset(field_offset);
- field_offset = MemberOffset(field_offset.Uint32Value() +
- ((type == Primitive::kPrimLong || type == Primitive::kPrimDouble)
- ? sizeof(uint64_t)
- : sizeof(uint32_t)));
- current_field++;
- }
+ // Gaps are stored as a max heap which means that we must shuffle from largest to smallest
+ // otherwise we could end up with suboptimal gap fills.
+ ShuffleForward<8>(num_fields, ¤t_field, &field_offset,
+ fields, &grouped_and_sorted_fields, &gaps);
+ ShuffleForward<4>(num_fields, ¤t_field, &field_offset,
+ fields, &grouped_and_sorted_fields, &gaps);
+ ShuffleForward<2>(num_fields, ¤t_field, &field_offset,
+ fields, &grouped_and_sorted_fields, &gaps);
+ ShuffleForward<1>(num_fields, ¤t_field, &field_offset,
+ fields, &grouped_and_sorted_fields, &gaps);
+ CHECK(grouped_and_sorted_fields.empty()) << "Missed " << grouped_and_sorted_fields.size() <<
+ " fields.";
+ self->EndAssertNoThreadSuspension(old_no_suspend_cause);
// We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
if (!is_static && klass->DescriptorEquals("Ljava/lang/ref/Reference;")) {
@@ -5124,20 +5199,13 @@
return;
}
}
- CreateReferenceOffsets(klass, false, reference_offsets);
+ CreateReferenceOffsets(klass, reference_offsets);
}
-void ClassLinker::CreateReferenceStaticOffsets(Handle<mirror::Class> klass) {
- CreateReferenceOffsets(klass, true, 0);
-}
-
-void ClassLinker::CreateReferenceOffsets(Handle<mirror::Class> klass, bool is_static,
+void ClassLinker::CreateReferenceOffsets(Handle<mirror::Class> klass,
uint32_t reference_offsets) {
- size_t num_reference_fields =
- is_static ? klass->NumReferenceStaticFieldsDuringLinking()
- : klass->NumReferenceInstanceFieldsDuringLinking();
- mirror::ObjectArray<mirror::ArtField>* fields =
- is_static ? klass->GetSFields() : klass->GetIFields();
+ size_t num_reference_fields = klass->NumReferenceInstanceFieldsDuringLinking();
+ mirror::ObjectArray<mirror::ArtField>* fields = klass->GetIFields();
// All of the fields that contain object references are guaranteed
// to be at the beginning of the fields list.
for (size_t i = 0; i < num_reference_fields; ++i) {
@@ -5155,12 +5223,7 @@
break;
}
}
- // Update fields in klass
- if (is_static) {
- klass->SetReferenceStaticOffsets(reference_offsets);
- } else {
- klass->SetReferenceInstanceOffsets(reference_offsets);
- }
+ klass->SetReferenceInstanceOffsets(reference_offsets);
}
mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx,
@@ -5474,10 +5537,12 @@
}
void ClassLinker::DumpForSigQuit(std::ostream& os) {
+ Thread* self = Thread::Current();
if (dex_cache_image_class_lookup_required_) {
+ ScopedObjectAccess soa(self);
MoveImageClassesToClassTable();
}
- ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
os << "Loaded classes: " << class_table_.size() << " allocated classes\n";
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 107a4b2..111dd63 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_CLASS_LINKER_H_
#define ART_RUNTIME_CLASS_LINKER_H_
+#include <deque>
#include <string>
#include <utility>
#include <vector>
@@ -47,6 +48,7 @@
class StackTraceElement;
} // namespace mirror
+template<class T> class Handle;
class InternTable;
template<class T> class ObjectLock;
class ScopedObjectAccessAlreadyRunnable;
@@ -90,17 +92,20 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns true if the class linker is initialized.
- bool IsInitialized() const;
+ bool IsInitialized() const {
+ return init_done_;
+ }
// Define a new a class based on a ClassDef from a DexFile
- mirror::Class* DefineClass(const char* descriptor,
+ mirror::Class* DefineClass(Thread* self, const char* descriptor,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file, const DexFile::ClassDef& dex_class_def)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
// by the given 'class_loader'.
- mirror::Class* LookupClass(const char* descriptor, const mirror::ClassLoader* class_loader)
+ mirror::Class* LookupClass(Thread* self, const char* descriptor,
+ const mirror::ClassLoader* class_loader)
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -122,8 +127,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DumpForSigQuit(std::ostream& os)
- LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ LOCKS_EXCLUDED(Locks::classlinker_classes_lock_);
size_t NumLoadedClasses()
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
@@ -219,7 +223,8 @@
// Returns true on success, false if there's an exception pending.
// can_run_clinit=false allows the compiler to attempt to init a class,
// given the restriction that no <clinit> execution is possible.
- bool EnsureInitialized(Handle<mirror::Class> c, bool can_init_fields, bool can_init_parents)
+ bool EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields,
+ bool can_init_parents)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Initializes classes that have instances in the image but that have
@@ -294,6 +299,9 @@
InstructionSet instruction_set,
std::string* error_msg);
+ // Allocate an instance of a java.lang.Object.
+ mirror::Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// TODO: replace this with multiple methods that allocate the correct managed type.
template <class T>
mirror::ObjectArray<T>* AllocObjectArray(Thread* self, size_t length)
@@ -318,7 +326,8 @@
size_t length)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VerifyClass(Handle<mirror::Class> klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VerifyClass(Thread* self, Handle<mirror::Class> klass)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class* klass,
mirror::Class::Status& oat_file_class_status)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -342,18 +351,22 @@
// Get the oat code for a method when its class isn't yet initialized
const void* GetQuickOatCodeFor(mirror::ArtMethod* method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#if defined(ART_USE_PORTABLE_COMPILER)
const void* GetPortableOatCodeFor(mirror::ArtMethod* method, bool* have_portable_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#endif
// Get the oat code for a method from a method index.
const void* GetQuickOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#if defined(ART_USE_PORTABLE_COMPILER)
const void* GetPortableOatCodeFor(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#endif
+
+ // Get compiled code for a method, return null if no code
+ // exists. This is unlike Get..OatCodeFor which will return a bridge
+ // or interpreter entrypoint.
+ const void* GetOatMethodQuickCodeFor(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const void* GetOatMethodPortableCodeFor(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
pid_t GetClassesLockOwner(); // For SignalCatcher.
pid_t GetDexLockOwner(); // For SignalCatcher.
@@ -403,7 +416,7 @@
}
private:
- bool FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod* oat_method)
+ const OatFile::OatMethod FindOatMethodFor(mirror::ArtMethod* method, bool* found)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
OatFile& GetImageOatFile(gc::space::ImageSpace* space)
@@ -435,29 +448,21 @@
Handle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void AppendToBootClassPath(const DexFile& dex_file)
+ void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
- mirror::Class* c, SafeMap<uint32_t, mirror::ArtField*>& field_map)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Precomputes size needed for Class, in the case of a non-temporary class this size must be
// sufficient to hold all static fields.
uint32_t SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def);
- void LoadClass(const DexFile& dex_file,
- const DexFile::ClassDef& dex_class_def,
- Handle<mirror::Class> klass,
- mirror::ClassLoader* class_loader)
+ void LoadClass(Thread* self, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
+ Handle<mirror::Class> klass, mirror::ClassLoader* class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void LoadClassMembers(const DexFile& dex_file,
- const byte* class_data,
- Handle<mirror::Class> klass,
- mirror::ClassLoader* class_loader,
+ void LoadClassMembers(Thread* self, const DexFile& dex_file, const byte* class_data,
+ Handle<mirror::Class> klass, mirror::ClassLoader* class_loader,
const OatFile::OatClass* oat_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -472,9 +477,9 @@
void FixupStaticTrampolines(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Finds the associated oat class for a dex_file and descriptor. Returns whether the class
- // was found, and sets the data in oat_class.
- bool FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, OatFile::OatClass* oat_class)
+ // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on
+ // error and sets found to false.
+ OatFile::OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void RegisterDexFileLocked(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
@@ -483,7 +488,7 @@
bool IsDexFileRegisteredLocked(const DexFile& dex_file)
SHARED_LOCKS_REQUIRED(dex_lock_, Locks::mutator_lock_);
- bool InitializeClass(Handle<mirror::Class> klass, bool can_run_clinit,
+ bool InitializeClass(Thread* self, Handle<mirror::Class> klass, bool can_run_clinit,
bool can_init_parents)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool WaitForInitializeClass(Handle<mirror::Class> klass, Thread* self,
@@ -523,22 +528,18 @@
Handle<mirror::ObjectArray<mirror::Class>> interfaces)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkStaticFields(Handle<mirror::Class> klass, size_t* class_size)
+ bool LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkInstanceFields(Handle<mirror::Class> klass)
+ bool LinkInstanceFields(Thread* self, Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool LinkFields(Handle<mirror::Class> klass, bool is_static, size_t* class_size)
+ bool LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_static, size_t* class_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,
const DexFile& dex_file, uint32_t dex_method_index, uint32_t method_index)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void CreateReferenceInstanceOffsets(Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CreateReferenceStaticOffsets(Handle<mirror::Class> klass)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CreateReferenceOffsets(Handle<mirror::Class> klass, bool is_static,
- uint32_t reference_offsets)
+ void CreateReferenceOffsets(Handle<mirror::Class> klass, uint32_t reference_offsets)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// For use by ImageWriter to find DexCaches for its roots
@@ -672,7 +673,7 @@
std::vector<const DexFile*> boot_class_path_;
mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);;
+ std::vector<size_t> new_dex_cache_roots_ GUARDED_BY(dex_lock_);
std::vector<GcRoot<mirror::DexCache>> dex_caches_ GUARDED_BY(dex_lock_);
std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 37564e8..613ac66 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -278,10 +278,11 @@
// Confirm that all instances fields are packed together at the start
EXPECT_GE(klass->NumInstanceFields(), klass->NumReferenceInstanceFields());
StackHandleScope<1> hs(Thread::Current());
- FieldHelper fh(hs.NewHandle<mirror::ArtField>(nullptr));
+ MutableHandle<mirror::ArtField> fhandle = hs.NewHandle<mirror::ArtField>(nullptr);
for (size_t i = 0; i < klass->NumReferenceInstanceFields(); i++) {
mirror::ArtField* field = klass->GetInstanceField(i);
- fh.ChangeField(field);
+ fhandle.Assign(field);
+ FieldHelper fh(fhandle);
ASSERT_TRUE(!field->IsPrimitiveType());
mirror::Class* field_type = fh.GetType();
ASSERT_TRUE(field_type != NULL);
@@ -289,7 +290,8 @@
}
for (size_t i = klass->NumReferenceInstanceFields(); i < klass->NumInstanceFields(); i++) {
mirror::ArtField* field = klass->GetInstanceField(i);
- fh.ChangeField(field);
+ fhandle.Assign(field);
+ FieldHelper fh(fhandle);
mirror::Class* field_type = fh.GetType();
ASSERT_TRUE(field_type != NULL);
if (!fh.GetField()->IsPrimitiveType() || !field_type->IsPrimitive()) {
@@ -488,9 +490,7 @@
// alphabetical 64-bit
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_interpreter_), "entryPointFromInterpreter"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_jni_), "entryPointFromJni"));
-#if defined(ART_USE_PORTABLE_COMPILER)
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode"));
-#endif
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_), "entryPointFromQuickCompiledCode"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_), "gcMap"));
@@ -530,7 +530,6 @@
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, object_size_), "objectSize"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, primitive_type_), "primitiveType"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_instance_offsets_), "referenceInstanceOffsets"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, reference_static_offsets_), "referenceStaticOffsets"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, status_), "status"));
};
};
@@ -858,7 +857,7 @@
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
Handle<mirror::Class> statics(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
- class_linker_->EnsureInitialized(statics, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), statics, true, true);
// Static final primitives that are initialized by a compile-time constant
// expression resolve to a copy of a constant value from the constant pool.
@@ -1136,7 +1135,7 @@
CheckPreverified(security_manager.Get(), false);
- class_linker_->EnsureInitialized(security_manager, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), security_manager, true, true);
CheckPreverified(security_manager.Get(), true);
}
@@ -1151,7 +1150,7 @@
CheckPreverified(statics.Get(), false);
- class_linker_->EnsureInitialized(statics, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), statics, true, true);
CheckPreverified(statics.Get(), true);
}
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 989384e..eed6f71 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -341,23 +341,22 @@
for (const DexFile* dex_file : dex_files) {
class_linker_->RegisterDexFile(*dex_file);
}
- ScopedObjectAccessUnchecked soa(Thread::Current());
- ScopedLocalRef<jobject> class_loader_local(soa.Env(),
- soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
- jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
- soa.Self()->SetClassLoaderOverride(soa.Decode<mirror::ClassLoader*>(class_loader_local.get()));
+ Thread* self = Thread::Current();
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedLocalRef<jobject> class_loader_local(env,
+ env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
+ jobject class_loader = env->NewGlobalRef(class_loader_local.get());
+ self->SetClassLoaderOverride(class_loader_local.get());
Runtime::Current()->SetCompileTimeClassPath(class_loader, dex_files);
return class_loader;
}
CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
- vm_->check_jni_abort_hook = Hook;
- vm_->check_jni_abort_hook_data = &actual_;
+ vm_->SetCheckJniAbortHook(Hook, &actual_);
}
CheckJniAbortCatcher::~CheckJniAbortCatcher() {
- vm_->check_jni_abort_hook = nullptr;
- vm_->check_jni_abort_hook_data = nullptr;
+ vm_->SetCheckJniAbortHook(nullptr, nullptr);
EXPECT_TRUE(actual_.empty()) << actual_;
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 5b014b3..1ca6eb3 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -132,7 +132,7 @@
private:
static void Hook(void* data, const std::string& reason);
- JavaVMExt* vm_;
+ JavaVMExt* const vm_;
std::string actual_;
DISALLOW_COPY_AND_ASSIGN(CheckJniAbortCatcher);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index bb48be3..846216c 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -449,6 +449,10 @@
break;
}
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: {
// Since we replaced the field index, we ask the verifier to tell us which
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7fb199c..96b44bf 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -147,7 +147,7 @@
size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
size_t depth = 0;
- while (depth < kMaxAllocRecordStackDepth && stack_[depth].Method() != NULL) {
+ while (depth < kMaxAllocRecordStackDepth && stack_[depth].Method() != nullptr) {
++depth;
}
return depth;
@@ -178,7 +178,7 @@
jobject type_; // This is a weak global.
size_t byte_count_;
uint16_t thin_lock_id_;
- AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth]; // Unused entries have NULL method.
+ AllocRecordStackTraceElement stack_[kMaxAllocRecordStackDepth]; // Unused entries have nullptr method.
};
class Breakpoint {
@@ -297,7 +297,7 @@
static JDWP::JdwpOptions gJdwpOptions;
// Runtime JDWP state.
-static JDWP::JdwpState* gJdwpState = NULL;
+static JDWP::JdwpState* gJdwpState = nullptr;
static bool gDebuggerConnected; // debugger or DDMS is connected.
static bool gDebuggerActive; // debugger is making requests.
static bool gDisposed; // debugger called VirtualMachine.Dispose, so we should drop the connection.
@@ -399,58 +399,60 @@
return thread->IsSuspended() && thread->GetDebugSuspendCount() > 0;
}
-static mirror::Array* DecodeArray(JDWP::RefTypeId id, JDWP::JdwpError& status)
+static mirror::Array* DecodeNonNullArray(JDWP::RefTypeId id, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
- status = JDWP::ERR_INVALID_OBJECT;
- return NULL;
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id, error);
+ if (o == nullptr) {
+ *error = JDWP::ERR_INVALID_OBJECT;
+ return nullptr;
}
if (!o->IsArrayInstance()) {
- status = JDWP::ERR_INVALID_ARRAY;
- return NULL;
+ *error = JDWP::ERR_INVALID_ARRAY;
+ return nullptr;
}
- status = JDWP::ERR_NONE;
+ *error = JDWP::ERR_NONE;
return o->AsArray();
}
-static mirror::Class* DecodeClass(JDWP::RefTypeId id, JDWP::JdwpError& status)
+static mirror::Class* DecodeClass(JDWP::RefTypeId id, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
- status = JDWP::ERR_INVALID_OBJECT;
- return NULL;
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(id, error);
+ if (o == nullptr) {
+ *error = JDWP::ERR_INVALID_OBJECT;
+ return nullptr;
}
if (!o->IsClass()) {
- status = JDWP::ERR_INVALID_CLASS;
- return NULL;
+ *error = JDWP::ERR_INVALID_CLASS;
+ return nullptr;
}
- status = JDWP::ERR_NONE;
+ *error = JDWP::ERR_NONE;
return o->AsClass();
}
-static JDWP::JdwpError DecodeThread(ScopedObjectAccessUnchecked& soa, JDWP::ObjectId thread_id, Thread*& thread)
+static Thread* DecodeThread(ScopedObjectAccessUnchecked& soa, JDWP::ObjectId thread_id,
+ JDWP::JdwpError* error)
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* thread_peer = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_id);
- if (thread_peer == NULL || thread_peer == ObjectRegistry::kInvalidObject) {
+ mirror::Object* thread_peer = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_id, error);
+ if (thread_peer == nullptr) {
// This isn't even an object.
- return JDWP::ERR_INVALID_OBJECT;
+ *error = JDWP::ERR_INVALID_OBJECT;
+ return nullptr;
}
mirror::Class* java_lang_Thread = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread);
if (!java_lang_Thread->IsAssignableFrom(thread_peer->GetClass())) {
// This isn't a thread.
- return JDWP::ERR_INVALID_THREAD;
+ *error = JDWP::ERR_INVALID_THREAD;
+ return nullptr;
}
- thread = Thread::FromManagedThread(soa, thread_peer);
- if (thread == NULL) {
- // This is a java.lang.Thread without a Thread*. Must be a zombie.
- return JDWP::ERR_THREAD_NOT_ALIVE;
- }
- return JDWP::ERR_NONE;
+ Thread* thread = Thread::FromManagedThread(soa, thread_peer);
+ // If thread is null then this a java.lang.Thread without a Thread*. Must be a un-started or a
+ // zombie.
+ *error = (thread == nullptr) ? JDWP::ERR_THREAD_NOT_ALIVE : JDWP::ERR_NONE;
+ return thread;
}
static JDWP::JdwpTag BasicTagFromDescriptor(const char* descriptor) {
@@ -468,7 +470,7 @@
static JDWP::JdwpTag TagFromClass(const ScopedObjectAccessUnchecked& soa, mirror::Class* c)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK(c != NULL);
+ CHECK(c != nullptr);
if (c->IsArrayClass()) {
return JDWP::JT_ARRAY;
}
@@ -510,7 +512,7 @@
* Null objects are tagged JT_OBJECT.
*/
JDWP::JdwpTag Dbg::TagFromObject(const ScopedObjectAccessUnchecked& soa, mirror::Object* o) {
- return (o == NULL) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
+ return (o == nullptr) ? JDWP::JT_OBJECT : TagFromClass(soa, o->GetClass());
}
static bool IsPrimitiveTag(JDWP::JdwpTag tag) {
@@ -651,7 +653,7 @@
// debugger, passively listen for a debugger, or block waiting for a
// debugger.
gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
- if (gJdwpState == NULL) {
+ if (gJdwpState == nullptr) {
// We probably failed because some other process has the port already, which means that
// if we don't abort the user is likely to think they're talking to us when they're actually
// talking to that other process.
@@ -709,7 +711,7 @@
}
Thread* Dbg::GetDebugThread() {
- return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
+ return (gJdwpState != nullptr) ? gJdwpState->GetDebugThread() : nullptr;
}
void Dbg::ClearWaitForEventThread() {
@@ -827,12 +829,14 @@
}
std::string Dbg::GetClassName(JDWP::RefTypeId class_id) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(class_id);
- if (o == NULL) {
- return "NULL";
- }
- if (o == ObjectRegistry::kInvalidObject) {
- return StringPrintf("invalid object %p", reinterpret_cast<void*>(class_id));
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(class_id, &error);
+ if (o == nullptr) {
+ if (error == JDWP::ERR_NONE) {
+ return "NULL";
+ } else {
+ return StringPrintf("invalid object %p", reinterpret_cast<void*>(class_id));
+ }
}
if (!o->IsClass()) {
return StringPrintf("non-class %p", o); // This is only used for debugging output anyway.
@@ -848,34 +852,37 @@
return DescriptorToName(klass->GetDescriptor(&temp));
}
-JDWP::JdwpError Dbg::GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId& class_object_id) {
+JDWP::JdwpError Dbg::GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId* class_object_id) {
JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(id, status);
- if (c == NULL) {
+ mirror::Class* c = DecodeClass(id, &status);
+ if (c == nullptr) {
+ *class_object_id = 0;
return status;
}
- class_object_id = gRegistry->Add(c);
+ *class_object_id = gRegistry->Add(c);
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId& superclass_id) {
+JDWP::JdwpError Dbg::GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId* superclass_id) {
JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(id, status);
- if (c == NULL) {
+ mirror::Class* c = DecodeClass(id, &status);
+ if (c == nullptr) {
+ *superclass_id = 0;
return status;
}
if (c->IsInterface()) {
// http://code.google.com/p/android/issues/detail?id=20856
- superclass_id = 0;
+ *superclass_id = 0;
} else {
- superclass_id = gRegistry->Add(c->GetSuperClass());
+ *superclass_id = gRegistry->Add(c->GetSuperClass());
}
return JDWP::ERR_NONE;
}
JDWP::JdwpError Dbg::GetClassLoader(JDWP::RefTypeId id, JDWP::ExpandBuf* pReply) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(id, &error);
+ if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
expandBufAddObjectId(pReply, gRegistry->Add(o->GetClass()->GetClassLoader()));
@@ -883,10 +890,10 @@
}
JDWP::JdwpError Dbg::GetModifiers(JDWP::RefTypeId id, JDWP::ExpandBuf* pReply) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(id, status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(id, &error);
+ if (c == nullptr) {
+ return error;
}
uint32_t access_flags = c->GetAccessFlags() & kAccJavaFlagsMask;
@@ -903,10 +910,10 @@
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* reply)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+JDWP::JdwpError Dbg::GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* reply) {
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
@@ -921,10 +928,10 @@
Runtime::Current()->GetThreadList()->ResumeAll();
self->TransitionFromSuspendedToRunnable();
- if (monitor_info.owner_ != NULL) {
+ if (monitor_info.owner_ != nullptr) {
expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
} else {
- expandBufAddObjectId(reply, gRegistry->Add(NULL));
+ expandBufAddObjectId(reply, gRegistry->Add(nullptr));
}
expandBufAdd4BE(reply, monitor_info.entry_count_);
expandBufAdd4BE(reply, monitor_info.waiters_.size());
@@ -935,8 +942,8 @@
}
JDWP::JdwpError Dbg::GetOwnedMonitors(JDWP::ObjectId thread_id,
- std::vector<JDWP::ObjectId>& monitors,
- std::vector<uint32_t>& stack_depths) {
+ std::vector<JDWP::ObjectId>* monitors,
+ std::vector<uint32_t>* stack_depths) {
struct OwnedMonitorVisitor : public StackVisitor {
OwnedMonitorVisitor(Thread* thread, Context* context,
std::vector<JDWP::ObjectId>* monitor_vector,
@@ -963,16 +970,17 @@
}
size_t current_stack_depth;
- std::vector<JDWP::ObjectId>* monitors;
- std::vector<uint32_t>* stack_depths;
+ std::vector<JDWP::ObjectId>* const monitors;
+ std::vector<uint32_t>* const stack_depths;
};
ScopedObjectAccessUnchecked soa(Thread::Current());
Thread* thread;
{
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
- if (error != JDWP::ERR_NONE) {
+ JDWP::JdwpError error;
+ thread = DecodeThread(soa, thread_id, &error);
+ if (thread == nullptr) {
return error;
}
if (!IsSuspendedForDebugger(soa, thread)) {
@@ -980,20 +988,21 @@
}
}
std::unique_ptr<Context> context(Context::Create());
- OwnedMonitorVisitor visitor(thread, context.get(), &monitors, &stack_depths);
+ OwnedMonitorVisitor visitor(thread, context.get(), monitors, stack_depths);
visitor.WalkStack();
return JDWP::ERR_NONE;
}
JDWP::JdwpError Dbg::GetContendedMonitor(JDWP::ObjectId thread_id,
- JDWP::ObjectId& contended_monitor) {
+ JDWP::ObjectId* contended_monitor) {
mirror::Object* contended_monitor_obj;
ScopedObjectAccessUnchecked soa(Thread::Current());
+ *contended_monitor = 0;
{
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
- if (error != JDWP::ERR_NONE) {
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
+ if (thread == nullptr) {
return error;
}
if (!IsSuspendedForDebugger(soa, thread)) {
@@ -1003,108 +1012,105 @@
}
// Add() requires the thread_list_lock_ not held to avoid the lock
// level violation.
- contended_monitor = gRegistry->Add(contended_monitor_obj);
+ *contended_monitor = gRegistry->Add(contended_monitor_obj);
return JDWP::ERR_NONE;
}
JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class_ids,
- std::vector<uint64_t>& counts)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::vector<uint64_t>* counts) {
gc::Heap* heap = Runtime::Current()->GetHeap();
heap->CollectGarbage(false);
std::vector<mirror::Class*> classes;
- counts.clear();
+ counts->clear();
for (size_t i = 0; i < class_ids.size(); ++i) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_ids[i], status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_ids[i], &error);
+ if (c == nullptr) {
+ return error;
}
classes.push_back(c);
- counts.push_back(0);
+ counts->push_back(0);
}
- heap->CountInstances(classes, false, &counts[0]);
+ heap->CountInstances(classes, false, &(*counts)[0]);
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, std::vector<JDWP::ObjectId>& instances)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count,
+ std::vector<JDWP::ObjectId>* instances) {
gc::Heap* heap = Runtime::Current()->GetHeap();
// We only want reachable instances, so do a GC.
heap->CollectGarbage(false);
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
if (c == nullptr) {
- return status;
+ return error;
}
std::vector<mirror::Object*> raw_instances;
Runtime::Current()->GetHeap()->GetInstances(c, max_count, raw_instances);
for (size_t i = 0; i < raw_instances.size(); ++i) {
- instances.push_back(gRegistry->Add(raw_instances[i]));
+ instances->push_back(gRegistry->Add(raw_instances[i]));
}
return JDWP::ERR_NONE;
}
JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count,
- std::vector<JDWP::ObjectId>& referring_objects)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::vector<JDWP::ObjectId>* referring_objects) {
gc::Heap* heap = Runtime::Current()->GetHeap();
heap->CollectGarbage(false);
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
std::vector<mirror::Object*> raw_instances;
heap->GetReferringObjects(o, max_count, raw_instances);
for (size_t i = 0; i < raw_instances.size(); ++i) {
- referring_objects.push_back(gRegistry->Add(raw_instances[i]));
+ referring_objects->push_back(gRegistry->Add(raw_instances[i]));
}
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::DisableCollection(JDWP::ObjectId object_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+JDWP::JdwpError Dbg::DisableCollection(JDWP::ObjectId object_id) {
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
gRegistry->DisableCollection(object_id);
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::EnableCollection(JDWP::ObjectId object_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
+JDWP::JdwpError Dbg::EnableCollection(JDWP::ObjectId object_id) {
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
// Unlike DisableCollection, JDWP specs do not state an invalid object causes an error. The RI
// also ignores these cases and never return an error. However it's not obvious why this command
// should behave differently from DisableCollection and IsCollected commands. So let's be more
// strict and return an error if this happens.
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+ if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
gRegistry->EnableCollection(object_id);
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::IsCollected(JDWP::ObjectId object_id, bool& is_collected)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+JDWP::JdwpError Dbg::IsCollected(JDWP::ObjectId object_id, bool* is_collected) {
+ *is_collected = true;
if (object_id == 0) {
// Null object id is invalid.
return JDWP::ERR_INVALID_OBJECT;
}
// JDWP specs state an INVALID_OBJECT error is returned if the object ID is not valid. However
// the RI seems to ignore this and assume object has been collected.
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
- is_collected = true;
- } else {
- is_collected = gRegistry->IsCollected(object_id);
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (o != nullptr) {
+ *is_collected = gRegistry->IsCollected(object_id);
}
return JDWP::ERR_NONE;
}
-void Dbg::DisposeObject(JDWP::ObjectId object_id, uint32_t reference_count)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void Dbg::DisposeObject(JDWP::ObjectId object_id, uint32_t reference_count) {
gRegistry->DisposeObject(object_id, reference_count);
}
@@ -1120,10 +1126,10 @@
}
JDWP::JdwpError Dbg::GetReflectedType(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ return error;
}
JDWP::JdwpTypeTag type_tag = GetTypeTag(c);
@@ -1132,12 +1138,12 @@
return JDWP::ERR_NONE;
}
-void Dbg::GetClassList(std::vector<JDWP::RefTypeId>& classes) {
+void Dbg::GetClassList(std::vector<JDWP::RefTypeId>* classes) {
// Get the complete list of reference classes (i.e. all classes except
// the primitive types).
// Returns a newly-allocated buffer full of RefTypeId values.
struct ClassListCreator {
- explicit ClassListCreator(std::vector<JDWP::RefTypeId>& classes) : classes(classes) {
+ explicit ClassListCreator(std::vector<JDWP::RefTypeId>* classes) : classes(classes) {
}
static bool Visit(mirror::Class* c, void* arg) {
@@ -1148,12 +1154,12 @@
// annotalysis.
bool Visit(mirror::Class* c) NO_THREAD_SAFETY_ANALYSIS {
if (!c->IsPrimitive()) {
- classes.push_back(gRegistry->AddRefType(c));
+ classes->push_back(gRegistry->AddRefType(c));
}
return true;
}
- std::vector<JDWP::RefTypeId>& classes;
+ std::vector<JDWP::RefTypeId>* const classes;
};
ClassListCreator clc(classes);
@@ -1163,10 +1169,10 @@
JDWP::JdwpError Dbg::GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* pTypeTag,
uint32_t* pStatus, std::string* pDescriptor) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ return error;
}
if (c->IsArrayClass()) {
@@ -1181,26 +1187,26 @@
*pTypeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
}
- if (pDescriptor != NULL) {
+ if (pDescriptor != nullptr) {
std::string temp;
*pDescriptor = c->GetDescriptor(&temp);
}
return JDWP::ERR_NONE;
}
-void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids) {
+void Dbg::FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>* ids) {
std::vector<mirror::Class*> classes;
Runtime::Current()->GetClassLinker()->LookupClasses(descriptor, classes);
- ids.clear();
+ ids->clear();
for (size_t i = 0; i < classes.size(); ++i) {
- ids.push_back(gRegistry->Add(classes[i]));
+ ids->push_back(gRegistry->Add(classes[i]));
}
}
-JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
- if (o == NULL || o == ObjectRegistry::kInvalidObject) {
+JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply) {
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
@@ -1214,37 +1220,39 @@
}
JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string* signature) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ return error;
}
std::string temp;
*signature = c->GetDescriptor(&temp);
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetSourceFile(JDWP::RefTypeId class_id, std::string& result) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
+JDWP::JdwpError Dbg::GetSourceFile(JDWP::RefTypeId class_id, std::string* result) {
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
if (c == nullptr) {
- return status;
+ return error;
}
const char* source_file = c->GetSourceFile();
if (source_file == nullptr) {
return JDWP::ERR_ABSENT_INFORMATION;
}
- result = source_file;
+ *result = source_file;
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag) {
+JDWP::JdwpError Dbg::GetObjectTag(JDWP::ObjectId object_id, uint8_t* tag) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id);
- if (o == ObjectRegistry::kInvalidObject) {
- return JDWP::ERR_INVALID_OBJECT;
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ *tag = JDWP::JT_VOID;
+ return error;
}
- tag = TagFromObject(soa, o);
+ *tag = TagFromObject(soa, o);
return JDWP::ERR_NONE;
}
@@ -1278,21 +1286,21 @@
}
}
-JDWP::JdwpError Dbg::GetArrayLength(JDWP::ObjectId array_id, int& length) {
- JDWP::JdwpError status;
- mirror::Array* a = DecodeArray(array_id, status);
- if (a == NULL) {
- return status;
+JDWP::JdwpError Dbg::GetArrayLength(JDWP::ObjectId array_id, int32_t* length) {
+ JDWP::JdwpError error;
+ mirror::Array* a = DecodeNonNullArray(array_id, &error);
+ if (a == nullptr) {
+ return error;
}
- length = a->GetLength();
+ *length = a->GetLength();
return JDWP::ERR_NONE;
}
JDWP::JdwpError Dbg::OutputArray(JDWP::ObjectId array_id, int offset, int count, JDWP::ExpandBuf* pReply) {
- JDWP::JdwpError status;
- mirror::Array* a = DecodeArray(array_id, status);
+ JDWP::JdwpError error;
+ mirror::Array* a = DecodeNonNullArray(array_id, &error);
if (a == nullptr) {
- return status;
+ return error;
}
if (offset < 0 || count < 0 || offset > a->GetLength() || a->GetLength() - offset < count) {
@@ -1335,24 +1343,23 @@
}
template <typename T>
-static void CopyArrayData(mirror::Array* a, JDWP::Request& src, int offset, int count)
+static void CopyArrayData(mirror::Array* a, JDWP::Request* src, int offset, int count)
NO_THREAD_SAFETY_ANALYSIS {
// TODO: fix when annotalysis correctly handles non-member functions.
DCHECK(a->GetClass()->IsPrimitiveArray());
T* dst = reinterpret_cast<T*>(a->GetRawData(sizeof(T), offset));
for (int i = 0; i < count; ++i) {
- *dst++ = src.ReadValue(sizeof(T));
+ *dst++ = src->ReadValue(sizeof(T));
}
}
JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int count,
- JDWP::Request& request)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- JDWP::JdwpError status;
- mirror::Array* dst = DecodeArray(array_id, status);
- if (dst == NULL) {
- return status;
+ JDWP::Request* request) {
+ JDWP::JdwpError error;
+ mirror::Array* dst = DecodeNonNullArray(array_id, &error);
+ if (dst == nullptr) {
+ return error;
}
if (offset < 0 || count < 0 || offset > dst->GetLength() || dst->GetLength() - offset < count) {
@@ -1375,10 +1382,11 @@
} else {
mirror::ObjectArray<mirror::Object>* oa = dst->AsObjectArray<mirror::Object>();
for (int i = 0; i < count; ++i) {
- JDWP::ObjectId id = request.ReadObjectId();
- mirror::Object* o = gRegistry->Get<mirror::Object*>(id);
- if (o == ObjectRegistry::kInvalidObject) {
- return JDWP::ERR_INVALID_OBJECT;
+ JDWP::ObjectId id = request->ReadObjectId();
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
}
oa->Set<false>(offset + i, o);
}
@@ -1391,13 +1399,14 @@
return gRegistry->Add(mirror::String::AllocFromModifiedUtf8(Thread::Current(), str.c_str()));
}
-JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId& new_object) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+JDWP::JdwpError Dbg::CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object) {
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ *new_object = 0;
+ return error;
}
- new_object = gRegistry->Add(c->AllocObject(Thread::Current()));
+ *new_object = gRegistry->Add(c->AllocObject(Thread::Current()));
return JDWP::ERR_NONE;
}
@@ -1405,15 +1414,16 @@
* Used by Eclipse's "Display" view to evaluate "new byte[5]" to get "(byte[]) [0, 0, 0, 0, 0]".
*/
JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t length,
- JDWP::ObjectId& new_array) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(array_class_id, status);
- if (c == NULL) {
- return status;
+ JDWP::ObjectId* new_array) {
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(array_class_id, &error);
+ if (c == nullptr) {
+ *new_array = 0;
+ return error;
}
- new_array = gRegistry->Add(mirror::Array::Alloc<true>(Thread::Current(), c, length,
- c->GetComponentSize(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator()));
+ *new_array = gRegistry->Add(mirror::Array::Alloc<true>(Thread::Current(), c, length,
+ c->GetComponentSizeShift(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
return JDWP::ERR_NONE;
}
@@ -1442,7 +1452,9 @@
bool Dbg::MatchThread(JDWP::ObjectId expected_thread_id, Thread* event_thread) {
CHECK(event_thread != nullptr);
- mirror::Object* expected_thread_peer = gRegistry->Get<mirror::Object*>(expected_thread_id);
+ JDWP::JdwpError error;
+ mirror::Object* expected_thread_peer = gRegistry->Get<mirror::Object*>(expected_thread_id,
+ &error);
return expected_thread_peer == event_thread->GetPeer();
}
@@ -1459,8 +1471,8 @@
if (event_class == nullptr) {
return false;
}
- JDWP::JdwpError status;
- mirror::Class* expected_class = DecodeClass(class_id, status);
+ JDWP::JdwpError error;
+ mirror::Class* expected_class = DecodeClass(class_id, &error);
CHECK(expected_class != nullptr);
return expected_class->IsAssignableFrom(event_class);
}
@@ -1475,7 +1487,8 @@
}
bool Dbg::MatchInstance(JDWP::ObjectId expected_instance_id, mirror::Object* event_instance) {
- mirror::Object* modifier_instance = gRegistry->Get<mirror::Object*>(expected_instance_id);
+ JDWP::JdwpError error;
+ mirror::Object* modifier_instance = gRegistry->Get<mirror::Object*>(expected_instance_id, &error);
return modifier_instance == event_instance;
}
@@ -1492,8 +1505,7 @@
}
}
-std::string Dbg::GetMethodName(JDWP::MethodId method_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+std::string Dbg::GetMethodName(JDWP::MethodId method_id) {
mirror::ArtMethod* m = FromMethodId(method_id);
if (m == nullptr) {
return "NULL";
@@ -1501,8 +1513,7 @@
return m->GetName();
}
-std::string Dbg::GetFieldName(JDWP::FieldId field_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+std::string Dbg::GetFieldName(JDWP::FieldId field_id) {
mirror::ArtField* f = FromFieldId(field_id);
if (f == nullptr) {
return "NULL";
@@ -1569,10 +1580,10 @@
}
JDWP::JdwpError Dbg::OutputDeclaredFields(JDWP::RefTypeId class_id, bool with_generic, JDWP::ExpandBuf* pReply) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ return error;
}
size_t instance_field_count = c->NumInstanceFields();
@@ -1596,10 +1607,10 @@
JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_generic,
JDWP::ExpandBuf* pReply) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ return error;
}
size_t direct_method_count = c->NumDirectMethods();
@@ -1622,12 +1633,12 @@
}
JDWP::JdwpError Dbg::OutputDeclaredInterfaces(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply) {
- JDWP::JdwpError status;
+ JDWP::JdwpError error;
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
- Handle<mirror::Class> c(hs.NewHandle(DecodeClass(class_id, status)));
+ Handle<mirror::Class> c(hs.NewHandle(DecodeClass(class_id, &error)));
if (c.Get() == nullptr) {
- return status;
+ return error;
}
size_t interface_count = c->NumDirectInterfaces();
expandBufAdd4BE(pReply, interface_count);
@@ -1638,8 +1649,7 @@
return JDWP::ERR_NONE;
}
-void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::ExpandBuf* pReply)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::ExpandBuf* pReply) {
struct DebugCallbackContext {
int numItems;
JDWP::ExpandBuf* pReply;
@@ -1678,7 +1688,7 @@
if (code_item != nullptr) {
m->GetDexFile()->DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(),
- DebugCallbackContext::Callback, NULL, &context);
+ DebugCallbackContext::Callback, nullptr, &context);
}
JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
@@ -1736,7 +1746,7 @@
const DexFile::CodeItem* code_item = m->GetCodeItem();
if (code_item != nullptr) {
m->GetDexFile()->DecodeDebugInfo(
- code_item, m->IsStatic(), m->GetDexMethodIndex(), NULL, DebugCallbackContext::Callback,
+ code_item, m->IsStatic(), m->GetDexMethodIndex(), nullptr, DebugCallbackContext::Callback,
&context);
}
@@ -1758,10 +1768,9 @@
}
JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id,
- std::vector<uint8_t>& bytecodes)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::vector<uint8_t>* bytecodes) {
mirror::ArtMethod* m = FromMethodId(method_id);
- if (m == NULL) {
+ if (m == nullptr) {
return JDWP::ERR_INVALID_METHODID;
}
const DexFile::CodeItem* code_item = m->GetCodeItem();
@@ -1769,7 +1778,7 @@
const uint8_t* begin = reinterpret_cast<const uint8_t*>(code_item->insns_);
const uint8_t* end = begin + byte_count;
for (const uint8_t* p = begin; p != end; ++p) {
- bytecodes.push_back(*p);
+ bytecodes->push_back(*p);
}
return JDWP::ERR_NONE;
}
@@ -1786,24 +1795,24 @@
JDWP::FieldId field_id, JDWP::ExpandBuf* pReply,
bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- JDWP::JdwpError status;
- mirror::Class* c = DecodeClass(ref_type_id, status);
- if (ref_type_id != 0 && c == NULL) {
- return status;
+ JDWP::JdwpError error;
+ mirror::Class* c = DecodeClass(ref_type_id, &error);
+ if (ref_type_id != 0 && c == nullptr) {
+ return error;
}
- mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id);
- if ((!is_static && o == NULL) || o == ObjectRegistry::kInvalidObject) {
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error);
+ if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
mirror::ArtField* f = FromFieldId(field_id);
mirror::Class* receiver_class = c;
- if (receiver_class == NULL && o != NULL) {
+ if (receiver_class == nullptr && o != nullptr) {
receiver_class = o->GetClass();
}
- // TODO: should we give up now if receiver_class is NULL?
- if (receiver_class != NULL && !f->GetDeclaringClass()->IsAssignableFrom(receiver_class)) {
+ // TODO: should we give up now if receiver_class is nullptr?
+ if (receiver_class != nullptr && !f->GetDeclaringClass()->IsAssignableFrom(receiver_class)) {
LOG(INFO) << "ERR_INVALID_FIELDID: " << PrettyField(f) << " " << PrettyClass(receiver_class);
return JDWP::ERR_INVALID_FIELDID;
}
@@ -1816,7 +1825,8 @@
}
} else {
if (f->IsStatic()) {
- LOG(WARNING) << "Ignoring non-NULL receiver for ObjectReference.SetValues on static field " << PrettyField(f);
+ LOG(WARNING) << "Ignoring non-nullptr receiver for ObjectReference.SetValues on static field "
+ << PrettyField(f);
}
}
if (f->IsStatic()) {
@@ -1844,15 +1854,17 @@
return GetFieldValueImpl(0, object_id, field_id, pReply, false);
}
-JDWP::JdwpError Dbg::GetStaticFieldValue(JDWP::RefTypeId ref_type_id, JDWP::FieldId field_id, JDWP::ExpandBuf* pReply) {
+JDWP::JdwpError Dbg::GetStaticFieldValue(JDWP::RefTypeId ref_type_id, JDWP::FieldId field_id,
+ JDWP::ExpandBuf* pReply) {
return GetFieldValueImpl(ref_type_id, 0, field_id, pReply, true);
}
static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId field_id,
uint64_t value, int width, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id);
- if ((!is_static && o == NULL) || o == ObjectRegistry::kInvalidObject) {
+ JDWP::JdwpError error;
+ mirror::Object* o = Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error);
+ if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
mirror::ArtField* f = FromFieldId(field_id);
@@ -1865,7 +1877,7 @@
}
} else {
if (f->IsStatic()) {
- LOG(WARNING) << "Ignoring non-NULL receiver for ObjectReference.SetValues on static field " << PrettyField(f);
+ LOG(WARNING) << "Ignoring non-nullptr receiver for ObjectReference.SetValues on static field " << PrettyField(f);
}
}
if (f->IsStatic()) {
@@ -1885,11 +1897,11 @@
f->Set32<false>(o, value);
}
} else {
- mirror::Object* v = Dbg::GetObjectRegistry()->Get<mirror::Object*>(value);
- if (v == ObjectRegistry::kInvalidObject) {
+ mirror::Object* v = Dbg::GetObjectRegistry()->Get<mirror::Object*>(value, &error);
+ if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
- if (v != NULL) {
+ if (v != nullptr) {
mirror::Class* field_type;
{
StackHandleScope<3> hs(Thread::Current());
@@ -1919,8 +1931,12 @@
}
JDWP::JdwpError Dbg::StringToUtf8(JDWP::ObjectId string_id, std::string* str) {
- mirror::Object* obj = gRegistry->Get<mirror::Object*>(string_id);
- if (obj == nullptr || obj == ObjectRegistry::kInvalidObject) {
+ JDWP::JdwpError error;
+ mirror::Object* obj = gRegistry->Get<mirror::Object*>(string_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ if (obj == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
{
@@ -1957,40 +1973,42 @@
}
}
-JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string& name) {
+JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string* name) {
ScopedObjectAccessUnchecked soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
+ UNUSED(thread);
if (error != JDWP::ERR_NONE && error != JDWP::ERR_THREAD_NOT_ALIVE) {
return error;
}
// We still need to report the zombie threads' names, so we can't just call Thread::GetThreadName.
- mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id);
+ mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id, &error);
+ CHECK(thread_object != nullptr) << error;
mirror::ArtField* java_lang_Thread_name_field =
soa.DecodeField(WellKnownClasses::java_lang_Thread_name);
mirror::String* s =
reinterpret_cast<mirror::String*>(java_lang_Thread_name_field->GetObject(thread_object));
- if (s != NULL) {
- name = s->ToModifiedUtf8();
+ if (s != nullptr) {
+ *name = s->ToModifiedUtf8();
}
return JDWP::ERR_NONE;
}
JDWP::JdwpError Dbg::GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id);
- if (thread_object == ObjectRegistry::kInvalidObject) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ JDWP::JdwpError error;
+ mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id, &error);
+ if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
- const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroup");
+ ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroup");
// Okay, so it's an object, but is it actually a thread?
- JDWP::JdwpError error;
{
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- error = DecodeThread(soa, thread_id, thread);
+ Thread* thread = DecodeThread(soa, thread_id, &error);
+ UNUSED(thread);
}
if (error == JDWP::ERR_THREAD_NOT_ALIVE) {
// Zombie threads are in the null group.
@@ -2006,36 +2024,124 @@
JDWP::ObjectId thread_group_id = gRegistry->Add(group);
expandBufAddObjectId(pReply, thread_group_id);
}
- soa.Self()->EndAssertNoThreadSuspension(old_cause);
return error;
}
-std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
- CHECK(thread_group != nullptr);
- const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
+static mirror::Object* DecodeThreadGroup(ScopedObjectAccessUnchecked& soa,
+ JDWP::ObjectId thread_group_id, JDWP::JdwpError* error)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Object* thread_group = Dbg::GetObjectRegistry()->Get<mirror::Object*>(thread_group_id,
+ error);
+ if (*error != JDWP::ERR_NONE) {
+ return nullptr;
+ }
+ if (thread_group == nullptr) {
+ *error = JDWP::ERR_INVALID_OBJECT;
+ return nullptr;
+ }
mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
CHECK(c != nullptr);
- mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
- CHECK(f != NULL);
- mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
- soa.Self()->EndAssertNoThreadSuspension(old_cause);
- return s->ToModifiedUtf8();
+ if (!c->IsAssignableFrom(thread_group->GetClass())) {
+ // This is not a java.lang.ThreadGroup.
+ *error = JDWP::ERR_INVALID_THREAD_GROUP;
+ return nullptr;
+ }
+ *error = JDWP::ERR_NONE;
+ return thread_group;
}
-JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) {
+JDWP::JdwpError Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id, JDWP::ExpandBuf* pReply) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
- CHECK(thread_group != nullptr);
- const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupParent");
+ JDWP::JdwpError error;
+ mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupName");
mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
- CHECK(c != nullptr);
- mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
- CHECK(f != NULL);
- mirror::Object* parent = f->GetObject(thread_group);
- soa.Self()->EndAssertNoThreadSuspension(old_cause);
- return gRegistry->Add(parent);
+ mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;");
+ CHECK(f != nullptr);
+ mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group));
+
+ std::string thread_group_name(s->ToModifiedUtf8());
+ expandBufAddUtf8String(pReply, thread_group_name);
+ return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id, JDWP::ExpandBuf* pReply) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ JDWP::JdwpError error;
+ mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ mirror::Object* parent;
+ {
+ ScopedAssertNoThreadSuspension ants(soa.Self(), "Debugger: GetThreadGroupParent");
+ mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup);
+ CHECK(c != nullptr);
+ mirror::ArtField* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
+ CHECK(f != nullptr);
+ parent = f->GetObject(thread_group);
+ }
+ JDWP::ObjectId parent_group_id = gRegistry->Add(parent);
+ expandBufAddObjectId(pReply, parent_group_id);
+ return JDWP::ERR_NONE;
+}
+
+static void GetChildThreadGroups(ScopedObjectAccessUnchecked& soa, mirror::Object* thread_group,
+ std::vector<JDWP::ObjectId>* child_thread_group_ids)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(thread_group != nullptr);
+
+ // Get the ArrayList<ThreadGroup> "groups" out of this thread group...
+ mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;");
+ mirror::Object* groups_array_list = groups_field->GetObject(thread_group);
+
+ // Get the array and size out of the ArrayList<ThreadGroup>...
+ mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;");
+ mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I");
+ mirror::ObjectArray<mirror::Object>* groups_array =
+ array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>();
+ const int32_t size = size_field->GetInt(groups_array_list);
+
+ // Copy the first 'size' elements out of the array into the result.
+ ObjectRegistry* registry = Dbg::GetObjectRegistry();
+ for (int32_t i = 0; i < size; ++i) {
+ child_thread_group_ids->push_back(registry->Add(groups_array->Get(i)));
+ }
+}
+
+JDWP::JdwpError Dbg::GetThreadGroupChildren(JDWP::ObjectId thread_group_id,
+ JDWP::ExpandBuf* pReply) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ JDWP::JdwpError error;
+ mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+
+ // Add child threads.
+ {
+ std::vector<JDWP::ObjectId> child_thread_ids;
+ GetThreads(thread_group, &child_thread_ids);
+ expandBufAdd4BE(pReply, child_thread_ids.size());
+ for (JDWP::ObjectId child_thread_id : child_thread_ids) {
+ expandBufAddObjectId(pReply, child_thread_id);
+ }
+ }
+
+ // Add child thread groups.
+ {
+ std::vector<JDWP::ObjectId> child_thread_groups_ids;
+ GetChildThreadGroups(soa, thread_group, &child_thread_groups_ids);
+ expandBufAdd4BE(pReply, child_thread_groups_ids.size());
+ for (JDWP::ObjectId child_thread_group_id : child_thread_groups_ids) {
+ expandBufAddObjectId(pReply, child_thread_group_id);
+ }
+ }
+
+ return JDWP::ERR_NONE;
}
JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
@@ -2045,13 +2151,6 @@
return gRegistry->Add(group);
}
-JDWP::ObjectId Dbg::GetMainThreadGroupId() {
- ScopedObjectAccess soa(Thread::Current());
- mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup);
- mirror::Object* group = f->GetObject(f->GetDeclaringClass());
- return gRegistry->Add(group);
-}
-
JDWP::JdwpThreadStatus Dbg::ToJdwpThreadStatus(ThreadState state) {
switch (state) {
case kBlocked:
@@ -2093,8 +2192,8 @@
*pSuspendStatus = JDWP::SUSPEND_STATUS_NOT_SUSPENDED;
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
if (error == JDWP::ERR_THREAD_NOT_ALIVE) {
*pThreadStatus = JDWP::TS_ZOMBIE;
@@ -2114,8 +2213,8 @@
JDWP::JdwpError Dbg::GetThreadDebugSuspendCount(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply) {
ScopedObjectAccess soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
return error;
}
@@ -2127,8 +2226,8 @@
JDWP::JdwpError Dbg::Interrupt(JDWP::ObjectId thread_id) {
ScopedObjectAccess soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
return error;
}
@@ -2149,9 +2248,8 @@
return (group == desired_thread_group);
}
-void Dbg::GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& thread_ids) {
+void Dbg::GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* thread_ids) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
std::list<Thread*> all_threads_list;
{
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
@@ -2178,37 +2276,15 @@
continue;
}
if (IsInDesiredThreadGroup(soa, thread_group, peer)) {
- thread_ids.push_back(gRegistry->Add(peer));
+ thread_ids->push_back(gRegistry->Add(peer));
}
}
}
-void Dbg::GetChildThreadGroups(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& child_thread_group_ids) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id);
-
- // Get the ArrayList<ThreadGroup> "groups" out of this thread group...
- mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;");
- mirror::Object* groups_array_list = groups_field->GetObject(thread_group);
-
- // Get the array and size out of the ArrayList<ThreadGroup>...
- mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;");
- mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I");
- mirror::ObjectArray<mirror::Object>* groups_array =
- array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>();
- const int32_t size = size_field->GetInt(groups_array_list);
-
- // Copy the first 'size' elements out of the array into the result.
- for (int32_t i = 0; i < size; ++i) {
- child_thread_group_ids.push_back(gRegistry->Add(groups_array->Get(i)));
- }
-}
-
-static int GetStackDepth(Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static int GetStackDepth(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
struct CountStackDepthVisitor : public StackVisitor {
explicit CountStackDepthVisitor(Thread* thread)
- : StackVisitor(thread, NULL), depth(0) {}
+ : StackVisitor(thread, nullptr), depth(0) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
// annotalysis.
@@ -2226,18 +2302,19 @@
return visitor.depth;
}
-JDWP::JdwpError Dbg::GetThreadFrameCount(JDWP::ObjectId thread_id, size_t& result) {
+JDWP::JdwpError Dbg::GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* result) {
ScopedObjectAccess soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ *result = 0;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
return error;
}
if (!IsSuspendedForDebugger(soa, thread)) {
return JDWP::ERR_THREAD_NOT_SUSPENDED;
}
- result = GetStackDepth(thread);
+ *result = GetStackDepth(thread);
return JDWP::ERR_NONE;
}
@@ -2247,7 +2324,7 @@
public:
GetFrameVisitor(Thread* thread, size_t start_frame, size_t frame_count, JDWP::ExpandBuf* buf)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, NULL), depth_(0),
+ : StackVisitor(thread, nullptr), depth_(0),
start_frame_(start_frame), frame_count_(frame_count), buf_(buf) {
expandBufAdd4BE(buf_, frame_count_);
}
@@ -2282,8 +2359,8 @@
ScopedObjectAccessUnchecked soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
return error;
}
@@ -2314,12 +2391,13 @@
JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) {
Thread* self = Thread::Current();
- ScopedLocalRef<jobject> peer(self->GetJniEnv(), NULL);
+ ScopedLocalRef<jobject> peer(self->GetJniEnv(), nullptr);
{
ScopedObjectAccess soa(self);
- peer.reset(soa.AddLocalReference<jobject>(gRegistry->Get<mirror::Object*>(thread_id)));
+ JDWP::JdwpError error;
+ peer.reset(soa.AddLocalReference<jobject>(gRegistry->Get<mirror::Object*>(thread_id, &error)));
}
- if (peer.get() == NULL) {
+ if (peer.get() == nullptr) {
return JDWP::ERR_THREAD_NOT_ALIVE;
}
// Suspend thread to build stack trace. Take suspend thread lock to avoid races with threads
@@ -2329,7 +2407,7 @@
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* thread = thread_list->SuspendThreadByPeer(peer.get(), request_suspension, true,
&timed_out);
- if (thread != NULL) {
+ if (thread != nullptr) {
return JDWP::ERR_NONE;
} else if (timed_out) {
return JDWP::ERR_INTERNAL;
@@ -2340,13 +2418,15 @@
void Dbg::ResumeThread(JDWP::ObjectId thread_id) {
ScopedObjectAccessUnchecked soa(Thread::Current());
- mirror::Object* peer = gRegistry->Get<mirror::Object*>(thread_id);
+ JDWP::JdwpError error;
+ mirror::Object* peer = gRegistry->Get<mirror::Object*>(thread_id, &error);
+ CHECK(peer != nullptr) << error;
Thread* thread;
{
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
thread = Thread::FromManagedThread(soa, peer);
}
- if (thread == NULL) {
+ if (thread == nullptr) {
LOG(WARNING) << "No such thread for resume: " << peer;
return;
}
@@ -2367,7 +2447,7 @@
struct GetThisVisitor : public StackVisitor {
GetThisVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, context), this_object(NULL), frame_id(frame_id) {}
+ : StackVisitor(thread, context), this_object(nullptr), frame_id(frame_id) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
// annotalysis.
@@ -2390,7 +2470,8 @@
Thread* thread;
{
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ thread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
return error;
}
@@ -2405,298 +2486,329 @@
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
- JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
- struct GetLocalVisitor : public StackVisitor {
- GetLocalVisitor(const ScopedObjectAccessUnchecked& soa, Thread* thread, Context* context,
- JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, context), soa_(soa), frame_id_(frame_id), slot_(slot), tag_(tag),
- buf_(buf), width_(width), error_(JDWP::ERR_NONE) {}
+// Walks the stack until we find the frame with the given FrameId.
+class FindFrameVisitor FINAL : public StackVisitor {
+ public:
+ FindFrameVisitor(Thread* thread, Context* context, JDWP::FrameId frame_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, context), frame_id_(frame_id), error_(JDWP::ERR_INVALID_FRAMEID) {}
- // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
- // annotalysis.
- bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
- if (GetFrameId() != frame_id_) {
- return true; // Not our frame, carry on.
- }
- // TODO: check that the tag is compatible with the actual type of the slot!
- // TODO: check slot is valid for this method or return INVALID_SLOT error.
- mirror::ArtMethod* m = GetMethod();
- if (m->IsNative()) {
- // We can't read local value from native method.
- error_ = JDWP::ERR_OPAQUE_FRAME;
- return false;
- }
- uint16_t reg = DemangleSlot(slot_, m);
- constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
- switch (tag_) {
- case JDWP::JT_BOOLEAN: {
- CHECK_EQ(width_, 1U);
- uint32_t intVal;
- if (GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
- JDWP::Set1(buf_+1, intVal != 0);
- } else {
- VLOG(jdwp) << "failed to get boolean local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_BYTE: {
- CHECK_EQ(width_, 1U);
- uint32_t intVal;
- if (GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
- JDWP::Set1(buf_+1, intVal);
- } else {
- VLOG(jdwp) << "failed to get byte local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_SHORT:
- case JDWP::JT_CHAR: {
- CHECK_EQ(width_, 2U);
- uint32_t intVal;
- if (GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
- JDWP::Set2BE(buf_+1, intVal);
- } else {
- VLOG(jdwp) << "failed to get short/char local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_INT: {
- CHECK_EQ(width_, 4U);
- uint32_t intVal;
- if (GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get int local " << reg << " = " << intVal;
- JDWP::Set4BE(buf_+1, intVal);
- } else {
- VLOG(jdwp) << "failed to get int local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_FLOAT: {
- CHECK_EQ(width_, 4U);
- uint32_t intVal;
- if (GetVReg(m, reg, kFloatVReg, &intVal)) {
- VLOG(jdwp) << "get float local " << reg << " = " << intVal;
- JDWP::Set4BE(buf_+1, intVal);
- } else {
- VLOG(jdwp) << "failed to get float local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_ARRAY:
- case JDWP::JT_CLASS_LOADER:
- case JDWP::JT_CLASS_OBJECT:
- case JDWP::JT_OBJECT:
- case JDWP::JT_STRING:
- case JDWP::JT_THREAD:
- case JDWP::JT_THREAD_GROUP: {
- CHECK_EQ(width_, sizeof(JDWP::ObjectId));
- uint32_t intVal;
- if (GetVReg(m, reg, kReferenceVReg, &intVal)) {
- mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
- VLOG(jdwp) << "get " << tag_ << " object local " << reg << " = " << o;
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
- LOG(FATAL) << "Register " << reg << " expected to hold " << tag_ << " object: " << o;
- }
- tag_ = TagFromObject(soa_, o);
- JDWP::SetObjectId(buf_+1, gRegistry->Add(o));
- } else {
- VLOG(jdwp) << "failed to get " << tag_ << " object local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_DOUBLE: {
- CHECK_EQ(width_, 8U);
- uint64_t longVal;
- if (GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
- VLOG(jdwp) << "get double local " << reg << " = " << longVal;
- JDWP::Set8BE(buf_+1, longVal);
- } else {
- VLOG(jdwp) << "failed to get double local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_LONG: {
- CHECK_EQ(width_, 8U);
- uint64_t longVal;
- if (GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
- VLOG(jdwp) << "get long local " << reg << " = " << longVal;
- JDWP::Set8BE(buf_+1, longVal);
- } else {
- VLOG(jdwp) << "failed to get long local " << reg;
- error_ = kFailureErrorCode;
- }
- break;
- }
- default:
- LOG(FATAL) << "Unknown tag " << tag_;
- break;
- }
-
- // Prepend tag, which may have been updated.
- JDWP::Set1(buf_, tag_);
- return false;
+ // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
+ // annotalysis.
+ bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+ if (GetFrameId() != frame_id_) {
+ return true; // Not our frame, carry on.
}
- const ScopedObjectAccessUnchecked& soa_;
- const JDWP::FrameId frame_id_;
- const int slot_;
- JDWP::JdwpTag tag_;
- uint8_t* const buf_;
- const size_t width_;
- JDWP::JdwpError error_;
- };
+ mirror::ArtMethod* m = GetMethod();
+ if (m->IsNative()) {
+ // We can't read/write local value from/into native method.
+ error_ = JDWP::ERR_OPAQUE_FRAME;
+ } else {
+ // We found our frame.
+ error_ = JDWP::ERR_NONE;
+ }
+ return false;
+ }
+
+ JDWP::JdwpError GetError() const {
+ return error_;
+ }
+
+ private:
+ const JDWP::FrameId frame_id_;
+ JDWP::JdwpError error_;
+};
+
+JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply) {
+ JDWP::ObjectId thread_id = request->ReadThreadId();
+ JDWP::FrameId frame_id = request->ReadFrameId();
ScopedObjectAccessUnchecked soa(Thread::Current());
- MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
- if (error != JDWP::ERR_NONE) {
- return error;
+ {
+ MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+ JDWP::JdwpError error;
+ thread = DecodeThread(soa, thread_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
}
- // TODO check thread is suspended by the debugger ?
+ // Find the frame with the given frame_id.
std::unique_ptr<Context> context(Context::Create());
- GetLocalVisitor visitor(soa, thread, context.get(), frame_id, slot, tag, buf, width);
+ FindFrameVisitor visitor(thread, context.get(), frame_id);
visitor.WalkStack();
- return visitor.error_;
+ if (visitor.GetError() != JDWP::ERR_NONE) {
+ return visitor.GetError();
+ }
+
+ // Read the values from visitor's context.
+ int32_t slot_count = request->ReadSigned32("slot count");
+ expandBufAdd4BE(pReply, slot_count); /* "int values" */
+ for (int32_t i = 0; i < slot_count; ++i) {
+ uint32_t slot = request->ReadUnsigned32("slot");
+ JDWP::JdwpTag reqSigByte = request->ReadTag();
+
+ VLOG(jdwp) << " --> slot " << slot << " " << reqSigByte;
+
+ size_t width = Dbg::GetTagWidth(reqSigByte);
+ uint8_t* ptr = expandBufAddSpace(pReply, width+1);
+ JDWP::JdwpError error = Dbg::GetLocalValue(visitor, soa, slot, reqSigByte, ptr, width);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ }
+ return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
- JDWP::JdwpTag tag, uint64_t value, size_t width) {
- struct SetLocalVisitor : public StackVisitor {
- SetLocalVisitor(Thread* thread, Context* context,
- JDWP::FrameId frame_id, int slot, JDWP::JdwpTag tag, uint64_t value,
- size_t width)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, context),
- frame_id_(frame_id), slot_(slot), tag_(tag), value_(value), width_(width),
- error_(JDWP::ERR_NONE) {}
-
- // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
- // annotalysis.
- bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
- if (GetFrameId() != frame_id_) {
- return true; // Not our frame, carry on.
+JDWP::JdwpError Dbg::GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa,
+ int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
+ mirror::ArtMethod* m = visitor.GetMethod();
+ uint16_t reg = DemangleSlot(slot, m);
+ // TODO: check that the tag is compatible with the actual type of the slot!
+ // TODO: check slot is valid for this method or return INVALID_SLOT error.
+ constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
+ switch (tag) {
+ case JDWP::JT_BOOLEAN: {
+ CHECK_EQ(width, 1U);
+ uint32_t intVal;
+ if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+ VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
+ JDWP::Set1(buf + 1, intVal != 0);
+ } else {
+ VLOG(jdwp) << "failed to get boolean local " << reg;
+ return kFailureErrorCode;
}
- // TODO: check that the tag is compatible with the actual type of the slot!
- // TODO: check slot is valid for this method or return INVALID_SLOT error.
- mirror::ArtMethod* m = GetMethod();
- if (m->IsNative()) {
- // We can't read local value from native method.
- error_ = JDWP::ERR_OPAQUE_FRAME;
- return false;
- }
- uint16_t reg = DemangleSlot(slot_, m);
- constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
- switch (tag_) {
- case JDWP::JT_BOOLEAN:
- case JDWP::JT_BYTE:
- CHECK_EQ(width_, 1U);
- if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
- VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
- << static_cast<uint32_t>(value_);
- error_ = kFailureErrorCode;
- }
- break;
- case JDWP::JT_SHORT:
- case JDWP::JT_CHAR:
- CHECK_EQ(width_, 2U);
- if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
- VLOG(jdwp) << "failed to set short/char local " << reg << " = "
- << static_cast<uint32_t>(value_);
- error_ = kFailureErrorCode;
- }
- break;
- case JDWP::JT_INT:
- CHECK_EQ(width_, 4U);
- if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg)) {
- VLOG(jdwp) << "failed to set int local " << reg << " = "
- << static_cast<uint32_t>(value_);
- error_ = kFailureErrorCode;
- }
- break;
- case JDWP::JT_FLOAT:
- CHECK_EQ(width_, 4U);
- if (!SetVReg(m, reg, static_cast<uint32_t>(value_), kFloatVReg)) {
- VLOG(jdwp) << "failed to set float local " << reg << " = "
- << static_cast<uint32_t>(value_);
- error_ = kFailureErrorCode;
- }
- break;
- case JDWP::JT_ARRAY:
- case JDWP::JT_CLASS_LOADER:
- case JDWP::JT_CLASS_OBJECT:
- case JDWP::JT_OBJECT:
- case JDWP::JT_STRING:
- case JDWP::JT_THREAD:
- case JDWP::JT_THREAD_GROUP: {
- CHECK_EQ(width_, sizeof(JDWP::ObjectId));
- mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value_));
- if (o == ObjectRegistry::kInvalidObject) {
- VLOG(jdwp) << tag_ << " object " << o << " is an invalid object";
- error_ = JDWP::ERR_INVALID_OBJECT;
- } else if (!SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
- kReferenceVReg)) {
- VLOG(jdwp) << "failed to set " << tag_ << " object local " << reg << " = " << o;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_DOUBLE: {
- CHECK_EQ(width_, 8U);
- bool success = SetVRegPair(m, reg, value_, kDoubleLoVReg, kDoubleHiVReg);
- if (!success) {
- VLOG(jdwp) << "failed to set double local " << reg << " = " << value_;
- error_ = kFailureErrorCode;
- }
- break;
- }
- case JDWP::JT_LONG: {
- CHECK_EQ(width_, 8U);
- bool success = SetVRegPair(m, reg, value_, kLongLoVReg, kLongHiVReg);
- if (!success) {
- VLOG(jdwp) << "failed to set double local " << reg << " = " << value_;
- error_ = kFailureErrorCode;
- }
- break;
- }
- default:
- LOG(FATAL) << "Unknown tag " << tag_;
- break;
- }
- return false;
+ break;
}
+ case JDWP::JT_BYTE: {
+ CHECK_EQ(width, 1U);
+ uint32_t intVal;
+ if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+ VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
+ JDWP::Set1(buf + 1, intVal);
+ } else {
+ VLOG(jdwp) << "failed to get byte local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_SHORT:
+ case JDWP::JT_CHAR: {
+ CHECK_EQ(width, 2U);
+ uint32_t intVal;
+ if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+ VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
+ JDWP::Set2BE(buf + 1, intVal);
+ } else {
+ VLOG(jdwp) << "failed to get short/char local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_INT: {
+ CHECK_EQ(width, 4U);
+ uint32_t intVal;
+ if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
+ VLOG(jdwp) << "get int local " << reg << " = " << intVal;
+ JDWP::Set4BE(buf + 1, intVal);
+ } else {
+ VLOG(jdwp) << "failed to get int local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_FLOAT: {
+ CHECK_EQ(width, 4U);
+ uint32_t intVal;
+ if (visitor.GetVReg(m, reg, kFloatVReg, &intVal)) {
+ VLOG(jdwp) << "get float local " << reg << " = " << intVal;
+ JDWP::Set4BE(buf + 1, intVal);
+ } else {
+ VLOG(jdwp) << "failed to get float local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_ARRAY:
+ case JDWP::JT_CLASS_LOADER:
+ case JDWP::JT_CLASS_OBJECT:
+ case JDWP::JT_OBJECT:
+ case JDWP::JT_STRING:
+ case JDWP::JT_THREAD:
+ case JDWP::JT_THREAD_GROUP: {
+ CHECK_EQ(width, sizeof(JDWP::ObjectId));
+ uint32_t intVal;
+ if (visitor.GetVReg(m, reg, kReferenceVReg, &intVal)) {
+ mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
+ VLOG(jdwp) << "get " << tag << " object local " << reg << " = " << o;
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+ LOG(FATAL) << "Register " << reg << " expected to hold " << tag << " object: " << o;
+ }
+ tag = TagFromObject(soa, o);
+ JDWP::SetObjectId(buf + 1, gRegistry->Add(o));
+ } else {
+ VLOG(jdwp) << "failed to get " << tag << " object local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_DOUBLE: {
+ CHECK_EQ(width, 8U);
+ uint64_t longVal;
+ if (visitor.GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
+ VLOG(jdwp) << "get double local " << reg << " = " << longVal;
+ JDWP::Set8BE(buf + 1, longVal);
+ } else {
+ VLOG(jdwp) << "failed to get double local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_LONG: {
+ CHECK_EQ(width, 8U);
+ uint64_t longVal;
+ if (visitor.GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
+ VLOG(jdwp) << "get long local " << reg << " = " << longVal;
+ JDWP::Set8BE(buf + 1, longVal);
+ } else {
+ VLOG(jdwp) << "failed to get long local " << reg;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unknown tag " << tag;
+ break;
+ }
- const JDWP::FrameId frame_id_;
- const int slot_;
- const JDWP::JdwpTag tag_;
- const uint64_t value_;
- const size_t width_;
- JDWP::JdwpError error_;
- };
+ // Prepend tag, which may have been updated.
+ JDWP::Set1(buf, tag);
+ return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::SetLocalValues(JDWP::Request* request) {
+ JDWP::ObjectId thread_id = request->ReadThreadId();
+ JDWP::FrameId frame_id = request->ReadFrameId();
ScopedObjectAccessUnchecked soa(Thread::Current());
- MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
- if (error != JDWP::ERR_NONE) {
- return error;
+ {
+ MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+ JDWP::JdwpError error;
+ thread = DecodeThread(soa, thread_id, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
}
- // TODO check thread is suspended by the debugger ?
+ // Find the frame with the given frame_id.
std::unique_ptr<Context> context(Context::Create());
- SetLocalVisitor visitor(thread, context.get(), frame_id, slot, tag, value, width);
+ FindFrameVisitor visitor(thread, context.get(), frame_id);
visitor.WalkStack();
- return visitor.error_;
+ if (visitor.GetError() != JDWP::ERR_NONE) {
+ return visitor.GetError();
+ }
+
+ // Writes the values into visitor's context.
+ int32_t slot_count = request->ReadSigned32("slot count");
+ for (int32_t i = 0; i < slot_count; ++i) {
+ uint32_t slot = request->ReadUnsigned32("slot");
+ JDWP::JdwpTag sigByte = request->ReadTag();
+ size_t width = Dbg::GetTagWidth(sigByte);
+ uint64_t value = request->ReadValue(width);
+
+ VLOG(jdwp) << " --> slot " << slot << " " << sigByte << " " << value;
+ JDWP::JdwpError error = Dbg::SetLocalValue(visitor, slot, sigByte, value, width);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
+ }
+ return JDWP::ERR_NONE;
+}
+
+JDWP::JdwpError Dbg::SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
+ uint64_t value, size_t width) {
+ mirror::ArtMethod* m = visitor.GetMethod();
+ uint16_t reg = DemangleSlot(slot, m);
+ // TODO: check that the tag is compatible with the actual type of the slot!
+ // TODO: check slot is valid for this method or return INVALID_SLOT error.
+ constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
+ switch (tag) {
+ case JDWP::JT_BOOLEAN:
+ case JDWP::JT_BYTE:
+ CHECK_EQ(width, 1U);
+ if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+ VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
+ << static_cast<uint32_t>(value);
+ return kFailureErrorCode;
+ }
+ break;
+ case JDWP::JT_SHORT:
+ case JDWP::JT_CHAR:
+ CHECK_EQ(width, 2U);
+ if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+ VLOG(jdwp) << "failed to set short/char local " << reg << " = "
+ << static_cast<uint32_t>(value);
+ return kFailureErrorCode;
+ }
+ break;
+ case JDWP::JT_INT:
+ CHECK_EQ(width, 4U);
+ if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
+ VLOG(jdwp) << "failed to set int local " << reg << " = "
+ << static_cast<uint32_t>(value);
+ return kFailureErrorCode;
+ }
+ break;
+ case JDWP::JT_FLOAT:
+ CHECK_EQ(width, 4U);
+ if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kFloatVReg)) {
+ VLOG(jdwp) << "failed to set float local " << reg << " = "
+ << static_cast<uint32_t>(value);
+ return kFailureErrorCode;
+ }
+ break;
+ case JDWP::JT_ARRAY:
+ case JDWP::JT_CLASS_LOADER:
+ case JDWP::JT_CLASS_OBJECT:
+ case JDWP::JT_OBJECT:
+ case JDWP::JT_STRING:
+ case JDWP::JT_THREAD:
+ case JDWP::JT_THREAD_GROUP: {
+ CHECK_EQ(width, sizeof(JDWP::ObjectId));
+ JDWP::JdwpError error;
+ mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value),
+ &error);
+ if (error != JDWP::ERR_NONE) {
+ VLOG(jdwp) << tag << " object " << o << " is an invalid object";
+ return JDWP::ERR_INVALID_OBJECT;
+ } else if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
+ kReferenceVReg)) {
+ VLOG(jdwp) << "failed to set " << tag << " object local " << reg << " = " << o;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_DOUBLE: {
+ CHECK_EQ(width, 8U);
+ if (!visitor.SetVRegPair(m, reg, value, kDoubleLoVReg, kDoubleHiVReg)) {
+ VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ case JDWP::JT_LONG: {
+ CHECK_EQ(width, 8U);
+ if (!visitor.SetVRegPair(m, reg, value, kLongLoVReg, kLongHiVReg)) {
+ VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
+ return kFailureErrorCode;
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unknown tag " << tag;
+ break;
+ }
+ return JDWP::ERR_NONE;
}
static void SetEventLocation(JDWP::EventLocation* location, mirror::ArtMethod* m, uint32_t dex_pc)
@@ -3055,12 +3167,13 @@
}
// Note: method verifier may cause thread suspension.
self->AssertThreadSuspensionIsAllowable();
- StackHandleScope<2> hs(self);
+ StackHandleScope<3> hs(self);
mirror::Class* declaring_class = m->GetDeclaringClass();
Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- verifier::MethodVerifier verifier(dex_cache->GetDexFile(), &dex_cache, &class_loader,
- &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m,
+ Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+ verifier::MethodVerifier verifier(self, dex_cache->GetDexFile(), dex_cache, class_loader,
+ &m->GetClassDef(), code_item, m->GetDexMethodIndex(), method,
m->GetAccessFlags(), false, true, false);
// Note: we don't need to verify the method.
return InlineMethodAnalyser::AnalyseMethodCode(&verifier, nullptr);
@@ -3192,7 +3305,7 @@
ScopedObjectAccessUnchecked soa(self);
{
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- error_ = DecodeThread(soa, thread_id, thread_);
+ thread_ = DecodeThread(soa, thread_id, &error_);
}
if (error_ == JDWP::ERR_NONE) {
if (thread_ == soa.Self()) {
@@ -3258,10 +3371,10 @@
explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control,
int32_t* line_number)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, NULL), single_step_control_(single_step_control),
+ : StackVisitor(thread, nullptr), single_step_control_(single_step_control),
line_number_(line_number) {
DCHECK_EQ(single_step_control_, thread->GetSingleStepControl());
- single_step_control_->method = NULL;
+ single_step_control_->method = nullptr;
single_step_control_->stack_depth = 0;
}
@@ -3271,11 +3384,11 @@
mirror::ArtMethod* m = GetMethod();
if (!m->IsRuntimeMethod()) {
++single_step_control_->stack_depth;
- if (single_step_control_->method == NULL) {
+ if (single_step_control_->method == nullptr) {
mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
single_step_control_->method = m;
*line_number_ = -1;
- if (dex_cache != NULL) {
+ if (dex_cache != nullptr) {
const DexFile& dex_file = *dex_cache->GetDexFile();
*line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc());
}
@@ -3348,7 +3461,7 @@
const DexFile::CodeItem* const code_item = m->GetCodeItem();
DebugCallbackContext context(single_step_control, line_number, code_item);
m->GetDexFile()->DecodeDebugInfo(code_item, m->IsStatic(), m->GetDexMethodIndex(),
- DebugCallbackContext::Callback, NULL, &context);
+ DebugCallbackContext::Callback, nullptr, &context);
}
//
@@ -3378,8 +3491,8 @@
void Dbg::UnconfigureStep(JDWP::ObjectId thread_id) {
ScopedObjectAccessUnchecked soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- Thread* thread;
- JDWP::JdwpError error = DecodeThread(soa, thread_id, thread);
+ JDWP::JdwpError error;
+ Thread* thread = DecodeThread(soa, thread_id, &error);
if (error == JDWP::ERR_NONE) {
SingleStepControl* single_step_control = thread->GetSingleStepControl();
DCHECK(single_step_control != nullptr);
@@ -3423,13 +3536,14 @@
JDWP::ObjectId* pExceptionId) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
- Thread* targetThread = NULL;
- DebugInvokeReq* req = NULL;
+ Thread* targetThread = nullptr;
+ DebugInvokeReq* req = nullptr;
Thread* self = Thread::Current();
{
ScopedObjectAccessUnchecked soa(self);
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
- JDWP::JdwpError error = DecodeThread(soa, thread_id, targetThread);
+ JDWP::JdwpError error;
+ targetThread = DecodeThread(soa, thread_id, &error);
if (error != JDWP::ERR_NONE) {
LOG(ERROR) << "InvokeMethod request for invalid thread id " << thread_id;
return error;
@@ -3464,25 +3578,24 @@
return JDWP::ERR_THREAD_SUSPENDED; // Probably not expected here.
}
- JDWP::JdwpError status;
- mirror::Object* receiver = gRegistry->Get<mirror::Object*>(object_id);
- if (receiver == ObjectRegistry::kInvalidObject) {
+ mirror::Object* receiver = gRegistry->Get<mirror::Object*>(object_id, &error);
+ if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
- mirror::Object* thread = gRegistry->Get<mirror::Object*>(thread_id);
- if (thread == ObjectRegistry::kInvalidObject) {
+ mirror::Object* thread = gRegistry->Get<mirror::Object*>(thread_id, &error);
+ if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
// TODO: check that 'thread' is actually a java.lang.Thread!
- mirror::Class* c = DecodeClass(class_id, status);
- if (c == NULL) {
- return status;
+ mirror::Class* c = DecodeClass(class_id, &error);
+ if (c == nullptr) {
+ return error;
}
mirror::ArtMethod* m = FromMethodId(method_id);
- if (m->IsStatic() != (receiver == NULL)) {
+ if (m->IsStatic() != (receiver == nullptr)) {
return JDWP::ERR_INVALID_METHODID;
}
if (m->IsStatic()) {
@@ -3516,11 +3629,11 @@
if (shorty[i + 1] == 'L') {
// Did we really get an argument of an appropriate reference type?
mirror::Class* parameter_type = mh.GetClassFromTypeIdx(types->GetTypeItem(i).type_idx_);
- mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i]);
- if (argument == ObjectRegistry::kInvalidObject) {
+ mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
+ if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
}
- if (argument != NULL && !argument->InstanceOf(parameter_type)) {
+ if (argument != nullptr && !argument->InstanceOf(parameter_type)) {
return JDWP::ERR_ILLEGAL_ARGUMENT;
}
@@ -3630,8 +3743,8 @@
}
// Translate the method through the vtable, unless the debugger wants to suppress it.
- Handle<mirror::ArtMethod> m(hs.NewHandle(pReq->method));
- if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != NULL) {
+ MutableHandle<mirror::ArtMethod> m(hs.NewHandle(pReq->method));
+ if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != nullptr) {
mirror::ArtMethod* actual_method = pReq->klass->FindVirtualMethodForVirtualOrInterface(m.Get());
if (actual_method != m.Get()) {
VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.Get()) << " to " << PrettyMethod(actual_method);
@@ -3648,7 +3761,7 @@
pReq->result_value = InvokeWithJValues(soa, pReq->receiver, soa.EncodeMethod(m.Get()),
reinterpret_cast<jvalue*>(pReq->arg_values));
- mirror::Throwable* exception = soa.Self()->GetException(NULL);
+ mirror::Throwable* exception = soa.Self()->GetException(nullptr);
soa.Self()->ClearException();
pReq->exception = gRegistry->Add(exception);
pReq->result_tag = BasicTagFromDescriptor(m.Get()->GetShorty());
@@ -3676,7 +3789,7 @@
gRegistry->Add(pReq->result_value.GetL());
}
- if (old_exception.Get() != NULL) {
+ if (old_exception.Get() != nullptr) {
ThrowLocation gc_safe_throw_location(old_throw_this_object.Get(), old_throw_method.Get(),
old_throw_dex_pc);
soa.Self()->SetException(gc_safe_throw_location, old_exception.Get());
@@ -3695,23 +3808,24 @@
* OLD-TODO: we currently assume that the request and reply include a single
* chunk. If this becomes inconvenient we will need to adapt.
*/
-bool Dbg::DdmHandlePacket(JDWP::Request& request, uint8_t** pReplyBuf, int* pReplyLen) {
+bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) {
Thread* self = Thread::Current();
JNIEnv* env = self->GetJniEnv();
- uint32_t type = request.ReadUnsigned32("type");
- uint32_t length = request.ReadUnsigned32("length");
+ uint32_t type = request->ReadUnsigned32("type");
+ uint32_t length = request->ReadUnsigned32("length");
// Create a byte[] corresponding to 'request'.
- size_t request_length = request.size();
+ size_t request_length = request->size();
ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(request_length));
- if (dataArray.get() == NULL) {
+ if (dataArray.get() == nullptr) {
LOG(WARNING) << "byte[] allocation failed: " << request_length;
env->ExceptionClear();
return false;
}
- env->SetByteArrayRegion(dataArray.get(), 0, request_length, reinterpret_cast<const jbyte*>(request.data()));
- request.Skip(request_length);
+ env->SetByteArrayRegion(dataArray.get(), 0, request_length,
+ reinterpret_cast<const jbyte*>(request->data()));
+ request->Skip(request_length);
// Run through and find all chunks. [Currently just find the first.]
ScopedByteArrayRO contents(env, dataArray.get());
@@ -3731,7 +3845,7 @@
return false;
}
- if (chunk.get() == NULL) {
+ if (chunk.get() == nullptr) {
return false;
}
@@ -3753,13 +3867,13 @@
type = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type);
VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length);
- if (length == 0 || replyData.get() == NULL) {
+ if (length == 0 || replyData.get() == nullptr) {
return false;
}
const int kChunkHdrLen = 8;
uint8_t* reply = new uint8_t[length + kChunkHdrLen];
- if (reply == NULL) {
+ if (reply == nullptr) {
LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen);
return false;
}
@@ -3824,8 +3938,8 @@
ScopedObjectAccessUnchecked soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
Handle<mirror::String> name(hs.NewHandle(t->GetThreadName(soa)));
- size_t char_count = (name.Get() != NULL) ? name->GetLength() : 0;
- const jchar* chars = (name.Get() != NULL) ? name->GetCharArray()->GetData() : NULL;
+ size_t char_count = (name.Get() != nullptr) ? name->GetLength() : 0;
+ const jchar* chars = (name.Get() != nullptr) ? name->GetCharArray()->GetData() : nullptr;
std::vector<uint8_t> bytes;
JDWP::Append4BE(bytes, t->GetThreadId());
@@ -3875,7 +3989,7 @@
}
void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
- CHECK(buf != NULL);
+ CHECK(buf != nullptr);
iovec vec[1];
vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
vec[0].iov_len = byte_count;
@@ -3887,7 +4001,7 @@
}
void Dbg::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) {
- if (gJdwpState == NULL) {
+ if (gJdwpState == nullptr) {
VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type;
} else {
gJdwpState->DdmSendChunkV(type, iov, iov_count);
@@ -4044,7 +4158,7 @@
}
void Flush() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (pieceLenField_ == NULL) {
+ if (pieceLenField_ == nullptr) {
// Flush immediately post Reset (maybe back-to-back Flush). Ignore.
CHECK(needHeader_);
return;
@@ -4072,7 +4186,7 @@
ResetStartOfNextChunk();
totalAllocationUnits_ = 0;
needHeader_ = true;
- pieceLenField_ = NULL;
+ pieceLenField_ = nullptr;
}
void HeapChunkCallback(void* start, void* /*end*/, size_t used_bytes)
@@ -4081,9 +4195,9 @@
// Note: heap call backs cannot manipulate the heap upon which they are crawling, care is taken
// in the following code not to allocate memory, by ensuring buf_ is of the correct size
if (used_bytes == 0) {
- if (start == NULL) {
+ if (start == nullptr) {
// Reset for start of new heap.
- startOfNextMemoryChunk_ = NULL;
+ startOfNextMemoryChunk_ = nullptr;
Flush();
}
// Only process in use memory so that free region information
@@ -4098,7 +4212,7 @@
// TODO: I'm not sure using start of next chunk works well with multiple spaces. We shouldn't
// count gaps inbetween spaces as free memory.
- if (startOfNextMemoryChunk_ != NULL) {
+ if (startOfNextMemoryChunk_ != nullptr) {
// Transmit any pending free memory. Native free memory of
// over kMaxFreeLen could be because of the use of mmaps, so
// don't report. If not free memory then start a new segment.
@@ -4114,7 +4228,7 @@
}
}
if (flush) {
- startOfNextMemoryChunk_ = NULL;
+ startOfNextMemoryChunk_ = nullptr;
Flush();
}
}
@@ -4160,7 +4274,7 @@
uint8_t ExamineObject(mirror::Object* o, bool is_native_heap)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
- if (o == NULL) {
+ if (o == nullptr) {
return HPSG_STATE(SOLIDITY_FREE, 0);
}
@@ -4177,7 +4291,7 @@
}
mirror::Class* c = o->GetClass();
- if (c == NULL) {
+ if (c == nullptr) {
// The object was probably just created but hasn't been initialized yet.
return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
}
@@ -4327,7 +4441,7 @@
if (enable) {
{
MutexLock mu(self, *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ != NULL) {
+ if (recent_allocation_records_ != nullptr) {
return; // Already enabled, bail.
}
alloc_record_max_ = GetAllocTrackerMax();
@@ -4337,19 +4451,19 @@
DCHECK_EQ(alloc_record_head_, 0U);
DCHECK_EQ(alloc_record_count_, 0U);
recent_allocation_records_ = new AllocRecord[alloc_record_max_];
- CHECK(recent_allocation_records_ != NULL);
+ CHECK(recent_allocation_records_ != nullptr);
}
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
} else {
{
ScopedObjectAccess soa(self); // For type_cache_.Clear();
MutexLock mu(self, *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ == NULL) {
+ if (recent_allocation_records_ == nullptr) {
return; // Already disabled, bail.
}
LOG(INFO) << "Disabling alloc tracker";
delete[] recent_allocation_records_;
- recent_allocation_records_ = NULL;
+ recent_allocation_records_ = nullptr;
alloc_record_head_ = 0;
alloc_record_count_ = 0;
type_cache_.Clear();
@@ -4362,7 +4476,7 @@
struct AllocRecordStackVisitor : public StackVisitor {
AllocRecordStackVisitor(Thread* thread, AllocRecord* record)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, NULL), record(record), depth(0) {}
+ : StackVisitor(thread, nullptr), record(record), depth(0) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
// annotalysis.
@@ -4391,12 +4505,9 @@
size_t depth;
};
-void Dbg::RecordAllocation(mirror::Class* type, size_t byte_count) {
- Thread* self = Thread::Current();
- CHECK(self != NULL);
-
+void Dbg::RecordAllocation(Thread* self, mirror::Class* type, size_t byte_count) {
MutexLock mu(self, *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ == NULL) {
+ if (recent_allocation_records_ == nullptr) {
// In the process of shutting down recording, bail.
return;
}
@@ -4437,7 +4548,7 @@
void Dbg::DumpRecentAllocations() {
ScopedObjectAccess soa(Thread::Current());
MutexLock mu(soa.Self(), *Locks::alloc_tracker_lock_);
- if (recent_allocation_records_ == NULL) {
+ if (recent_allocation_records_ == nullptr) {
LOG(INFO) << "Not recording tracked allocations";
return;
}
@@ -4458,7 +4569,7 @@
for (size_t stack_frame = 0; stack_frame < kMaxAllocRecordStackDepth; ++stack_frame) {
AllocRecordStackTraceElement* stack_element = record->StackElement(stack_frame);
mirror::ArtMethod* m = stack_element->Method();
- if (m == NULL) {
+ if (m == nullptr) {
break;
}
LOG(INFO) << " " << PrettyMethod(m) << " line " << stack_element->LineNumber();
@@ -4587,7 +4698,7 @@
class_names.Add(record->Type()->GetDescriptor(&temp));
for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
mirror::ArtMethod* m = record->StackElement(i)->Method();
- if (m != NULL) {
+ if (m != nullptr) {
class_names.Add(m->GetDeclaringClassDescriptor());
method_names.Add(m->GetName());
filenames.Add(GetMethodSourceFile(m));
@@ -4670,7 +4781,7 @@
}
JNIEnv* env = self->GetJniEnv();
jbyteArray result = env->NewByteArray(bytes.size());
- if (result != NULL) {
+ if (result != nullptr) {
env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
}
return result;
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 5dc39d8..cb7adae 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -45,6 +45,7 @@
class AllocRecord;
class ObjectRegistry;
class ScopedObjectAccessUnchecked;
+class StackVisitor;
class Thread;
class ThrowLocation;
@@ -254,9 +255,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static std::string GetClassName(mirror::Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId& class_object_id)
+ static JDWP::JdwpError GetClassObject(JDWP::RefTypeId id, JDWP::ObjectId* class_object_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId& superclass_id)
+ static JDWP::JdwpError GetSuperclass(JDWP::RefTypeId id, JDWP::RefTypeId* superclass_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetClassLoader(JDWP::RefTypeId id, JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -264,38 +265,38 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetReflectedType(JDWP::RefTypeId class_id, JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void GetClassList(std::vector<JDWP::RefTypeId>& classes)
+ static void GetClassList(std::vector<JDWP::RefTypeId>* classes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetClassInfo(JDWP::RefTypeId class_id, JDWP::JdwpTypeTag* pTypeTag,
uint32_t* pStatus, std::string* pDescriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>& ids)
+ static void FindLoadedClassBySignature(const char* descriptor, std::vector<JDWP::RefTypeId>* ids)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetSignature(JDWP::RefTypeId ref_type_id, std::string* signature)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError GetSourceFile(JDWP::RefTypeId ref_type_id, std::string& source_file)
+ static JDWP::JdwpError GetSourceFile(JDWP::RefTypeId ref_type_id, std::string* source_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError GetObjectTag(JDWP::ObjectId object_id, uint8_t& tag)
+ static JDWP::JdwpError GetObjectTag(JDWP::ObjectId object_id, uint8_t* tag)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static size_t GetTagWidth(JDWP::JdwpTag tag);
- static JDWP::JdwpError GetArrayLength(JDWP::ObjectId array_id, int& length)
+ static JDWP::JdwpError GetArrayLength(JDWP::ObjectId array_id, int32_t* length)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError OutputArray(JDWP::ObjectId array_id, int offset, int count,
JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError SetArrayElements(JDWP::ObjectId array_id, int offset, int count,
- JDWP::Request& request)
+ JDWP::Request* request)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::ObjectId CreateString(const std::string& str)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId& new_object)
+ static JDWP::JdwpError CreateObject(JDWP::RefTypeId class_id, JDWP::ObjectId* new_object)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t length,
- JDWP::ObjectId& new_array)
+ JDWP::ObjectId* new_array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
//
@@ -324,12 +325,12 @@
static JDWP::JdwpError GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetOwnedMonitors(JDWP::ObjectId thread_id,
- std::vector<JDWP::ObjectId>& monitors,
- std::vector<uint32_t>& stack_depths)
+ std::vector<JDWP::ObjectId>* monitors,
+ std::vector<uint32_t>* stack_depths)
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetContendedMonitor(JDWP::ObjectId thread_id,
- JDWP::ObjectId& contended_monitor)
+ JDWP::ObjectId* contended_monitor)
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -337,19 +338,19 @@
// Heap.
//
static JDWP::JdwpError GetInstanceCounts(const std::vector<JDWP::RefTypeId>& class_ids,
- std::vector<uint64_t>& counts)
+ std::vector<uint64_t>* counts)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetInstances(JDWP::RefTypeId class_id, int32_t max_count,
- std::vector<JDWP::ObjectId>& instances)
+ std::vector<JDWP::ObjectId>* instances)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count,
- std::vector<JDWP::ObjectId>& referring_objects)
+ std::vector<JDWP::ObjectId>* referring_objects)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError DisableCollection(JDWP::ObjectId object_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError EnableCollection(JDWP::ObjectId object_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError IsCollected(JDWP::ObjectId object_id, bool& is_collected)
+ static JDWP::JdwpError IsCollected(JDWP::ObjectId object_id, bool* is_collected)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void DisposeObject(JDWP::ObjectId object_id, uint32_t reference_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -381,7 +382,7 @@
JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetBytecodes(JDWP::RefTypeId class_id, JDWP::MethodId method_id,
- std::vector<uint8_t>& bytecodes)
+ std::vector<uint8_t>* bytecodes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static std::string GetFieldName(JDWP::FieldId field_id)
@@ -410,17 +411,23 @@
/*
* Thread, ThreadGroup, Frame
*/
- static JDWP::JdwpError GetThreadName(JDWP::ObjectId thread_id, std::string& name)
+ static JDWP::JdwpError GetThreadName(JDWP::ObjectId thread_id, std::string* name)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::thread_list_lock_);
static JDWP::JdwpError GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::thread_list_lock_);
- static std::string GetThreadGroupName(JDWP::ObjectId thread_group_id);
- static JDWP::ObjectId GetThreadGroupParent(JDWP::ObjectId thread_group_id)
+ static JDWP::JdwpError GetThreadGroupName(JDWP::ObjectId thread_group_id,
+ JDWP::ExpandBuf* pReply)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static JDWP::JdwpError GetThreadGroupParent(JDWP::ObjectId thread_group_id,
+ JDWP::ExpandBuf* pReply)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static JDWP::JdwpError GetThreadGroupChildren(JDWP::ObjectId thread_group_id,
+ JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::ObjectId GetSystemThreadGroupId()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::ObjectId GetMainThreadGroupId();
static JDWP::JdwpThreadStatus ToJdwpThreadStatus(ThreadState state);
static JDWP::JdwpError GetThreadStatus(JDWP::ObjectId thread_id,
@@ -435,12 +442,11 @@
// Fills 'thread_ids' with the threads in the given thread group. If thread_group_id == 0,
// returns all threads.
- static void GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& thread_ids)
+ static void GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* thread_ids)
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void GetChildThreadGroups(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>& child_thread_group_ids);
- static JDWP::JdwpError GetThreadFrameCount(JDWP::ObjectId thread_id, size_t& result)
+ static JDWP::JdwpError GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* result)
LOCKS_EXCLUDED(Locks::thread_list_lock_);
static JDWP::JdwpError GetThreadFrames(JDWP::ObjectId thread_id, size_t start_frame,
size_t frame_count, JDWP::ExpandBuf* buf)
@@ -470,12 +476,10 @@
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
- JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen)
+ static JDWP::JdwpError GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pReply)
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError SetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int slot,
- JDWP::JdwpTag tag, uint64_t value, size_t width)
+ static JDWP::JdwpError SetLocalValues(JDWP::Request* request)
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -566,7 +570,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void DdmSetThreadNotification(bool enable)
LOCKS_EXCLUDED(Locks::thread_list_lock_);
- static bool DdmHandlePacket(JDWP::Request& request, uint8_t** pReplyBuf, int* pReplyLen);
+ static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen);
static void DdmConnected() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void DdmDisconnected() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes)
@@ -582,7 +586,7 @@
/*
* Recent allocation tracking support.
*/
- static void RecordAllocation(mirror::Class* type, size_t byte_count)
+ static void RecordAllocation(Thread* self, mirror::Class* type, size_t byte_count)
LOCKS_EXCLUDED(Locks::alloc_tracker_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void SetAllocTrackingEnabled(bool enabled) LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
@@ -636,6 +640,16 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
+ static JDWP::JdwpError GetLocalValue(const StackVisitor& visitor,
+ ScopedObjectAccessUnchecked& soa, int slot,
+ JDWP::JdwpTag tag, uint8_t* buf, size_t width)
+ LOCKS_EXCLUDED(Locks::thread_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static JDWP::JdwpError SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
+ uint64_t value, size_t width)
+ LOCKS_EXCLUDED(Locks::thread_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void PostThreadStartOrStop(Thread*, uint32_t)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index c783ee4..6ef62c5 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -205,19 +205,20 @@
const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
- const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release(), error_msg);
- if (dex_file == nullptr) {
+ std::unique_ptr<const DexFile> dex_file(OpenMemory(location, dex_header->checksum_, map.release(),
+ error_msg));
+ if (dex_file.get() == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location,
error_msg->c_str());
return nullptr;
}
- if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size(), location,
- error_msg)) {
+ if (verify && !DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
+ location, error_msg)) {
return nullptr;
}
- return dex_file;
+ return dex_file.release();
}
const char* DexFile::kClassesDex = "classes.dex";
@@ -353,8 +354,7 @@
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
find_class_def_misses_(0),
- class_def_index_(nullptr),
- build_class_def_index_mutex_("DexFile index creation mutex") {
+ class_def_index_(nullptr) {
CHECK(begin_ != NULL) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
}
@@ -443,20 +443,21 @@
// up. This isn't done eagerly at construction as construction is not performed in multi-threaded
// sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
// out which thread builds the index.
- find_class_def_misses_++;
const uint32_t kMaxFailedDexClassDefLookups = 100;
- if (find_class_def_misses_ > kMaxFailedDexClassDefLookups) {
- MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
- // Are we the first ones building the index?
- if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
- index = new Index(num_class_defs);
- for (uint32_t i = 0; i < num_class_defs; ++i) {
- const ClassDef& class_def = GetClassDef(i);
- const char* descriptor = GetClassDescriptor(class_def);
- index->insert(std::make_pair(descriptor, &class_def));
- }
- class_def_index_.StoreSequentiallyConsistent(index);
+ uint32_t old_misses = find_class_def_misses_.FetchAndAddSequentiallyConsistent(1);
+ if (old_misses == kMaxFailedDexClassDefLookups) {
+ // Are we the ones moving the miss count past the max? Sanity check the index doesn't exist.
+ CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
+ // Build the index.
+ index = new Index(num_class_defs);
+ for (uint32_t i = 0; i < num_class_defs; ++i) {
+ const ClassDef& class_def = GetClassDef(i);
+ const char* descriptor = GetClassDescriptor(class_def);
+ index->insert(std::make_pair(descriptor, &class_def));
}
+ // Sanity check the index still doesn't exist, only 1 thread should build it.
+ CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
+ class_def_index_.StoreSequentiallyConsistent(index);
}
return nullptr;
}
@@ -1191,7 +1192,7 @@
}
template<bool kTransactionActive>
-void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) const {
+void EncodedStaticFieldValueIterator::ReadValueToField(Handle<mirror::ArtField> field) const {
switch (type_) {
case kBoolean: field->SetBoolean<kTransactionActive>(field->GetDeclaringClass(), jval_.z); break;
case kByte: field->SetByte<kTransactionActive>(field->GetDeclaringClass(), jval_.b); break;
@@ -1218,8 +1219,8 @@
default: UNIMPLEMENTED(FATAL) << ": type " << type_;
}
}
-template void EncodedStaticFieldValueIterator::ReadValueToField<true>(mirror::ArtField* field) const;
-template void EncodedStaticFieldValueIterator::ReadValueToField<false>(mirror::ArtField* field) const;
+template void EncodedStaticFieldValueIterator::ReadValueToField<true>(Handle<mirror::ArtField> field) const;
+template void EncodedStaticFieldValueIterator::ReadValueToField<false>(Handle<mirror::ArtField> field) const;
CatchHandlerIterator::CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address) {
handler_.address_ = -1;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 1306f11..c160253 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -997,7 +997,6 @@
};
typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
mutable Atomic<Index*> class_def_index_;
- mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
};
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
@@ -1250,7 +1249,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive>
- void ReadValueToField(mirror::ArtField* field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ReadValueToField(Handle<mirror::ArtField> field) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool HasNext() { return pos_ < array_size_; }
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index b6810b0..b913220 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -190,7 +190,7 @@
}
// Reads an instruction out of the stream from the current address plus an offset.
- const Instruction* RelativeAt(int32_t offset) const {
+ const Instruction* RelativeAt(int32_t offset) const WARN_UNUSED {
return At(reinterpret_cast<const uint16_t*>(this) + offset);
}
diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h
index 80638ed..05214a4 100644
--- a/runtime/dex_instruction_list.h
+++ b/runtime/dex_instruction_list.h
@@ -253,10 +253,10 @@
V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \
V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \
- V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xEE, UNUSED_EE, "unused-ee", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
+ V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \
V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \
V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \
V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0, kVerifyError) \
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0ab6394..65a557b 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -1261,7 +1261,7 @@
return nullptr;
}
-struct PACKED(1) FDE {
+struct PACKED(1) FDE32 {
uint32_t raw_length_;
uint32_t GetLength() {
return raw_length_ + sizeof(raw_length_);
@@ -1272,25 +1272,186 @@
uint8_t instructions[0];
};
-static FDE* NextFDE(FDE* frame) {
+static FDE32* NextFDE(FDE32* frame) {
byte* fde_bytes = reinterpret_cast<byte*>(frame);
fde_bytes += frame->GetLength();
- return reinterpret_cast<FDE*>(fde_bytes);
+ return reinterpret_cast<FDE32*>(fde_bytes);
}
-static bool IsFDE(FDE* frame) {
+static bool IsFDE(FDE32* frame) {
return frame->CIE_pointer != 0;
}
-// TODO This only works for 32-bit Elf Files.
-static bool FixupEHFrame(uintptr_t text_start, byte* eh_frame, size_t eh_frame_size) {
- FDE* last_frame = reinterpret_cast<FDE*>(eh_frame + eh_frame_size);
- FDE* frame = NextFDE(reinterpret_cast<FDE*>(eh_frame));
- for (; frame < last_frame; frame = NextFDE(frame)) {
- if (!IsFDE(frame)) {
+struct PACKED(1) FDE64 {
+ uint32_t raw_length_;
+ uint64_t extended_length_;
+ uint64_t GetLength() {
+ return extended_length_ + sizeof(raw_length_) + sizeof(extended_length_);
+ }
+ uint64_t CIE_pointer;
+ uint64_t initial_location;
+ uint64_t address_range;
+ uint8_t instructions[0];
+};
+
+static FDE64* NextFDE(FDE64* frame) {
+ byte* fde_bytes = reinterpret_cast<byte*>(frame);
+ fde_bytes += frame->GetLength();
+ return reinterpret_cast<FDE64*>(fde_bytes);
+}
+
+static bool IsFDE(FDE64* frame) {
+ return frame->CIE_pointer != 0;
+}
+
+static bool FixupEHFrame(off_t base_address_delta,
+ byte* eh_frame, size_t eh_frame_size) {
+ if (*(reinterpret_cast<uint32_t*>(eh_frame)) == 0xffffffff) {
+ FDE64* last_frame = reinterpret_cast<FDE64*>(eh_frame + eh_frame_size);
+ FDE64* frame = NextFDE(reinterpret_cast<FDE64*>(eh_frame));
+ for (; frame < last_frame; frame = NextFDE(frame)) {
+ if (!IsFDE(frame)) {
+ return false;
+ }
+ frame->initial_location += base_address_delta;
+ }
+ return true;
+ } else {
+ FDE32* last_frame = reinterpret_cast<FDE32*>(eh_frame + eh_frame_size);
+ FDE32* frame = NextFDE(reinterpret_cast<FDE32*>(eh_frame));
+ for (; frame < last_frame; frame = NextFDE(frame)) {
+ if (!IsFDE(frame)) {
+ return false;
+ }
+ frame->initial_location += base_address_delta;
+ }
+ return true;
+ }
+}
+
+static uint8_t* NextLeb128(uint8_t* current) {
+ DecodeUnsignedLeb128(const_cast<const uint8_t**>(¤t));
+ return current;
+}
+
+struct PACKED(1) DebugLineHeader {
+ uint32_t unit_length_; // TODO 32-bit specific size
+ uint16_t version_;
+ uint32_t header_length_; // TODO 32-bit specific size
+ uint8_t minimum_instruction_lenght_;
+ uint8_t maximum_operations_per_instruction_;
+ uint8_t default_is_stmt_;
+ int8_t line_base_;
+ uint8_t line_range_;
+ uint8_t opcode_base_;
+ uint8_t remaining_[0];
+
+ bool IsStandardOpcode(const uint8_t* op) const {
+ return *op != 0 && *op < opcode_base_;
+ }
+
+ bool IsExtendedOpcode(const uint8_t* op) const {
+ return *op == 0;
+ }
+
+ const uint8_t* GetStandardOpcodeLengths() const {
+ return remaining_;
+ }
+
+ uint8_t* GetNextOpcode(uint8_t* op) const {
+ if (IsExtendedOpcode(op)) {
+ uint8_t* length_field = op + 1;
+ uint32_t length = DecodeUnsignedLeb128(const_cast<const uint8_t**>(&length_field));
+ return length_field + length;
+ } else if (!IsStandardOpcode(op)) {
+ return op + 1;
+ } else if (*op == DW_LNS_fixed_advance_pc) {
+ return op + 1 + sizeof(uint16_t);
+ } else {
+ uint8_t num_args = GetStandardOpcodeLengths()[*op - 1];
+ op += 1;
+ for (int i = 0; i < num_args; i++) {
+ op = NextLeb128(op);
+ }
+ return op;
+ }
+ }
+
+ uint8_t* GetDebugLineData() const {
+ const uint8_t* hdr_start =
+ reinterpret_cast<const uint8_t*>(&header_length_) + sizeof(header_length_);
+ return const_cast<uint8_t*>(hdr_start + header_length_);
+ }
+};
+
+class DebugLineInstructionIterator {
+ public:
+ static DebugLineInstructionIterator* Create(DebugLineHeader* header, size_t section_size) {
+ std::unique_ptr<DebugLineInstructionIterator> line_iter(
+ new DebugLineInstructionIterator(header, section_size));
+ if (line_iter.get() == nullptr) {
+ return nullptr;
+ } else {
+ return line_iter.release();
+ }
+ }
+
+ ~DebugLineInstructionIterator() {}
+
+ bool Next() {
+ if (current_instruction_ == nullptr) {
return false;
}
- frame->initial_location += text_start;
+ current_instruction_ = header_->GetNextOpcode(current_instruction_);
+ if (current_instruction_ >= last_instruction_) {
+ current_instruction_ = nullptr;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ uint8_t* GetInstruction() {
+ return current_instruction_;
+ }
+
+ bool IsExtendedOpcode() {
+ return header_->IsExtendedOpcode(current_instruction_);
+ }
+
+ uint8_t GetOpcode() {
+ if (!IsExtendedOpcode()) {
+ return *current_instruction_;
+ } else {
+ uint8_t* len_ptr = current_instruction_ + 1;
+ return *NextLeb128(len_ptr);
+ }
+ }
+
+ uint8_t* GetArguments() {
+ if (!IsExtendedOpcode()) {
+ return current_instruction_ + 1;
+ } else {
+ uint8_t* len_ptr = current_instruction_ + 1;
+ return NextLeb128(len_ptr) + 1;
+ }
+ }
+
+ private:
+ DebugLineInstructionIterator(DebugLineHeader* header, size_t size)
+ : header_(header), last_instruction_(reinterpret_cast<uint8_t*>(header) + size),
+ current_instruction_(header->GetDebugLineData()) {}
+
+ DebugLineHeader* header_;
+ uint8_t* last_instruction_;
+ uint8_t* current_instruction_;
+};
+
+static bool FixupDebugLine(off_t base_offset_delta, DebugLineInstructionIterator* iter) {
+ while (iter->Next()) {
+ if (iter->IsExtendedOpcode() && iter->GetOpcode() == DW_LNE_set_address) {
+ *reinterpret_cast<uint32_t*>(iter->GetArguments()) += base_offset_delta;
+ }
}
return true;
}
@@ -1430,20 +1591,29 @@
public:
~DebugAbbrev() {}
static DebugAbbrev* Create(const byte* dbg_abbrev, size_t dbg_abbrev_size) {
- std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev);
- const byte* last = dbg_abbrev + dbg_abbrev_size;
- while (dbg_abbrev < last) {
- std::unique_ptr<DebugTag> tag(DebugTag::Create(&dbg_abbrev));
- if (tag.get() == nullptr) {
- return nullptr;
- } else {
- abbrev->tags_.insert(std::pair<uint32_t, uint32_t>(tag->index_, abbrev->tag_list_.size()));
- abbrev->tag_list_.push_back(std::move(tag));
- }
+ std::unique_ptr<DebugAbbrev> abbrev(new DebugAbbrev(dbg_abbrev, dbg_abbrev + dbg_abbrev_size));
+ if (!abbrev->ReadAtOffset(0)) {
+ return nullptr;
}
return abbrev.release();
}
+ bool ReadAtOffset(uint32_t abbrev_offset) {
+ tags_.clear();
+ tag_list_.clear();
+ const byte* dbg_abbrev = begin_ + abbrev_offset;
+ while (dbg_abbrev < end_ && *dbg_abbrev != 0) {
+ std::unique_ptr<DebugTag> tag(DebugTag::Create(&dbg_abbrev));
+ if (tag.get() == nullptr) {
+ return false;
+ } else {
+ tags_.insert(std::pair<uint32_t, uint32_t>(tag->index_, tag_list_.size()));
+ tag_list_.push_back(std::move(tag));
+ }
+ }
+ return true;
+ }
+
DebugTag* ReadTag(const byte* entry) {
uint32_t tag_num = DecodeUnsignedLeb128(&entry);
auto it = tags_.find(tag_num);
@@ -1456,7 +1626,9 @@
}
private:
- DebugAbbrev() {}
+ DebugAbbrev(const byte* begin, const byte* end) : begin_(begin), end_(end) {}
+ const byte* begin_;
+ const byte* end_;
std::map<uint32_t, uint32_t> tags_;
std::vector<std::unique_ptr<DebugTag>> tag_list_;
};
@@ -1480,11 +1652,21 @@
if (current_entry_ == nullptr || current_tag_ == nullptr) {
return false;
}
+ bool reread_abbrev = false;
current_entry_ += current_tag_->GetSize();
+ if (reinterpret_cast<DebugInfoHeader*>(current_entry_) >= next_cu_) {
+ current_cu_ = next_cu_;
+ next_cu_ = GetNextCu(current_cu_);
+ current_entry_ = reinterpret_cast<byte*>(current_cu_) + sizeof(DebugInfoHeader);
+ reread_abbrev = true;
+ }
if (current_entry_ >= last_entry_) {
current_entry_ = nullptr;
return false;
}
+ if (reread_abbrev) {
+ abbrev_->ReadAtOffset(current_cu_->debug_abbrev_offset);
+ }
current_tag_ = abbrev_->ReadTag(current_entry_);
if (current_tag_ == nullptr) {
current_entry_ = nullptr;
@@ -1512,49 +1694,91 @@
}
private:
+ static DebugInfoHeader* GetNextCu(DebugInfoHeader* hdr) {
+ byte* hdr_byte = reinterpret_cast<byte*>(hdr);
+ return reinterpret_cast<DebugInfoHeader*>(hdr_byte + sizeof(uint32_t) + hdr->unit_length);
+ }
+
DebugInfoIterator(DebugInfoHeader* header, size_t frame_size, DebugAbbrev* abbrev)
: abbrev_(abbrev),
+ current_cu_(header),
+ next_cu_(GetNextCu(header)),
last_entry_(reinterpret_cast<byte*>(header) + frame_size),
current_entry_(reinterpret_cast<byte*>(header) + sizeof(DebugInfoHeader)),
current_tag_(abbrev_->ReadTag(current_entry_)) {}
DebugAbbrev* abbrev_;
+ DebugInfoHeader* current_cu_;
+ DebugInfoHeader* next_cu_;
byte* last_entry_;
byte* current_entry_;
DebugTag* current_tag_;
};
-static bool FixupDebugInfo(uint32_t text_start, DebugInfoIterator* iter) {
+static bool FixupDebugInfo(off_t base_address_delta, DebugInfoIterator* iter) {
do {
if (iter->GetCurrentTag()->GetAttrSize(DW_AT_low_pc) != sizeof(int32_t) ||
iter->GetCurrentTag()->GetAttrSize(DW_AT_high_pc) != sizeof(int32_t)) {
+ LOG(ERROR) << "DWARF information with 64 bit pointers is not supported yet.";
return false;
}
uint32_t* PC_low = reinterpret_cast<uint32_t*>(iter->GetPointerToField(DW_AT_low_pc));
uint32_t* PC_high = reinterpret_cast<uint32_t*>(iter->GetPointerToField(DW_AT_high_pc));
if (PC_low != nullptr && PC_high != nullptr) {
- *PC_low += text_start;
- *PC_high += text_start;
+ *PC_low += base_address_delta;
+ *PC_high += base_address_delta;
}
} while (iter->next());
return true;
}
-static bool FixupDebugSections(const byte* dbg_abbrev, size_t dbg_abbrev_size,
- uintptr_t text_start,
- byte* dbg_info, size_t dbg_info_size,
- byte* eh_frame, size_t eh_frame_size) {
- std::unique_ptr<DebugAbbrev> abbrev(DebugAbbrev::Create(dbg_abbrev, dbg_abbrev_size));
+bool ElfFile::FixupDebugSections(off_t base_address_delta) {
+ const Elf32_Shdr* debug_info = FindSectionByName(".debug_info");
+ const Elf32_Shdr* debug_abbrev = FindSectionByName(".debug_abbrev");
+ const Elf32_Shdr* eh_frame = FindSectionByName(".eh_frame");
+ const Elf32_Shdr* debug_str = FindSectionByName(".debug_str");
+ const Elf32_Shdr* debug_line = FindSectionByName(".debug_line");
+ const Elf32_Shdr* strtab_sec = FindSectionByName(".strtab");
+ const Elf32_Shdr* symtab_sec = FindSectionByName(".symtab");
+
+ if (debug_info == nullptr || debug_abbrev == nullptr ||
+ debug_str == nullptr || strtab_sec == nullptr || symtab_sec == nullptr) {
+ // Release version of ART does not generate debug info.
+ return true;
+ }
+ if (base_address_delta == 0) {
+ return true;
+ }
+ if (eh_frame != nullptr &&
+ !FixupEHFrame(base_address_delta, Begin() + eh_frame->sh_offset, eh_frame->sh_size)) {
+ return false;
+ }
+
+ std::unique_ptr<DebugAbbrev> abbrev(DebugAbbrev::Create(Begin() + debug_abbrev->sh_offset,
+ debug_abbrev->sh_size));
if (abbrev.get() == nullptr) {
return false;
}
- std::unique_ptr<DebugInfoIterator> iter(
- DebugInfoIterator::Create(reinterpret_cast<DebugInfoHeader*>(dbg_info),
- dbg_info_size, abbrev.get()));
- if (iter.get() == nullptr) {
+ DebugInfoHeader* info_header =
+ reinterpret_cast<DebugInfoHeader*>(Begin() + debug_info->sh_offset);
+ std::unique_ptr<DebugInfoIterator> info_iter(DebugInfoIterator::Create(info_header,
+ debug_info->sh_size,
+ abbrev.get()));
+ if (info_iter.get() == nullptr) {
return false;
}
- return FixupDebugInfo(text_start, iter.get())
- && FixupEHFrame(text_start, eh_frame, eh_frame_size);
+ if (debug_line != nullptr) {
+ DebugLineHeader* line_header =
+ reinterpret_cast<DebugLineHeader*>(Begin() + debug_line->sh_offset);
+ std::unique_ptr<DebugLineInstructionIterator> line_iter(
+ DebugLineInstructionIterator::Create(line_header, debug_line->sh_size));
+ if (line_iter.get() == nullptr) {
+ return false;
+ }
+ if (!FixupDebugLine(base_address_delta, line_iter.get())) {
+ return false;
+ }
+ }
+ return FixupDebugInfo(base_address_delta, info_iter.get());
}
void ElfFile::GdbJITSupport() {
@@ -1572,19 +1796,13 @@
}
ElfFile& all = *all_ptr;
- // Do we have interesting sections?
- const Elf32_Shdr* debug_info = all.FindSectionByName(".debug_info");
- const Elf32_Shdr* debug_abbrev = all.FindSectionByName(".debug_abbrev");
+ // We need the eh_frame for gdb but debug info might be present without it.
const Elf32_Shdr* eh_frame = all.FindSectionByName(".eh_frame");
- const Elf32_Shdr* debug_str = all.FindSectionByName(".debug_str");
- const Elf32_Shdr* strtab_sec = all.FindSectionByName(".strtab");
- const Elf32_Shdr* symtab_sec = all.FindSectionByName(".symtab");
- Elf32_Shdr* text_sec = all.FindSectionByName(".text");
- if (debug_info == nullptr || debug_abbrev == nullptr || eh_frame == nullptr ||
- debug_str == nullptr || text_sec == nullptr || strtab_sec == nullptr ||
- symtab_sec == nullptr) {
+ if (eh_frame == nullptr) {
return;
}
+
+ // Do we have interesting sections?
// We need to add in a strtab and symtab to the image.
// all is MAP_PRIVATE so it can be written to freely.
// We also already have strtab and symtab so we are fine there.
@@ -1595,13 +1813,9 @@
elf_hdr.e_phentsize = 0;
elf_hdr.e_type = ET_EXEC;
- text_sec->sh_type = SHT_NOBITS;
- text_sec->sh_offset = 0;
-
- if (!FixupDebugSections(
- all.Begin() + debug_abbrev->sh_offset, debug_abbrev->sh_size, text_sec->sh_addr,
- all.Begin() + debug_info->sh_offset, debug_info->sh_size,
- all.Begin() + eh_frame->sh_offset, eh_frame->sh_size)) {
+ // Since base_address_ is 0 if we are actually loaded at a known address (i.e. this is boot.oat)
+ // and the actual address stuff starts at in regular files this is good.
+ if (!all.FixupDebugSections(reinterpret_cast<intptr_t>(base_address_))) {
LOG(ERROR) << "Failed to load GDB data";
return;
}
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 985be76..916d693 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -108,6 +108,8 @@
// executable is true at run time, false at compile time.
bool Load(bool executable, std::string* error_msg);
+ bool FixupDebugSections(off_t base_address_delta);
+
private:
ElfFile(File* file, bool writable, bool program_header_only);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index ccbedc0..9fb9a3b 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -37,6 +37,7 @@
// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <const bool kAccessCheck>
+ALWAYS_INLINE
static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
mirror::ArtMethod* method,
Thread* self, bool* slow_path) {
@@ -78,7 +79,7 @@
// has changed and to null-check the return value in case the
// initialization fails.
*slow_path = true;
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
DCHECK(self->IsExceptionPending());
return nullptr; // Failure
} else {
@@ -90,6 +91,7 @@
}
// TODO: Fix no thread safety analysis when annotalysis is smarter.
+ALWAYS_INLINE
static inline mirror::Class* CheckClassInitializedForObjectAlloc(mirror::Class* klass,
Thread* self,
bool* slow_path) {
@@ -105,7 +107,7 @@
// has changed and to null-check the return value in case the
// initialization fails.
*slow_path = true;
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
DCHECK(self->IsExceptionPending());
return nullptr; // Failure
}
@@ -120,6 +122,7 @@
// check.
// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE
static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
mirror::ArtMethod* method,
Thread* self,
@@ -139,6 +142,7 @@
// Given the context of a calling Method and a resolved class, create an instance.
// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
template <bool kInstrumented>
+ALWAYS_INLINE
static inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass,
mirror::ArtMethod* method,
Thread* self,
@@ -161,6 +165,7 @@
// Given the context of a calling Method and an initialized class, create an instance.
// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
template <bool kInstrumented>
+ALWAYS_INLINE
static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass,
mirror::ArtMethod* method,
Thread* self,
@@ -173,6 +178,7 @@
// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <bool kAccessCheck>
+ALWAYS_INLINE
static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
mirror::ArtMethod* method,
int32_t component_count,
@@ -209,6 +215,7 @@
// check.
// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE
static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
mirror::ArtMethod* method,
int32_t component_count,
@@ -223,14 +230,15 @@
}
gc::Heap* heap = Runtime::Current()->GetHeap();
return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSize(),
+ klass->GetComponentSizeShift(),
heap->GetCurrentAllocator());
}
return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSize(), allocator_type);
+ klass->GetComponentSizeShift(), allocator_type);
}
template <bool kAccessCheck, bool kInstrumented>
+ALWAYS_INLINE
static inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
mirror::ArtMethod* method,
int32_t component_count,
@@ -251,7 +259,7 @@
// No need to retry a slow-path allocation as the above code won't cause a GC or thread
// suspension.
return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSize(), allocator_type);
+ klass->GetComponentSizeShift(), allocator_type);
}
template<FindFieldType type, bool access_check>
@@ -316,7 +324,7 @@
} else {
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(fields_class));
- if (LIKELY(class_linker->EnsureInitialized(h_class, true, true))) {
+ if (LIKELY(class_linker->EnsureInitialized(self, h_class, true, true))) {
// Otherwise let's ensure the class is initialized before resolving the field.
return resolved_field;
}
@@ -595,7 +603,7 @@
}
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(klass));
- if (!class_linker->EnsureInitialized(h_class, true, true)) {
+ if (!class_linker->EnsureInitialized(self, h_class, true, true)) {
CHECK(self->IsExceptionPending());
return nullptr; // Failure - Indicate to caller to deliver exception
}
@@ -632,18 +640,6 @@
}
}
-static inline void CheckSuspend(Thread* thread) {
- for (;;) {
- if (thread->ReadFlag(kCheckpointRequest)) {
- thread->RunCheckpointFunction();
- } else if (thread->ReadFlag(kSuspendRequest)) {
- thread->FullSuspendCheck();
- } else {
- break;
- }
- }
-}
-
template <typename INT_TYPE, typename FLOAT_TYPE>
static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) {
const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max());
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 6ede6de..835d6e2 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -90,7 +90,8 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
// Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
// the heap switched the allocator type while we were suspended.
- return mirror::Array::Alloc<false>(self, klass, component_count, klass->GetComponentSize(),
+ return mirror::Array::Alloc<false>(self, klass, component_count,
+ klass->GetComponentSizeShift(),
heap->GetCurrentAllocator());
}
@@ -109,7 +110,8 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
// Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
// the heap switched the allocator type while we were suspended.
- return mirror::Array::Alloc<true>(self, klass, component_count, klass->GetComponentSize(),
+ return mirror::Array::Alloc<true>(self, klass, component_count,
+ klass->GetComponentSizeShift(),
heap->GetCurrentAllocator());
}
@@ -213,21 +215,20 @@
}
void CheckReferenceResult(mirror::Object* o, Thread* self) {
- if (o == NULL) {
+ if (o == nullptr) {
return;
}
- mirror::ArtMethod* m = self->GetCurrentMethod(NULL);
- if (o == kInvalidIndirectRefObject) {
- JniAbortF(NULL, "invalid reference returned from %s", PrettyMethod(m).c_str());
- }
+ mirror::ArtMethod* m = self->GetCurrentMethod(nullptr);
// Make sure that the result is an instance of the type this method was expected to return.
StackHandleScope<1> hs(self);
Handle<mirror::ArtMethod> h_m(hs.NewHandle(m));
mirror::Class* return_type = MethodHelper(h_m).GetReturnType();
if (!o->InstanceOf(return_type)) {
- JniAbortF(NULL, "attempt to return an instance of %s from %s", PrettyTypeOf(o).c_str(),
- PrettyMethod(h_m.Get()).c_str());
+ Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
+ "attempt to return an instance of %s from %s",
+ PrettyTypeOf(o).c_str(),
+ PrettyMethod(h_m.Get()).c_str());
}
}
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index bc95c23..08edecf 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -174,8 +174,6 @@
void CheckReferenceResult(mirror::Object* o, Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty,
jobject rcvr_jobj, jobject interface_art_method_jobj,
std::vector<jvalue>& args)
@@ -199,7 +197,6 @@
return reinterpret_cast<uintptr_t>(art_quick_instrumentation_exit);
}
-#if defined(ART_USE_PORTABLE_COMPILER)
extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*);
static inline const void* GetPortableToInterpreterBridge() {
return reinterpret_cast<void*>(art_portable_to_interpreter_bridge);
@@ -209,14 +206,12 @@
// TODO: portable to quick bridge. Bug: 8196384
return GetPortableToInterpreterBridge();
}
-#endif
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
static inline const void* GetQuickToInterpreterBridge() {
return reinterpret_cast<void*>(art_quick_to_interpreter_bridge);
}
-#if defined(ART_USE_PORTABLE_COMPILER)
static inline const void* GetQuickToPortableBridge() {
// TODO: quick to portable bridge. Bug: 8196384
return GetQuickToInterpreterBridge();
@@ -226,7 +221,6 @@
static inline const void* GetPortableProxyInvokeHandler() {
return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
}
-#endif
extern "C" void art_quick_proxy_invoke_handler();
static inline const void* GetQuickProxyInvokeHandler() {
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index 64faf76..b617636 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -36,7 +36,8 @@
self->PushShadowFrame(shadow_frame);
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(declaringClass));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true))) {
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true,
+ true))) {
self->PopShadowFrame();
DCHECK(self->IsExceptionPending());
return;
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
index 16ce471..6f9c083 100644
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
@@ -37,11 +37,7 @@
}
}
DCHECK(!self->IsExceptionPending());
-#if defined(ART_USE_PORTABLE_COMPILER)
const void* code = method->GetEntryPointFromPortableCompiledCode();
-#else
- const void* code = nullptr;
-#endif
// When we return, the caller will branch to this address, so it had better not be 0!
if (UNLIKELY(code == NULL)) {
diff --git a/runtime/entrypoints/portable/portable_thread_entrypoints.cc b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
index 23e1c36..ecbc65e 100644
--- a/runtime/entrypoints/portable/portable_thread_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_thread_entrypoints.cc
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method.h"
-#include "mirror/object-inl.h"
+#include "mirror/art_method-inl.h"
#include "verifier/dex_gc_map.h"
#include "stack.h"
+#include "thread-inl.h"
namespace art {
@@ -71,7 +70,7 @@
extern "C" void art_portable_test_suspend_from_code(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CheckSuspend(self);
+ self->CheckSuspend();
if (Runtime::Current()->GetInstrumentation()->ShouldPortableCodeDeoptimize()) {
// Save out the shadow frame to the heap
ShadowFrameCopyVisitor visitor(self);
diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
index f90288b..7f6144b 100644
--- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc
@@ -215,7 +215,7 @@
if (method->IsStatic() && !method->GetDeclaringClass()->IsInitialized()) {
// Ensure static method's class is initialized.
Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
DCHECK(Thread::Current()->IsExceptionPending());
self->PopManagedStackFragment(fragment);
return 0;
@@ -399,41 +399,27 @@
// Ensure that the called method's class is initialized.
StackHandleScope<1> hs(self);
Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass()));
- linker->EnsureInitialized(called_class, true, true);
+ linker->EnsureInitialized(self, called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
-#if defined(ART_USE_PORTABLE_COMPILER)
code = called->GetEntryPointFromPortableCompiledCode();
-#else
- code = nullptr;
-#endif
// TODO: remove this after we solve the link issue.
if (code == nullptr) {
-#if defined(ART_USE_PORTABLE_COMPILER)
bool have_portable_code;
code = linker->GetPortableOatCodeFor(called, &have_portable_code);
-#endif
}
} else if (called_class->IsInitializing()) {
if (invoke_type == kStatic) {
// Class is still initializing, go to oat and grab code (trampoline must be left in place
// until class is initialized to stop races between threads).
-#if defined(ART_USE_PORTABLE_COMPILER)
bool have_portable_code;
code = linker->GetPortableOatCodeFor(called, &have_portable_code);
-#endif
} else {
// No trampoline for non-static methods.
-#if defined(ART_USE_PORTABLE_COMPILER)
code = called->GetEntryPointFromPortableCompiledCode();
-#else
- code = nullptr;
-#endif
// TODO: remove this after we solve the link issue.
if (code == nullptr) {
-#if defined(ART_USE_PORTABLE_COMPILER)
bool have_portable_code;
code = linker->GetPortableOatCodeFor(called, &have_portable_code);
-#endif
}
}
} else {
diff --git a/runtime/entrypoints/quick/callee_save_frame.h b/runtime/entrypoints/quick/callee_save_frame.h
index e573d6d..e728f7d 100644
--- a/runtime/entrypoints/quick/callee_save_frame.h
+++ b/runtime/entrypoints/quick/callee_save_frame.h
@@ -18,7 +18,9 @@
#define ART_RUNTIME_ENTRYPOINTS_QUICK_CALLEE_SAVE_FRAME_H_
#include "base/mutex.h"
+#include "gc_root-inl.h"
#include "instruction_set.h"
+#include "runtime-inl.h"
#include "thread-inl.h"
// Specific frame size code is in architecture-specific files. We include this to compile-time
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 1f2713a..d8da463 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -25,11 +25,34 @@
namespace art {
+static constexpr bool kUseTlabFastPath = true;
+
#define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR_INST(suffix, suffix2, instrumented_bool, allocator_type) \
extern "C" mirror::Object* artAllocObjectFromCode ##suffix##suffix2( \
uint32_t type_idx, mirror::ArtMethod* method, Thread* self, \
StackReference<mirror::ArtMethod>* sp) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+ if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
+ mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx); \
+ if (LIKELY(klass != nullptr && klass->IsInitialized() && !klass->IsFinalizable())) { \
+ size_t byte_count = klass->GetObjectSize(); \
+ byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
+ mirror::Object* obj; \
+ if (LIKELY(byte_count < self->TlabSize())) { \
+ obj = self->AllocTlab(byte_count); \
+ DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
+ obj->SetClass(klass); \
+ if (kUseBakerOrBrooksReadBarrier) { \
+ if (kUseBrooksReadBarrier) { \
+ obj->SetReadBarrierPointer(obj); \
+ } \
+ obj->AssertReadBarrierPointer(); \
+ } \
+ QuasiAtomic::ThreadFenceForConstructor(); \
+ return obj; \
+ } \
+ } \
+ } \
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
return AllocObjectFromCode<false, instrumented_bool>(type_idx, method, self, allocator_type); \
} \
@@ -37,6 +60,26 @@
mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \
StackReference<mirror::ArtMethod>* sp) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+ if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
+ if (LIKELY(klass->IsInitialized())) { \
+ size_t byte_count = klass->GetObjectSize(); \
+ byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
+ mirror::Object* obj; \
+ if (LIKELY(byte_count < self->TlabSize())) { \
+ obj = self->AllocTlab(byte_count); \
+ DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
+ obj->SetClass(klass); \
+ if (kUseBakerOrBrooksReadBarrier) { \
+ if (kUseBrooksReadBarrier) { \
+ obj->SetReadBarrierPointer(obj); \
+ } \
+ obj->AssertReadBarrierPointer(); \
+ } \
+ QuasiAtomic::ThreadFenceForConstructor(); \
+ return obj; \
+ } \
+ } \
+ } \
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
return AllocObjectFromCodeResolved<instrumented_bool>(klass, method, self, allocator_type); \
} \
@@ -44,6 +87,24 @@
mirror::Class* klass, mirror::ArtMethod* method, Thread* self, \
StackReference<mirror::ArtMethod>* sp) \
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+ if (kUseTlabFastPath && !instrumented_bool && allocator_type == gc::kAllocatorTypeTLAB) { \
+ size_t byte_count = klass->GetObjectSize(); \
+ byte_count = RoundUp(byte_count, gc::space::BumpPointerSpace::kAlignment); \
+ mirror::Object* obj; \
+ if (LIKELY(byte_count < self->TlabSize())) { \
+ obj = self->AllocTlab(byte_count); \
+ DCHECK(obj != nullptr) << "AllocTlab can't fail"; \
+ obj->SetClass(klass); \
+ if (kUseBakerOrBrooksReadBarrier) { \
+ if (kUseBrooksReadBarrier) { \
+ obj->SetReadBarrierPointer(obj); \
+ } \
+ obj->AssertReadBarrierPointer(); \
+ } \
+ QuasiAtomic::ThreadFenceForConstructor(); \
+ return obj; \
+ } \
+ } \
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly); \
return AllocObjectFromCodeInitialized<instrumented_bool>(klass, method, self, allocator_type); \
} \
@@ -155,10 +216,10 @@
// Generate the entrypoint functions.
#if !defined(__APPLE__) || !defined(__LP64__)
-GENERATE_ENTRYPOINTS(_dlmalloc);
-GENERATE_ENTRYPOINTS(_rosalloc);
-GENERATE_ENTRYPOINTS(_bump_pointer);
-GENERATE_ENTRYPOINTS(_tlab);
+GENERATE_ENTRYPOINTS(_dlmalloc)
+GENERATE_ENTRYPOINTS(_rosalloc)
+GENERATE_ENTRYPOINTS(_bump_pointer)
+GENERATE_ENTRYPOINTS(_tlab)
#endif
static bool entry_points_instrumented = false;
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.h b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
index 7fd3fe9..ec0aef5 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.h
@@ -17,15 +17,12 @@
#ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ALLOC_ENTRYPOINTS_H_
#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ALLOC_ENTRYPOINTS_H_
-#include "gc/heap.h"
+#include "base/mutex.h"
+#include "gc/allocator_type.h"
#include "quick_entrypoints.h"
namespace art {
-namespace gc {
-enum AllocatorType;
-} // namespace gc
-
void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
// Runtime shutdown lock is necessary to prevent races in thread initialization. When the thread is
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index f858743..fbc7913 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -38,12 +38,24 @@
V(InitializeType, void*, uint32_t, void*) \
V(ResolveString, void*, void*, uint32_t) \
\
+ V(Set8Instance, int, uint32_t, void*, int8_t) \
+ V(Set8Static, int, uint32_t, int8_t) \
+ V(Set16Instance, int, uint32_t, void*, int16_t) \
+ V(Set16Static, int, uint32_t, int16_t) \
V(Set32Instance, int, uint32_t, void*, int32_t) \
V(Set32Static, int, uint32_t, int32_t) \
V(Set64Instance, int, uint32_t, void*, int64_t) \
V(Set64Static, int, uint32_t, int64_t) \
V(SetObjInstance, int, uint32_t, void*, void*) \
V(SetObjStatic, int, uint32_t, void*) \
+ V(GetByteInstance, int8_t, uint32_t, void*) \
+ V(GetBooleanInstance, uint8_t, uint32_t, void*) \
+ V(GetByteStatic, int8_t, uint32_t) \
+ V(GetBooleanStatic, uint8_t, uint32_t) \
+ V(GetShortInstance, int16_t, uint32_t, void*) \
+ V(GetCharInstance, uint16_t, uint32_t, void*) \
+ V(GetShortStatic, int16_t, uint32_t) \
+ V(GetCharStatic, uint16_t, uint32_t) \
V(Get32Instance, int32_t, uint32_t, void*) \
V(Get32Static, int32_t, uint32_t) \
V(Get64Instance, int64_t, uint32_t, void*) \
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index cd1e247..b89c015 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -25,6 +25,74 @@
namespace art {
+extern "C" int8_t artGetByteStaticFromCode(uint32_t field_idx,
+ mirror::ArtMethod* referrer,
+ Thread* self, StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetByte(field->GetDeclaringClass());
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetByte(field->GetDeclaringClass());
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint8_t artGetBooleanStaticFromCode(uint32_t field_idx,
+ mirror::ArtMethod* referrer,
+ Thread* self, StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetBoolean(field->GetDeclaringClass());
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetBoolean(field->GetDeclaringClass());
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
+extern "C" int16_t artGetShortStaticFromCode(uint32_t field_idx,
+ mirror::ArtMethod* referrer,
+ Thread* self, StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetShort(field->GetDeclaringClass());
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetShort(field->GetDeclaringClass());
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint16_t artGetCharStaticFromCode(uint32_t field_idx,
+ mirror::ArtMethod* referrer,
+ Thread* self, StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveRead,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetChar(field->GetDeclaringClass());
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ return field->GetChar(field->GetDeclaringClass());
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx,
mirror::ArtMethod* referrer,
Thread* self, StackReference<mirror::ArtMethod>* sp)
@@ -78,6 +146,97 @@
return NULL; // Will throw exception by checking with Thread::Current
}
+extern "C" int8_t artGetByteInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL && obj != NULL)) {
+ return field->GetByte(obj);
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+ } else {
+ return field->GetByte(obj);
+ }
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint8_t artGetBooleanInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL && obj != NULL)) {
+ return field->GetBoolean(obj);
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+ } else {
+ return field->GetBoolean(obj);
+ }
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+extern "C" int16_t artGetShortInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL && obj != NULL)) {
+ return field->GetShort(obj);
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+ } else {
+ return field->GetShort(obj);
+ }
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
+extern "C" uint16_t artGetCharInstanceFromCode(uint32_t field_idx, mirror::Object* obj,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveRead,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL && obj != NULL)) {
+ return field->GetChar(obj);
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+ } else {
+ return field->GetChar(obj);
+ }
+ }
+ return 0; // Will throw exception by checking with Thread::Current
+}
+
extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
mirror::ArtMethod* referrer, Thread* self,
StackReference<mirror::ArtMethod>* sp)
@@ -148,6 +307,72 @@
return NULL; // Will throw exception by checking with Thread::Current
}
+extern "C" int artSet8StaticFromCode(uint32_t field_idx, uint32_t new_value,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimBoolean) {
+ field->SetBoolean<false>(field->GetDeclaringClass(), new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimByte, type);
+ field->SetByte<false>(field->GetDeclaringClass(), new_value);
+ }
+ return 0; // success
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int8_t));
+ if (LIKELY(field != NULL)) {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimBoolean) {
+ field->SetBoolean<false>(field->GetDeclaringClass(), new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimByte, type);
+ field->SetByte<false>(field->GetDeclaringClass(), new_value);
+ }
+ return 0; // success
+ }
+ return -1; // failure
+}
+
+extern "C" int artSet16StaticFromCode(uint32_t field_idx, uint16_t new_value,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimChar) {
+ field->SetChar<false>(field->GetDeclaringClass(), new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimShort, type);
+ field->SetShort<false>(field->GetDeclaringClass(), new_value);
+ }
+ return 0; // success
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int16_t));
+ if (LIKELY(field != NULL)) {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimChar) {
+ field->SetChar<false>(field->GetDeclaringClass(), new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimShort, type);
+ field->SetShort<false>(field->GetDeclaringClass(), new_value);
+ }
+ return 0; // success
+ }
+ return -1; // failure
+}
+
extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
mirror::ArtMethod* referrer, Thread* self,
StackReference<mirror::ArtMethod>* sp)
@@ -214,6 +439,91 @@
return -1; // failure
}
+extern "C" int artSet8InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint8_t new_value,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
+ sizeof(int8_t));
+ if (LIKELY(field != NULL && obj != NULL)) {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimBoolean) {
+ field->SetBoolean<false>(obj, new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimByte, type);
+ field->SetByte<false>(obj, new_value);
+ }
+ return 0; // success
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ {
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+ sizeof(int8_t));
+ }
+ if (LIKELY(field != NULL)) {
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+ } else {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimBoolean) {
+ field->SetBoolean<false>(obj, new_value);
+ } else {
+ field->SetByte<false>(obj, new_value);
+ }
+ return 0; // success
+ }
+ }
+ return -1; // failure
+}
+
+extern "C" int artSet16InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint16_t new_value,
+ mirror::ArtMethod* referrer, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite,
+ sizeof(int16_t));
+ if (LIKELY(field != NULL && obj != NULL)) {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimChar) {
+ field->SetChar<false>(obj, new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimShort, type);
+ field->SetShort<false>(obj, new_value);
+ }
+ return 0; // success
+ }
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ {
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj));
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+ sizeof(int16_t));
+ }
+ if (LIKELY(field != NULL)) {
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+ } else {
+ Primitive::Type type = field->GetTypeAsPrimitiveType();
+ // Compiled code can't use transactional mode.
+ if (type == Primitive::kPrimChar) {
+ field->SetChar<false>(obj, new_value);
+ } else {
+ DCHECK_EQ(Primitive::kPrimShort, type);
+ field->SetShort<false>(obj, new_value);
+ }
+ return 0; // success
+ }
+ }
+ return -1; // failure
+}
+
extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj, uint32_t new_value,
mirror::ArtMethod* referrer, Thread* self,
StackReference<mirror::ArtMethod>* sp)
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 6537249..c239535 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -14,15 +14,9 @@
* limitations under the License.
*/
-#include "dex_file-inl.h"
#include "entrypoints/entrypoint_utils-inl.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object.h"
#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "verify_object-inl.h"
namespace art {
@@ -56,7 +50,7 @@
// In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there
// is a flag raised.
DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
- CheckSuspend(self);
+ self->CheckSuspend();
}
}
diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
index 118cd7f..ea75fb6 100644
--- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
@@ -15,17 +15,15 @@
*/
#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils-inl.h"
-#include "thread.h"
-#include "thread_list.h"
+#include "thread-inl.h"
namespace art {
-extern "C" void artTestSuspendFromCode(Thread* thread, StackReference<mirror::ArtMethod>* sp)
+extern "C" void artTestSuspendFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Called when suspend count check value is 0 and thread->suspend_count_ != 0
- FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly);
- CheckSuspend(thread);
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ self->CheckSuspend();
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index dfd2e11..1dbbb70 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -496,7 +496,7 @@
// Ensure static method's class is initialized.
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
DCHECK(Thread::Current()->IsExceptionPending()) << PrettyMethod(method);
self->PopManagedStackFragment(fragment);
return 0;
@@ -808,7 +808,7 @@
// Ensure that the called method's class is initialized.
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> called_class(hs.NewHandle(called->GetDeclaringClass()));
- linker->EnsureInitialized(called_class, true, true);
+ linker->EnsureInitialized(soa.Self(), called_class, true, true);
if (LIKELY(called_class->IsInitialized())) {
code = called->GetEntryPointFromQuickCompiledCode();
} else if (called_class->IsInitializing()) {
@@ -1488,7 +1488,7 @@
// Initialize padding entries.
size_t expected_slots = handle_scope_->NumberOfReferences();
while (cur_entry_ < expected_slots) {
- handle_scope_->GetHandle(cur_entry_++).Assign(nullptr);
+ handle_scope_->GetMutableHandle(cur_entry_++).Assign(nullptr);
}
DCHECK_NE(cur_entry_, 0U);
}
@@ -1509,7 +1509,7 @@
uintptr_t BuildGenericJniFrameVisitor::FillJniCall::PushHandle(mirror::Object* ref) {
uintptr_t tmp;
- Handle<mirror::Object> h = handle_scope_->GetHandle(cur_entry_);
+ MutableHandle<mirror::Object> h = handle_scope_->GetMutableHandle(cur_entry_);
h.Assign(ref);
tmp = reinterpret_cast<uintptr_t>(h.ToJObject());
cur_entry_++;
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index d205e2a..305e5a2 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -186,13 +186,25 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType,
kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, kPointerSize);
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet32Instance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet16Instance, pSet16Static, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet16Static, pSet32Instance, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet32Instance, pSet32Static, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet32Static, pSet64Instance, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet64Instance, pSet64Static, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet64Static, pSetObjInstance, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjInstance, pSetObjStatic, kPointerSize);
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjStatic, pGet32Instance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSetObjStatic, pGetByteInstance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetByteInstance, pGetBooleanInstance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetBooleanInstance, pGetByteStatic, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetByteStatic, pGetBooleanStatic, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetBooleanStatic, pGetShortInstance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetShortInstance, pGetCharInstance, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetCharInstance, pGetShortStatic, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetShortStatic, pGetCharStatic, kPointerSize);
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetCharStatic, pGet32Instance, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Instance, pGet32Static, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet32Static, pGet64Instance, kPointerSize);
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, kPointerSize);
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 99633a3..6033a5f 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -45,7 +45,7 @@
my_klass_ = class_linker_->FindClass(soa.Self(), "LExceptionHandle;", class_loader);
ASSERT_TRUE(my_klass_ != NULL);
Handle<mirror::Class> klass(hs.NewHandle(my_klass_));
- class_linker_->EnsureInitialized(klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), klass, true, true);
my_klass_ = klass.Get();
dex_ = my_klass_->GetDexCache()->GetDexFile();
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 25f87c5..fede2f8 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -19,6 +19,7 @@
#include <setjmp.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
+#include "base/stl_util.h"
#include "mirror/art_method.h"
#include "mirror/class.h"
#include "sigchain.h"
@@ -115,13 +116,23 @@
initialized_ = true;
}
-void FaultManager::Shutdown() {
+void FaultManager::Release() {
if (initialized_) {
UnclaimSignalChain(SIGSEGV);
initialized_ = false;
}
}
+void FaultManager::Shutdown() {
+ if (initialized_) {
+ Release();
+
+ // Free all handlers.
+ STLDeleteElements(&generated_code_handlers_);
+ STLDeleteElements(&other_handlers_);
+ }
+}
+
void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
// BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...)
//
@@ -151,12 +162,73 @@
// We hit a signal we didn't handle. This might be something for which
// we can give more information about so call all registered handlers to see
// if it is.
- for (const auto& handler : other_handlers_) {
- if (handler->Action(sig, info, context)) {
- return;
+
+ Thread* self = Thread::Current();
+
+ // Now set up the nested signal handler.
+
+ // Release the fault manager so that it will remove the signal chain for
+ // SIGSEGV and we call the real sigaction.
+ fault_manager.Release();
+
+ // The action for SIGSEGV should be the default handler now.
+
+ // Unblock the signals we allow so that they can be delivered in the signal handler.
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGSEGV);
+ sigaddset(&sigset, SIGABRT);
+ pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
+
+ // If we get a signal in this code we want to invoke our nested signal
+ // handler.
+ struct sigaction action, oldsegvaction, oldabortaction;
+ action.sa_sigaction = art_nested_signal_handler;
+
+ // Explicitly mask out SIGSEGV and SIGABRT from the nested signal handler. This
+ // should be the default but we definitely don't want these happening in our
+ // nested signal handler.
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGSEGV);
+ sigaddset(&action.sa_mask, SIGABRT);
+
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+#if !defined(__APPLE__) && !defined(__mips__)
+ action.sa_restorer = nullptr;
+#endif
+
+ // Catch SIGSEGV and SIGABRT to invoke our nested handler
+ int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
+ int e2 = sigaction(SIGABRT, &action, &oldabortaction);
+ if (e1 != 0 || e2 != 0) {
+ LOG(ERROR) << "Unable to set up nested signal handler";
+ } else {
+ // Save the current state and call the handlers. If anything causes a signal
+ // our nested signal handler will be invoked and this will longjmp to the saved
+ // state.
+ if (setjmp(*self->GetNestedSignalState()) == 0) {
+ for (const auto& handler : other_handlers_) {
+ if (handler->Action(sig, info, context)) {
+ // Restore the signal handlers, reinit the fault manager and return. Signal was
+ // handled.
+ sigaction(SIGSEGV, &oldsegvaction, nullptr);
+ sigaction(SIGABRT, &oldabortaction, nullptr);
+ fault_manager.Init();
+ return;
+ }
+ }
+ } else {
+ LOG(ERROR) << "Nested signal detected - original signal being reported";
}
+
+ // Restore the signal handlers.
+ sigaction(SIGSEGV, &oldsegvaction, nullptr);
+ sigaction(SIGABRT, &oldabortaction, nullptr);
}
+ // Now put the fault manager back in place.
+ fault_manager.Init();
+
// Set a breakpoint in this function to catch unhandled signals.
art_sigsegv_fault();
@@ -165,6 +237,7 @@
}
void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) {
+ DCHECK(initialized_);
if (generated_code) {
generated_code_handlers_.push_back(handler);
} else {
@@ -311,75 +384,18 @@
uintptr_t sp = 0;
Thread* self = Thread::Current();
- // Shutdown the fault manager so that it will remove the signal chain for
- // SIGSEGV and we call the real sigaction.
- fault_manager.Shutdown();
-
- // The action for SIGSEGV should be the default handler now.
-
- // Unblock the signals we allow so that they can be delivered in the signal handler.
- sigset_t sigset;
- sigemptyset(&sigset);
- sigaddset(&sigset, SIGSEGV);
- sigaddset(&sigset, SIGABRT);
- pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
-
- // If we get a signal in this code we want to invoke our nested signal
- // handler.
- struct sigaction action, oldsegvaction, oldabortaction;
- action.sa_sigaction = art_nested_signal_handler;
-
- // Explictly mask out SIGSEGV and SIGABRT from the nested signal handler. This
- // should be the default but we definitely don't want these happening in our
- // nested signal handler.
- sigemptyset(&action.sa_mask);
- sigaddset(&action.sa_mask, SIGSEGV);
- sigaddset(&action.sa_mask, SIGABRT);
-
- action.sa_flags = SA_SIGINFO | SA_ONSTACK;
-#if !defined(__APPLE__) && !defined(__mips__)
- action.sa_restorer = nullptr;
-#endif
-
- // Catch SIGSEGV and SIGABRT to invoke our nested handler
- int e1 = sigaction(SIGSEGV, &action, &oldsegvaction);
- int e2 = sigaction(SIGABRT, &action, &oldabortaction);
- if (e1 != 0 || e2 != 0) {
- LOG(ERROR) << "Unable to register nested signal handler - no stack trace possible";
- // If sigaction failed we have a serious problem. We cannot catch
- // any failures in the stack tracer and it's likely to occur since
- // the program state is bad. Therefore we don't even try to give
- // a stack trace.
- } else {
- // Save the current state and try to dump the stack. If this causes a signal
- // our nested signal handler will be invoked and this will longjmp to the saved
- // state.
- if (setjmp(*self->GetNestedSignalState()) == 0) {
- manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
- // Inside of generated code, sp[0] is the method, so sp is the frame.
- StackReference<mirror::ArtMethod>* frame =
- reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
- self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
+ manager_->GetMethodAndReturnPcAndSp(siginfo, context, &method, &return_pc, &sp);
+ // Inside of generated code, sp[0] is the method, so sp is the frame.
+ StackReference<mirror::ArtMethod>* frame =
+ reinterpret_cast<StackReference<mirror::ArtMethod>*>(sp);
+ self->SetTopOfStack(frame, 0); // Since we don't necessarily have a dex pc, pass in 0.
#ifdef TEST_NESTED_SIGNAL
- // To test the nested signal handler we raise a signal here. This will cause the
- // nested signal handler to be called and perform a longjmp back to the setjmp
- // above.
- abort();
+ // To test the nested signal handler we raise a signal here. This will cause the
+ // nested signal handler to be called and perform a longjmp back to the setjmp
+ // above.
+ abort();
#endif
- self->DumpJavaStack(LOG(ERROR));
- } else {
- LOG(ERROR) << "Stack trace aborted due to nested signal - original signal being reported";
- }
-
- // Restore the signal handlers.
- sigaction(SIGSEGV, &oldsegvaction, nullptr);
- sigaction(SIGABRT, &oldabortaction, nullptr);
- }
-
- // Now put the fault manager back in place.
- fault_manager.Init();
-
- // And we're done.
+ self->DumpJavaStack(LOG(ERROR));
}
return false; // Return false since we want to propagate the fault to the main signal handler.
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index bb26780..8b66a6f 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -39,10 +39,17 @@
~FaultManager();
void Init();
+
+ // Unclaim signals.
+ void Release();
+
+ // Unclaim signals and delete registered handlers.
void Shutdown();
void HandleFault(int sig, siginfo_t* info, void* context);
void HandleNestedSignal(int sig, siginfo_t* info, void* context);
+
+ // Added handlers are owned by the fault handler and will be freed on Shutdown().
void AddHandler(FaultHandler* handler, bool generated_code);
void RemoveHandler(FaultHandler* handler);
diff --git a/runtime/field_helper.h b/runtime/field_helper.h
index 5eae55e..8097025 100644
--- a/runtime/field_helper.h
+++ b/runtime/field_helper.h
@@ -27,11 +27,6 @@
public:
explicit FieldHelper(Handle<mirror::ArtField> f) : field_(f) {}
- void ChangeField(mirror::ArtField* new_f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(new_f != nullptr);
- field_.Assign(new_f);
- }
-
mirror::ArtField* GetField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return field_.Get();
}
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index a88b2c9..433855a 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -33,14 +33,16 @@
class Object;
} // namespace mirror
+namespace gc {
+namespace accounting {
+
class CardTableTest : public CommonRuntimeTest {
public:
- std::unique_ptr<gc::accounting::CardTable> card_table_;
- static constexpr size_t kCardSize = gc::accounting::CardTable::kCardSize;
+ std::unique_ptr<CardTable> card_table_;
void CommonSetup() {
if (card_table_.get() == nullptr) {
- card_table_.reset(gc::accounting::CardTable::Create(heap_begin_, heap_size_));
+ card_table_.reset(CardTable::Create(heap_begin_, heap_size_));
EXPECT_TRUE(card_table_.get() != nullptr);
} else {
ClearCardTable();
@@ -58,15 +60,16 @@
byte* HeapLimit() const {
return HeapBegin() + heap_size_;
}
- byte PRandCard(const byte* addr) const {
- size_t offset = RoundDown(addr - heap_begin_, kCardSize);
+ // Return a pseudo random card for an address.
+ byte PseudoRandomCard(const byte* addr) const {
+ size_t offset = RoundDown(addr - heap_begin_, CardTable::kCardSize);
return 1 + offset % 254;
}
void FillRandom() {
- for (const byte* addr = HeapBegin(); addr != HeapLimit(); addr += kCardSize) {
+ for (const byte* addr = HeapBegin(); addr != HeapLimit(); addr += CardTable::kCardSize) {
EXPECT_TRUE(card_table_->AddrIsInCardTable(addr));
byte* card = card_table_->CardFromAddr(addr);
- *card = PRandCard(addr);
+ *card = PseudoRandomCard(addr);
}
}
@@ -79,15 +82,15 @@
CommonSetup();
for (const byte* addr = HeapBegin(); addr < HeapLimit(); addr += kObjectAlignment) {
auto obj = reinterpret_cast<const mirror::Object*>(addr);
- EXPECT_EQ(card_table_->GetCard(obj), gc::accounting::CardTable::kCardClean);
+ EXPECT_EQ(card_table_->GetCard(obj), CardTable::kCardClean);
EXPECT_TRUE(!card_table_->IsDirty(obj));
card_table_->MarkCard(addr);
EXPECT_TRUE(card_table_->IsDirty(obj));
- EXPECT_EQ(card_table_->GetCard(obj), gc::accounting::CardTable::kCardDirty);
+ EXPECT_EQ(card_table_->GetCard(obj), CardTable::kCardDirty);
byte* card_addr = card_table_->CardFromAddr(addr);
- EXPECT_EQ(*card_addr, gc::accounting::CardTable::kCardDirty);
- *card_addr = gc::accounting::CardTable::kCardClean;
- EXPECT_EQ(*card_addr, gc::accounting::CardTable::kCardClean);
+ EXPECT_EQ(*card_addr, CardTable::kCardDirty);
+ *card_addr = CardTable::kCardClean;
+ EXPECT_EQ(*card_addr, CardTable::kCardClean);
}
}
@@ -103,33 +106,36 @@
TEST_F(CardTableTest, TestModifyCardsAtomic) {
CommonSetup();
FillRandom();
- const size_t delta = std::min(static_cast<size_t>(HeapLimit() - HeapBegin()), 8U * kCardSize);
+ const size_t delta = std::min(static_cast<size_t>(HeapLimit() - HeapBegin()),
+ 8U * CardTable::kCardSize);
UpdateVisitor visitor;
size_t start_offset = 0;
- for (byte* cstart = HeapBegin(); cstart < HeapBegin() + delta; cstart += kCardSize) {
- start_offset = (start_offset + kObjectAlignment) % kCardSize;
+ for (byte* cstart = HeapBegin(); cstart < HeapBegin() + delta; cstart += CardTable::kCardSize) {
+ start_offset = (start_offset + kObjectAlignment) % CardTable::kCardSize;
size_t end_offset = 0;
- for (byte* cend = HeapLimit() - delta; cend < HeapLimit(); cend += kCardSize) {
+ for (byte* cend = HeapLimit() - delta; cend < HeapLimit(); cend += CardTable::kCardSize) {
// Don't always start at a card boundary.
byte* start = cstart + start_offset;
byte* end = cend - end_offset;
- end_offset = (end_offset + kObjectAlignment) % kCardSize;
+ end_offset = (end_offset + kObjectAlignment) % CardTable::kCardSize;
// Modify cards.
card_table_->ModifyCardsAtomic(start, end, visitor, visitor);
// Check adjacent cards not modified.
- for (byte* cur = start - kCardSize; cur >= HeapBegin(); cur -= kCardSize) {
- EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)), PRandCard(cur));
+ for (byte* cur = start - CardTable::kCardSize; cur >= HeapBegin();
+ cur -= CardTable::kCardSize) {
+ EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)),
+ PseudoRandomCard(cur));
}
- for (byte* cur = end + kCardSize; cur < HeapLimit(); cur += kCardSize) {
- EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)), PRandCard(cur));
+ for (byte* cur = end + CardTable::kCardSize; cur < HeapLimit();
+ cur += CardTable::kCardSize) {
+ EXPECT_EQ(card_table_->GetCard(reinterpret_cast<mirror::Object*>(cur)),
+ PseudoRandomCard(cur));
}
// Verify Range.
- for (byte* cur = start; cur < AlignUp(end, kCardSize); cur += kCardSize) {
+ for (byte* cur = start; cur < AlignUp(end, CardTable::kCardSize);
+ cur += CardTable::kCardSize) {
byte* card = card_table_->CardFromAddr(cur);
- byte value = PRandCard(cur);
- if (visitor(value) != *card) {
- LOG(ERROR) << reinterpret_cast<void*>(start) << " " << reinterpret_cast<void*>(cur) << " " << reinterpret_cast<void*>(end);
- }
+ byte value = PseudoRandomCard(cur);
EXPECT_EQ(visitor(value), *card);
// Restore for next iteration.
*card = value;
@@ -139,5 +145,6 @@
}
// TODO: Add test for CardTable::Scan.
-
+} // namespace accounting
+} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 2686af0..3acf80d 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -72,9 +72,11 @@
class ModUnionUpdateObjectReferencesVisitor {
public:
- ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg)
- : callback_(callback),
- arg_(arg) {
+ ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg,
+ space::ContinuousSpace* from_space,
+ bool* contains_reference_to_other_space)
+ : callback_(callback), arg_(arg), from_space_(from_space),
+ contains_reference_to_other_space_(contains_reference_to_other_space) {
}
// Extra parameters are required since we use this same visitor signature for checking objects.
@@ -82,7 +84,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Only add the reference if it is non null and fits our criteria.
mirror::HeapReference<Object>* obj_ptr = obj->GetFieldObjectReferenceAddr(offset);
- if (obj_ptr->AsMirrorPtr() != nullptr) {
+ mirror::Object* ref = obj_ptr->AsMirrorPtr();
+ if (ref != nullptr && !from_space_->HasAddress(ref)) {
+ *contains_reference_to_other_space_ = true;
callback_(obj_ptr, arg_);
}
}
@@ -90,24 +94,36 @@
private:
MarkHeapReferenceCallback* const callback_;
void* arg_;
+ // Space which we are scanning
+ space::ContinuousSpace* const from_space_;
+ // Set if we have any references to another space.
+ bool* const contains_reference_to_other_space_;
};
class ModUnionScanImageRootVisitor {
public:
- ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg)
- : callback_(callback), arg_(arg) {}
+ ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg,
+ space::ContinuousSpace* from_space,
+ bool* contains_reference_to_other_space)
+ : callback_(callback), arg_(arg), from_space_(from_space),
+ contains_reference_to_other_space_(contains_reference_to_other_space) {}
void operator()(Object* root) const
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(root != NULL);
- ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_);
+ ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_,
+ contains_reference_to_other_space_);
root->VisitReferences<kMovingClasses>(ref_visitor, VoidFunctor());
}
private:
MarkHeapReferenceCallback* const callback_;
void* const arg_;
+ // Space which we are scanning
+ space::ContinuousSpace* const from_space_;
+ // Set if we have any references to another space.
+ bool* const contains_reference_to_other_space_;
};
void ModUnionTableReferenceCache::ClearCards() {
@@ -313,12 +329,20 @@
void ModUnionTableCardCache::UpdateAndMarkReferences(MarkHeapReferenceCallback* callback,
void* arg) {
CardTable* card_table = heap_->GetCardTable();
- ModUnionScanImageRootVisitor scan_visitor(callback, arg);
ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
- for (const byte* card_addr : cleared_cards_) {
- uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
+ bool reference_to_other_space = false;
+ ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, &reference_to_other_space);
+ for (auto it = cleared_cards_.begin(), end = cleared_cards_.end(); it != end; ) {
+ uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(*it));
DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
+ reference_to_other_space = false;
bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
+ if (!reference_to_other_space) {
+ // No non null reference to another space, remove the card.
+ it = cleared_cards_.erase(it);
+ } else {
+ ++it;
+ }
}
}
@@ -333,6 +357,17 @@
os << "]";
}
+void ModUnionTableCardCache::SetCards() {
+ CardTable* card_table = heap_->GetCardTable();
+ for (byte* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
+ addr += CardTable::kCardSize) {
+ cleared_cards_.insert(card_table->CardFromAddr(addr));
+ }
+}
+
+void ModUnionTableReferenceCache::SetCards() {
+}
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index f9e8261..d0e11e0 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -66,6 +66,9 @@
// determining references to track.
virtual void ClearCards() = 0;
+ // Set all the cards.
+ virtual void SetCards() = 0;
+
// Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards
// before a call to update, for example, back-to-back sticky GCs. Also mark references to other
// spaces which are stored in the mod-union table.
@@ -121,6 +124,8 @@
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetCards() OVERRIDE;
+
protected:
// Cleared card array, used to update the mod-union table.
ModUnionTable::CardSet cleared_cards_;
@@ -150,6 +155,8 @@
void Dump(std::ostream& os);
+ void SetCards() OVERRIDE;
+
protected:
// Cleared card array, used to update the mod-union table.
CardSet cleared_cards_;
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index ad22a2e..a7e5e74 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -569,7 +569,7 @@
RosAlloc::Run* RosAlloc::RefillRun(Thread* self, size_t idx) {
// Get the lowest address non-full run from the binary tree.
- std::set<Run*>* const bt = &non_full_runs_[idx];
+ auto* const bt = &non_full_runs_[idx];
if (!bt->empty()) {
// If there's one, use it as the current run.
auto it = bt->begin();
@@ -767,7 +767,7 @@
}
// Free the slot in the run.
run->FreeSlot(ptr);
- std::set<Run*>* non_full_runs = &non_full_runs_[idx];
+ auto* non_full_runs = &non_full_runs_[idx];
if (run->IsAllFree()) {
// It has just become completely free. Free the pages of this run.
std::set<Run*>::iterator pos = non_full_runs->find(run);
@@ -793,9 +793,8 @@
// already in the non-full run set (i.e., it was full) insert it
// into the non-full run set.
if (run != current_runs_[idx]) {
- std::unordered_set<Run*, hash_run, eq_run>* full_runs =
- kIsDebugBuild ? &full_runs_[idx] : NULL;
- std::set<Run*>::iterator pos = non_full_runs->find(run);
+ auto* full_runs = kIsDebugBuild ? &full_runs_[idx] : NULL;
+ auto pos = non_full_runs->find(run);
if (pos == non_full_runs->end()) {
DCHECK(run_was_full);
DCHECK(full_runs->find(run) != full_runs->end());
@@ -1266,9 +1265,8 @@
}
// Check if the run should be moved to non_full_runs_ or
// free_page_runs_.
- std::set<Run*>* non_full_runs = &non_full_runs_[idx];
- std::unordered_set<Run*, hash_run, eq_run>* full_runs =
- kIsDebugBuild ? &full_runs_[idx] : NULL;
+ auto* non_full_runs = &non_full_runs_[idx];
+ auto* full_runs = kIsDebugBuild ? &full_runs_[idx] : NULL;
if (run->IsAllFree()) {
// It has just become completely free. Free the pages of the
// run.
@@ -2056,7 +2054,7 @@
// in a run set.
if (!is_current_run) {
MutexLock mu(self, rosalloc->lock_);
- std::set<Run*>& non_full_runs = rosalloc->non_full_runs_[idx];
+ auto& non_full_runs = rosalloc->non_full_runs_[idx];
// If it's all free, it must be a free page run rather than a run.
CHECK(!IsAllFree()) << "A free run must be in a free page run set " << Dump();
if (!IsFull()) {
@@ -2066,7 +2064,7 @@
} else {
// If it's full, it must in the full run set (debug build only.)
if (kIsDebugBuild) {
- std::unordered_set<Run*, hash_run, eq_run>& full_runs = rosalloc->full_runs_[idx];
+ auto& full_runs = rosalloc->full_runs_[idx];
CHECK(full_runs.find(this) != full_runs.end())
<< " A full run isn't in the full run set " << Dump();
}
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index b2a5a3c..2fbd97a 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -26,6 +26,7 @@
#include <unordered_set>
#include <vector>
+#include "base/allocator.h"
#include "base/mutex.h"
#include "base/logging.h"
#include "globals.h"
@@ -53,7 +54,7 @@
size_t pm_idx = rosalloc->ToPageMapIndex(fpr_base);
size_t byte_size = rosalloc->free_page_run_size_map_[pm_idx];
DCHECK_GE(byte_size, static_cast<size_t>(0));
- DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0));
+ DCHECK_ALIGNED(byte_size, kPageSize);
return byte_size;
}
void SetByteSize(RosAlloc* rosalloc, size_t byte_size)
@@ -403,6 +404,7 @@
// We use thread-local runs for the size Brackets whose indexes
// are less than this index. We use shared (current) runs for the rest.
+
static const size_t kNumThreadLocalSizeBrackets = 11;
private:
@@ -423,12 +425,13 @@
// The run sets that hold the runs whose slots are not all
// full. non_full_runs_[i] is guarded by size_bracket_locks_[i].
- std::set<Run*> non_full_runs_[kNumOfSizeBrackets];
+ AllocationTrackingSet<Run*, kAllocatorTagRosAlloc> non_full_runs_[kNumOfSizeBrackets];
// The run sets that hold the runs whose slots are all full. This is
// debug only. full_runs_[i] is guarded by size_bracket_locks_[i].
- std::unordered_set<Run*, hash_run, eq_run> full_runs_[kNumOfSizeBrackets];
+ std::unordered_set<Run*, hash_run, eq_run, TrackingAllocator<Run*, kAllocatorTagRosAlloc>>
+ full_runs_[kNumOfSizeBrackets];
// The set of free pages.
- std::set<FreePageRun*> free_page_runs_ GUARDED_BY(lock_);
+ AllocationTrackingSet<FreePageRun*, kAllocatorTagRosAlloc> free_page_runs_ GUARDED_BY(lock_);
// The dedicated full run, it is always full and shared by all threads when revoking happens.
// This is an optimization since enables us to avoid a null check for revoked runs.
static Run* dedicated_full_run_;
@@ -460,7 +463,8 @@
// The table that indicates the size of free page runs. These sizes
// are stored here to avoid storing in the free page header and
// release backing pages.
- std::vector<size_t> free_page_run_size_map_ GUARDED_BY(lock_);
+ std::vector<size_t, TrackingAllocator<size_t, kAllocatorTagRosAlloc>> free_page_run_size_map_
+ GUARDED_BY(lock_);
// The global lock. Used to guard the page map, the free page set,
// and the footprint.
Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 46d79bf..07b61e6 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -55,7 +55,8 @@
: heap_(heap),
name_(name),
pause_histogram_((name_ + " paused").c_str(), kPauseBucketSize, kPauseBucketCount),
- cumulative_timings_(name) {
+ cumulative_timings_(name),
+ pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true) {
ResetCumulativeStatistics();
}
@@ -65,10 +66,11 @@
void GarbageCollector::ResetCumulativeStatistics() {
cumulative_timings_.Reset();
- pause_histogram_.Reset();
total_time_ns_ = 0;
total_freed_objects_ = 0;
total_freed_bytes_ = 0;
+ MutexLock mu(Thread::Current(), pause_histogram_lock_);
+ pause_histogram_.Reset();
}
void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
@@ -95,6 +97,7 @@
}
total_time_ns_ += current_iteration->GetDurationNs();
for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
+ MutexLock mu(self, pause_histogram_lock_);
pause_histogram_.AddValue(pause_time / 1000);
}
ATRACE_END();
@@ -137,8 +140,11 @@
}
void GarbageCollector::ResetMeasurements() {
+ {
+ MutexLock mu(Thread::Current(), pause_histogram_lock_);
+ pause_histogram_.Reset();
+ }
cumulative_timings_.Reset();
- pause_histogram_.Reset();
total_time_ns_ = 0;
total_freed_objects_ = 0;
total_freed_bytes_ = 0;
@@ -171,6 +177,38 @@
heap_->RecordFree(freed.objects, freed.bytes);
}
+uint64_t GarbageCollector::GetTotalPausedTimeNs() {
+ MutexLock mu(Thread::Current(), pause_histogram_lock_);
+ return pause_histogram_.AdjustedSum();
+}
+
+void GarbageCollector::DumpPerformanceInfo(std::ostream& os) {
+ const CumulativeLogger& logger = GetCumulativeTimings();
+ const size_t iterations = logger.GetIterations();
+ if (iterations == 0) {
+ return;
+ }
+ os << ConstDumpable<CumulativeLogger>(logger);
+ const uint64_t total_ns = logger.GetTotalNs();
+ double seconds = NsToMs(logger.GetTotalNs()) / 1000.0;
+ const uint64_t freed_bytes = GetTotalFreedBytes();
+ const uint64_t freed_objects = GetTotalFreedObjects();
+ {
+ MutexLock mu(Thread::Current(), pause_histogram_lock_);
+ if (pause_histogram_.SampleSize() > 0) {
+ Histogram<uint64_t>::CumulativeData cumulative_data;
+ pause_histogram_.CreateHistogram(&cumulative_data);
+ pause_histogram_.PrintConfidenceIntervals(os, 0.99, cumulative_data);
+ }
+ }
+ os << GetName() << " total time: " << PrettyDuration(total_ns)
+ << " mean time: " << PrettyDuration(total_ns / iterations) << "\n"
+ << GetName() << " freed: " << freed_objects
+ << " objects with total size " << PrettySize(freed_bytes) << "\n"
+ << GetName() << " throughput: " << freed_objects / seconds << "/s / "
+ << PrettySize(freed_bytes / seconds) << "/s\n";
+}
+
} // namespace collector
} // namespace gc
} // namespace art
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index 885569e..b809469 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -119,18 +119,13 @@
GarbageCollector(Heap* heap, const std::string& name);
virtual ~GarbageCollector() { }
-
const char* GetName() const {
return name_.c_str();
}
-
virtual GcType GetGcType() const = 0;
-
virtual CollectorType GetCollectorType() const = 0;
-
// Run the garbage collector.
void Run(GcCause gc_cause, bool clear_soft_references);
-
Heap* GetHeap() const {
return heap_;
}
@@ -138,24 +133,17 @@
const CumulativeLogger& GetCumulativeTimings() const {
return cumulative_timings_;
}
-
void ResetCumulativeStatistics();
-
// Swap the live and mark bitmaps of spaces that are active for the collector. For partial GC,
// this is the allocation space, for full GC then we swap the zygote bitmaps too.
void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- uint64_t GetTotalPausedTimeNs() const {
- return pause_histogram_.AdjustedSum();
- }
+ uint64_t GetTotalPausedTimeNs() LOCKS_EXCLUDED(pause_histogram_lock_);
int64_t GetTotalFreedBytes() const {
return total_freed_bytes_;
}
uint64_t GetTotalFreedObjects() const {
return total_freed_objects_;
}
- const Histogram<uint64_t>& GetPauseHistogram() const {
- return pause_histogram_;
- }
// Reset the cumulative timings and pause histogram.
void ResetMeasurements();
// Returns the estimated throughput in bytes / second.
@@ -174,11 +162,11 @@
void RecordFree(const ObjectBytePair& freed);
// Record a free of large objects.
void RecordFreeLOS(const ObjectBytePair& freed);
+ void DumpPerformanceInfo(std::ostream& os) LOCKS_EXCLUDED(pause_histogram_lock_);
protected:
// Run all of the GC phases.
virtual void RunPhases() = 0;
-
// Revoke all the thread-local buffers.
virtual void RevokeAllThreadLocalBuffers() = 0;
@@ -188,11 +176,12 @@
Heap* const heap_;
std::string name_;
// Cumulative statistics.
- Histogram<uint64_t> pause_histogram_;
+ Histogram<uint64_t> pause_histogram_ GUARDED_BY(pause_histogram_lock_);
uint64_t total_time_ns_;
uint64_t total_freed_objects_;
int64_t total_freed_bytes_;
CumulativeLogger cumulative_timings_;
+ mutable Mutex pause_histogram_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
};
} // namespace collector
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index 4044852..b3bed64 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -547,8 +547,11 @@
}
void MarkCompact::SweepLargeObjects(bool swap_bitmaps) {
- TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
- RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+ space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+ if (los != nullptr) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());\
+ RecordFreeLOS(los->Sweep(swap_bitmaps));
+ }
}
// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 95530be..930499a 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -374,7 +374,8 @@
}
space::LargeObjectSpace* large_object_space = mark_sweep_->GetHeap()->GetLargeObjectsSpace();
if (UNLIKELY(obj == nullptr || !IsAligned<kPageSize>(obj) ||
- (kIsDebugBuild && !large_object_space->Contains(obj)))) {
+ (kIsDebugBuild && large_object_space != nullptr &&
+ !large_object_space->Contains(obj)))) {
LOG(ERROR) << "Tried to mark " << obj << " not contained by any spaces";
LOG(ERROR) << "Attempting see if it's a bad root";
mark_sweep_->VerifyRoots();
@@ -481,7 +482,7 @@
// See if the root is on any space bitmap.
if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
- if (!large_object_space->Contains(root)) {
+ if (large_object_space != nullptr && !large_object_space->Contains(root)) {
LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type;
if (visitor != NULL) {
LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg;
@@ -1074,20 +1075,22 @@
}
// Handle the large object space.
space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
- accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap();
- accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap();
- if (swap_bitmaps) {
- std::swap(large_live_objects, large_mark_objects);
- }
- for (size_t i = 0; i < count; ++i) {
- Object* obj = objects[i];
- // Handle large objects.
- if (kUseThreadLocalAllocationStack && obj == nullptr) {
- continue;
+ if (large_object_space != nullptr) {
+ accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap();
+ accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap();
+ if (swap_bitmaps) {
+ std::swap(large_live_objects, large_mark_objects);
}
- if (!large_mark_objects->Test(obj)) {
- ++freed_los.objects;
- freed_los.bytes += large_object_space->Free(self, obj);
+ for (size_t i = 0; i < count; ++i) {
+ Object* obj = objects[i];
+ // Handle large objects.
+ if (kUseThreadLocalAllocationStack && obj == nullptr) {
+ continue;
+ }
+ if (!large_mark_objects->Test(obj)) {
+ ++freed_los.objects;
+ freed_los.bytes += large_object_space->Free(self, obj);
+ }
}
}
{
@@ -1125,8 +1128,11 @@
}
void MarkSweep::SweepLargeObjects(bool swap_bitmaps) {
- TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
- RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+ space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+ if (los != nullptr) {
+ TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
+ RecordFreeLOS(los->Sweep(swap_bitmaps));
+ }
}
// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 8fb33ce..c8fa869 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -365,23 +365,23 @@
}
CHECK_EQ(is_large_object_space_immune_, collect_from_space_only_);
- if (is_large_object_space_immune_) {
+ space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace();
+ if (is_large_object_space_immune_ && los != nullptr) {
TimingLogger::ScopedTiming t("VisitLargeObjects", GetTimings());
DCHECK(collect_from_space_only_);
// Delay copying the live set to the marked set until here from
// BindBitmaps() as the large objects on the allocation stack may
// be newly added to the live set above in MarkAllocStackAsLive().
- GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked();
+ los->CopyLiveToMarked();
// When the large object space is immune, we need to scan the
// large object space as roots as they contain references to their
// classes (primitive array classes) that could move though they
// don't contain any other references.
- space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
- accounting::LargeObjectBitmap* large_live_bitmap = large_object_space->GetLiveBitmap();
+ accounting::LargeObjectBitmap* large_live_bitmap = los->GetLiveBitmap();
SemiSpaceScanObjectVisitor visitor(this);
- large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(large_object_space->Begin()),
- reinterpret_cast<uintptr_t>(large_object_space->End()),
+ large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()),
+ reinterpret_cast<uintptr_t>(los->End()),
visitor);
}
// Recursively process the mark stack.
@@ -655,8 +655,11 @@
void SemiSpace::SweepLargeObjects(bool swap_bitmaps) {
DCHECK(!is_large_object_space_immune_);
- TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
- RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps));
+ space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+ if (los != nullptr) {
+ TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings());
+ RecordFreeLOS(los->Sweep(swap_bitmaps));
+ }
}
// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been
@@ -751,6 +754,7 @@
from_space_ = nullptr;
CHECK(mark_stack_->IsEmpty());
mark_stack_->Reset();
+ space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace();
if (generational_) {
// Decide whether to do a whole heap collection or a bump pointer
// only space collection at the next collection by updating
@@ -762,7 +766,7 @@
bytes_promoted_since_last_whole_heap_collection_ += bytes_promoted_;
bool bytes_promoted_threshold_exceeded =
bytes_promoted_since_last_whole_heap_collection_ >= kBytesPromotedThreshold;
- uint64_t current_los_bytes_allocated = GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated();
+ uint64_t current_los_bytes_allocated = los != nullptr ? los->GetBytesAllocated() : 0U;
uint64_t last_los_bytes_allocated =
large_object_bytes_allocated_at_last_whole_heap_collection_;
bool large_object_bytes_threshold_exceeded =
@@ -775,7 +779,7 @@
// Reset the counters.
bytes_promoted_since_last_whole_heap_collection_ = bytes_promoted_;
large_object_bytes_allocated_at_last_whole_heap_collection_ =
- GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated();
+ los != nullptr ? los->GetBytesAllocated() : 0U;
collect_from_space_only_ = true;
}
}
diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc
index 5a58446..4ed6abc 100644
--- a/runtime/gc/collector/sticky_mark_sweep.cc
+++ b/runtime/gc/collector/sticky_mark_sweep.cc
@@ -16,7 +16,7 @@
#include "gc/heap.h"
#include "gc/space/large_object_space.h"
-#include "gc/space/space.h"
+#include "gc/space/space-inl.h"
#include "sticky_mark_sweep.h"
#include "thread-inl.h"
@@ -32,7 +32,6 @@
void StickyMarkSweep::BindBitmaps() {
PartialMarkSweep::BindBitmaps();
-
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
// For sticky GC, we want to bind the bitmaps of all spaces as the allocation stack lets us
// know what was allocated since the last GC. A side-effect of binding the allocation space mark
@@ -44,7 +43,10 @@
space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap();
}
}
- GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked();
+ for (const auto& space : GetHeap()->GetDiscontinuousSpaces()) {
+ CHECK(space->IsLargeObjectSpace());
+ space->AsLargeObjectSpace()->CopyLiveToMarked();
+ }
}
void StickyMarkSweep::MarkReachableObjects() {
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index d1fb600..3101c68 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -140,7 +140,7 @@
}
if (kInstrumented) {
if (Dbg::IsAllocTrackingEnabled()) {
- Dbg::RecordAllocation(klass, bytes_allocated);
+ Dbg::RecordAllocation(self, klass, bytes_allocated);
}
} else {
DCHECK(!Dbg::IsAllocTrackingEnabled());
@@ -277,7 +277,7 @@
heap_->total_allocation_time_.FetchAndAddSequentiallyConsistent(allocation_end_time - allocation_start_time_);
}
}
-};
+}
inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const {
// We need to have a zygote space or else our newly allocated large object can end up in the
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 6af98cf..d672510 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -83,13 +83,6 @@
// relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator
// threads (lower pauses, use less memory bandwidth).
static constexpr double kStickyGcThroughputAdjustment = 1.0;
-// Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
-// since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
-#if USE_ART_LOW_4G_ALLOCATOR
-static constexpr bool kUseFreeListSpaceForLOS = true;
-#else
-static constexpr bool kUseFreeListSpaceForLOS = false;
-#endif
// Whether or not we compact the zygote in PreZygoteFork.
static constexpr bool kCompactZygote = kMovingCollector;
// How many reserve entries are at the end of the allocation stack, these are only needed if the
@@ -107,8 +100,9 @@
double target_utilization, double foreground_heap_growth_multiplier,
size_t capacity, size_t non_moving_space_capacity, const std::string& image_file_name,
const InstructionSet image_instruction_set, CollectorType foreground_collector_type,
- CollectorType background_collector_type, size_t parallel_gc_threads,
- size_t conc_gc_threads, bool low_memory_mode,
+ CollectorType background_collector_type,
+ space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold,
+ size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
size_t long_pause_log_threshold, size_t long_gc_log_threshold,
bool ignore_max_footprint, bool use_tlab,
bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap,
@@ -134,8 +128,8 @@
long_gc_log_threshold_(long_gc_log_threshold),
ignore_max_footprint_(ignore_max_footprint),
zygote_creation_lock_("zygote creation lock", kZygoteCreationLock),
- have_zygote_space_(false),
- large_object_threshold_(std::numeric_limits<size_t>::max()), // Starts out disabled.
+ zygote_space_(nullptr),
+ large_object_threshold_(large_object_threshold),
collector_type_running_(kCollectorTypeNone),
last_gc_type_(collector::kGcTypeNone),
next_gc_type_(collector::kGcTypePartial),
@@ -195,7 +189,6 @@
// entrypoints.
const bool is_zygote = Runtime::Current()->IsZygote();
if (!is_zygote) {
- large_object_threshold_ = kDefaultLargeObjectThreshold;
// Background compaction is currently not supported for command line runs.
if (background_collector_type_ != foreground_collector_type_) {
VLOG(heap) << "Disabling background compaction for non zygote";
@@ -339,13 +332,21 @@
CHECK(non_moving_space_ != nullptr);
CHECK(!non_moving_space_->CanMoveObjects());
// Allocate the large object space.
- if (kUseFreeListSpaceForLOS) {
- large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_);
+ if (large_object_space_type == space::kLargeObjectSpaceTypeFreeList) {
+ large_object_space_ = space::FreeListSpace::Create("free list large object space", nullptr,
+ capacity_);
+ CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
+ } else if (large_object_space_type == space::kLargeObjectSpaceTypeMap) {
+ large_object_space_ = space::LargeObjectMapSpace::Create("mem map large object space");
+ CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
} else {
- large_object_space_ = space::LargeObjectMapSpace::Create("large object space");
+ // Disable the large object space by making the cutoff excessively large.
+ large_object_threshold_ = std::numeric_limits<size_t>::max();
+ large_object_space_ = nullptr;
}
- CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
- AddSpace(large_object_space_);
+ if (large_object_space_ != nullptr) {
+ AddSpace(large_object_space_);
+ }
// Compute heap capacity. Continuous spaces are sorted in order of Begin().
CHECK(!continuous_spaces_.empty());
// Relies on the spaces being sorted.
@@ -412,9 +413,11 @@
mark_compact_collector_ = new collector::MarkCompact(this);
garbage_collectors_.push_back(mark_compact_collector_);
}
- if (GetImageSpace() != nullptr && non_moving_space_ != nullptr) {
+ if (GetImageSpace() != nullptr && non_moving_space_ != nullptr &&
+ (is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) {
// Check that there's no gap between the image space and the non moving space so that the
- // immune region won't break (eg. due to a large object allocated in the gap).
+ // immune region won't break (eg. due to a large object allocated in the gap). This is only
+ // required when we're the zygote or using GSS.
bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),
non_moving_space_->GetMemMap());
if (!no_gap) {
@@ -482,7 +485,7 @@
// After the zygote we want this to be false if we don't have background compaction enabled so
// that getting primitive array elements is faster.
// We never have homogeneous compaction with GSS and don't need a space with movable objects.
- can_move_objects = !have_zygote_space_ && foreground_collector_type_ != kCollectorTypeGSS;
+ can_move_objects = !HasZygoteSpace() && foreground_collector_type_ != kCollectorTypeGSS;
}
if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) {
RemoveRememberedSet(main_space_);
@@ -682,9 +685,8 @@
}
void Heap::VisitObjects(ObjectCallback callback, void* arg) {
- Thread* self = Thread::Current();
// GCs can move objects, so don't allow this.
- const char* old_cause = self->StartAssertNoThreadSuspension("Visiting objects");
+ ScopedAssertNoThreadSuspension ants(Thread::Current(), "Visiting objects");
if (bump_pointer_space_ != nullptr) {
// Visit objects in bump pointer space.
bump_pointer_space_->Walk(callback, arg);
@@ -701,7 +703,6 @@
}
}
GetLiveBitmap()->Walk(callback, arg);
- self->EndAssertNoThreadSuspension(old_cause);
}
void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -711,7 +712,8 @@
CHECK(space1 != nullptr);
CHECK(space2 != nullptr);
MarkAllocStack(space1->GetLiveBitmap(), space2->GetLiveBitmap(),
- large_object_space_->GetLiveBitmap(), stack);
+ (large_object_space_ != nullptr ? large_object_space_->GetLiveBitmap() : nullptr),
+ stack);
}
void Heap::DeleteThreadPool() {
@@ -800,28 +802,9 @@
// Dump cumulative loggers for each GC type.
uint64_t total_paused_time = 0;
for (auto& collector : garbage_collectors_) {
- const CumulativeLogger& logger = collector->GetCumulativeTimings();
- const size_t iterations = logger.GetIterations();
- const Histogram<uint64_t>& pause_histogram = collector->GetPauseHistogram();
- if (iterations != 0 && pause_histogram.SampleSize() != 0) {
- os << ConstDumpable<CumulativeLogger>(logger);
- const uint64_t total_ns = logger.GetTotalNs();
- const uint64_t total_pause_ns = collector->GetTotalPausedTimeNs();
- double seconds = NsToMs(logger.GetTotalNs()) / 1000.0;
- const uint64_t freed_bytes = collector->GetTotalFreedBytes();
- const uint64_t freed_objects = collector->GetTotalFreedObjects();
- Histogram<uint64_t>::CumulativeData cumulative_data;
- pause_histogram.CreateHistogram(&cumulative_data);
- pause_histogram.PrintConfidenceIntervals(os, 0.99, cumulative_data);
- os << collector->GetName() << " total time: " << PrettyDuration(total_ns)
- << " mean time: " << PrettyDuration(total_ns / iterations) << "\n"
- << collector->GetName() << " freed: " << freed_objects
- << " objects with total size " << PrettySize(freed_bytes) << "\n"
- << collector->GetName() << " throughput: " << freed_objects / seconds << "/s / "
- << PrettySize(freed_bytes / seconds) << "/s\n";
- total_duration += total_ns;
- total_paused_time += total_pause_ns;
- }
+ total_duration += collector->GetCumulativeTimings().GetTotalNs();
+ total_paused_time += collector->GetTotalPausedTimeNs();
+ collector->DumpPerformanceInfo(os);
collector->ResetMeasurements();
}
uint64_t allocation_time =
@@ -848,6 +831,9 @@
os << "Mean allocation time: " << PrettyDuration(allocation_time / total_objects_allocated)
<< "\n";
}
+ if (HasZygoteSpace()) {
+ os << "Zygote space size " << PrettySize(zygote_space_->Size()) << "\n";
+ }
os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
BaseMutex::DumpAll(os);
@@ -1017,7 +1003,10 @@
total_alloc_space_size += malloc_space->Size();
}
}
- total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated();
+ total_alloc_space_allocated = GetBytesAllocated();
+ if (large_object_space_ != nullptr) {
+ total_alloc_space_allocated -= large_object_space_->GetBytesAllocated();
+ }
if (bump_pointer_space_ != nullptr) {
total_alloc_space_allocated -= bump_pointer_space_->Size();
}
@@ -1438,12 +1427,10 @@
void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from,
uint64_t* counts) {
// Can't do any GC in this function since this may move classes.
- Thread* self = Thread::Current();
- auto* old_cause = self->StartAssertNoThreadSuspension("CountInstances");
+ ScopedAssertNoThreadSuspension ants(Thread::Current(), "CountInstances");
InstanceCounter counter(classes, use_is_assignable_from, counts);
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
VisitObjects(InstanceCounter::Callback, &counter);
- self->EndAssertNoThreadSuspension(old_cause);
}
class InstanceCollector {
@@ -1456,8 +1443,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
DCHECK(arg != nullptr);
InstanceCollector* instance_collector = reinterpret_cast<InstanceCollector*>(arg);
- mirror::Class* instance_class = obj->GetClass();
- if (instance_class == instance_collector->class_) {
+ if (obj->GetClass() == instance_collector->class_) {
if (instance_collector->max_count_ == 0 ||
instance_collector->instances_.size() < instance_collector->max_count_) {
instance_collector->instances_.push_back(obj);
@@ -1466,8 +1452,8 @@
}
private:
- mirror::Class* class_;
- uint32_t max_count_;
+ const mirror::Class* const class_;
+ const uint32_t max_count_;
std::vector<mirror::Object*>& instances_;
DISALLOW_COPY_AND_ASSIGN(InstanceCollector);
};
@@ -1475,12 +1461,10 @@
void Heap::GetInstances(mirror::Class* c, int32_t max_count,
std::vector<mirror::Object*>& instances) {
// Can't do any GC in this function since this may move classes.
- Thread* self = Thread::Current();
- auto* old_cause = self->StartAssertNoThreadSuspension("GetInstances");
+ ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetInstances");
InstanceCollector collector(c, max_count, instances);
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
VisitObjects(&InstanceCollector::Callback, &collector);
- self->EndAssertNoThreadSuspension(old_cause);
}
class ReferringObjectsFinder {
@@ -1513,8 +1497,8 @@
}
private:
- mirror::Object* object_;
- uint32_t max_count_;
+ const mirror::Object* const object_;
+ const uint32_t max_count_;
std::vector<mirror::Object*>& referring_objects_;
DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder);
};
@@ -1522,12 +1506,10 @@
void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count,
std::vector<mirror::Object*>& referring_objects) {
// Can't do any GC in this function since this may move the object o.
- Thread* self = Thread::Current();
- auto* old_cause = self->StartAssertNoThreadSuspension("GetReferringObjects");
+ ScopedAssertNoThreadSuspension ants(Thread::Current(), "GetReferringObjects");
ReferringObjectsFinder finder(o, max_count, referring_objects);
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ ReaderMutexLock mu(ants.Self(), *Locks::heap_bitmap_lock_);
VisitObjects(&ReferringObjectsFinder::Callback, &finder);
- self->EndAssertNoThreadSuspension(old_cause);
}
void Heap::CollectGarbage(bool clear_soft_references) {
@@ -1905,7 +1887,8 @@
Thread* self = Thread::Current();
MutexLock mu(self, zygote_creation_lock_);
// Try to see if we have any Zygote spaces.
- if (have_zygote_space_) {
+ if (HasZygoteSpace()) {
+ LOG(WARNING) << __FUNCTION__ << " called when we already have a zygote space.";
return;
}
VLOG(heap) << "Starting PreZygoteFork";
@@ -1980,26 +1963,26 @@
// from this point on.
RemoveRememberedSet(old_alloc_space);
}
- space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space",
- low_memory_mode_,
- &non_moving_space_);
+ zygote_space_ = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_,
+ &non_moving_space_);
CHECK(!non_moving_space_->CanMoveObjects());
if (same_space) {
main_space_ = non_moving_space_;
SetSpaceAsDefault(main_space_);
}
delete old_alloc_space;
- CHECK(zygote_space != nullptr) << "Failed creating zygote space";
- AddSpace(zygote_space);
+ CHECK(HasZygoteSpace()) << "Failed creating zygote space";
+ AddSpace(zygote_space_);
non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
AddSpace(non_moving_space_);
- have_zygote_space_ = true;
- // Enable large object space allocations.
- large_object_threshold_ = kDefaultLargeObjectThreshold;
// Create the zygote space mod union table.
accounting::ModUnionTable* mod_union_table =
- new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space);
+ new accounting::ModUnionTableCardCache("zygote space mod-union table", this,
+ zygote_space_);
CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
+ // Set all the cards in the mod-union table since we don't know which objects contain references
+ // to large objects.
+ mod_union_table->SetCards();
AddModUnionTable(mod_union_table);
if (collector::SemiSpace::kUseRememberedSet) {
// Add a new remembered set for the post-zygote non-moving space.
@@ -2032,6 +2015,7 @@
} else if (bitmap2->HasAddress(obj)) {
bitmap2->Set(obj);
} else {
+ DCHECK(large_objects != nullptr);
large_objects->Set(obj);
}
}
@@ -2069,7 +2053,7 @@
// If the heap can't run the GC, silently fail and return that no GC was run.
switch (gc_type) {
case collector::kGcTypePartial: {
- if (!have_zygote_space_) {
+ if (!HasZygoteSpace()) {
return collector::kGcTypeNone;
}
break;
@@ -2601,6 +2585,17 @@
}
}
+void Heap::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+ if (kIsDebugBuild) {
+ if (rosalloc_space_ != nullptr) {
+ rosalloc_space_->AssertThreadLocalBuffersAreRevoked(thread);
+ }
+ if (bump_pointer_space_ != nullptr) {
+ bump_pointer_space_->AssertThreadLocalBuffersAreRevoked(thread);
+ }
+ }
+}
+
void Heap::AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked() {
if (kIsDebugBuild) {
if (bump_pointer_space_ != nullptr) {
@@ -2882,7 +2877,7 @@
next_gc_type_ = collector::kGcTypeSticky;
} else {
collector::GcType non_sticky_gc_type =
- have_zygote_space_ ? collector::kGcTypePartial : collector::kGcTypeFull;
+ HasZygoteSpace() ? collector::kGcTypePartial : collector::kGcTypeFull;
// Find what the next non sticky collector will be.
collector::GarbageCollector* non_sticky_collector = FindCollectorByGcType(non_sticky_gc_type);
// If the throughput of the current sticky GC >= throughput of the non sticky collector, then
@@ -3095,8 +3090,6 @@
}
env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
WellKnownClasses::java_lang_System_runFinalization);
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
- WellKnownClasses::java_lang_System_runFinalization);
}
void Heap::RegisterNativeAllocation(JNIEnv* env, size_t bytes) {
@@ -3110,7 +3103,7 @@
size_t new_native_bytes_allocated = native_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes);
new_native_bytes_allocated += bytes;
if (new_native_bytes_allocated > native_footprint_gc_watermark_) {
- collector::GcType gc_type = have_zygote_space_ ? collector::kGcTypePartial :
+ collector::GcType gc_type = HasZygoteSpace() ? collector::kGcTypePartial :
collector::kGcTypeFull;
// The second watermark is higher than the gc watermark. If you hit this it means you are
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cf297bd..faaea40 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -30,6 +30,7 @@
#include "gc/collector/garbage_collector.h"
#include "gc/collector/gc_type.h"
#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
#include "globals.h"
#include "gtest/gtest.h"
#include "instruction_set.h"
@@ -79,6 +80,7 @@
namespace space {
class AllocSpace;
class BumpPointerSpace;
+ class ContinuousMemMapAllocSpace;
class DiscontinuousSpace;
class DlMallocSpace;
class ImageSpace;
@@ -87,7 +89,7 @@
class RosAllocSpace;
class Space;
class SpaceTest;
- class ContinuousMemMapAllocSpace;
+ class ZygoteSpace;
} // namespace space
class AgeCardVisitor {
@@ -128,8 +130,6 @@
public:
// If true, measure the total allocation time.
static constexpr bool kMeasureAllocationTime = false;
- // Primitive arrays larger than this size are put in the large object space.
- static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
static constexpr size_t kDefaultStartingSize = kPageSize;
static constexpr size_t kDefaultInitialSize = 2 * MB;
static constexpr size_t kDefaultMaximumSize = 256 * MB;
@@ -141,7 +141,17 @@
static constexpr size_t kDefaultTLABSize = 256 * KB;
static constexpr double kDefaultTargetUtilization = 0.5;
static constexpr double kDefaultHeapGrowthMultiplier = 2.0;
-
+ // Primitive arrays larger than this size are put in the large object space.
+ static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize;
+ // Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
+ // since this means that we have to use the slow msync loop in MemMap::MapAnonymous.
+#if USE_ART_LOW_4G_ALLOCATOR
+ static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
+ space::kLargeObjectSpaceTypeFreeList;
+#else
+ static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
+ space::kLargeObjectSpaceTypeMap;
+#endif
// Used so that we don't overflow the allocation time atomic integer.
static constexpr size_t kTimeAdjust = 1024;
@@ -160,6 +170,7 @@
const std::string& original_image_file_name,
InstructionSet image_instruction_set,
CollectorType foreground_collector_type, CollectorType background_collector_type,
+ space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold,
size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
size_t long_pause_threshold, size_t long_gc_threshold,
bool ignore_max_footprint, bool use_tlab,
@@ -458,7 +469,7 @@
bool fail_ok) const;
space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const;
- void DumpForSigQuit(std::ostream& os) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DumpForSigQuit(std::ostream& os);
// Do a pending heap transition or trim.
void DoPendingTransitionOrTrim() LOCKS_EXCLUDED(heap_trim_request_lock_);
@@ -469,6 +480,7 @@
void RevokeThreadLocalBuffers(Thread* thread);
void RevokeRosAllocThreadLocalBuffers(Thread* thread);
void RevokeAllThreadLocalBuffers();
+ void AssertThreadLocalBuffersAreRevoked(Thread* thread);
void AssertAllBumpPointerSpaceThreadLocalBuffersAreRevoked();
void RosAllocVerification(TimingLogger* timings, const char* name)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -599,6 +611,10 @@
return &reference_processor_;
}
+ bool HasZygoteSpace() const {
+ return zygote_space_ != nullptr;
+ }
+
private:
// Compact source space to target space.
void Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -676,7 +692,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template <bool kGrow>
- bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
+ ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
// Returns true if the address passed in is within the address range of a continuous space.
bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const
@@ -851,8 +867,9 @@
// Lock which guards zygote space creation.
Mutex zygote_creation_lock_;
- // If we have a zygote space.
- bool have_zygote_space_;
+ // Non-null iff we have a zygote space. Doesn't contain the large objects allocated before
+ // zygote space creation.
+ space::ZygoteSpace* zygote_space_;
// Minimum allocation size of large object.
size_t large_object_threshold_;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 2a43712..dad5855 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -120,7 +120,7 @@
mirror::Object* obj = reinterpret_cast<mirror::Object*>(mem_map->Begin());
large_objects_.push_back(obj);
mem_maps_.Put(obj, mem_map);
- size_t allocation_size = mem_map->Size();
+ const size_t allocation_size = mem_map->BaseSize();
DCHECK(bytes_allocated != nullptr);
begin_ = std::min(begin_, reinterpret_cast<byte*>(obj));
byte* obj_end = reinterpret_cast<byte*>(obj) + allocation_size;
@@ -145,8 +145,9 @@
Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR));
LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live";
}
- DCHECK_GE(num_bytes_allocated_, found->second->Size());
- size_t allocation_size = found->second->Size();
+ const size_t map_size = found->second->BaseSize();
+ DCHECK_GE(num_bytes_allocated_, map_size);
+ size_t allocation_size = map_size;
num_bytes_allocated_ -= allocation_size;
--num_objects_allocated_;
delete found->second;
@@ -158,7 +159,7 @@
MutexLock mu(Thread::Current(), lock_);
auto found = mem_maps_.find(obj);
CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live";
- return found->second->Size();
+ return found->second->BaseSize();
}
size_t LargeObjectSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 9d5e090..a63c5c0 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -31,6 +31,12 @@
class AllocationInfo;
+enum LargeObjectSpaceType {
+ kLargeObjectSpaceTypeDisabled,
+ kLargeObjectSpaceTypeMap,
+ kLargeObjectSpaceTypeFreeList,
+};
+
// Abstraction implemented by all large object spaces.
class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace {
public:
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 92c6f53..3f39c77 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -338,6 +338,12 @@
rosalloc_->RevokeAllThreadLocalRuns();
}
+void RosAllocSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
+ if (kIsDebugBuild) {
+ rosalloc_->AssertThreadLocalRunsAreRevoked(thread);
+ }
+}
+
void RosAllocSpace::AssertAllThreadLocalBuffersAreRevoked() {
if (kIsDebugBuild) {
rosalloc_->AssertAllThreadLocalRunsAreRevoked();
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index f505305..f1ce115 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -101,6 +101,7 @@
void RevokeThreadLocalBuffers(Thread* thread);
void RevokeAllThreadLocalBuffers();
+ void AssertThreadLocalBuffersAreRevoked(Thread* thread);
void AssertAllThreadLocalBuffersAreRevoked();
// Returns the class of a recently freed object.
diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h
index 0291155..7211bb4 100644
--- a/runtime/gc/space/space_test.h
+++ b/runtime/gc/space/space_test.h
@@ -181,7 +181,7 @@
// Succeeds, fits without adjusting the footprint limit.
size_t ptr1_bytes_allocated, ptr1_usable_size;
StackHandleScope<3> hs(soa.Self());
- Handle<mirror::Object> ptr1(
+ MutableHandle<mirror::Object> ptr1(
hs.NewHandle(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size)));
EXPECT_TRUE(ptr1.Get() != nullptr);
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
@@ -194,7 +194,7 @@
// Succeeds, adjusts the footprint.
size_t ptr3_bytes_allocated, ptr3_usable_size;
- Handle<mirror::Object> ptr3(
+ MutableHandle<mirror::Object> ptr3(
hs.NewHandle(AllocWithGrowth(space, self, 8 * MB, &ptr3_bytes_allocated, &ptr3_usable_size)));
EXPECT_TRUE(ptr3.Get() != nullptr);
EXPECT_LE(8U * MB, ptr3_bytes_allocated);
@@ -284,7 +284,7 @@
// Succeeds, fits without adjusting the footprint limit.
size_t ptr1_bytes_allocated, ptr1_usable_size;
StackHandleScope<3> hs(soa.Self());
- Handle<mirror::Object> ptr1(
+ MutableHandle<mirror::Object> ptr1(
hs.NewHandle(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size)));
EXPECT_TRUE(ptr1.Get() != nullptr);
EXPECT_LE(1U * MB, ptr1_bytes_allocated);
@@ -297,7 +297,7 @@
// Succeeds, adjusts the footprint.
size_t ptr3_bytes_allocated, ptr3_usable_size;
- Handle<mirror::Object> ptr3(
+ MutableHandle<mirror::Object> ptr3(
hs.NewHandle(AllocWithGrowth(space, self, 8 * MB, &ptr3_bytes_allocated, &ptr3_usable_size)));
EXPECT_TRUE(ptr3.Get() != nullptr);
EXPECT_LE(8U * MB, ptr3_bytes_allocated);
diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h
index 2661e54..482f7bc 100644
--- a/runtime/gc_root-inl.h
+++ b/runtime/gc_root-inl.h
@@ -29,10 +29,5 @@
return ReadBarrier::BarrierForRoot<MirrorType, kReadBarrierOption>(&root_);
}
-template<class MirrorType>
-inline void GcRoot<MirrorType>::Assign(MirrorType* value) {
- root_ = value;
-}
-
} // namespace art
#endif // ART_RUNTIME_GC_ROOT_INL_H_
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index 3928f5d..b10a55c 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -28,7 +28,6 @@
public:
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE MirrorType* Read() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ALWAYS_INLINE void Assign(MirrorType* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) {
callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
diff --git a/runtime/handle.h b/runtime/handle.h
index f70faf4..addb663 100644
--- a/runtime/handle.h
+++ b/runtime/handle.h
@@ -30,23 +30,23 @@
// Handles are memory locations that contain GC roots. As the mirror::Object*s within a handle are
// GC visible then the GC may move the references within them, something that couldn't be done with
-// a wrap pointer. Handles are generally allocated within HandleScopes. ConstHandle is a super-class
-// of Handle and doesn't support assignment operations.
+// a wrap pointer. Handles are generally allocated within HandleScopes. Handle is a super-class
+// of MutableHandle and doesn't support assignment operations.
template<class T>
-class ConstHandle {
+class Handle {
public:
- ConstHandle() : reference_(nullptr) {
+ Handle() : reference_(nullptr) {
}
- ALWAYS_INLINE ConstHandle(const ConstHandle<T>& handle) : reference_(handle.reference_) {
+ ALWAYS_INLINE Handle(const Handle<T>& handle) : reference_(handle.reference_) {
}
- ALWAYS_INLINE ConstHandle<T>& operator=(const ConstHandle<T>& handle) {
+ ALWAYS_INLINE Handle<T>& operator=(const Handle<T>& handle) {
reference_ = handle.reference_;
return *this;
}
- ALWAYS_INLINE explicit ConstHandle(StackReference<T>* reference) : reference_(reference) {
+ ALWAYS_INLINE explicit Handle(StackReference<T>* reference) : reference_(reference) {
}
ALWAYS_INLINE T& operator*() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -73,11 +73,11 @@
StackReference<T>* reference_;
template<typename S>
- explicit ConstHandle(StackReference<S>* reference)
+ explicit Handle(StackReference<S>* reference)
: reference_(reinterpret_cast<StackReference<T>*>(reference)) {
}
template<typename S>
- explicit ConstHandle(const ConstHandle<S>& handle)
+ explicit Handle(const Handle<S>& handle)
: reference_(reinterpret_cast<StackReference<T>*>(handle.reference_)) {
}
@@ -91,7 +91,7 @@
private:
friend class BuildGenericJniFrameVisitor;
- template<class S> friend class ConstHandle;
+ template<class S> friend class Handle;
friend class HandleScope;
template<class S> friend class HandleWrapper;
template<size_t kNumReferences> friend class StackHandleScope;
@@ -99,42 +99,43 @@
// Handles that support assignment.
template<class T>
-class Handle : public ConstHandle<T> {
+class MutableHandle : public Handle<T> {
public:
- Handle() {
+ MutableHandle() {
}
- ALWAYS_INLINE Handle(const Handle<T>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstHandle<T>(handle.reference_) {
+ ALWAYS_INLINE MutableHandle(const MutableHandle<T>& handle)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Handle<T>(handle.reference_) {
}
- ALWAYS_INLINE Handle<T>& operator=(const Handle<T>& handle)
+ ALWAYS_INLINE MutableHandle<T>& operator=(const MutableHandle<T>& handle)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ConstHandle<T>::operator=(handle);
+ Handle<T>::operator=(handle);
return *this;
}
- ALWAYS_INLINE explicit Handle(StackReference<T>* reference)
+ ALWAYS_INLINE explicit MutableHandle(StackReference<T>* reference)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstHandle<T>(reference) {
+ : Handle<T>(reference) {
}
ALWAYS_INLINE T* Assign(T* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- StackReference<T>* ref = ConstHandle<T>::GetReference();
+ StackReference<T>* ref = Handle<T>::GetReference();
T* const old = ref->AsMirrorPtr();
ref->Assign(reference);
return old;
}
template<typename S>
- explicit Handle(const Handle<S>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstHandle<T>(handle) {
+ explicit MutableHandle(const MutableHandle<S>& handle) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Handle<T>(handle) {
}
protected:
template<typename S>
- explicit Handle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstHandle<T>(reference) {
+ explicit MutableHandle(StackReference<S>* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Handle<T>(reference) {
}
private:
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index 42ef779..99059f9 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -89,6 +89,12 @@
return Handle<mirror::Object>(&references_[i]);
}
+ MutableHandle<mirror::Object> GetMutableHandle(size_t i)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE {
+ DCHECK_LT(i, number_of_references_);
+ return MutableHandle<mirror::Object>(&references_[i]);
+ }
+
void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
ALWAYS_INLINE {
DCHECK_LT(i, number_of_references_);
@@ -139,14 +145,14 @@
// A wrapper which wraps around Object** and restores the pointer in the destructor.
// TODO: Add more functionality.
template<class T>
-class HandleWrapper : public Handle<T> {
+class HandleWrapper : public MutableHandle<T> {
public:
- HandleWrapper(T** obj, const Handle<T>& handle)
- : Handle<T>(handle), obj_(obj) {
+ HandleWrapper(T** obj, const MutableHandle<T>& handle)
+ : MutableHandle<T>(handle), obj_(obj) {
}
~HandleWrapper() {
- *obj_ = Handle<T>::Get();
+ *obj_ = MutableHandle<T>::Get();
}
private:
@@ -169,10 +175,10 @@
return references_storage_[i].AsMirrorPtr();
}
- Handle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ MutableHandle<mirror::Object> GetHandle(size_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
ALWAYS_INLINE {
DCHECK_LT(i, number_of_references_);
- return Handle<mirror::Object>(&references_storage_[i]);
+ return MutableHandle<mirror::Object>(&references_storage_[i]);
}
void SetReference(size_t i, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
@@ -182,9 +188,9 @@
}
template<class T>
- Handle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ MutableHandle<T> NewHandle(T* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
SetReference(pos_, object);
- Handle<T> h(GetHandle(pos_));
+ MutableHandle<T> h(GetHandle(pos_));
pos_++;
return h;
}
@@ -192,7 +198,7 @@
template<class T>
HandleWrapper<T> NewHandleWrapper(T** object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
SetReference(pos_, *object);
- Handle<T> h(GetHandle(pos_));
+ MutableHandle<T> h(GetHandle(pos_));
pos_++;
return HandleWrapper<T>(object, h);
}
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index c826716..7e770f6 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -19,6 +19,7 @@
#include "indirect_reference_table.h"
+#include "runtime-inl.h"
#include "verify_object-inl.h"
namespace art {
@@ -46,7 +47,7 @@
AbortIfNoCheckJNI();
return false;
}
- if (UNLIKELY(table_[idx].IsNull())) {
+ if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref;
AbortIfNoCheckJNI();
return false;
@@ -73,15 +74,11 @@
template<ReadBarrierOption kReadBarrierOption>
inline mirror::Object* IndirectReferenceTable::Get(IndirectRef iref) const {
if (!GetChecked(iref)) {
- return kInvalidIndirectRefObject;
+ return nullptr;
}
uint32_t idx = ExtractIndex(iref);
- mirror::Object* obj = table_[idx].Read<kWithoutReadBarrier>();
- if (LIKELY(obj != kClearedJniWeakGlobal)) {
- // The read barrier or VerifyObject won't handle kClearedJniWeakGlobal.
- obj = table_[idx].Read();
- VerifyObject(obj);
- }
+ mirror::Object* obj = table_[idx].GetReference()->Read<kWithoutReadBarrier>();
+ VerifyObject(obj);
return obj;
}
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 9b2b82e..c1455fd 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -56,41 +56,30 @@
void IndirectReferenceTable::AbortIfNoCheckJNI() {
// If -Xcheck:jni is on, it'll give a more detailed error before aborting.
- if (!Runtime::Current()->GetJavaVM()->check_jni) {
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ if (!vm->IsCheckJniEnabled()) {
// Otherwise, we want to abort rather than hand back a bad reference.
LOG(FATAL) << "JNI ERROR (app bug): see above.";
}
}
IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
- size_t maxCount, IndirectRefKind desiredKind) {
+ size_t maxCount, IndirectRefKind desiredKind)
+ : kind_(desiredKind),
+ max_entries_(maxCount) {
CHECK_GT(initialCount, 0U);
CHECK_LE(initialCount, maxCount);
CHECK_NE(desiredKind, kHandleScopeOrInvalid);
std::string error_str;
- const size_t initial_bytes = initialCount * sizeof(const mirror::Object*);
- const size_t table_bytes = maxCount * sizeof(const mirror::Object*);
+ const size_t table_bytes = maxCount * sizeof(IrtEntry);
table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
PROT_READ | PROT_WRITE, false, &error_str));
CHECK(table_mem_map_.get() != nullptr) << error_str;
CHECK_EQ(table_mem_map_->Size(), table_bytes);
-
- table_ = reinterpret_cast<GcRoot<mirror::Object>*>(table_mem_map_->Begin());
+ table_ = reinterpret_cast<IrtEntry*>(table_mem_map_->Begin());
CHECK(table_ != nullptr);
- memset(table_, 0xd1, initial_bytes);
-
- const size_t slot_bytes = maxCount * sizeof(IndirectRefSlot);
- slot_mem_map_.reset(MemMap::MapAnonymous("indirect ref table slots", nullptr, slot_bytes,
- PROT_READ | PROT_WRITE, false, &error_str));
- CHECK(slot_mem_map_.get() != nullptr) << error_str;
- slot_data_ = reinterpret_cast<IndirectRefSlot*>(slot_mem_map_->Begin());
- CHECK(slot_data_ != nullptr);
-
segment_state_.all = IRT_FIRST_SEGMENT;
- alloc_entries_ = initialCount;
- max_entries_ = maxCount;
- kind_ = desiredKind;
}
IndirectReferenceTable::~IndirectReferenceTable() {
@@ -104,24 +93,12 @@
CHECK(obj != NULL);
VerifyObject(obj);
DCHECK(table_ != NULL);
- DCHECK_LE(alloc_entries_, max_entries_);
DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
- if (topIndex == alloc_entries_) {
- // reached end of allocated space; did we hit buffer max?
- if (topIndex == max_entries_) {
- LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")\n"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
- }
-
- size_t newSize = alloc_entries_ * 2;
- if (newSize > max_entries_) {
- newSize = max_entries_;
- }
- DCHECK_GT(newSize, alloc_entries_);
-
- alloc_entries_ = newSize;
+ if (topIndex == max_entries_) {
+ LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
+ << "(max=" << max_entries_ << ")\n"
+ << MutatorLockedDumpable<IndirectReferenceTable>(*this);
}
// We know there's enough room in the table. Now we just need to find
@@ -129,27 +106,26 @@
// add to the end of the list.
IndirectRef result;
int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
+ size_t index;
if (numHoles > 0) {
DCHECK_GT(topIndex, 1U);
// Find the first hole; likely to be near the end of the list.
- GcRoot<mirror::Object>* pScan = &table_[topIndex - 1];
- DCHECK(!pScan->IsNull());
+ IrtEntry* pScan = &table_[topIndex - 1];
+ DCHECK(!pScan->GetReference()->IsNull());
--pScan;
- while (!pScan->IsNull()) {
+ while (!pScan->GetReference()->IsNull()) {
DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
--pScan;
}
- UpdateSlotAdd(obj, pScan - table_);
- result = ToIndirectRef(pScan - table_);
- *pScan = GcRoot<mirror::Object>(obj);
+ index = pScan - table_;
segment_state_.parts.numHoles--;
} else {
// Add to the end.
- UpdateSlotAdd(obj, topIndex);
- result = ToIndirectRef(topIndex);
- table_[topIndex++] = GcRoot<mirror::Object>(obj);
+ index = topIndex++;
segment_state_.parts.topIndex = topIndex;
}
+ table_[index].Add(obj);
+ result = ToIndirectRef(index);
if (false) {
LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
<< " holes=" << segment_state_.parts.numHoles;
@@ -160,10 +136,12 @@
}
void IndirectReferenceTable::AssertEmpty() {
- if (UNLIKELY(begin() != end())) {
- ScopedObjectAccess soa(Thread::Current());
- LOG(FATAL) << "Internal Error: non-empty local reference table\n"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+ for (size_t i = 0; i < Capacity(); ++i) {
+ if (!table_[i].GetReference()->IsNull()) {
+ ScopedObjectAccess soa(Thread::Current());
+ LOG(FATAL) << "Internal Error: non-empty local reference table\n"
+ << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+ }
}
}
@@ -182,7 +160,6 @@
int bottomIndex = prevState.parts.topIndex;
DCHECK(table_ != NULL);
- DCHECK_LE(alloc_entries_, max_entries_);
DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
int idx = ExtractIndex(iref);
@@ -192,7 +169,6 @@
LOG(WARNING) << "Attempt to remove local handle scope entry from IRT, ignoring";
return true;
}
-
if (idx < bottomIndex) {
// Wrong segment.
LOG(WARNING) << "Attempt to remove index outside index area (" << idx
@@ -206,23 +182,23 @@
return false;
}
- if (idx == topIndex-1) {
+ if (idx == topIndex - 1) {
// Top-most entry. Scan up and consume holes.
if (!CheckEntry("remove", iref, idx)) {
return false;
}
- table_[idx] = GcRoot<mirror::Object>(nullptr);
+ *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
if (numHoles != 0) {
while (--topIndex > bottomIndex && numHoles != 0) {
if (false) {
- LOG(INFO) << "+++ checking for hole at " << topIndex-1
+ LOG(INFO) << "+++ checking for hole at " << topIndex - 1
<< " (cookie=" << cookie << ") val="
- << table_[topIndex - 1].Read<kWithoutReadBarrier>();
+ << table_[topIndex - 1].GetReference()->Read<kWithoutReadBarrier>();
}
- if (!table_[topIndex-1].IsNull()) {
+ if (!table_[topIndex - 1].GetReference()->IsNull()) {
break;
}
if (false) {
@@ -242,7 +218,7 @@
// Not the top-most entry. This creates a hole. We NULL out the
// entry to prevent somebody from deleting it twice and screwing up
// the hole count.
- if (table_[idx].IsNull()) {
+ if (table_[idx].GetReference()->IsNull()) {
LOG(INFO) << "--- WEIRD: removing null entry " << idx;
return false;
}
@@ -250,7 +226,7 @@
return false;
}
- table_[idx] = GcRoot<mirror::Object>(nullptr);
+ *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
segment_state_.parts.numHoles++;
if (false) {
LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles;
@@ -263,6 +239,11 @@
void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
RootType root_type) {
for (auto ref : *this) {
+ if (*ref == nullptr) {
+ // Need to skip null entries to make it possible to do the
+ // non-null check after the call back.
+ continue;
+ }
callback(ref, arg, tid, root_type);
DCHECK(*ref != nullptr);
}
@@ -272,15 +253,11 @@
os << kind_ << " table dump:\n";
ReferenceTable::Table entries;
for (size_t i = 0; i < Capacity(); ++i) {
- mirror::Object* obj = table_[i].Read<kWithoutReadBarrier>();
+ mirror::Object* obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
if (UNLIKELY(obj == nullptr)) {
// Remove NULLs.
- } else if (UNLIKELY(obj == kClearedJniWeakGlobal)) {
- // ReferenceTable::Dump() will handle kClearedJniWeakGlobal
- // while the read barrier won't.
- entries.push_back(GcRoot<mirror::Object>(obj));
} else {
- obj = table_[i].Read();
+ obj = table_[i].GetReference()->Read();
entries.push_back(GcRoot<mirror::Object>(obj));
}
}
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fb910e2..168f9f2 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -25,16 +25,18 @@
#include "base/logging.h"
#include "base/mutex.h"
#include "gc_root.h"
-#include "mem_map.h"
#include "object_callbacks.h"
#include "offsets.h"
#include "read_barrier_option.h"
namespace art {
+
namespace mirror {
class Object;
} // namespace mirror
+class MemMap;
+
/*
* Maintain a table of indirect references. Used for local/global JNI
* references.
@@ -103,10 +105,6 @@
*/
typedef void* IndirectRef;
-// Magic failure values; must not pass Heap::ValidateObject() or Heap::IsHeapAddress().
-static mirror::Object* const kInvalidIndirectRefObject = reinterpret_cast<mirror::Object*>(0xdead4321);
-static mirror::Object* const kClearedJniWeakGlobal = reinterpret_cast<mirror::Object*>(0xdead1234);
-
/*
* Indirect reference kind, used as the two low bits of IndirectRef.
*
@@ -127,16 +125,6 @@
return static_cast<IndirectRefKind>(reinterpret_cast<uintptr_t>(iref) & 0x03);
}
-/*
- * Extended debugging structure. We keep a parallel array of these, one
- * per slot in the table.
- */
-static const size_t kIRTPrevCount = 4;
-struct IndirectRefSlot {
- uint32_t serial;
- const mirror::Object* previous[kIRTPrevCount];
-};
-
/* use as initial value for "cookie", and when table has only one segment */
static const uint32_t IRT_FIRST_SEGMENT = 0;
@@ -203,23 +191,47 @@
} parts;
};
+// Try to choose kIRTPrevCount so that sizeof(IrtEntry) is a power of 2.
+// Contains multiple entries but only one active one, this helps us detect use after free errors
+// since the serial stored in the indirect ref wont match.
+static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3;
+class PACKED(4) IrtEntry {
+ public:
+ void Add(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ ++serial_;
+ if (serial_ == kIRTPrevCount) {
+ serial_ = 0;
+ }
+ references_[serial_] = GcRoot<mirror::Object>(obj);
+ }
+ GcRoot<mirror::Object>* GetReference() {
+ DCHECK_LT(serial_, kIRTPrevCount);
+ return &references_[serial_];
+ }
+ uint32_t GetSerial() const {
+ return serial_;
+ }
+
+ private:
+ uint32_t serial_;
+ GcRoot<mirror::Object> references_[kIRTPrevCount];
+};
+
class IrtIterator {
public:
- explicit IrtIterator(GcRoot<mirror::Object>* table, size_t i, size_t capacity)
+ explicit IrtIterator(IrtEntry* table, size_t i, size_t capacity)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: table_(table), i_(i), capacity_(capacity) {
- SkipNullsAndTombstones();
}
IrtIterator& operator++() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
++i_;
- SkipNullsAndTombstones();
return *this;
}
mirror::Object** operator*() {
// This does not have a read barrier as this is used to visit roots.
- return table_[i_].AddressWithoutBarrier();
+ return table_[i_].GetReference()->AddressWithoutBarrier();
}
bool equals(const IrtIterator& rhs) const {
@@ -227,18 +239,9 @@
}
private:
- void SkipNullsAndTombstones() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // We skip NULLs and tombstones. Clients don't want to see implementation details.
- while (i_ < capacity_ &&
- (table_[i_].IsNull() ||
- table_[i_].Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal)) {
- ++i_;
- }
- }
-
- GcRoot<mirror::Object>* const table_;
+ IrtEntry* const table_;
size_t i_;
- size_t capacity_;
+ const size_t capacity_;
};
bool inline operator==(const IrtIterator& lhs, const IrtIterator& rhs) {
@@ -329,9 +332,7 @@
}
private:
- /*
- * Extract the table index from an indirect reference.
- */
+ // Extract the table index from an indirect reference.
static uint32_t ExtractIndex(IndirectRef iref) {
uintptr_t uref = reinterpret_cast<uintptr_t>(iref);
return (uref >> 2) & 0xffff;
@@ -343,25 +344,11 @@
*/
IndirectRef ToIndirectRef(uint32_t tableIndex) const {
DCHECK_LT(tableIndex, 65536U);
- uint32_t serialChunk = slot_data_[tableIndex].serial;
- uintptr_t uref = serialChunk << 20 | (tableIndex << 2) | kind_;
+ uint32_t serialChunk = table_[tableIndex].GetSerial();
+ uintptr_t uref = (serialChunk << 20) | (tableIndex << 2) | kind_;
return reinterpret_cast<IndirectRef>(uref);
}
- /*
- * Update extended debug info when an entry is added.
- *
- * We advance the serial number, invalidating any outstanding references to
- * this slot.
- */
- void UpdateSlotAdd(const mirror::Object* obj, int slot) {
- if (slot_data_ != NULL) {
- IndirectRefSlot* pSlot = &slot_data_[slot];
- pSlot->serial++;
- pSlot->previous[pSlot->serial % kIRTPrevCount] = obj;
- }
- }
-
// Abort if check_jni is not enabled.
static void AbortIfNoCheckJNI();
@@ -374,19 +361,13 @@
// Mem map where we store the indirect refs.
std::unique_ptr<MemMap> table_mem_map_;
- // Mem map where we store the extended debugging info.
- std::unique_ptr<MemMap> slot_mem_map_;
// bottom of the stack. Do not directly access the object references
// in this as they are roots. Use Get() that has a read barrier.
- GcRoot<mirror::Object>* table_;
+ IrtEntry* table_;
/* bit mask, ORed into all irefs */
- IndirectRefKind kind_;
- /* extended debugging info */
- IndirectRefSlot* slot_data_;
- /* #of entries we have space for */
- size_t alloc_entries_;
+ const IndirectRefKind kind_;
/* max #of entries allowed */
- size_t max_entries_;
+ const size_t max_entries_;
};
} // namespace art
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index a33a981..99ee597 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -49,15 +49,15 @@
IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
- ASSERT_TRUE(c != NULL);
+ ASSERT_TRUE(c != nullptr);
mirror::Object* obj0 = c->AllocObject(soa.Self());
- ASSERT_TRUE(obj0 != NULL);
+ ASSERT_TRUE(obj0 != nullptr);
mirror::Object* obj1 = c->AllocObject(soa.Self());
- ASSERT_TRUE(obj1 != NULL);
+ ASSERT_TRUE(obj1 != nullptr);
mirror::Object* obj2 = c->AllocObject(soa.Self());
- ASSERT_TRUE(obj2 != NULL);
+ ASSERT_TRUE(obj2 != nullptr);
mirror::Object* obj3 = c->AllocObject(soa.Self());
- ASSERT_TRUE(obj3 != NULL);
+ ASSERT_TRUE(obj3 != nullptr);
const uint32_t cookie = IRT_FIRST_SEGMENT;
@@ -68,13 +68,13 @@
// Add three, check, remove in the order in which they were added.
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
IndirectRef iref1 = irt.Add(cookie, obj1);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 2, 2);
IndirectRef iref2 = irt.Add(cookie, obj2);
- EXPECT_TRUE(iref2 != NULL);
+ EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
EXPECT_EQ(obj0, irt.Get(iref0));
@@ -92,15 +92,15 @@
EXPECT_EQ(0U, irt.Capacity());
// Get invalid entry (off the end of the list).
- EXPECT_EQ(kInvalidIndirectRefObject, irt.Get(iref0));
+ EXPECT_TRUE(irt.Get(iref0) == nullptr);
// Add three, remove in the opposite order.
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
iref1 = irt.Add(cookie, obj1);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
iref2 = irt.Add(cookie, obj2);
- EXPECT_TRUE(iref2 != NULL);
+ EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
ASSERT_TRUE(irt.Remove(cookie, iref2));
@@ -116,11 +116,11 @@
// Add three, remove middle / middle / bottom / top. (Second attempt
// to remove middle should fail.)
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
iref1 = irt.Add(cookie, obj1);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
iref2 = irt.Add(cookie, obj2);
- EXPECT_TRUE(iref2 != NULL);
+ EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
ASSERT_EQ(3U, irt.Capacity());
@@ -131,7 +131,7 @@
CheckDump(&irt, 2, 2);
// Get invalid entry (from hole).
- EXPECT_EQ(kInvalidIndirectRefObject, irt.Get(iref1));
+ EXPECT_TRUE(irt.Get(iref1) == nullptr);
ASSERT_TRUE(irt.Remove(cookie, iref2));
CheckDump(&irt, 1, 1);
@@ -145,20 +145,20 @@
// is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify
// that we delete one and don't hole-compact the other.
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
iref1 = irt.Add(cookie, obj1);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
iref2 = irt.Add(cookie, obj2);
- EXPECT_TRUE(iref2 != NULL);
+ EXPECT_TRUE(iref2 != nullptr);
IndirectRef iref3 = irt.Add(cookie, obj3);
- EXPECT_TRUE(iref3 != NULL);
+ EXPECT_TRUE(iref3 != nullptr);
CheckDump(&irt, 4, 4);
ASSERT_TRUE(irt.Remove(cookie, iref1));
CheckDump(&irt, 3, 3);
iref1 = irt.Add(cookie, obj1);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
CheckDump(&irt, 4, 4);
@@ -181,12 +181,12 @@
// iref. They have the same slot number but are for different objects.
// With the extended checks in place, this should fail.
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
ASSERT_TRUE(irt.Remove(cookie, iref0));
CheckDump(&irt, 0, 0);
iref1 = irt.Add(cookie, obj1);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 1, 1);
ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
CheckDump(&irt, 1, 1);
@@ -197,12 +197,12 @@
// Same as above, but with the same object. A more rigorous checker
// (e.g. with slot serialization) will catch this.
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
ASSERT_TRUE(irt.Remove(cookie, iref0));
CheckDump(&irt, 0, 0);
iref1 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref1 != NULL);
+ EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 1, 1);
if (iref0 != iref1) {
// Try 0, should not work.
@@ -212,15 +212,15 @@
ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty";
CheckDump(&irt, 0, 0);
- // NULL isn't a valid iref.
- ASSERT_EQ(kInvalidIndirectRefObject, irt.Get(NULL));
+ // nullptr isn't a valid iref.
+ ASSERT_TRUE(irt.Get(nullptr) == nullptr);
// Stale lookup.
iref0 = irt.Add(cookie, obj0);
- EXPECT_TRUE(iref0 != NULL);
+ EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
ASSERT_TRUE(irt.Remove(cookie, iref0));
- EXPECT_EQ(kInvalidIndirectRefObject, irt.Get(iref0)) << "stale lookup succeeded";
+ EXPECT_TRUE(irt.Get(iref0) == nullptr) << "stale lookup succeeded";
CheckDump(&irt, 0, 0);
// Test table resizing.
@@ -228,12 +228,12 @@
IndirectRef manyRefs[kTableInitial];
for (size_t i = 0; i < kTableInitial; i++) {
manyRefs[i] = irt.Add(cookie, obj0);
- ASSERT_TRUE(manyRefs[i] != NULL) << "Failed adding " << i;
+ ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
CheckDump(&irt, i + 1, 1);
}
// ...this one causes overflow.
iref0 = irt.Add(cookie, obj0);
- ASSERT_TRUE(iref0 != NULL);
+ ASSERT_TRUE(iref0 != nullptr);
ASSERT_EQ(kTableInitial + 1, irt.Capacity());
CheckDump(&irt, kTableInitial + 1, 1);
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index ae8eeac..de6d0f4 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -56,6 +56,7 @@
static constexpr size_t kArmPointerSize = 4;
static constexpr size_t kArm64PointerSize = 8;
static constexpr size_t kMipsPointerSize = 4;
+static constexpr size_t kMips64PointerSize = 8;
static constexpr size_t kX86PointerSize = 4;
static constexpr size_t kX86_64PointerSize = 8;
@@ -93,6 +94,8 @@
return kX86_64PointerSize;
case kMips:
return kMipsPointerSize;
+ case kMips64:
+ return kMips64PointerSize;
case kNone:
LOG(FATAL) << "ISA kNone does not have pointer size.";
return 0;
@@ -114,6 +117,7 @@
case kArm64:
case kX86_64:
+ case kMips64:
return true;
case kNone:
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 652d29b..15be6b7 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -85,9 +85,7 @@
static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code,
const void* portable_code, bool have_portable_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(portable_code);
-#endif
method->SetEntryPointFromQuickCompiledCode(quick_code);
bool portable_enabled = method->IsPortableCompiled();
if (have_portable_code && !portable_enabled) {
@@ -104,13 +102,9 @@
&& !method->IsNative() && !method->IsProxyMethod())) {
if (kIsDebugBuild) {
if (quick_code == GetQuickToInterpreterBridge()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK(portable_code == GetPortableToInterpreterBridge());
-#endif
} else if (quick_code == class_linker->GetQuickResolutionTrampoline()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK(portable_code == class_linker->GetPortableResolutionTrampoline());
-#endif
}
}
DCHECK(!method->IsNative()) << PrettyMethod(method);
@@ -138,32 +132,21 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
bool is_class_initialized = method->GetDeclaringClass()->IsInitialized();
bool have_portable_code = false;
-#if !defined(ART_USE_PORTABLE_COMPILER)
- new_portable_code = nullptr;
-#endif
if (uninstall) {
if ((forced_interpret_only_ || IsDeoptimized(method)) && !method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
-#endif
new_quick_code = GetQuickToInterpreterBridge();
} else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-#endif
new_quick_code = class_linker->GetQuickOatCodeFor(method);
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = class_linker->GetPortableResolutionTrampoline();
-#endif
new_quick_code = class_linker->GetQuickResolutionTrampoline();
}
} else { // !uninstall
if ((interpreter_stubs_installed_ || forced_interpret_only_ || IsDeoptimized(method)) &&
!method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
-#endif
new_quick_code = GetQuickToInterpreterBridge();
} else {
// Do not overwrite resolution trampoline. When the trampoline initializes the method's
@@ -171,21 +154,15 @@
// For more details, see ClassLinker::FixupStaticTrampolines.
if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
if (entry_exit_stubs_installed_) {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
-#endif
new_quick_code = GetQuickInstrumentationEntryPoint();
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-#endif
new_quick_code = class_linker->GetQuickOatCodeFor(method);
DCHECK(new_quick_code != class_linker->GetQuickToInterpreterBridgeTrampoline());
}
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = class_linker->GetPortableResolutionTrampoline();
-#endif
new_quick_code = class_linker->GetQuickResolutionTrampoline();
}
}
@@ -687,11 +664,7 @@
new_have_portable_code = have_portable_code;
} else {
if ((interpreter_stubs_installed_ || IsDeoptimized(method)) && !method->IsNative()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
-#else
- new_portable_code = portable_code;
-#endif
new_quick_code = GetQuickToInterpreterBridge();
new_have_portable_code = false;
} else {
@@ -699,20 +672,14 @@
if (quick_code == class_linker->GetQuickResolutionTrampoline() ||
quick_code == class_linker->GetQuickToInterpreterBridgeTrampoline() ||
quick_code == GetQuickToInterpreterBridge()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
DCHECK((portable_code == class_linker->GetPortableResolutionTrampoline()) ||
(portable_code == GetPortableToInterpreterBridge()));
-#endif
new_portable_code = portable_code;
new_quick_code = quick_code;
new_have_portable_code = have_portable_code;
} else if (entry_exit_stubs_installed_) {
new_quick_code = GetQuickInstrumentationEntryPoint();
-#if defined(ART_USE_PORTABLE_COMPILER)
new_portable_code = GetPortableToInterpreterBridge();
-#else
- new_portable_code = portable_code;
-#endif
new_have_portable_code = false;
} else {
new_portable_code = portable_code;
@@ -794,12 +761,7 @@
<< " is already deoptimized";
}
if (!interpreter_stubs_installed_) {
- UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(),
-#if defined(ART_USE_PORTABLE_COMPILER)
- GetPortableToInterpreterBridge(),
-#else
- nullptr,
-#endif
+ UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(),
false);
// Install instrumentation exit stub and instrumentation frames. We may already have installed
@@ -833,20 +795,11 @@
!method->GetDeclaringClass()->IsInitialized()) {
// TODO: we're updating to entrypoints in the image here, we can avoid the trampoline.
UpdateEntrypoints(method, class_linker->GetQuickResolutionTrampoline(),
-#if defined(ART_USE_PORTABLE_COMPILER)
- class_linker->GetPortableResolutionTrampoline(),
-#else
- nullptr,
-#endif
- false);
+ class_linker->GetPortableResolutionTrampoline(), false);
} else {
bool have_portable_code = false;
const void* quick_code = class_linker->GetQuickOatCodeFor(method);
-#if defined(ART_USE_PORTABLE_COMPILER)
const void* portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
-#else
- const void* portable_code = nullptr;
-#endif
UpdateEntrypoints(method, quick_code, portable_code, have_portable_code);
}
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index c66f99e..f6e6661 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -103,7 +103,7 @@
Locks::intern_table_lock_->AssertHeld(Thread::Current());
auto it = table->find(GcRoot<mirror::String>(s));
if (LIKELY(it != table->end())) {
- return const_cast<GcRoot<mirror::String>&>(*it).Read<kWithReadBarrier>();
+ return const_cast<GcRoot<mirror::String>&>(*it).Read();
}
return nullptr;
}
@@ -299,7 +299,7 @@
if (new_object == nullptr) {
it = weak_interns_.erase(it);
} else {
- root.Assign(down_cast<mirror::String*>(new_object));
+ root = GcRoot<mirror::String>(down_cast<mirror::String*>(new_object));
++it;
}
}
@@ -309,8 +309,7 @@
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return static_cast<size_t>(
- const_cast<GcRoot<mirror::String>&>(root).Read<kWithoutReadBarrier>()->GetHashCode());
+ return static_cast<size_t>(const_cast<GcRoot<mirror::String>&>(root).Read()->GetHashCode());
}
bool InternTable::StringHashEquals::operator()(const GcRoot<mirror::String>& a,
@@ -318,8 +317,8 @@
if (kIsDebugBuild) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- return const_cast<GcRoot<mirror::String>&>(a).Read<kWithoutReadBarrier>()->Equals(
- const_cast<GcRoot<mirror::String>&>(b).Read<kWithoutReadBarrier>());
+ return const_cast<GcRoot<mirror::String>&>(a).Read()->Equals(
+ const_cast<GcRoot<mirror::String>&>(b).Read());
}
} // namespace art
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 47a7f0d..07224ef 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -36,8 +36,8 @@
mirror::Class* array_class = runtime->GetClassLinker()->FindArrayClass(self, &element_class);
DCHECK(array_class != nullptr);
gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
- result->SetL(mirror::Array::Alloc<true>(self, array_class, length,
- array_class->GetComponentSize(), allocator, true));
+ result->SetL(mirror::Array::Alloc<true, true>(self, array_class, length,
+ array_class->GetComponentSizeShift(), allocator));
} else if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") {
result->SetL(NULL);
} else if (name == "java.lang.Class dalvik.system.VMStack.getStackClass2()") {
@@ -462,7 +462,7 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
- if (UNLIKELY(!class_linker->EnsureInitialized(h_class, true, true))) {
+ if (UNLIKELY(!class_linker->EnsureInitialized(self, h_class, true, true))) {
CHECK(self->IsExceptionPending());
self->PopShadowFrame();
return;
@@ -537,7 +537,7 @@
StackHandleScope<1> hs(self);
HandleWrapper<Class> h_declaring_class(hs.NewHandleWrapper(&declaring_class));
if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- h_declaring_class, true, true))) {
+ self, h_declaring_class, true, true))) {
DCHECK(self->IsExceptionPending());
self->PopShadowFrame();
return;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 6705695..733f1d1 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -32,7 +32,7 @@
const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
ArtField* f = FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
- Primitive::FieldSize(field_type));
+ Primitive::ComponentSize(field_type));
if (UNLIKELY(f == nullptr)) {
CHECK(self->IsExceptionPending());
return false;
@@ -96,22 +96,22 @@
EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true);
// iget-XXX
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstanceObjectRead, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstanceObjectRead, Primitive::kPrimNot)
// sget-XXX
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot)
#undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
@@ -208,7 +208,7 @@
bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
ArtField* f = FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
- Primitive::FieldSize(field_type));
+ Primitive::ComponentSize(field_type));
if (UNLIKELY(f == nullptr)) {
CHECK(self->IsExceptionPending());
return false;
@@ -301,22 +301,22 @@
EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true, true);
// iput-XXX
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstanceObjectWrite, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstanceObjectWrite, Primitive::kPrimNot)
// sput-XXX
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimBoolean);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimByte);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimChar);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimShort);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimInt);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimLong);
-EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimBoolean)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimByte)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimChar)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimShort)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimInt)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimLong)
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot)
#undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL
@@ -346,6 +346,18 @@
}
// Note: iput-x-quick instructions are only for non-volatile fields.
switch (field_type) {
+ case Primitive::kPrimBoolean:
+ obj->SetFieldBoolean<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimByte:
+ obj->SetFieldByte<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimChar:
+ obj->SetFieldChar<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimShort:
+ obj->SetFieldShort<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
+ break;
case Primitive::kPrimInt:
obj->SetField32<transaction_active>(field_offset, shadow_frame.GetVReg(vregA));
break;
@@ -371,9 +383,13 @@
EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, false); \
EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type, true);
-EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
-EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
-EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimInt) // iput-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimBoolean) // iput-boolean-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimByte) // iput-byte-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimChar) // iput-char-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimShort) // iput-short-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimLong) // iput-wide-quick.
+EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL(Primitive::kPrimNot) // iput-object-quick.
#undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
@@ -439,7 +455,7 @@
Thread* const self_;
StackHandleScope<1> handle_scope_;
Handle<mirror::Throwable>* exception_;
- Handle<mirror::ArtMethod> catch_method_;
+ MutableHandle<mirror::ArtMethod> catch_method_;
uint32_t catch_dex_pc_;
bool clear_exception_;
@@ -692,7 +708,8 @@
}
return false;
}
- Object* newArray = Array::Alloc<true>(self, arrayClass, length, arrayClass->GetComponentSize(),
+ Object* newArray = Array::Alloc<true>(self, arrayClass, length,
+ arrayClass->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator());
if (UNLIKELY(newArray == NULL)) {
DCHECK(self->IsExceptionPending());
@@ -784,7 +801,7 @@
if (found != nullptr && initialize_class) {
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(found));
- if (!class_linker->EnsureInitialized(h_class, true, true)) {
+ if (!class_linker->EnsureInitialized(self, h_class, true, true)) {
CHECK(self->IsExceptionPending());
return;
}
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 5a1d01e..a8345ad 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -192,7 +192,7 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(java_lang_string_class));
- if (UNLIKELY(!class_linker->EnsureInitialized(h_class, true, true))) {
+ if (UNLIKELY(!class_linker->EnsureInitialized(self, h_class, true, true))) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
@@ -388,11 +388,11 @@
EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false); \
EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true);
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic); // invoke-static/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect); // invoke-direct/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual); // invoke-virtual/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper); // invoke-super/range.
-EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface); // invoke-interface/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic) // invoke-static/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect) // invoke-direct/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual) // invoke-virtual/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper) // invoke-super/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface) // invoke-interface/range.
#undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index e098ac8..5c8a6c6 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -249,9 +249,7 @@
// perform the memory barrier now.
QuasiAtomic::ThreadFenceForConstructor();
}
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -268,9 +266,7 @@
HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) {
QuasiAtomic::ThreadFenceForConstructor();
JValue result;
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -288,9 +284,7 @@
JValue result;
result.SetJ(0);
result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -307,9 +301,7 @@
HANDLE_INSTRUCTION_START(RETURN_WIDE) {
JValue result;
result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
@@ -325,9 +317,7 @@
HANDLE_INSTRUCTION_START(RETURN_OBJECT) {
JValue result;
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
const uint8_t vreg_index = inst->VRegA_11x(inst_data);
Object* obj_result = shadow_frame.GetVRegReference(vreg_index);
if (do_assignability_check && obj_result != NULL) {
@@ -632,7 +622,7 @@
int8_t offset = inst->VRegA_10t(inst_data);
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -644,7 +634,7 @@
int16_t offset = inst->VRegA_20t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -656,7 +646,7 @@
int32_t offset = inst->VRegA_30t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -668,7 +658,7 @@
int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -680,7 +670,7 @@
int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -773,7 +763,7 @@
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -789,7 +779,7 @@
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -805,7 +795,7 @@
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -821,7 +811,7 @@
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -837,7 +827,7 @@
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -853,7 +843,7 @@
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -869,7 +859,7 @@
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -885,7 +875,7 @@
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -901,7 +891,7 @@
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -917,7 +907,7 @@
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -933,7 +923,7 @@
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -949,7 +939,7 @@
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
}
@@ -1369,6 +1359,30 @@
}
HANDLE_INSTRUCTION_END();
+ HANDLE_INSTRUCTION_START(IPUT_BOOLEAN_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_BYTE_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_CHAR_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_SHORT_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) {
bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
@@ -2304,22 +2318,6 @@
UnexpectedOpcode(inst, mh);
HANDLE_INSTRUCTION_END();
- HANDLE_INSTRUCTION_START(UNUSED_EB)
- UnexpectedOpcode(inst, mh);
- HANDLE_INSTRUCTION_END();
-
- HANDLE_INSTRUCTION_START(UNUSED_EC)
- UnexpectedOpcode(inst, mh);
- HANDLE_INSTRUCTION_END();
-
- HANDLE_INSTRUCTION_START(UNUSED_ED)
- UnexpectedOpcode(inst, mh);
- HANDLE_INSTRUCTION_END();
-
- HANDLE_INSTRUCTION_START(UNUSED_EE)
- UnexpectedOpcode(inst, mh);
- HANDLE_INSTRUCTION_END();
-
HANDLE_INSTRUCTION_START(UNUSED_EF)
UnexpectedOpcode(inst, mh);
HANDLE_INSTRUCTION_END();
@@ -2391,7 +2389,7 @@
exception_pending_label: {
CHECK(self->IsExceptionPending());
if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
+ self->CheckSuspend();
UPDATE_HANDLER_TABLE();
}
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 5401495..c6cef6a 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -22,9 +22,7 @@
#define HANDLE_PENDING_EXCEPTION() \
do { \
DCHECK(self->IsExceptionPending()); \
- if (UNLIKELY(self->TestAllFlags())) { \
- CheckSuspend(self); \
- } \
+ self->AllowThreadSuspension(); \
uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \
inst->GetDexPc(insns), \
instrumentation); \
@@ -175,9 +173,7 @@
// perform the memory barrier now.
QuasiAtomic::ThreadFenceForConstructor();
}
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -191,9 +187,7 @@
case Instruction::RETURN_VOID_BARRIER: {
QuasiAtomic::ThreadFenceForConstructor();
JValue result;
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -208,9 +202,7 @@
JValue result;
result.SetJ(0);
result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -224,9 +216,7 @@
case Instruction::RETURN_WIDE: {
JValue result;
result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
shadow_frame.GetMethod(), inst->GetDexPc(insns),
@@ -239,9 +229,7 @@
}
case Instruction::RETURN_OBJECT: {
JValue result;
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
const size_t ref_idx = inst->VRegA_11x(inst_data);
Object* obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != NULL) {
@@ -545,9 +533,7 @@
PREAMBLE();
int8_t offset = inst->VRegA_10t(inst_data);
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
break;
@@ -556,9 +542,7 @@
PREAMBLE();
int16_t offset = inst->VRegA_20t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
break;
@@ -567,9 +551,7 @@
PREAMBLE();
int32_t offset = inst->VRegA_30t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
break;
@@ -578,9 +560,7 @@
PREAMBLE();
int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
break;
@@ -589,9 +569,7 @@
PREAMBLE();
int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
break;
@@ -682,9 +660,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -697,9 +673,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -712,9 +686,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -727,9 +699,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -742,9 +712,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -757,9 +725,7 @@
if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
int16_t offset = inst->VRegC_22t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -772,9 +738,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -787,9 +751,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -802,9 +764,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -817,9 +777,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -832,9 +790,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -847,9 +803,7 @@
if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
int16_t offset = inst->VRegB_21t();
if (IsBackwardBranch(offset)) {
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
+ self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
} else {
@@ -1266,6 +1220,30 @@
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
break;
}
+ case Instruction::IPUT_BOOLEAN_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_BYTE_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_CHAR_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_SHORT_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
case Instruction::IPUT_WIDE_QUICK: {
PREAMBLE();
bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(shadow_frame, inst, inst_data);
@@ -2164,7 +2142,7 @@
inst = inst->Next_2xx();
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_EF ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
UnexpectedOpcode(inst, mh);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
new file mode 100644
index 0000000..1444d97
--- /dev/null
+++ b/runtime/java_vm_ext.cc
@@ -0,0 +1,808 @@
+/*
+ * 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 "jni_internal.h"
+
+#include <dlfcn.h>
+
+#include "base/mutex.h"
+#include "base/stl_util.h"
+#include "check_jni.h"
+#include "indirect_reference_table-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "nativebridge/native_bridge.h"
+#include "java_vm_ext.h"
+#include "parsed_options.h"
+#include "runtime-inl.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+
+namespace art {
+
+static size_t gGlobalsInitial = 512; // Arbitrary.
+static size_t gGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
+
+static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
+static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
+
+static bool IsBadJniVersion(int version) {
+ // We don't support JNI_VERSION_1_1. These are the only other valid versions.
+ return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
+}
+
+class SharedLibrary {
+ public:
+ SharedLibrary(JNIEnv* env, Thread* self, const std::string& path, void* handle,
+ jobject class_loader)
+ : path_(path),
+ handle_(handle),
+ needs_native_bridge_(false),
+ class_loader_(env->NewGlobalRef(class_loader)),
+ jni_on_load_lock_("JNI_OnLoad lock"),
+ jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
+ jni_on_load_thread_id_(self->GetThreadId()),
+ jni_on_load_result_(kPending) {
+ }
+
+ ~SharedLibrary() {
+ Thread* self = Thread::Current();
+ if (self != nullptr) {
+ self->GetJniEnv()->DeleteGlobalRef(class_loader_);
+ }
+ }
+
+ jobject GetClassLoader() const {
+ return class_loader_;
+ }
+
+ const std::string& GetPath() const {
+ return path_;
+ }
+
+ /*
+ * Check the result of an earlier call to JNI_OnLoad on this library.
+ * If the call has not yet finished in another thread, wait for it.
+ */
+ bool CheckOnLoadResult()
+ LOCKS_EXCLUDED(jni_on_load_lock_) {
+ Thread* self = Thread::Current();
+ bool okay;
+ {
+ MutexLock mu(self, jni_on_load_lock_);
+
+ if (jni_on_load_thread_id_ == self->GetThreadId()) {
+ // Check this so we don't end up waiting for ourselves. We need to return "true" so the
+ // caller can continue.
+ LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
+ okay = true;
+ } else {
+ while (jni_on_load_result_ == kPending) {
+ VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
+ jni_on_load_cond_.Wait(self);
+ }
+
+ okay = (jni_on_load_result_ == kOkay);
+ VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
+ << (okay ? "succeeded" : "failed") << "]";
+ }
+ }
+ return okay;
+ }
+
+ void SetResult(bool result) LOCKS_EXCLUDED(jni_on_load_lock_) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, jni_on_load_lock_);
+
+ jni_on_load_result_ = result ? kOkay : kFailed;
+ jni_on_load_thread_id_ = 0;
+
+ // Broadcast a wakeup to anybody sleeping on the condition variable.
+ jni_on_load_cond_.Broadcast(self);
+ }
+
+ void SetNeedsNativeBridge() {
+ needs_native_bridge_ = true;
+ }
+
+ bool NeedsNativeBridge() const {
+ return needs_native_bridge_;
+ }
+
+ void* FindSymbol(const std::string& symbol_name) {
+ return dlsym(handle_, symbol_name.c_str());
+ }
+
+ void* FindSymbolWithNativeBridge(const std::string& symbol_name, const char* shorty) {
+ CHECK(NeedsNativeBridge());
+
+ uint32_t len = 0;
+ return android::NativeBridgeGetTrampoline(handle_, symbol_name.c_str(), shorty, len);
+ }
+
+ private:
+ enum JNI_OnLoadState {
+ kPending,
+ kFailed,
+ kOkay,
+ };
+
+ // Path to library "/system/lib/libjni.so".
+ const std::string path_;
+
+ // The void* returned by dlopen(3).
+ void* const handle_;
+
+ // True if a native bridge is required.
+ bool needs_native_bridge_;
+
+ // The ClassLoader this library is associated with, a global JNI reference that is
+ // created/deleted with the scope of the library.
+ const jobject class_loader_;
+
+ // Guards remaining items.
+ Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Wait for JNI_OnLoad in other thread.
+ ConditionVariable jni_on_load_cond_ GUARDED_BY(jni_on_load_lock_);
+ // Recursive invocation guard.
+ uint32_t jni_on_load_thread_id_ GUARDED_BY(jni_on_load_lock_);
+ // Result of earlier JNI_OnLoad call.
+ JNI_OnLoadState jni_on_load_result_ GUARDED_BY(jni_on_load_lock_);
+};
+
+// This exists mainly to keep implementation details out of the header file.
+class Libraries {
+ public:
+ Libraries() {
+ }
+
+ ~Libraries() {
+ STLDeleteValues(&libraries_);
+ }
+
+ void Dump(std::ostream& os) const {
+ bool first = true;
+ for (const auto& library : libraries_) {
+ if (!first) {
+ os << ' ';
+ }
+ first = false;
+ os << library.first;
+ }
+ }
+
+ size_t size() const {
+ return libraries_.size();
+ }
+
+ SharedLibrary* Get(const std::string& path) {
+ auto it = libraries_.find(path);
+ return (it == libraries_.end()) ? nullptr : it->second;
+ }
+
+ void Put(const std::string& path, SharedLibrary* library) {
+ libraries_.Put(path, library);
+ }
+
+ // See section 11.3 "Linking Native Methods" of the JNI spec.
+ void* FindNativeMethod(mirror::ArtMethod* m, std::string& detail)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::jni_libraries_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::string jni_short_name(JniShortName(m));
+ std::string jni_long_name(JniLongName(m));
+ const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ for (const auto& lib : libraries_) {
+ SharedLibrary* library = lib.second;
+ if (soa.Decode<mirror::ClassLoader*>(library->GetClassLoader()) != declaring_class_loader) {
+ // We only search libraries loaded by the appropriate ClassLoader.
+ continue;
+ }
+ // Try the short name then the long name...
+ void* fn;
+ if (library->NeedsNativeBridge()) {
+ const char* shorty = m->GetShorty();
+ fn = library->FindSymbolWithNativeBridge(jni_short_name, shorty);
+ if (fn == nullptr) {
+ fn = library->FindSymbolWithNativeBridge(jni_long_name, shorty);
+ }
+ } else {
+ fn = library->FindSymbol(jni_short_name);
+ if (fn == nullptr) {
+ fn = library->FindSymbol(jni_long_name);
+ }
+ }
+ if (fn == nullptr) {
+ fn = library->FindSymbol(jni_long_name);
+ }
+ if (fn != nullptr) {
+ VLOG(jni) << "[Found native code for " << PrettyMethod(m)
+ << " in \"" << library->GetPath() << "\"]";
+ return fn;
+ }
+ }
+ detail += "No implementation found for ";
+ detail += PrettyMethod(m);
+ detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
+ LOG(ERROR) << detail;
+ return nullptr;
+ }
+
+ private:
+ AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibrarires> libraries_;
+};
+
+
+class JII {
+ public:
+ static jint DestroyJavaVM(JavaVM* vm) {
+ if (vm == nullptr) {
+ return JNI_ERR;
+ }
+ JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+ delete raw_vm->GetRuntime();
+ return JNI_OK;
+ }
+
+ static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+ return AttachCurrentThreadInternal(vm, p_env, thr_args, false);
+ }
+
+ static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
+ return AttachCurrentThreadInternal(vm, p_env, thr_args, true);
+ }
+
+ static jint DetachCurrentThread(JavaVM* vm) {
+ if (vm == nullptr || Thread::Current() == nullptr) {
+ return JNI_ERR;
+ }
+ JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
+ Runtime* runtime = raw_vm->GetRuntime();
+ runtime->DetachCurrentThread();
+ return JNI_OK;
+ }
+
+ static jint GetEnv(JavaVM* vm, void** env, jint version) {
+ // GetEnv always returns a JNIEnv* for the most current supported JNI version,
+ // and unlike other calls that take a JNI version doesn't care if you supply
+ // JNI_VERSION_1_1, which we don't otherwise support.
+ if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
+ LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
+ return JNI_EVERSION;
+ }
+ if (vm == nullptr || env == nullptr) {
+ return JNI_ERR;
+ }
+ Thread* thread = Thread::Current();
+ if (thread == nullptr) {
+ *env = nullptr;
+ return JNI_EDETACHED;
+ }
+ *env = thread->GetJniEnv();
+ return JNI_OK;
+ }
+
+ private:
+ static jint AttachCurrentThreadInternal(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
+ if (vm == nullptr || p_env == nullptr) {
+ return JNI_ERR;
+ }
+
+ // Return immediately if we're already attached.
+ Thread* self = Thread::Current();
+ if (self != nullptr) {
+ *p_env = self->GetJniEnv();
+ return JNI_OK;
+ }
+
+ Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->GetRuntime();
+
+ // No threads allowed in zygote mode.
+ if (runtime->IsZygote()) {
+ LOG(ERROR) << "Attempt to attach a thread in the zygote";
+ return JNI_ERR;
+ }
+
+ JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
+ const char* thread_name = nullptr;
+ jobject thread_group = nullptr;
+ if (args != nullptr) {
+ if (IsBadJniVersion(args->version)) {
+ LOG(ERROR) << "Bad JNI version passed to "
+ << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
+ << args->version;
+ return JNI_EVERSION;
+ }
+ thread_name = args->name;
+ thread_group = args->group;
+ }
+
+ if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
+ *p_env = nullptr;
+ return JNI_ERR;
+ } else {
+ *p_env = Thread::Current()->GetJniEnv();
+ return JNI_OK;
+ }
+ }
+};
+
+const JNIInvokeInterface gJniInvokeInterface = {
+ nullptr, // reserved0
+ nullptr, // reserved1
+ nullptr, // reserved2
+ JII::DestroyJavaVM,
+ JII::AttachCurrentThread,
+ JII::DetachCurrentThread,
+ JII::GetEnv,
+ JII::AttachCurrentThreadAsDaemon
+};
+
+JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
+ : runtime_(runtime),
+ check_jni_abort_hook_(nullptr),
+ check_jni_abort_hook_data_(nullptr),
+ check_jni_(false), // Initialized properly in the constructor body below.
+ force_copy_(options->force_copy_),
+ tracing_enabled_(!options->jni_trace_.empty() || VLOG_IS_ON(third_party_jni)),
+ trace_(options->jni_trace_),
+ globals_lock_("JNI global reference table lock"),
+ globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
+ libraries_(new Libraries),
+ unchecked_functions_(&gJniInvokeInterface),
+ weak_globals_lock_("JNI weak global reference table lock"),
+ weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
+ allow_new_weak_globals_(true),
+ weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
+ functions = unchecked_functions_;
+ if (options->check_jni_) {
+ SetCheckJniEnabled(true);
+ }
+}
+
+JavaVMExt::~JavaVMExt() {
+}
+
+void JavaVMExt::JniAbort(const char* jni_function_name, const char* msg) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ mirror::ArtMethod* current_method = self->GetCurrentMethod(nullptr);
+
+ std::ostringstream os;
+ os << "JNI DETECTED ERROR IN APPLICATION: " << msg;
+
+ if (jni_function_name != nullptr) {
+ os << "\n in call to " << jni_function_name;
+ }
+ // TODO: is this useful given that we're about to dump the calling thread's stack?
+ if (current_method != nullptr) {
+ os << "\n from " << PrettyMethod(current_method);
+ }
+ os << "\n";
+ self->Dump(os);
+
+ if (check_jni_abort_hook_ != nullptr) {
+ check_jni_abort_hook_(check_jni_abort_hook_data_, os.str());
+ } else {
+ // Ensure that we get a native stack trace for this thread.
+ self->TransitionFromRunnableToSuspended(kNative);
+ LOG(FATAL) << os.str();
+ self->TransitionFromSuspendedToRunnable(); // Unreachable, keep annotalysis happy.
+ }
+}
+
+void JavaVMExt::JniAbortV(const char* jni_function_name, const char* fmt, va_list ap) {
+ std::string msg;
+ StringAppendV(&msg, fmt, ap);
+ JniAbort(jni_function_name, msg.c_str());
+}
+
+void JavaVMExt::JniAbortF(const char* jni_function_name, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ JniAbortV(jni_function_name, fmt, args);
+ va_end(args);
+}
+
+bool JavaVMExt::ShouldTrace(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Fast where no tracing is enabled.
+ if (trace_.empty() && !VLOG_IS_ON(third_party_jni)) {
+ return false;
+ }
+ // Perform checks based on class name.
+ StringPiece class_name(method->GetDeclaringClassDescriptor());
+ if (!trace_.empty() && class_name.find(trace_) != std::string::npos) {
+ return true;
+ }
+ if (!VLOG_IS_ON(third_party_jni)) {
+ return false;
+ }
+ // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
+ // like part of Android.
+ static const char* gBuiltInPrefixes[] = {
+ "Landroid/",
+ "Lcom/android/",
+ "Lcom/google/android/",
+ "Ldalvik/",
+ "Ljava/",
+ "Ljavax/",
+ "Llibcore/",
+ "Lorg/apache/harmony/",
+ };
+ for (size_t i = 0; i < arraysize(gBuiltInPrefixes); ++i) {
+ if (class_name.starts_with(gBuiltInPrefixes[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+jobject JavaVMExt::AddGlobalRef(Thread* self, mirror::Object* obj) {
+ // Check for null after decoding the object to handle cleared weak globals.
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ WriterMutexLock mu(self, globals_lock_);
+ IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj);
+ return reinterpret_cast<jobject>(ref);
+}
+
+jweak JavaVMExt::AddWeakGlobalRef(Thread* self, mirror::Object* obj) {
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ MutexLock mu(self, weak_globals_lock_);
+ while (UNLIKELY(!allow_new_weak_globals_)) {
+ weak_globals_add_condition_.WaitHoldingLocks(self);
+ }
+ IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
+ return reinterpret_cast<jweak>(ref);
+}
+
+void JavaVMExt::DeleteGlobalRef(Thread* self, jobject obj) {
+ if (obj == nullptr) {
+ return;
+ }
+ WriterMutexLock mu(self, globals_lock_);
+ if (!globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
+ LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
+ << "failed to find entry";
+ }
+}
+
+void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
+ if (obj == nullptr) {
+ return;
+ }
+ MutexLock mu(self, weak_globals_lock_);
+ if (!weak_globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
+ LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
+ << "failed to find entry";
+ }
+}
+
+static void ThreadEnableCheckJni(Thread* thread, void* arg) {
+ bool* check_jni = reinterpret_cast<bool*>(arg);
+ thread->GetJniEnv()->SetCheckJniEnabled(*check_jni);
+}
+
+bool JavaVMExt::SetCheckJniEnabled(bool enabled) {
+ bool old_check_jni = check_jni_;
+ check_jni_ = enabled;
+ functions = enabled ? GetCheckJniInvokeInterface() : unchecked_functions_;
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ runtime_->GetThreadList()->ForEach(ThreadEnableCheckJni, &check_jni_);
+ return old_check_jni;
+}
+
+void JavaVMExt::DumpForSigQuit(std::ostream& os) {
+ os << "JNI: CheckJNI is " << (check_jni_ ? "on" : "off");
+ if (force_copy_) {
+ os << " (with forcecopy)";
+ }
+ Thread* self = Thread::Current();
+ {
+ ReaderMutexLock mu(self, globals_lock_);
+ os << "; globals=" << globals_.Capacity();
+ }
+ {
+ MutexLock mu(self, weak_globals_lock_);
+ if (weak_globals_.Capacity() > 0) {
+ os << " (plus " << weak_globals_.Capacity() << " weak)";
+ }
+ }
+ os << '\n';
+
+ {
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ os << "Libraries: " << Dumpable<Libraries>(*libraries_) << " (" << libraries_->size() << ")\n";
+ }
+}
+
+void JavaVMExt::DisallowNewWeakGlobals() {
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ allow_new_weak_globals_ = false;
+}
+
+void JavaVMExt::AllowNewWeakGlobals() {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, weak_globals_lock_);
+ allow_new_weak_globals_ = true;
+ weak_globals_add_condition_.Broadcast(self);
+}
+
+mirror::Object* JavaVMExt::DecodeGlobal(Thread* self, IndirectRef ref) {
+ return globals_.SynchronizedGet(self, &globals_lock_, ref);
+}
+
+mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
+ MutexLock mu(self, weak_globals_lock_);
+ while (UNLIKELY(!allow_new_weak_globals_)) {
+ weak_globals_add_condition_.WaitHoldingLocks(self);
+ }
+ return weak_globals_.Get(ref);
+}
+
+void JavaVMExt::DumpReferenceTables(std::ostream& os) {
+ Thread* self = Thread::Current();
+ {
+ ReaderMutexLock mu(self, globals_lock_);
+ globals_.Dump(os);
+ }
+ {
+ MutexLock mu(self, weak_globals_lock_);
+ weak_globals_.Dump(os);
+ }
+}
+
+bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
+ std::string* error_msg) {
+ error_msg->clear();
+
+ // See if we've already loaded this library. If we have, and the class loader
+ // matches, return successfully without doing anything.
+ // TODO: for better results we should canonicalize the pathname (or even compare
+ // inodes). This implementation is fine if everybody is using System.loadLibrary.
+ SharedLibrary* library;
+ Thread* self = Thread::Current();
+ {
+ // TODO: move the locking (and more of this logic) into Libraries.
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ library = libraries_->Get(path);
+ }
+ if (library != nullptr) {
+ if (env->IsSameObject(library->GetClassLoader(), class_loader) == JNI_FALSE) {
+ // The library will be associated with class_loader. The JNI
+ // spec says we can't load the same library into more than one
+ // class loader.
+ StringAppendF(error_msg, "Shared library \"%s\" already opened by "
+ "ClassLoader %p; can't open in ClassLoader %p",
+ path.c_str(), library->GetClassLoader(), class_loader);
+ LOG(WARNING) << error_msg;
+ return false;
+ }
+ VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
+ << " ClassLoader " << class_loader << "]";
+ if (!library->CheckOnLoadResult()) {
+ StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
+ "to load \"%s\"", path.c_str());
+ return false;
+ }
+ return true;
+ }
+
+ // Open the shared library. Because we're using a full path, the system
+ // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
+ // resolve this library's dependencies though.)
+
+ // Failures here are expected when java.library.path has several entries
+ // and we have to hunt for the lib.
+
+ // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
+ // class unloading. Libraries will only be unloaded when the reference count (incremented by
+ // dlopen) becomes zero from dlclose.
+
+ Locks::mutator_lock_->AssertNotHeld(self);
+ const char* path_str = path.empty() ? nullptr : path.c_str();
+ void* handle = dlopen(path_str, RTLD_LAZY);
+ bool needs_native_bridge = false;
+ if (handle == nullptr) {
+ if (android::NativeBridgeIsSupported(path_str)) {
+ handle = android::NativeBridgeLoadLibrary(path_str, RTLD_LAZY);
+ needs_native_bridge = true;
+ }
+ }
+
+ VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
+
+ if (handle == nullptr) {
+ *error_msg = dlerror();
+ LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << *error_msg;
+ return false;
+ }
+
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ LOG(ERROR) << "Unexpected exception:";
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ // Create a new entry.
+ // TODO: move the locking (and more of this logic) into Libraries.
+ bool created_library = false;
+ {
+ // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
+ std::unique_ptr<SharedLibrary> new_library(
+ new SharedLibrary(env, self, path, handle, class_loader));
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ library = libraries_->Get(path);
+ if (library == nullptr) { // We won race to get libraries_lock.
+ library = new_library.release();
+ libraries_->Put(path, library);
+ created_library = true;
+ }
+ }
+ if (!created_library) {
+ LOG(INFO) << "WOW: we lost a race to add shared library: "
+ << "\"" << path << "\" ClassLoader=" << class_loader;
+ return library->CheckOnLoadResult();
+ }
+ VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+
+ bool was_successful = false;
+ void* sym;
+ if (needs_native_bridge) {
+ library->SetNeedsNativeBridge();
+ sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
+ } else {
+ sym = dlsym(handle, "JNI_OnLoad");
+ }
+ if (sym == nullptr) {
+ VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
+ was_successful = true;
+ } else {
+ // Call JNI_OnLoad. We have to override the current class
+ // loader, which will always be "null" since the stuff at the
+ // top of the stack is around Runtime.loadLibrary(). (See
+ // the comments in the JNI FindClass function.)
+ ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
+ self->SetClassLoaderOverride(class_loader);
+
+ VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+ typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+ JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
+ int version = (*jni_on_load)(this, nullptr);
+
+ self->SetClassLoaderOverride(old_class_loader.get());
+
+ if (version == JNI_ERR) {
+ StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
+ } else if (IsBadJniVersion(version)) {
+ StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
+ path.c_str(), version);
+ // It's unwise to call dlclose() here, but we can mark it
+ // as bad and ensure that future load attempts will fail.
+ // We don't know how far JNI_OnLoad got, so there could
+ // be some partially-initialized stuff accessible through
+ // newly-registered native method calls. We could try to
+ // unregister them, but that doesn't seem worthwhile.
+ } else {
+ was_successful = true;
+ }
+ VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
+ << " from JNI_OnLoad in \"" << path << "\"]";
+ }
+
+ library->SetResult(was_successful);
+ return was_successful;
+}
+
+void* JavaVMExt::FindCodeForNativeMethod(mirror::ArtMethod* m) {
+ CHECK(m->IsNative());
+ mirror::Class* c = m->GetDeclaringClass();
+ // If this is a static method, it could be called before the class has been initialized.
+ CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
+ std::string detail;
+ void* native_method;
+ Thread* self = Thread::Current();
+ {
+ MutexLock mu(self, *Locks::jni_libraries_lock_);
+ native_method = libraries_->FindNativeMethod(m, detail);
+ }
+ // Throwing can cause libraries_lock to be reacquired.
+ if (native_method == nullptr) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
+ }
+ return native_method;
+}
+
+void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ for (mirror::Object** entry : weak_globals_) {
+ // Since this is called by the GC, we don't need a read barrier.
+ mirror::Object* obj = *entry;
+ if (obj == nullptr) {
+ // Need to skip null here to distinguish between null entries
+ // and cleared weak ref entries.
+ continue;
+ }
+ mirror::Object* new_obj = callback(obj, arg);
+ if (new_obj == nullptr) {
+ new_obj = Runtime::Current()->GetClearedJniWeakGlobal();
+ }
+ *entry = new_obj;
+ }
+}
+
+void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
+ Thread* self = Thread::Current();
+ {
+ ReaderMutexLock mu(self, globals_lock_);
+ globals_.VisitRoots(callback, arg, 0, kRootJNIGlobal);
+ }
+ // The weak_globals table is visited by the GC itself (because it mutates the table).
+}
+
+// JNI Invocation interface.
+
+extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+ const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
+ if (IsBadJniVersion(args->version)) {
+ LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
+ return JNI_EVERSION;
+ }
+ RuntimeOptions options;
+ for (int i = 0; i < args->nOptions; ++i) {
+ JavaVMOption* option = &args->options[i];
+ options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
+ }
+ bool ignore_unrecognized = args->ignoreUnrecognized;
+ if (!Runtime::Create(options, ignore_unrecognized)) {
+ return JNI_ERR;
+ }
+ Runtime* runtime = Runtime::Current();
+ bool started = runtime->Start();
+ if (!started) {
+ delete Thread::Current()->GetJniEnv();
+ delete runtime->GetJavaVM();
+ LOG(WARNING) << "CreateJavaVM failed";
+ return JNI_ERR;
+ }
+ *p_env = Thread::Current()->GetJniEnv();
+ *p_vm = runtime->GetJavaVM();
+ return JNI_OK;
+}
+
+extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime == nullptr) {
+ *vm_count = 0;
+ } else {
+ *vm_count = 1;
+ vms[0] = runtime->GetJavaVM();
+ }
+ return JNI_OK;
+}
+
+// Historically unsupported.
+extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* /*vm_args*/) {
+ return JNI_ERR;
+}
+
+} // namespace art
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
new file mode 100644
index 0000000..2957ba3
--- /dev/null
+++ b/runtime/java_vm_ext.h
@@ -0,0 +1,173 @@
+/*
+ * 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_JAVA_VM_EXT_H_
+#define ART_RUNTIME_JAVA_VM_EXT_H_
+
+#include "jni.h"
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "indirect_reference_table.h"
+#include "reference_table.h"
+
+namespace art {
+
+namespace mirror {
+ class ArtMethod;
+ class Array;
+} // namespace mirror
+
+class Libraries;
+class ParsedOptions;
+class Runtime;
+
+class JavaVMExt : public JavaVM {
+ public:
+ JavaVMExt(Runtime* runtime, ParsedOptions* options);
+ ~JavaVMExt();
+
+ bool ForceCopy() const {
+ return force_copy_;
+ }
+
+ bool IsCheckJniEnabled() const {
+ return check_jni_;
+ }
+
+ bool IsTracingEnabled() const {
+ return tracing_enabled_;
+ }
+
+ Runtime* GetRuntime() const {
+ return runtime_;
+ }
+
+ void SetCheckJniAbortHook(void (*hook)(void*, const std::string&), void* data) {
+ check_jni_abort_hook_ = hook;
+ check_jni_abort_hook_data_ = data;
+ }
+
+ // Aborts execution unless there is an abort handler installed in which case it will return. Its
+ // therefore important that callers return after aborting as otherwise code following the abort
+ // will be executed in the abort handler case.
+ void JniAbort(const char* jni_function_name, const char* msg);
+
+ void JniAbortV(const char* jni_function_name, const char* fmt, va_list ap);
+
+ void JniAbortF(const char* jni_function_name, const char* fmt, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+ // If both "-Xcheck:jni" and "-Xjnitrace:" are enabled, we print trace messages
+ // when a native method that matches the -Xjnitrace argument calls a JNI function
+ // such as NewByteArray.
+ // If -verbose:third-party-jni is on, we want to log any JNI function calls
+ // made by a third-party native method.
+ bool ShouldTrace(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /**
+ * Loads the given shared library. 'path' is an absolute pathname.
+ *
+ * Returns 'true' on success. On failure, sets 'detail' to a
+ * human-readable description of the error.
+ */
+ bool LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject javaLoader,
+ std::string* error_msg);
+
+ /**
+ * Returns a pointer to the code for the native method 'm', found
+ * using dlsym(3) on every native library that's been loaded so far.
+ */
+ void* FindCodeForNativeMethod(mirror::ArtMethod* m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DumpForSigQuit(std::ostream& os)
+ LOCKS_EXCLUDED(Locks::jni_libraries_lock_, globals_lock_, weak_globals_lock_);
+
+ void DumpReferenceTables(std::ostream& os)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool SetCheckJniEnabled(bool enabled);
+
+ void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ jobject AddGlobalRef(Thread* self, mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ jweak AddWeakGlobalRef(Thread* self, mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void DeleteGlobalRef(Thread* self, jobject obj);
+
+ void DeleteWeakGlobalRef(Thread* self, jweak obj);
+
+ void SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Object* DecodeGlobal(Thread* self, IndirectRef ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ const JNIInvokeInterface* GetUncheckedFunctions() const {
+ return unchecked_functions_;
+ }
+
+ private:
+ Runtime* const runtime_;
+
+ // Used for testing. By default, we'll LOG(FATAL) the reason.
+ void (*check_jni_abort_hook_)(void* data, const std::string& reason);
+ void* check_jni_abort_hook_data_;
+
+ // Extra checking.
+ bool check_jni_;
+ bool force_copy_;
+ const bool tracing_enabled_;
+
+ // Extra diagnostics.
+ const std::string trace_;
+
+ // JNI global references.
+ ReaderWriterMutex globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
+ IndirectReferenceTable globals_;
+
+ std::unique_ptr<Libraries> libraries_ GUARDED_BY(Locks::jni_libraries_lock_);
+
+ // Used by -Xcheck:jni.
+ const JNIInvokeInterface* const unchecked_functions_;
+
+ // JNI weak global references.
+ Mutex weak_globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Since weak_globals_ contain weak roots, be careful not to
+ // directly access the object references in it. Use Get() with the
+ // read barrier enabled.
+ IndirectReferenceTable weak_globals_ GUARDED_BY(weak_globals_lock_);
+ bool allow_new_weak_globals_ GUARDED_BY(weak_globals_lock_);
+ ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(JavaVMExt);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JAVA_VM_EXT_H_
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index bb89813..0c9451c 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -299,7 +299,7 @@
private:
explicit JdwpState(const JdwpOptions* options);
- size_t ProcessRequest(Request& request, ExpandBuf* pReply);
+ size_t ProcessRequest(Request* request, ExpandBuf* pReply);
bool InvokeInProgress();
bool IsConnected();
void SuspendByPolicy(JdwpSuspendPolicy suspend_policy, JDWP::ObjectId thread_self_id)
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index e49a408..7c8c63c 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -783,7 +783,7 @@
<< StringPrintf(" (requestId=%#" PRIx32 ")", pEvent->requestId);
}
std::string thread_name;
- JdwpError error = Dbg::GetThreadName(thread_id, thread_name);
+ JdwpError error = Dbg::GetThreadName(thread_id, &thread_name);
if (error != JDWP::ERR_NONE) {
thread_name = "<unknown>";
}
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 1d56fe7..16a774f 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -65,7 +65,7 @@
static JdwpError WriteTaggedObject(ExpandBuf* reply, ObjectId object_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint8_t tag;
- JdwpError rc = Dbg::GetObjectTag(object_id, tag);
+ JdwpError rc = Dbg::GetObjectTag(object_id, &tag);
if (rc == ERR_NONE) {
expandBufAdd1(reply, tag);
expandBufAddObjectId(reply, object_id);
@@ -91,13 +91,13 @@
* If "is_constructor" is set, this returns "object_id" rather than the
* expected-to-be-void return value of the called function.
*/
-static JdwpError FinishInvoke(JdwpState*, Request& request, ExpandBuf* pReply,
+static JdwpError FinishInvoke(JdwpState*, Request* request, ExpandBuf* pReply,
ObjectId thread_id, ObjectId object_id,
RefTypeId class_id, MethodId method_id, bool is_constructor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK(!is_constructor || object_id != 0);
- int32_t arg_count = request.ReadSigned32("argument count");
+ int32_t arg_count = request->ReadSigned32("argument count");
VLOG(jdwp) << StringPrintf(" --> thread_id=%#" PRIx64 " object_id=%#" PRIx64,
thread_id, object_id);
@@ -109,14 +109,14 @@
std::unique_ptr<JdwpTag[]> argTypes(arg_count > 0 ? new JdwpTag[arg_count] : NULL);
std::unique_ptr<uint64_t[]> argValues(arg_count > 0 ? new uint64_t[arg_count] : NULL);
for (int32_t i = 0; i < arg_count; ++i) {
- argTypes[i] = request.ReadTag();
+ argTypes[i] = request->ReadTag();
size_t width = Dbg::GetTagWidth(argTypes[i]);
- argValues[i] = request.ReadValue(width);
+ argValues[i] = request->ReadValue(width);
VLOG(jdwp) << " " << argTypes[i] << StringPrintf("(%zd): %#" PRIx64, width,
argValues[i]);
}
- uint32_t options = request.ReadUnsigned32("InvokeOptions bit flags");
+ uint32_t options = request->ReadUnsigned32("InvokeOptions bit flags");
VLOG(jdwp) << StringPrintf(" options=0x%04x%s%s", options,
(options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
(options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
@@ -166,7 +166,7 @@
return err;
}
-static JdwpError VM_Version(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_Version(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Text information on runtime version.
std::string version(StringPrintf("Android Runtime %s", Runtime::Current()->GetVersion()));
@@ -190,12 +190,12 @@
* referenceTypeID. We need to send back more than one if the class has
* been loaded by multiple class loaders.
*/
-static JdwpError VM_ClassesBySignature(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError VM_ClassesBySignature(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::string classDescriptor(request.ReadUtf8String());
+ std::string classDescriptor(request->ReadUtf8String());
std::vector<RefTypeId> ids;
- Dbg::FindLoadedClassBySignature(classDescriptor.c_str(), ids);
+ Dbg::FindLoadedClassBySignature(classDescriptor.c_str(), &ids);
expandBufAdd4BE(pReply, ids.size());
@@ -222,10 +222,10 @@
* We exclude ourselves from the list, because we don't allow ourselves
* to be suspended, and that violates some JDWP expectations.
*/
-static JdwpError VM_AllThreads(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_AllThreads(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::vector<ObjectId> thread_ids;
- Dbg::GetThreads(0, thread_ids);
+ Dbg::GetThreads(nullptr /* all thread groups */, &thread_ids);
expandBufAdd4BE(pReply, thread_ids.size());
for (uint32_t i = 0; i < thread_ids.size(); ++i) {
@@ -238,7 +238,7 @@
/*
* List all thread groups that do not have a parent.
*/
-static JdwpError VM_TopLevelThreadGroups(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_TopLevelThreadGroups(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
/*
* TODO: maintain a list of parentless thread groups in the VM.
@@ -259,7 +259,7 @@
*
* All IDs are 8 bytes.
*/
-static JdwpError VM_IDSizes(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_IDSizes(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
expandBufAdd4BE(pReply, sizeof(FieldId));
expandBufAdd4BE(pReply, sizeof(MethodId));
@@ -269,7 +269,7 @@
return ERR_NONE;
}
-static JdwpError VM_Dispose(JdwpState*, Request&, ExpandBuf*)
+static JdwpError VM_Dispose(JdwpState*, Request*, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Dbg::Disposed();
return ERR_NONE;
@@ -281,7 +281,7 @@
*
* This needs to increment the "suspend count" on all threads.
*/
-static JdwpError VM_Suspend(JdwpState*, Request&, ExpandBuf*)
+static JdwpError VM_Suspend(JdwpState*, Request*, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Thread* self = Thread::Current();
self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
@@ -293,16 +293,16 @@
/*
* Resume execution. Decrements the "suspend count" of all threads.
*/
-static JdwpError VM_Resume(JdwpState*, Request&, ExpandBuf*)
+static JdwpError VM_Resume(JdwpState*, Request*, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Dbg::ProcessDelayedFullUndeoptimizations();
Dbg::ResumeVM();
return ERR_NONE;
}
-static JdwpError VM_Exit(JdwpState* state, Request& request, ExpandBuf*)
+static JdwpError VM_Exit(JdwpState* state, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uint32_t exit_status = request.ReadUnsigned32("exit_status");
+ uint32_t exit_status = request->ReadUnsigned32("exit_status");
state->ExitAfterReplying(exit_status);
return ERR_NONE;
}
@@ -313,9 +313,9 @@
* (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
* string "java.util.Arrays".)
*/
-static JdwpError VM_CreateString(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError VM_CreateString(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::string str(request.ReadUtf8String());
+ std::string str(request->ReadUtf8String());
ObjectId stringId = Dbg::CreateString(str);
if (stringId == 0) {
return ERR_OUT_OF_MEMORY;
@@ -324,7 +324,7 @@
return ERR_NONE;
}
-static JdwpError VM_ClassPaths(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_ClassPaths(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
expandBufAddUtf8String(pReply, "/");
@@ -345,18 +345,18 @@
return ERR_NONE;
}
-static JdwpError VM_DisposeObjects(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError VM_DisposeObjects(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- size_t object_count = request.ReadUnsigned32("object_count");
+ size_t object_count = request->ReadUnsigned32("object_count");
for (size_t i = 0; i < object_count; ++i) {
- ObjectId object_id = request.ReadObjectId();
- uint32_t reference_count = request.ReadUnsigned32("reference_count");
+ ObjectId object_id = request->ReadObjectId();
+ uint32_t reference_count = request->ReadUnsigned32("reference_count");
Dbg::DisposeObject(object_id, reference_count);
}
return ERR_NONE;
}
-static JdwpError VM_Capabilities(JdwpState*, Request&, ExpandBuf* reply)
+static JdwpError VM_Capabilities(JdwpState*, Request*, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
expandBufAdd1(reply, true); // canWatchFieldModification
expandBufAdd1(reply, true); // canWatchFieldAccess
@@ -368,7 +368,7 @@
return ERR_NONE;
}
-static JdwpError VM_CapabilitiesNew(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError VM_CapabilitiesNew(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// The first few capabilities are the same as those reported by the older call.
VM_Capabilities(NULL, request, reply);
@@ -398,7 +398,7 @@
static JdwpError VM_AllClassesImpl(ExpandBuf* pReply, bool descriptor_and_status, bool generic)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
std::vector<JDWP::RefTypeId> classes;
- Dbg::GetClassList(classes);
+ Dbg::GetClassList(&classes);
expandBufAdd4BE(pReply, classes.size());
@@ -426,29 +426,29 @@
return ERR_NONE;
}
-static JdwpError VM_AllClasses(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_AllClasses(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return VM_AllClassesImpl(pReply, true, false);
}
-static JdwpError VM_AllClassesWithGeneric(JdwpState*, Request&, ExpandBuf* pReply)
+static JdwpError VM_AllClassesWithGeneric(JdwpState*, Request*, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return VM_AllClassesImpl(pReply, true, true);
}
-static JdwpError VM_InstanceCounts(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError VM_InstanceCounts(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- int32_t class_count = request.ReadSigned32("class count");
+ int32_t class_count = request->ReadSigned32("class count");
if (class_count < 0) {
return ERR_ILLEGAL_ARGUMENT;
}
std::vector<RefTypeId> class_ids;
for (int32_t i = 0; i < class_count; ++i) {
- class_ids.push_back(request.ReadRefTypeId());
+ class_ids.push_back(request->ReadRefTypeId());
}
std::vector<uint64_t> counts;
- JdwpError rc = Dbg::GetInstanceCounts(class_ids, counts);
+ JdwpError rc = Dbg::GetInstanceCounts(class_ids, &counts);
if (rc != ERR_NONE) {
return rc;
}
@@ -460,22 +460,22 @@
return ERR_NONE;
}
-static JdwpError RT_Modifiers(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Modifiers(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::GetModifiers(refTypeId, pReply);
}
/*
* Get values from static fields in a reference type.
*/
-static JdwpError RT_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
- int32_t field_count = request.ReadSigned32("field count");
+ RefTypeId refTypeId = request->ReadRefTypeId();
+ int32_t field_count = request->ReadSigned32("field count");
expandBufAdd4BE(pReply, field_count);
for (int32_t i = 0; i < field_count; ++i) {
- FieldId fieldId = request.ReadFieldId();
+ FieldId fieldId = request->ReadFieldId();
JdwpError status = Dbg::GetStaticFieldValue(refTypeId, fieldId, pReply);
if (status != ERR_NONE) {
return status;
@@ -487,11 +487,11 @@
/*
* Get the name of the source file in which a reference type was declared.
*/
-static JdwpError RT_SourceFile(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_SourceFile(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
std::string source_file;
- JdwpError status = Dbg::GetSourceFile(refTypeId, source_file);
+ JdwpError status = Dbg::GetSourceFile(refTypeId, &source_file);
if (status != ERR_NONE) {
return status;
}
@@ -502,9 +502,9 @@
/*
* Return the current status of the reference type.
*/
-static JdwpError RT_Status(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Status(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
JDWP::JdwpTypeTag type_tag;
uint32_t class_status;
JDWP::JdwpError status = Dbg::GetClassInfo(refTypeId, &type_tag, &class_status, NULL);
@@ -518,20 +518,20 @@
/*
* Return interfaces implemented directly by this class.
*/
-static JdwpError RT_Interfaces(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Interfaces(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::OutputDeclaredInterfaces(refTypeId, pReply);
}
/*
* Return the class object corresponding to this type.
*/
-static JdwpError RT_ClassObject(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_ClassObject(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
ObjectId class_object_id;
- JdwpError status = Dbg::GetClassObject(refTypeId, class_object_id);
+ JdwpError status = Dbg::GetClassObject(refTypeId, &class_object_id);
if (status != ERR_NONE) {
return status;
}
@@ -545,15 +545,15 @@
*
* JDB seems interested, but DEX files don't currently support this.
*/
-static JdwpError RT_SourceDebugExtension(JdwpState*, Request&, ExpandBuf*)
+static JdwpError RT_SourceDebugExtension(JdwpState*, Request*, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
/* referenceTypeId in, string out */
return ERR_ABSENT_INFORMATION;
}
-static JdwpError RT_Signature(JdwpState*, Request& request, ExpandBuf* pReply, bool with_generic)
+static JdwpError RT_Signature(JdwpState*, Request* request, ExpandBuf* pReply, bool with_generic)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
std::string signature;
JdwpError status = Dbg::GetSignature(refTypeId, &signature);
@@ -567,12 +567,12 @@
return ERR_NONE;
}
-static JdwpError RT_Signature(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Signature(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return RT_Signature(state, request, pReply, false);
}
-static JdwpError RT_SignatureWithGeneric(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError RT_SignatureWithGeneric(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return RT_Signature(state, request, pReply, true);
}
@@ -581,9 +581,9 @@
* Return the instance of java.lang.ClassLoader that loaded the specified
* reference type, or null if it was loaded by the system loader.
*/
-static JdwpError RT_ClassLoader(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_ClassLoader(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::GetClassLoader(refTypeId, pReply);
}
@@ -591,16 +591,16 @@
* Given a referenceTypeId, return a block of stuff that describes the
* fields declared by a class.
*/
-static JdwpError RT_FieldsWithGeneric(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_FieldsWithGeneric(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::OutputDeclaredFields(refTypeId, true, pReply);
}
// Obsolete equivalent of FieldsWithGeneric, without the generic type information.
-static JdwpError RT_Fields(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Fields(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::OutputDeclaredFields(refTypeId, false, pReply);
}
@@ -608,29 +608,29 @@
* Given a referenceTypeID, return a block of goodies describing the
* methods declared by a class.
*/
-static JdwpError RT_MethodsWithGeneric(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_MethodsWithGeneric(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::OutputDeclaredMethods(refTypeId, true, pReply);
}
// Obsolete equivalent of MethodsWithGeneric, without the generic type information.
-static JdwpError RT_Methods(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError RT_Methods(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
return Dbg::OutputDeclaredMethods(refTypeId, false, pReply);
}
-static JdwpError RT_Instances(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError RT_Instances(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
- int32_t max_count = request.ReadSigned32("max count");
+ RefTypeId class_id = request->ReadRefTypeId();
+ int32_t max_count = request->ReadSigned32("max count");
if (max_count < 0) {
return ERR_ILLEGAL_ARGUMENT;
}
std::vector<ObjectId> instances;
- JdwpError rc = Dbg::GetInstances(class_id, max_count, instances);
+ JdwpError rc = Dbg::GetInstances(class_id, max_count, &instances);
if (rc != ERR_NONE) {
return rc;
}
@@ -641,11 +641,11 @@
/*
* Return the immediate superclass of a class.
*/
-static JdwpError CT_Superclass(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError CT_Superclass(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
+ RefTypeId class_id = request->ReadRefTypeId();
RefTypeId superClassId;
- JdwpError status = Dbg::GetSuperclass(class_id, superClassId);
+ JdwpError status = Dbg::GetSuperclass(class_id, &superClassId);
if (status != ERR_NONE) {
return status;
}
@@ -656,18 +656,18 @@
/*
* Set static class values.
*/
-static JdwpError CT_SetValues(JdwpState* , Request& request, ExpandBuf*)
+static JdwpError CT_SetValues(JdwpState* , Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
- int32_t values_count = request.ReadSigned32("values count");
+ RefTypeId class_id = request->ReadRefTypeId();
+ int32_t values_count = request->ReadSigned32("values count");
UNUSED(class_id);
for (int32_t i = 0; i < values_count; ++i) {
- FieldId fieldId = request.ReadFieldId();
+ FieldId fieldId = request->ReadFieldId();
JDWP::JdwpTag fieldTag = Dbg::GetStaticFieldBasicTag(fieldId);
size_t width = Dbg::GetTagWidth(fieldTag);
- uint64_t value = request.ReadValue(width);
+ uint64_t value = request->ReadValue(width);
VLOG(jdwp) << " --> field=" << fieldId << " tag=" << fieldTag << " --> " << value;
JdwpError status = Dbg::SetStaticFieldValue(fieldId, value, width);
@@ -685,11 +685,11 @@
* Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
* values in the "variables" display.
*/
-static JdwpError CT_InvokeMethod(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError CT_InvokeMethod(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
- ObjectId thread_id = request.ReadThreadId();
- MethodId method_id = request.ReadMethodId();
+ RefTypeId class_id = request->ReadRefTypeId();
+ ObjectId thread_id = request->ReadThreadId();
+ MethodId method_id = request->ReadMethodId();
return FinishInvoke(state, request, pReply, thread_id, 0, class_id, method_id, false);
}
@@ -701,14 +701,14 @@
* Example: in IntelliJ, create a watch on "new String(myByteArray)" to
* see the contents of a byte[] as a string.
*/
-static JdwpError CT_NewInstance(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError CT_NewInstance(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
- ObjectId thread_id = request.ReadThreadId();
- MethodId method_id = request.ReadMethodId();
+ RefTypeId class_id = request->ReadRefTypeId();
+ ObjectId thread_id = request->ReadThreadId();
+ MethodId method_id = request->ReadMethodId();
ObjectId object_id;
- JdwpError status = Dbg::CreateObject(class_id, object_id);
+ JdwpError status = Dbg::CreateObject(class_id, &object_id);
if (status != ERR_NONE) {
return status;
}
@@ -721,13 +721,13 @@
/*
* Create a new array object of the requested type and length.
*/
-static JdwpError AT_newInstance(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError AT_newInstance(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId arrayTypeId = request.ReadRefTypeId();
- int32_t length = request.ReadSigned32("length");
+ RefTypeId arrayTypeId = request->ReadRefTypeId();
+ int32_t length = request->ReadSigned32("length");
ObjectId object_id;
- JdwpError status = Dbg::CreateArrayObject(arrayTypeId, length, object_id);
+ JdwpError status = Dbg::CreateArrayObject(arrayTypeId, length, &object_id);
if (status != ERR_NONE) {
return status;
}
@@ -742,21 +742,21 @@
/*
* Return line number information for the method, if present.
*/
-static JdwpError M_LineTable(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError M_LineTable(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId refTypeId = request.ReadRefTypeId();
- MethodId method_id = request.ReadMethodId();
+ RefTypeId refTypeId = request->ReadRefTypeId();
+ MethodId method_id = request->ReadMethodId();
Dbg::OutputLineTable(refTypeId, method_id, pReply);
return ERR_NONE;
}
-static JdwpError M_VariableTable(JdwpState*, Request& request, ExpandBuf* pReply,
+static JdwpError M_VariableTable(JdwpState*, Request* request, ExpandBuf* pReply,
bool generic)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
- MethodId method_id = request.ReadMethodId();
+ RefTypeId class_id = request->ReadRefTypeId();
+ MethodId method_id = request->ReadMethodId();
// We could return ERR_ABSENT_INFORMATION here if the DEX file was built without local variable
// information. That will cause Eclipse to make a best-effort attempt at displaying local
@@ -766,23 +766,23 @@
return ERR_NONE;
}
-static JdwpError M_VariableTable(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError M_VariableTable(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return M_VariableTable(state, request, pReply, false);
}
-static JdwpError M_VariableTableWithGeneric(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError M_VariableTableWithGeneric(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return M_VariableTable(state, request, pReply, true);
}
-static JdwpError M_Bytecodes(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError M_Bytecodes(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_id = request.ReadRefTypeId();
- MethodId method_id = request.ReadMethodId();
+ RefTypeId class_id = request->ReadRefTypeId();
+ MethodId method_id = request->ReadMethodId();
std::vector<uint8_t> bytecodes;
- JdwpError rc = Dbg::GetBytecodes(class_id, method_id, bytecodes);
+ JdwpError rc = Dbg::GetBytecodes(class_id, method_id, &bytecodes);
if (rc != ERR_NONE) {
return rc;
}
@@ -802,23 +802,23 @@
* This can get called on different things, e.g. thread_id gets
* passed in here.
*/
-static JdwpError OR_ReferenceType(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError OR_ReferenceType(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
+ ObjectId object_id = request->ReadObjectId();
return Dbg::GetReferenceType(object_id, pReply);
}
/*
* Get values from the fields of an object.
*/
-static JdwpError OR_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError OR_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
- int32_t field_count = request.ReadSigned32("field count");
+ ObjectId object_id = request->ReadObjectId();
+ int32_t field_count = request->ReadSigned32("field count");
expandBufAdd4BE(pReply, field_count);
for (int32_t i = 0; i < field_count; ++i) {
- FieldId fieldId = request.ReadFieldId();
+ FieldId fieldId = request->ReadFieldId();
JdwpError status = Dbg::GetFieldValue(object_id, fieldId, pReply);
if (status != ERR_NONE) {
return status;
@@ -831,17 +831,17 @@
/*
* Set values in the fields of an object.
*/
-static JdwpError OR_SetValues(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError OR_SetValues(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
- int32_t field_count = request.ReadSigned32("field count");
+ ObjectId object_id = request->ReadObjectId();
+ int32_t field_count = request->ReadSigned32("field count");
for (int32_t i = 0; i < field_count; ++i) {
- FieldId fieldId = request.ReadFieldId();
+ FieldId fieldId = request->ReadFieldId();
JDWP::JdwpTag fieldTag = Dbg::GetFieldBasicTag(fieldId);
size_t width = Dbg::GetTagWidth(fieldTag);
- uint64_t value = request.ReadValue(width);
+ uint64_t value = request->ReadValue(width);
VLOG(jdwp) << " --> fieldId=" << fieldId << " tag=" << fieldTag << "(" << width << ") value=" << value;
JdwpError status = Dbg::SetFieldValue(object_id, fieldId, value, width);
@@ -853,9 +853,9 @@
return ERR_NONE;
}
-static JdwpError OR_MonitorInfo(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError OR_MonitorInfo(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
+ ObjectId object_id = request->ReadObjectId();
return Dbg::GetMonitorInfo(object_id, reply);
}
@@ -870,47 +870,47 @@
* object), it will try to invoke the object's toString() function. This
* feature becomes crucial when examining ArrayLists with Eclipse.
*/
-static JdwpError OR_InvokeMethod(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError OR_InvokeMethod(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
- ObjectId thread_id = request.ReadThreadId();
- RefTypeId class_id = request.ReadRefTypeId();
- MethodId method_id = request.ReadMethodId();
+ ObjectId object_id = request->ReadObjectId();
+ ObjectId thread_id = request->ReadThreadId();
+ RefTypeId class_id = request->ReadRefTypeId();
+ MethodId method_id = request->ReadMethodId();
return FinishInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, false);
}
-static JdwpError OR_DisableCollection(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError OR_DisableCollection(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
+ ObjectId object_id = request->ReadObjectId();
return Dbg::DisableCollection(object_id);
}
-static JdwpError OR_EnableCollection(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError OR_EnableCollection(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
+ ObjectId object_id = request->ReadObjectId();
return Dbg::EnableCollection(object_id);
}
-static JdwpError OR_IsCollected(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError OR_IsCollected(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
+ ObjectId object_id = request->ReadObjectId();
bool is_collected;
- JdwpError rc = Dbg::IsCollected(object_id, is_collected);
+ JdwpError rc = Dbg::IsCollected(object_id, &is_collected);
expandBufAdd1(pReply, is_collected ? 1 : 0);
return rc;
}
-static JdwpError OR_ReferringObjects(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError OR_ReferringObjects(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId object_id = request.ReadObjectId();
- int32_t max_count = request.ReadSigned32("max count");
+ ObjectId object_id = request->ReadObjectId();
+ int32_t max_count = request->ReadSigned32("max count");
if (max_count < 0) {
return ERR_ILLEGAL_ARGUMENT;
}
std::vector<ObjectId> referring_objects;
- JdwpError rc = Dbg::GetReferringObjects(object_id, max_count, referring_objects);
+ JdwpError rc = Dbg::GetReferringObjects(object_id, max_count, &referring_objects);
if (rc != ERR_NONE) {
return rc;
}
@@ -921,9 +921,9 @@
/*
* Return the string value in a string object.
*/
-static JdwpError SR_Value(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError SR_Value(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId stringObject = request.ReadObjectId();
+ ObjectId stringObject = request->ReadObjectId();
std::string str;
JDWP::JdwpError error = Dbg::StringToUtf8(stringObject, &str);
if (error != JDWP::ERR_NONE) {
@@ -940,12 +940,12 @@
/*
* Return a thread's name.
*/
-static JdwpError TR_Name(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_Name(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
std::string name;
- JdwpError error = Dbg::GetThreadName(thread_id, name);
+ JdwpError error = Dbg::GetThreadName(thread_id, &name);
if (error != ERR_NONE) {
return error;
}
@@ -961,9 +961,9 @@
* It's supposed to remain suspended even if interpreted code wants to
* resume it; only the JDI is allowed to resume it.
*/
-static JdwpError TR_Suspend(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError TR_Suspend(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
if (thread_id == Dbg::GetThreadSelfId()) {
LOG(INFO) << " Warning: ignoring request to suspend self";
@@ -980,9 +980,9 @@
/*
* Resume the specified thread.
*/
-static JdwpError TR_Resume(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError TR_Resume(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
if (thread_id == Dbg::GetThreadSelfId()) {
LOG(INFO) << " Warning: ignoring request to resume self";
@@ -998,9 +998,9 @@
/*
* Return status of specified thread.
*/
-static JdwpError TR_Status(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_Status(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
JDWP::JdwpThreadStatus threadStatus;
JDWP::JdwpSuspendStatus suspendStatus;
@@ -1020,9 +1020,9 @@
/*
* Return the thread group that the specified thread is a member of.
*/
-static JdwpError TR_ThreadGroup(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_ThreadGroup(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
return Dbg::GetThreadGroup(thread_id, pReply);
}
@@ -1032,14 +1032,14 @@
* If the thread isn't suspended, the error code isn't defined, but should
* be THREAD_NOT_SUSPENDED.
*/
-static JdwpError TR_Frames(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_Frames(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
- uint32_t start_frame = request.ReadUnsigned32("start frame");
- uint32_t length = request.ReadUnsigned32("length");
+ ObjectId thread_id = request->ReadThreadId();
+ uint32_t start_frame = request->ReadUnsigned32("start frame");
+ uint32_t length = request->ReadUnsigned32("length");
size_t actual_frame_count;
- JdwpError error = Dbg::GetThreadFrameCount(thread_id, actual_frame_count);
+ JdwpError error = Dbg::GetThreadFrameCount(thread_id, &actual_frame_count);
if (error != ERR_NONE) {
return error;
}
@@ -1064,12 +1064,12 @@
/*
* Returns the #of frames on the specified thread, which must be suspended.
*/
-static JdwpError TR_FrameCount(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_FrameCount(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
size_t frame_count;
- JdwpError rc = Dbg::GetThreadFrameCount(thread_id, frame_count);
+ JdwpError rc = Dbg::GetThreadFrameCount(thread_id, &frame_count);
if (rc != ERR_NONE) {
return rc;
}
@@ -1078,13 +1078,13 @@
return ERR_NONE;
}
-static JdwpError TR_OwnedMonitors(Request& request, ExpandBuf* reply, bool with_stack_depths)
+static JdwpError TR_OwnedMonitors(Request* request, ExpandBuf* reply, bool with_stack_depths)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
std::vector<ObjectId> monitors;
std::vector<uint32_t> stack_depths;
- JdwpError rc = Dbg::GetOwnedMonitors(thread_id, monitors, stack_depths);
+ JdwpError rc = Dbg::GetOwnedMonitors(thread_id, &monitors, &stack_depths);
if (rc != ERR_NONE) {
return rc;
}
@@ -1102,31 +1102,31 @@
return ERR_NONE;
}
-static JdwpError TR_OwnedMonitors(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_OwnedMonitors(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return TR_OwnedMonitors(request, reply, false);
}
-static JdwpError TR_OwnedMonitorsStackDepthInfo(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_OwnedMonitorsStackDepthInfo(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return TR_OwnedMonitors(request, reply, true);
}
-static JdwpError TR_CurrentContendedMonitor(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_CurrentContendedMonitor(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
ObjectId contended_monitor;
- JdwpError rc = Dbg::GetContendedMonitor(thread_id, contended_monitor);
+ JdwpError rc = Dbg::GetContendedMonitor(thread_id, &contended_monitor);
if (rc != ERR_NONE) {
return rc;
}
return WriteTaggedObject(reply, contended_monitor);
}
-static JdwpError TR_Interrupt(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError TR_Interrupt(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
return Dbg::Interrupt(thread_id);
}
@@ -1136,9 +1136,9 @@
* (The thread *might* still be running -- it might not have examined
* its suspend count recently.)
*/
-static JdwpError TR_DebugSuspendCount(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TR_DebugSuspendCount(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
return Dbg::GetThreadDebugSuspendCount(thread_id, pReply);
}
@@ -1147,63 +1147,41 @@
*
* The Eclipse debugger recognizes "main" and "system" as special.
*/
-static JdwpError TGR_Name(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TGR_Name(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_group_id = request.ReadThreadGroupId();
-
- expandBufAddUtf8String(pReply, Dbg::GetThreadGroupName(thread_group_id));
-
- return ERR_NONE;
+ ObjectId thread_group_id = request->ReadThreadGroupId();
+ return Dbg::GetThreadGroupName(thread_group_id, pReply);
}
/*
* Returns the thread group -- if any -- that contains the specified
* thread group.
*/
-static JdwpError TGR_Parent(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TGR_Parent(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_group_id = request.ReadThreadGroupId();
-
- ObjectId parentGroup = Dbg::GetThreadGroupParent(thread_group_id);
- expandBufAddObjectId(pReply, parentGroup);
-
- return ERR_NONE;
+ ObjectId thread_group_id = request->ReadThreadGroupId();
+ return Dbg::GetThreadGroupParent(thread_group_id, pReply);
}
/*
* Return the active threads and thread groups that are part of the
* specified thread group.
*/
-static JdwpError TGR_Children(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError TGR_Children(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_group_id = request.ReadThreadGroupId();
-
- std::vector<ObjectId> thread_ids;
- Dbg::GetThreads(thread_group_id, thread_ids);
- expandBufAdd4BE(pReply, thread_ids.size());
- for (uint32_t i = 0; i < thread_ids.size(); ++i) {
- expandBufAddObjectId(pReply, thread_ids[i]);
- }
-
- std::vector<ObjectId> child_thread_groups_ids;
- Dbg::GetChildThreadGroups(thread_group_id, child_thread_groups_ids);
- expandBufAdd4BE(pReply, child_thread_groups_ids.size());
- for (uint32_t i = 0; i < child_thread_groups_ids.size(); ++i) {
- expandBufAddObjectId(pReply, child_thread_groups_ids[i]);
- }
-
- return ERR_NONE;
+ ObjectId thread_group_id = request->ReadThreadGroupId();
+ return Dbg::GetThreadGroupChildren(thread_group_id, pReply);
}
/*
* Return the #of components in the array.
*/
-static JdwpError AR_Length(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError AR_Length(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId array_id = request.ReadArrayId();
+ ObjectId array_id = request->ReadArrayId();
- int length;
- JdwpError status = Dbg::GetArrayLength(array_id, length);
+ int32_t length;
+ JdwpError status = Dbg::GetArrayLength(array_id, &length);
if (status != ERR_NONE) {
return status;
}
@@ -1217,28 +1195,28 @@
/*
* Return the values from an array.
*/
-static JdwpError AR_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError AR_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId array_id = request.ReadArrayId();
- uint32_t offset = request.ReadUnsigned32("offset");
- uint32_t length = request.ReadUnsigned32("length");
+ ObjectId array_id = request->ReadArrayId();
+ uint32_t offset = request->ReadUnsigned32("offset");
+ uint32_t length = request->ReadUnsigned32("length");
return Dbg::OutputArray(array_id, offset, length, pReply);
}
/*
* Set values in an array.
*/
-static JdwpError AR_SetValues(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError AR_SetValues(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId array_id = request.ReadArrayId();
- uint32_t offset = request.ReadUnsigned32("offset");
- uint32_t count = request.ReadUnsigned32("count");
+ ObjectId array_id = request->ReadArrayId();
+ uint32_t offset = request->ReadUnsigned32("offset");
+ uint32_t count = request->ReadUnsigned32("count");
return Dbg::SetArrayElements(array_id, offset, count, request);
}
-static JdwpError CLR_VisibleClasses(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError CLR_VisibleClasses(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- request.ReadObjectId(); // classLoaderObject
+ request->ReadObjectId(); // classLoaderObject
// TODO: we should only return classes which have the given class loader as a defining or
// initiating loader. The former would be easy; the latter is hard, because we don't have
// any such notion.
@@ -1250,11 +1228,11 @@
*
* Reply with a requestID.
*/
-static JdwpError ER_Set(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError ER_Set(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- JdwpEventKind event_kind = request.ReadEnum1<JdwpEventKind>("event kind");
- JdwpSuspendPolicy suspend_policy = request.ReadEnum1<JdwpSuspendPolicy>("suspend policy");
- int32_t modifier_count = request.ReadSigned32("modifier count");
+ JdwpEventKind event_kind = request->ReadEnum1<JdwpEventKind>("event kind");
+ JdwpSuspendPolicy suspend_policy = request->ReadEnum1<JdwpSuspendPolicy>("suspend policy");
+ int32_t modifier_count = request->ReadSigned32("modifier count");
CHECK_LT(modifier_count, 256); /* reasonableness check */
@@ -1269,12 +1247,12 @@
*/
for (int32_t i = 0; i < modifier_count; ++i) {
JdwpEventMod& mod = pEvent->mods[i];
- mod.modKind = request.ReadModKind();
+ mod.modKind = request->ReadModKind();
switch (mod.modKind) {
case MK_COUNT:
{
// Report once, when "--count" reaches 0.
- uint32_t count = request.ReadUnsigned32("count");
+ uint32_t count = request->ReadUnsigned32("count");
if (count == 0) {
return ERR_INVALID_COUNT;
}
@@ -1284,21 +1262,21 @@
case MK_CONDITIONAL:
{
// Conditional on expression.
- uint32_t exprId = request.ReadUnsigned32("expr id");
+ uint32_t exprId = request->ReadUnsigned32("expr id");
mod.conditional.exprId = exprId;
}
break;
case MK_THREAD_ONLY:
{
// Only report events in specified thread.
- ObjectId thread_id = request.ReadThreadId();
+ ObjectId thread_id = request->ReadThreadId();
mod.threadOnly.threadId = thread_id;
}
break;
case MK_CLASS_ONLY:
{
// For ClassPrepare, MethodEntry.
- RefTypeId class_id = request.ReadRefTypeId();
+ RefTypeId class_id = request->ReadRefTypeId();
mod.classOnly.refTypeId = class_id;
}
break;
@@ -1306,7 +1284,7 @@
{
// Restrict events to matching classes.
// pattern is "java.foo.*", we want "java/foo/*".
- std::string pattern(request.ReadUtf8String());
+ std::string pattern(request->ReadUtf8String());
std::replace(pattern.begin(), pattern.end(), '.', '/');
mod.classMatch.classPattern = strdup(pattern.c_str());
}
@@ -1315,7 +1293,7 @@
{
// Restrict events to non-matching classes.
// pattern is "java.foo.*", we want "java/foo/*".
- std::string pattern(request.ReadUtf8String());
+ std::string pattern(request->ReadUtf8String());
std::replace(pattern.begin(), pattern.end(), '.', '/');
mod.classExclude.classPattern = strdup(pattern.c_str());
}
@@ -1323,23 +1301,23 @@
case MK_LOCATION_ONLY:
{
// Restrict certain events based on location.
- JdwpLocation location = request.ReadLocation();
+ JdwpLocation location = request->ReadLocation();
mod.locationOnly.loc = location;
}
break;
case MK_EXCEPTION_ONLY:
{
// Modifies EK_EXCEPTION events,
- mod.exceptionOnly.refTypeId = request.ReadRefTypeId(); // null => all exceptions.
- mod.exceptionOnly.caught = request.ReadEnum1<uint8_t>("caught");
- mod.exceptionOnly.uncaught = request.ReadEnum1<uint8_t>("uncaught");
+ mod.exceptionOnly.refTypeId = request->ReadRefTypeId(); // null => all exceptions.
+ mod.exceptionOnly.caught = request->ReadEnum1<uint8_t>("caught");
+ mod.exceptionOnly.uncaught = request->ReadEnum1<uint8_t>("uncaught");
}
break;
case MK_FIELD_ONLY:
{
// For field access/modification events.
- RefTypeId declaring = request.ReadRefTypeId();
- FieldId fieldId = request.ReadFieldId();
+ RefTypeId declaring = request->ReadRefTypeId();
+ FieldId fieldId = request->ReadFieldId();
mod.fieldOnly.refTypeId = declaring;
mod.fieldOnly.fieldId = fieldId;
}
@@ -1347,9 +1325,9 @@
case MK_STEP:
{
// For use with EK_SINGLE_STEP.
- ObjectId thread_id = request.ReadThreadId();
- uint32_t size = request.ReadUnsigned32("step size");
- uint32_t depth = request.ReadUnsigned32("step depth");
+ ObjectId thread_id = request->ReadThreadId();
+ uint32_t size = request->ReadUnsigned32("step size");
+ uint32_t depth = request->ReadUnsigned32("step depth");
VLOG(jdwp) << StringPrintf(" Step: thread=%#" PRIx64, thread_id)
<< " size=" << JdwpStepSize(size) << " depth=" << JdwpStepDepth(depth);
@@ -1361,7 +1339,7 @@
case MK_INSTANCE_ONLY:
{
// Report events related to a specific object.
- ObjectId instance = request.ReadObjectId();
+ ObjectId instance = request->ReadObjectId();
mod.instanceOnly.objectId = instance;
}
break;
@@ -1391,10 +1369,10 @@
return err;
}
-static JdwpError ER_Clear(JdwpState* state, Request& request, ExpandBuf*)
+static JdwpError ER_Clear(JdwpState* state, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- request.ReadEnum1<JdwpEventKind>("event kind");
- uint32_t requestId = request.ReadUnsigned32("request id");
+ request->ReadEnum1<JdwpEventKind>("event kind");
+ uint32_t requestId = request->ReadUnsigned32("request id");
// Failure to find an event with a matching ID is a no-op
// and does not return an error.
@@ -1405,59 +1383,23 @@
/*
* Return the values of arguments and local variables.
*/
-static JdwpError SF_GetValues(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError SF_GetValues(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
- FrameId frame_id = request.ReadFrameId();
- int32_t slot_count = request.ReadSigned32("slot count");
-
- expandBufAdd4BE(pReply, slot_count); /* "int values" */
- for (int32_t i = 0; i < slot_count; ++i) {
- uint32_t slot = request.ReadUnsigned32("slot");
- JDWP::JdwpTag reqSigByte = request.ReadTag();
-
- VLOG(jdwp) << " --> slot " << slot << " " << reqSigByte;
-
- size_t width = Dbg::GetTagWidth(reqSigByte);
- uint8_t* ptr = expandBufAddSpace(pReply, width+1);
- JdwpError error = Dbg::GetLocalValue(thread_id, frame_id, slot, reqSigByte, ptr, width);
- if (error != ERR_NONE) {
- return error;
- }
- }
-
- return ERR_NONE;
+ return Dbg::GetLocalValues(request, pReply);
}
/*
* Set the values of arguments and local variables.
*/
-static JdwpError SF_SetValues(JdwpState*, Request& request, ExpandBuf*)
+static JdwpError SF_SetValues(JdwpState*, Request* request, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
- FrameId frame_id = request.ReadFrameId();
- int32_t slot_count = request.ReadSigned32("slot count");
-
- for (int32_t i = 0; i < slot_count; ++i) {
- uint32_t slot = request.ReadUnsigned32("slot");
- JDWP::JdwpTag sigByte = request.ReadTag();
- size_t width = Dbg::GetTagWidth(sigByte);
- uint64_t value = request.ReadValue(width);
-
- VLOG(jdwp) << " --> slot " << slot << " " << sigByte << " " << value;
- JdwpError error = Dbg::SetLocalValue(thread_id, frame_id, slot, sigByte, value, width);
- if (error != ERR_NONE) {
- return error;
- }
- }
-
- return ERR_NONE;
+ return Dbg::SetLocalValues(request);
}
-static JdwpError SF_ThisObject(JdwpState*, Request& request, ExpandBuf* reply)
+static JdwpError SF_ThisObject(JdwpState*, Request* request, ExpandBuf* reply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ObjectId thread_id = request.ReadThreadId();
- FrameId frame_id = request.ReadFrameId();
+ ObjectId thread_id = request->ReadThreadId();
+ FrameId frame_id = request->ReadFrameId();
ObjectId object_id;
JdwpError rc = Dbg::GetThisObject(thread_id, frame_id, &object_id);
@@ -1475,16 +1417,16 @@
* reused, whereas ClassIds can be recycled like any other object. (Either
* that, or I have no idea what this is for.)
*/
-static JdwpError COR_ReflectedType(JdwpState*, Request& request, ExpandBuf* pReply)
+static JdwpError COR_ReflectedType(JdwpState*, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RefTypeId class_object_id = request.ReadRefTypeId();
+ RefTypeId class_object_id = request->ReadRefTypeId();
return Dbg::GetReflectedType(class_object_id, pReply);
}
/*
* Handle a DDM packet with a single chunk in it.
*/
-static JdwpError DDM_Chunk(JdwpState* state, Request& request, ExpandBuf* pReply)
+static JdwpError DDM_Chunk(JdwpState* state, Request* request, ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
state->NotifyDdmsActive();
uint8_t* replyBuf = NULL;
@@ -1505,7 +1447,7 @@
/*
* Handler map decl.
*/
-typedef JdwpError (*JdwpRequestHandler)(JdwpState* state, Request& request, ExpandBuf* reply);
+typedef JdwpError (*JdwpRequestHandler)(JdwpState* state, Request* request, ExpandBuf* reply);
struct JdwpHandlerMap {
uint8_t cmdSet;
@@ -1648,20 +1590,20 @@
{ 199, 1, DDM_Chunk, "DDM.Chunk" },
};
-static const char* GetCommandName(Request& request) {
+static const char* GetCommandName(Request* request) {
for (size_t i = 0; i < arraysize(gHandlers); ++i) {
- if (gHandlers[i].cmdSet == request.GetCommandSet() && gHandlers[i].cmd == request.GetCommand()) {
+ if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand()) {
return gHandlers[i].name;
}
}
return "?UNKNOWN?";
}
-static std::string DescribeCommand(Request& request) {
+static std::string DescribeCommand(Request* request) {
std::string result;
result += "REQUEST: ";
result += GetCommandName(request);
- result += StringPrintf(" (length=%zu id=0x%06x)", request.GetLength(), request.GetId());
+ result += StringPrintf(" (length=%zu id=0x%06x)", request->GetLength(), request->GetId());
return result;
}
@@ -1670,10 +1612,10 @@
*
* On entry, the JDWP thread is in VMWAIT.
*/
-size_t JdwpState::ProcessRequest(Request& request, ExpandBuf* pReply) {
+size_t JdwpState::ProcessRequest(Request* request, ExpandBuf* pReply) {
JdwpError result = ERR_NONE;
- if (request.GetCommandSet() != kJDWPDdmCmdSet) {
+ if (request->GetCommandSet() != kJDWPDdmCmdSet) {
/*
* Activity from a debugger, not merely ddms. Mark us as having an
* active debugger session, and zero out the last-activity timestamp
@@ -1693,7 +1635,7 @@
* thread to finish, and then clear the block. Depending on the thread
* suspend policy, this may allow events in other threads to fire,
* but those events have no bearing on what the debugger has sent us
- * in the current request.
+ * in the current request->
*
* Note that we MUST clear the event token before waking the event
* thread up, or risk waiting for the thread to suspend after we've
@@ -1702,7 +1644,7 @@
SetWaitForEventThread(0);
/*
- * We do not want events to be sent while we process a request. Indicate the JDWP thread starts
+ * We do not want events to be sent while we process a request-> Indicate the JDWP thread starts
* to process a request so other threads wait for it to finish before sending an event.
*/
StartProcessingRequest();
@@ -1718,18 +1660,18 @@
size_t i;
for (i = 0; i < arraysize(gHandlers); ++i) {
- if (gHandlers[i].cmdSet == request.GetCommandSet() && gHandlers[i].cmd == request.GetCommand() && gHandlers[i].func != NULL) {
+ if (gHandlers[i].cmdSet == request->GetCommandSet() && gHandlers[i].cmd == request->GetCommand() && gHandlers[i].func != NULL) {
VLOG(jdwp) << DescribeCommand(request);
result = (*gHandlers[i].func)(this, request, pReply);
if (result == ERR_NONE) {
- request.CheckConsumed();
+ request->CheckConsumed();
}
break;
}
}
if (i == arraysize(gHandlers)) {
LOG(ERROR) << "Command not implemented: " << DescribeCommand(request);
- LOG(ERROR) << HexDump(request.data(), request.size(), false, "");
+ LOG(ERROR) << HexDump(request->data(), request->size(), false, "");
result = ERR_NOT_IMPLEMENTED;
}
@@ -1741,11 +1683,11 @@
uint8_t* replyBuf = expandBufGetBuffer(pReply);
size_t replyLength = (result == ERR_NONE) ? expandBufGetLength(pReply) : kJDWPHeaderLen;
Set4BE(replyBuf + 0, replyLength);
- Set4BE(replyBuf + 4, request.GetId());
+ Set4BE(replyBuf + 4, request->GetId());
Set1(replyBuf + 8, kJDWPFlagReply);
Set2BE(replyBuf + 9, result);
- CHECK_GT(expandBufGetLength(pReply), 0U) << GetCommandName(request) << " " << request.GetId();
+ CHECK_GT(expandBufGetLength(pReply), 0U) << GetCommandName(request) << " " << request->GetId();
size_t respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
VLOG(jdwp) << "REPLY: " << GetCommandName(request) << " " << result << " (length=" << respLen << ")";
@@ -1759,7 +1701,7 @@
* Update last-activity timestamp. We really only need this during
* the initial setup. Only update if this is a non-DDMS packet.
*/
- if (request.GetCommandSet() != kJDWPDdmCmdSet) {
+ if (request->GetCommandSet() != kJDWPDdmCmdSet) {
last_activity_time_ms_.StoreSequentiallyConsistent(MilliTime());
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index a4f427c..c500ef5 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -373,7 +373,7 @@
JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
ExpandBuf* pReply = expandBufAlloc();
- size_t replyLength = ProcessRequest(request, pReply);
+ size_t replyLength = ProcessRequest(&request, pReply);
ssize_t cc = netStateBase->WritePacket(pReply, replyLength);
/*
diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc
index ad18d8a..35aaf0a 100644
--- a/runtime/jdwp/object_registry.cc
+++ b/runtime/jdwp/object_registry.cc
@@ -21,8 +21,6 @@
namespace art {
-mirror::Object* const ObjectRegistry::kInvalidObject = reinterpret_cast<mirror::Object*>(1);
-
std::ostream& operator<<(std::ostream& os, const ObjectRegistryEntry& rhs) {
os << "ObjectRegistryEntry[" << rhs.jni_reference_type
<< ",reference=" << rhs.jni_reference
@@ -129,14 +127,16 @@
id_to_entry_.clear();
}
-mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id) {
+mirror::Object* ObjectRegistry::InternalGet(JDWP::ObjectId id, JDWP::JdwpError* error) {
Thread* self = Thread::Current();
MutexLock mu(self, lock_);
auto it = id_to_entry_.find(id);
if (it == id_to_entry_.end()) {
- return kInvalidObject;
+ *error = JDWP::ERR_INVALID_OBJECT;
+ return nullptr;
}
ObjectRegistryEntry& entry = *it->second;
+ *error = JDWP::ERR_NONE;
return self->DecodeJObject(entry.jni_reference);
}
diff --git a/runtime/jdwp/object_registry.h b/runtime/jdwp/object_registry.h
index bc3530b..faddff1 100644
--- a/runtime/jdwp/object_registry.h
+++ b/runtime/jdwp/object_registry.h
@@ -22,6 +22,7 @@
#include <map>
+#include "base/casts.h"
#include "jdwp/jdwp.h"
#include "safe_map.h"
@@ -65,11 +66,13 @@
JDWP::RefTypeId AddRefType(mirror::Class* c)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_);
- template<typename T> T Get(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ template<typename T> T Get(JDWP::ObjectId id, JDWP::JdwpError* error)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (id == 0) {
- return NULL;
+ *error = JDWP::ERR_NONE;
+ return nullptr;
}
- return reinterpret_cast<T>(InternalGet(id));
+ return down_cast<T>(InternalGet(id, error));
}
bool Contains(mirror::Object* o) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -90,9 +93,6 @@
void DisposeObject(JDWP::ObjectId id, uint32_t reference_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Returned by Get when passed an invalid object id.
- static mirror::Object* const kInvalidObject;
-
// This is needed to get the jobject instead of the Object*.
// Avoid using this and use standard Get when possible.
jobject GetJObject(JDWP::ObjectId id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -102,7 +102,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(lock_, Locks::thread_list_lock_);
- mirror::Object* InternalGet(JDWP::ObjectId id)
+ mirror::Object* InternalGet(JDWP::ObjectId id, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(lock_);
diff --git a/runtime/jni_internal-inl.h b/runtime/jni_env_ext-inl.h
similarity index 89%
rename from runtime/jni_internal-inl.h
rename to runtime/jni_env_ext-inl.h
index 6cf9a61..dc6a3e8 100644
--- a/runtime/jni_internal-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_JNI_INTERNAL_INL_H_
-#define ART_RUNTIME_JNI_INTERNAL_INL_H_
+#ifndef ART_RUNTIME_JNI_ENV_EXT_INL_H_
+#define ART_RUNTIME_JNI_ENV_EXT_INL_H_
-#include "jni_internal.h"
+#include "jni_env_ext.h"
#include "utils.h"
@@ -44,4 +44,4 @@
} // namespace art
-#endif // ART_RUNTIME_JNI_INTERNAL_INL_H_
+#endif // ART_RUNTIME_JNI_ENV_EXT_INL_H_
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
new file mode 100644
index 0000000..180e3d7
--- /dev/null
+++ b/runtime/jni_env_ext.cc
@@ -0,0 +1,89 @@
+/*
+ * 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 "jni_env_ext.h"
+
+#include "check_jni.h"
+#include "indirect_reference_table.h"
+#include "java_vm_ext.h"
+#include "jni_internal.h"
+
+namespace art {
+
+static constexpr size_t kMonitorsInitial = 32; // Arbitrary.
+static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check.
+
+static constexpr size_t kLocalsInitial = 64; // Arbitrary.
+
+JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
+ : self(self),
+ vm(vm),
+ local_ref_cookie(IRT_FIRST_SEGMENT),
+ locals(kLocalsInitial, kLocalsMax, kLocal),
+ check_jni(false),
+ critical(0),
+ monitors("monitors", kMonitorsInitial, kMonitorsMax) {
+ functions = unchecked_functions = GetJniNativeInterface();
+ if (vm->IsCheckJniEnabled()) {
+ SetCheckJniEnabled(true);
+ }
+}
+
+JNIEnvExt::~JNIEnvExt() {
+}
+
+jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
+}
+
+void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (obj != nullptr) {
+ locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
+ }
+}
+
+void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
+ check_jni = enabled;
+ functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
+void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
+ locals.Dump(os);
+ monitors.Dump(os);
+}
+
+void JNIEnvExt::PushFrame(int capacity) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UNUSED(capacity); // cpplint gets confused with (int) and thinks its a cast.
+ // TODO: take 'capacity' into account.
+ stacked_local_ref_cookies.push_back(local_ref_cookie);
+ local_ref_cookie = locals.GetSegmentState();
+}
+
+void JNIEnvExt::PopFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ locals.SetSegmentState(local_ref_cookie);
+ local_ref_cookie = stacked_local_ref_cookies.back();
+ stacked_local_ref_cookies.pop_back();
+}
+
+Offset JNIEnvExt::SegmentStateOffset() {
+ return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
+ IndirectReferenceTable::SegmentStateOffset().Int32Value());
+}
+
+} // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
new file mode 100644
index 0000000..af87cb4
--- /dev/null
+++ b/runtime/jni_env_ext.h
@@ -0,0 +1,115 @@
+/*
+ * 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_JNI_ENV_EXT_H_
+#define ART_RUNTIME_JNI_ENV_EXT_H_
+
+#include <jni.h>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "indirect_reference_table.h"
+#include "object_callbacks.h"
+#include "reference_table.h"
+
+namespace art {
+
+class JavaVMExt;
+
+// Maximum number of local references in the indirect reference table. The value is arbitrary but
+// low enough that it forces sanity checks.
+static constexpr size_t kLocalsMax = 512;
+
+struct JNIEnvExt : public JNIEnv {
+ JNIEnvExt(Thread* self, JavaVMExt* vm);
+ ~JNIEnvExt();
+
+ void DumpReferenceTables(std::ostream& os)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetCheckJniEnabled(bool enabled);
+
+ void PushFrame(int capacity);
+ void PopFrame();
+
+ template<typename T>
+ T AddLocalReference(mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static Offset SegmentStateOffset();
+
+ static Offset LocalRefCookieOffset() {
+ return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
+ }
+
+ static Offset SelfOffset() {
+ return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
+ }
+
+ jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ Thread* const self;
+ JavaVMExt* const vm;
+
+ // Cookie used when using the local indirect reference table.
+ uint32_t local_ref_cookie;
+
+ // JNI local references.
+ IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
+
+ // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
+ // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
+ // to a native method.
+ std::vector<uint32_t> stacked_local_ref_cookies;
+
+ // Frequently-accessed fields cached from JavaVM.
+ bool check_jni;
+
+ // How many nested "critical" JNI calls are we in?
+ int critical;
+
+ // Entered JNI monitors, for bulk exit on thread detach.
+ ReferenceTable monitors;
+
+ // Used by -Xcheck:jni.
+ const JNINativeInterface* unchecked_functions;
+};
+
+// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
+// compiler.
+class ScopedJniEnvLocalRefState {
+ public:
+ explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
+ saved_local_ref_cookie_ = env->local_ref_cookie;
+ env->local_ref_cookie = env->locals.GetSegmentState();
+ }
+
+ ~ScopedJniEnvLocalRefState() {
+ env_->locals.SetSegmentState(env_->local_ref_cookie);
+ env_->local_ref_cookie = saved_local_ref_cookie_;
+ }
+
+ private:
+ JNIEnvExt* const env_;
+ uint32_t saved_local_ref_cookie_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_JNI_ENV_EXT_H_
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 73effca..e2f9ac2 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -34,7 +34,8 @@
#include "gc/accounting/card_table-inl.h"
#include "indirect_reference_table-inl.h"
#include "interpreter/interpreter.h"
-#include "jni.h"
+#include "jni_env_ext.h"
+#include "java_vm_ext.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
@@ -56,28 +57,6 @@
namespace art {
-static const size_t kMonitorsInitial = 32; // Arbitrary.
-static const size_t kMonitorsMax = 4096; // Arbitrary sanity check.
-
-static const size_t kLocalsInitial = 64; // Arbitrary.
-static const size_t kLocalsMax = 512; // Arbitrary sanity check.
-
-static size_t gGlobalsInitial = 512; // Arbitrary.
-static size_t gGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
-
-static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
-static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
-
-static jweak AddWeakGlobalReference(ScopedObjectAccess& soa, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return soa.Vm()->AddWeakGlobalReference(soa.Self(), obj);
-}
-
-static bool IsBadJniVersion(int version) {
- // We don't support JNI_VERSION_1_1. These are the only other valid versions.
- return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
-}
-
// Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
// separated with slashes but aren't wrapped with "L;" like regular descriptors
// (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an
@@ -130,7 +109,7 @@
}
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_klass, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_klass, true, true)) {
return nullptr;
}
return h_klass.Get();
@@ -168,7 +147,7 @@
mirror::ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
// If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
if (method == soa.DecodeMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
- return soa.Self()->GetClassLoaderOverride();
+ return soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
}
// If we have a method, use its ClassLoader for context.
if (method != nullptr) {
@@ -181,7 +160,7 @@
return class_loader;
}
// See if the override ClassLoader is set for gtests.
- class_loader = soa.Self()->GetClassLoaderOverride();
+ class_loader = soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride());
if (class_loader != nullptr) {
// If so, CommonCompilerTest should have set UseCompileTimeClassPath.
CHECK(Runtime::Current()->UseCompileTimeClassPath());
@@ -303,255 +282,10 @@
return JNI_OK;
}
-static jint JII_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* raw_args, bool as_daemon) {
- if (vm == nullptr || p_env == nullptr) {
- return JNI_ERR;
- }
-
- // Return immediately if we're already attached.
- Thread* self = Thread::Current();
- if (self != nullptr) {
- *p_env = self->GetJniEnv();
- return JNI_OK;
- }
-
- Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
-
- // No threads allowed in zygote mode.
- if (runtime->IsZygote()) {
- LOG(ERROR) << "Attempt to attach a thread in the zygote";
- return JNI_ERR;
- }
-
- JavaVMAttachArgs* args = static_cast<JavaVMAttachArgs*>(raw_args);
- const char* thread_name = nullptr;
- jobject thread_group = nullptr;
- if (args != nullptr) {
- if (IsBadJniVersion(args->version)) {
- LOG(ERROR) << "Bad JNI version passed to "
- << (as_daemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread") << ": "
- << args->version;
- return JNI_EVERSION;
- }
- thread_name = args->name;
- thread_group = args->group;
- }
-
- if (!runtime->AttachCurrentThread(thread_name, as_daemon, thread_group, !runtime->IsCompiler())) {
- *p_env = nullptr;
- return JNI_ERR;
- } else {
- *p_env = Thread::Current()->GetJniEnv();
- return JNI_OK;
- }
+static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) {
+ return reinterpret_cast<JNIEnvExt*>(env)->vm;
}
-class SharedLibrary {
- public:
- SharedLibrary(const std::string& path, void* handle, mirror::Object* class_loader)
- : path_(path),
- handle_(handle),
- needs_native_bridge_(false),
- class_loader_(GcRoot<mirror::Object>(class_loader)),
- jni_on_load_lock_("JNI_OnLoad lock"),
- jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
- jni_on_load_thread_id_(Thread::Current()->GetThreadId()),
- jni_on_load_result_(kPending) {
- }
-
- mirror::Object* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return class_loader_.Read();
- }
-
- std::string GetPath() {
- return path_;
- }
-
- /*
- * Check the result of an earlier call to JNI_OnLoad on this library.
- * If the call has not yet finished in another thread, wait for it.
- */
- bool CheckOnLoadResult()
- LOCKS_EXCLUDED(jni_on_load_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Thread* self = Thread::Current();
- self->TransitionFromRunnableToSuspended(kWaitingForJniOnLoad);
- bool okay;
- {
- MutexLock mu(self, jni_on_load_lock_);
-
- if (jni_on_load_thread_id_ == self->GetThreadId()) {
- // Check this so we don't end up waiting for ourselves. We need to return "true" so the
- // caller can continue.
- LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
- okay = true;
- } else {
- while (jni_on_load_result_ == kPending) {
- VLOG(jni) << "[" << *self << " waiting for \"" << path_ << "\" " << "JNI_OnLoad...]";
- jni_on_load_cond_.Wait(self);
- }
-
- okay = (jni_on_load_result_ == kOkay);
- VLOG(jni) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
- << (okay ? "succeeded" : "failed") << "]";
- }
- }
- self->TransitionFromSuspendedToRunnable();
- return okay;
- }
-
- void SetResult(bool result) LOCKS_EXCLUDED(jni_on_load_lock_) {
- Thread* self = Thread::Current();
- MutexLock mu(self, jni_on_load_lock_);
-
- jni_on_load_result_ = result ? kOkay : kFailed;
- jni_on_load_thread_id_ = 0;
-
- // Broadcast a wakeup to anybody sleeping on the condition variable.
- jni_on_load_cond_.Broadcast(self);
- }
-
- void SetNeedsNativeBridge() {
- needs_native_bridge_ = true;
- }
-
- bool NeedsNativeBridge() const {
- return needs_native_bridge_;
- }
-
- void* FindSymbol(const std::string& symbol_name) {
- return dlsym(handle_, symbol_name.c_str());
- }
-
- void* FindSymbolWithNativeBridge(const std::string& symbol_name, mirror::ArtMethod* m)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK(NeedsNativeBridge());
-
- uint32_t len = 0;
- const char* shorty = nullptr;
- if (m != nullptr) {
- shorty = m->GetShorty(&len);
- }
- return android::NativeBridgeGetTrampoline(handle_, symbol_name.c_str(), shorty, len);
- }
-
- void VisitRoots(RootCallback* visitor, void* arg) {
- if (!class_loader_.IsNull()) {
- class_loader_.VisitRoot(visitor, arg, 0, kRootVMInternal);
- }
- }
-
- private:
- enum JNI_OnLoadState {
- kPending,
- kFailed,
- kOkay,
- };
-
- // Path to library "/system/lib/libjni.so".
- std::string path_;
-
- // The void* returned by dlopen(3).
- void* handle_;
-
- // True if a native bridge is required.
- bool needs_native_bridge_;
-
- // The ClassLoader this library is associated with.
- GcRoot<mirror::Object> class_loader_;
-
- // Guards remaining items.
- Mutex jni_on_load_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- // Wait for JNI_OnLoad in other thread.
- ConditionVariable jni_on_load_cond_ GUARDED_BY(jni_on_load_lock_);
- // Recursive invocation guard.
- uint32_t jni_on_load_thread_id_ GUARDED_BY(jni_on_load_lock_);
- // Result of earlier JNI_OnLoad call.
- JNI_OnLoadState jni_on_load_result_ GUARDED_BY(jni_on_load_lock_);
-};
-
-// This exists mainly to keep implementation details out of the header file.
-class Libraries {
- public:
- Libraries() {
- }
-
- ~Libraries() {
- STLDeleteValues(&libraries_);
- }
-
- void Dump(std::ostream& os) const {
- bool first = true;
- for (const auto& library : libraries_) {
- if (!first) {
- os << ' ';
- }
- first = false;
- os << library.first;
- }
- }
-
- size_t size() const {
- return libraries_.size();
- }
-
- SharedLibrary* Get(const std::string& path) {
- auto it = libraries_.find(path);
- return (it == libraries_.end()) ? nullptr : it->second;
- }
-
- void Put(const std::string& path, SharedLibrary* library) {
- libraries_.Put(path, library);
- }
-
- // See section 11.3 "Linking Native Methods" of the JNI spec.
- void* FindNativeMethod(mirror::ArtMethod* m, std::string& detail)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- std::string jni_short_name(JniShortName(m));
- std::string jni_long_name(JniLongName(m));
- const mirror::ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
- for (const auto& lib : libraries_) {
- SharedLibrary* library = lib.second;
- if (library->GetClassLoader() != declaring_class_loader) {
- // We only search libraries loaded by the appropriate ClassLoader.
- continue;
- }
- // Try the short name then the long name...
- void* fn = nullptr;
- if (UNLIKELY(library->NeedsNativeBridge())) {
- fn = library->FindSymbolWithNativeBridge(jni_short_name, m);
- if (fn == nullptr) {
- fn = library->FindSymbolWithNativeBridge(jni_long_name, m);
- }
- } else {
- fn = library->FindSymbol(jni_short_name);
- if (fn == nullptr) {
- fn = library->FindSymbol(jni_long_name);
- }
- }
- if (fn != nullptr) {
- VLOG(jni) << "[Found native code for " << PrettyMethod(m)
- << " in \"" << library->GetPath() << "\"]";
- return fn;
- }
- }
- detail += "No implementation found for ";
- detail += PrettyMethod(m);
- detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
- LOG(ERROR) << detail;
- return nullptr;
- }
-
- void VisitRoots(RootCallback* callback, void* arg) {
- for (auto& lib_pair : libraries_) {
- lib_pair.second->VisitRoots(callback, arg);
- }
- }
-
- private:
- AllocationTrackingSafeMap<std::string, SharedLibrary*, kAllocatorTagJNILibrarires> libraries_;
-};
-
#define CHECK_NON_NULL_ARGUMENT(value) \
CHECK_NON_NULL_ARGUMENT_FN_NAME(__FUNCTION__, value, nullptr)
@@ -566,13 +300,13 @@
#define CHECK_NON_NULL_ARGUMENT_FN_NAME(name, value, return_val) \
if (UNLIKELY(value == nullptr)) { \
- JniAbortF(name, #value " == null"); \
+ JavaVmExtFromEnv(env)->JniAbortF(name, #value " == null"); \
return return_val; \
}
#define CHECK_NON_NULL_MEMCPY_ARGUMENT(length, value) \
if (UNLIKELY(length != 0 && value == nullptr)) { \
- JniAbortF(__FUNCTION__, #value " == null"); \
+ JavaVmExtFromEnv(env)->JniAbortF(__FUNCTION__, #value " == null"); \
return; \
}
@@ -773,10 +507,10 @@
static jint PushLocalFrame(JNIEnv* env, jint capacity) {
// TODO: SOA may not be necessary but I do it to please lock annotations.
ScopedObjectAccess soa(env);
- if (EnsureLocalCapacity(soa, capacity, "PushLocalFrame") != JNI_OK) {
+ if (EnsureLocalCapacityInternal(soa, capacity, "PushLocalFrame") != JNI_OK) {
return JNI_ERR;
}
- static_cast<JNIEnvExt*>(env)->PushFrame(capacity);
+ down_cast<JNIEnvExt*>(env)->PushFrame(capacity);
return JNI_OK;
}
@@ -790,48 +524,31 @@
static jint EnsureLocalCapacity(JNIEnv* env, jint desired_capacity) {
// TODO: SOA may not be necessary but I do it to please lock annotations.
ScopedObjectAccess soa(env);
- return EnsureLocalCapacity(soa, desired_capacity, "EnsureLocalCapacity");
+ return EnsureLocalCapacityInternal(soa, desired_capacity, "EnsureLocalCapacity");
}
static jobject NewGlobalRef(JNIEnv* env, jobject obj) {
ScopedObjectAccess soa(env);
mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
- // Check for null after decoding the object to handle cleared weak globals.
- if (decoded_obj == nullptr) {
- return nullptr;
- }
- JavaVMExt* vm = soa.Vm();
- IndirectReferenceTable& globals = vm->globals;
- WriterMutexLock mu(soa.Self(), vm->globals_lock);
- IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, decoded_obj);
- return reinterpret_cast<jobject>(ref);
+ return soa.Vm()->AddGlobalRef(soa.Self(), decoded_obj);
}
static void DeleteGlobalRef(JNIEnv* env, jobject obj) {
- if (obj == nullptr) {
- return;
- }
- JavaVMExt* vm = reinterpret_cast<JNIEnvExt*>(env)->vm;
- IndirectReferenceTable& globals = vm->globals;
- Thread* self = reinterpret_cast<JNIEnvExt*>(env)->self;
- WriterMutexLock mu(self, vm->globals_lock);
-
- if (!globals.Remove(IRT_FIRST_SEGMENT, obj)) {
- LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
- << "failed to find entry";
- }
+ JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
+ Thread* self = down_cast<JNIEnvExt*>(env)->self;
+ vm->DeleteGlobalRef(self, obj);
}
static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) {
ScopedObjectAccess soa(env);
- return AddWeakGlobalReference(soa, soa.Decode<mirror::Object*>(obj));
+ mirror::Object* decoded_obj = soa.Decode<mirror::Object*>(obj);
+ return soa.Vm()->AddWeakGlobalRef(soa.Self(), decoded_obj);
}
static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) {
- if (obj != nullptr) {
- ScopedObjectAccess soa(env);
- soa.Vm()->DeleteWeakGlobalRef(soa.Self(), obj);
- }
+ JavaVMExt* vm = down_cast<JNIEnvExt*>(env)->vm;
+ Thread* self = down_cast<JNIEnvExt*>(env)->self;
+ vm->DeleteWeakGlobalRef(self, obj);
}
static jobject NewLocalRef(JNIEnv* env, jobject obj) {
@@ -848,7 +565,6 @@
if (obj == nullptr) {
return;
}
- ScopedObjectAccess soa(env);
IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env)->locals;
uint32_t cookie = reinterpret_cast<JNIEnvExt*>(env)->local_ref_cookie;
@@ -1914,11 +1630,11 @@
static jstring NewString(JNIEnv* env, const jchar* chars, jsize char_count) {
if (UNLIKELY(char_count < 0)) {
- JniAbortF("NewString", "char_count < 0: %d", char_count);
+ JavaVmExtFromEnv(env)->JniAbortF("NewString", "char_count < 0: %d", char_count);
return nullptr;
}
if (UNLIKELY(chars == nullptr && char_count > 0)) {
- JniAbortF("NewString", "chars == null && char_count > 0");
+ JavaVmExtFromEnv(env)->JniAbortF("NewString", "chars == null && char_count > 0");
return nullptr;
}
ScopedObjectAccess soa(env);
@@ -2066,7 +1782,8 @@
ScopedObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(java_array);
if (UNLIKELY(!obj->IsArrayInstance())) {
- JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
+ soa.Vm()->JniAbortF("GetArrayLength", "not an array: %s", PrettyTypeOf(obj).c_str());
+ return 0;
}
mirror::Array* array = obj->AsArray();
return array->GetLength();
@@ -2121,7 +1838,7 @@
static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass,
jobject initial_element) {
if (UNLIKELY(length < 0)) {
- JniAbortF("NewObjectArray", "negative array length: %d", length);
+ JavaVmExtFromEnv(env)->JniAbortF("NewObjectArray", "negative array length: %d", length);
return nullptr;
}
CHECK_NON_NULL_ARGUMENT(element_jclass);
@@ -2132,8 +1849,8 @@
{
mirror::Class* element_class = soa.Decode<mirror::Class*>(element_jclass);
if (UNLIKELY(element_class->IsPrimitive())) {
- JniAbortF("NewObjectArray", "not an object type: %s",
- PrettyDescriptor(element_class).c_str());
+ soa.Vm()->JniAbortF("NewObjectArray", "not an object type: %s",
+ PrettyDescriptor(element_class).c_str());
return nullptr;
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -2151,10 +1868,11 @@
if (initial_object != nullptr) {
mirror::Class* element_class = result->GetClass()->GetComponentType();
if (UNLIKELY(!element_class->IsAssignableFrom(initial_object->GetClass()))) {
- JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with element "
- "type of '%s'", PrettyDescriptor(initial_object->GetClass()).c_str(),
- PrettyDescriptor(element_class).c_str());
-
+ soa.Vm()->JniAbortF("NewObjectArray", "cannot assign object of type '%s' to array with "
+ "element type of '%s'",
+ PrettyDescriptor(initial_object->GetClass()).c_str(),
+ PrettyDescriptor(element_class).c_str());
+ return nullptr;
} else {
for (jsize i = 0; i < length; ++i) {
result->SetWithoutChecks<false>(i, initial_object);
@@ -2174,8 +1892,8 @@
ScopedObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
- JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
- PrettyDescriptor(array->GetClass()).c_str());
+ soa.Vm()->JniAbortF("GetPrimitiveArrayCritical", "expected primitive array, given %s",
+ PrettyDescriptor(array->GetClass()).c_str());
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -2196,8 +1914,8 @@
ScopedObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(java_array);
if (UNLIKELY(!array->GetClass()->IsPrimitiveArray())) {
- JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
- PrettyDescriptor(array->GetClass()).c_str());
+ soa.Vm()->JniAbortF("ReleasePrimitiveArrayCritical", "expected primitive array, given %s",
+ PrettyDescriptor(array->GetClass()).c_str());
return;
}
const size_t component_size = array->GetClass()->GetComponentSize();
@@ -2369,8 +2087,9 @@
static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
jint method_count, bool return_errors) {
if (UNLIKELY(method_count < 0)) {
- JniAbortF("RegisterNatives", "negative method count: %d", method_count);
- return JNI_ERR; // Not reached.
+ JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
+ method_count);
+ return JNI_ERR; // Not reached except in unit tests.
}
CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
ScopedObjectAccess soa(env);
@@ -2494,17 +2213,21 @@
static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
if (capacity < 0) {
- JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64, capacity);
+ JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer", "negative buffer capacity: %" PRId64,
+ capacity);
return nullptr;
}
if (address == nullptr && capacity != 0) {
- JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity);
+ JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer",
+ "non-zero capacity for nullptr pointer: %" PRId64, capacity);
return nullptr;
}
// At the moment, the capacity of DirectByteBuffer is limited to a signed int.
if (capacity > INT_MAX) {
- JniAbortF("NewDirectByteBuffer", "buffer capacity greater than maximum jint: %" PRId64, capacity);
+ JavaVmExtFromEnv(env)->JniAbortF("NewDirectByteBuffer",
+ "buffer capacity greater than maximum jint: %" PRId64,
+ capacity);
return nullptr;
}
jlong address_arg = reinterpret_cast<jlong>(address);
@@ -2533,33 +2256,24 @@
IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
IndirectRefKind kind = GetIndirectRefKind(ref);
switch (kind) {
- case kLocal: {
- ScopedObjectAccess soa(env);
- // The local refs don't need a read barrier.
- if (static_cast<JNIEnvExt*>(env)->locals.Get<kWithoutReadBarrier>(ref) !=
- kInvalidIndirectRefObject) {
- return JNILocalRefType;
- }
- return JNIInvalidRefType;
- }
+ case kLocal:
+ return JNILocalRefType;
case kGlobal:
return JNIGlobalRefType;
case kWeakGlobal:
return JNIWeakGlobalRefType;
case kHandleScopeOrInvalid:
- // Is it in a stack IRT?
- if (static_cast<JNIEnvExt*>(env)->self->HandleScopeContains(java_object)) {
- return JNILocalRefType;
- }
- return JNIInvalidRefType;
+ // Assume value is in a handle scope.
+ return JNILocalRefType;
}
LOG(FATAL) << "IndirectRefKind[" << kind << "]";
return JNIInvalidRefType;
}
private:
- static jint EnsureLocalCapacity(ScopedObjectAccess& soa, jint desired_capacity,
- const char* caller) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static jint EnsureLocalCapacityInternal(ScopedObjectAccess& soa, jint desired_capacity,
+ const char* caller)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// TODO: we should try to expand the table if necessary.
if (desired_capacity < 0 || desired_capacity > static_cast<jint>(kLocalsMax)) {
LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
@@ -2576,11 +2290,11 @@
template<typename JniT, typename ArtT>
static JniT NewPrimitiveArray(JNIEnv* env, jsize length) {
+ ScopedObjectAccess soa(env);
if (UNLIKELY(length < 0)) {
- JniAbortF("NewPrimitiveArray", "negative array length: %d", length);
+ soa.Vm()->JniAbortF("NewPrimitiveArray", "negative array length: %d", length);
return nullptr;
}
- ScopedObjectAccess soa(env);
ArtT* result = ArtT::Alloc(soa.Self(), length);
return soa.AddLocalReference<JniT>(result);
}
@@ -2591,9 +2305,11 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ArtArrayT* array = soa.Decode<ArtArrayT*>(java_array);
if (UNLIKELY(ArtArrayT::GetArrayClass() != array->GetClass())) {
- JniAbortF(fn_name, "attempt to %s %s primitive array elements with an object of type %s",
- operation, PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(),
- PrettyDescriptor(array->GetClass()).c_str());
+ soa.Vm()->JniAbortF(fn_name,
+ "attempt to %s %s primitive array elements with an object of type %s",
+ operation,
+ PrettyDescriptor(ArtArrayT::GetArrayClass()->GetComponentType()).c_str(),
+ PrettyDescriptor(array->GetClass()).c_str());
return nullptr;
}
DCHECK_EQ(sizeof(ElementT), array->GetClass()->GetComponentSize());
@@ -2655,8 +2371,9 @@
// heap address. TODO: This might be slow to check, may be worth keeping track of which
// copies we make?
if (heap->IsNonDiscontinuousSpaceHeapAddress(reinterpret_cast<mirror::Object*>(elements))) {
- JniAbortF("ReleaseArrayElements", "invalid element pointer %p, array elements are %p",
- reinterpret_cast<void*>(elements), array_data);
+ soa.Vm()->JniAbortF("ReleaseArrayElements",
+ "invalid element pointer %p, array elements are %p",
+ reinterpret_cast<void*>(elements), array_data);
return;
}
}
@@ -2951,473 +2668,8 @@
JNI::GetObjectRefType,
};
-JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
- : self(self),
- vm(vm),
- local_ref_cookie(IRT_FIRST_SEGMENT),
- locals(kLocalsInitial, kLocalsMax, kLocal),
- check_jni(false),
- critical(0),
- monitors("monitors", kMonitorsInitial, kMonitorsMax) {
- functions = unchecked_functions = &gJniNativeInterface;
- if (vm->check_jni) {
- SetCheckJniEnabled(true);
- }
-}
-
-JNIEnvExt::~JNIEnvExt() {
-}
-
-jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (obj == nullptr) {
- return nullptr;
- }
- return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj));
-}
-
-void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (obj != nullptr) {
- locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj));
- }
-}
-void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
- check_jni = enabled;
- functions = enabled ? GetCheckJniNativeInterface() : &gJniNativeInterface;
-}
-
-void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
- locals.Dump(os);
- monitors.Dump(os);
-}
-
-void JNIEnvExt::PushFrame(int capacity) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- UNUSED(capacity); // cpplint gets confused with (int) and thinks its a cast.
- // TODO: take 'capacity' into account.
- stacked_local_ref_cookies.push_back(local_ref_cookie);
- local_ref_cookie = locals.GetSegmentState();
-}
-
-void JNIEnvExt::PopFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- locals.SetSegmentState(local_ref_cookie);
- local_ref_cookie = stacked_local_ref_cookies.back();
- stacked_local_ref_cookies.pop_back();
-}
-
-Offset JNIEnvExt::SegmentStateOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, locals) +
- IndirectReferenceTable::SegmentStateOffset().Int32Value());
-}
-
-// JNI Invocation interface.
-
-extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
- const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
- if (IsBadJniVersion(args->version)) {
- LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
- return JNI_EVERSION;
- }
- RuntimeOptions options;
- for (int i = 0; i < args->nOptions; ++i) {
- JavaVMOption* option = &args->options[i];
- options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
- }
- bool ignore_unrecognized = args->ignoreUnrecognized;
- if (!Runtime::Create(options, ignore_unrecognized)) {
- return JNI_ERR;
- }
- Runtime* runtime = Runtime::Current();
- bool started = runtime->Start();
- if (!started) {
- delete Thread::Current()->GetJniEnv();
- delete runtime->GetJavaVM();
- LOG(WARNING) << "CreateJavaVM failed";
- return JNI_ERR;
- }
- *p_env = Thread::Current()->GetJniEnv();
- *p_vm = runtime->GetJavaVM();
- return JNI_OK;
-}
-
-extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
- Runtime* runtime = Runtime::Current();
- if (runtime == nullptr) {
- *vm_count = 0;
- } else {
- *vm_count = 1;
- vms[0] = runtime->GetJavaVM();
- }
- return JNI_OK;
-}
-
-// Historically unsupported.
-extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* /*vm_args*/) {
- return JNI_ERR;
-}
-
-class JII {
- public:
- static jint DestroyJavaVM(JavaVM* vm) {
- if (vm == nullptr) {
- return JNI_ERR;
- }
- JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
- delete raw_vm->runtime;
- return JNI_OK;
- }
-
- static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- return JII_AttachCurrentThread(vm, p_env, thr_args, false);
- }
-
- static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- return JII_AttachCurrentThread(vm, p_env, thr_args, true);
- }
-
- static jint DetachCurrentThread(JavaVM* vm) {
- if (vm == nullptr || Thread::Current() == nullptr) {
- return JNI_ERR;
- }
- JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
- Runtime* runtime = raw_vm->runtime;
- runtime->DetachCurrentThread();
- return JNI_OK;
- }
-
- static jint GetEnv(JavaVM* vm, void** env, jint version) {
- // GetEnv always returns a JNIEnv* for the most current supported JNI version,
- // and unlike other calls that take a JNI version doesn't care if you supply
- // JNI_VERSION_1_1, which we don't otherwise support.
- if (IsBadJniVersion(version) && version != JNI_VERSION_1_1) {
- LOG(ERROR) << "Bad JNI version passed to GetEnv: " << version;
- return JNI_EVERSION;
- }
- if (vm == nullptr || env == nullptr) {
- return JNI_ERR;
- }
- Thread* thread = Thread::Current();
- if (thread == nullptr) {
- *env = nullptr;
- return JNI_EDETACHED;
- }
- *env = thread->GetJniEnv();
- return JNI_OK;
- }
-};
-
-const JNIInvokeInterface gJniInvokeInterface = {
- nullptr, // reserved0
- nullptr, // reserved1
- nullptr, // reserved2
- JII::DestroyJavaVM,
- JII::AttachCurrentThread,
- JII::DetachCurrentThread,
- JII::GetEnv,
- JII::AttachCurrentThreadAsDaemon
-};
-
-JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
- : runtime(runtime),
- check_jni_abort_hook(nullptr),
- check_jni_abort_hook_data(nullptr),
- check_jni(false),
- force_copy(false), // TODO: add a way to enable this
- trace(options->jni_trace_),
- globals_lock("JNI global reference table lock"),
- globals(gGlobalsInitial, gGlobalsMax, kGlobal),
- libraries_lock("JNI shared libraries map lock", kLoadLibraryLock),
- libraries(new Libraries),
- weak_globals_lock_("JNI weak global reference table lock"),
- weak_globals_(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
- allow_new_weak_globals_(true),
- weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
- functions = unchecked_functions = &gJniInvokeInterface;
- if (options->check_jni_) {
- SetCheckJniEnabled(true);
- }
-}
-
-JavaVMExt::~JavaVMExt() {
- delete libraries;
-}
-
-jweak JavaVMExt::AddWeakGlobalReference(Thread* self, mirror::Object* obj) {
- if (obj == nullptr) {
- return nullptr;
- }
- MutexLock mu(self, weak_globals_lock_);
- while (UNLIKELY(!allow_new_weak_globals_)) {
- weak_globals_add_condition_.WaitHoldingLocks(self);
- }
- IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
- return reinterpret_cast<jweak>(ref);
-}
-
-void JavaVMExt::DeleteWeakGlobalRef(Thread* self, jweak obj) {
- MutexLock mu(self, weak_globals_lock_);
- if (!weak_globals_.Remove(IRT_FIRST_SEGMENT, obj)) {
- LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
- << "failed to find entry";
- }
-}
-
-void JavaVMExt::SetCheckJniEnabled(bool enabled) {
- check_jni = enabled;
- functions = enabled ? GetCheckJniInvokeInterface() : &gJniInvokeInterface;
-}
-
-void JavaVMExt::DumpForSigQuit(std::ostream& os) {
- os << "JNI: CheckJNI is " << (check_jni ? "on" : "off");
- if (force_copy) {
- os << " (with forcecopy)";
- }
- Thread* self = Thread::Current();
- {
- ReaderMutexLock mu(self, globals_lock);
- os << "; globals=" << globals.Capacity();
- }
- {
- MutexLock mu(self, weak_globals_lock_);
- if (weak_globals_.Capacity() > 0) {
- os << " (plus " << weak_globals_.Capacity() << " weak)";
- }
- }
- os << '\n';
-
- {
- MutexLock mu(self, libraries_lock);
- os << "Libraries: " << Dumpable<Libraries>(*libraries) << " (" << libraries->size() << ")\n";
- }
-}
-
-void JavaVMExt::DisallowNewWeakGlobals() {
- MutexLock mu(Thread::Current(), weak_globals_lock_);
- allow_new_weak_globals_ = false;
-}
-
-void JavaVMExt::AllowNewWeakGlobals() {
- Thread* self = Thread::Current();
- MutexLock mu(self, weak_globals_lock_);
- allow_new_weak_globals_ = true;
- weak_globals_add_condition_.Broadcast(self);
-}
-
-mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
- MutexLock mu(self, weak_globals_lock_);
- while (UNLIKELY(!allow_new_weak_globals_)) {
- weak_globals_add_condition_.WaitHoldingLocks(self);
- }
- return weak_globals_.Get(ref);
-}
-
-void JavaVMExt::DumpReferenceTables(std::ostream& os) {
- Thread* self = Thread::Current();
- {
- ReaderMutexLock mu(self, globals_lock);
- globals.Dump(os);
- }
- {
- MutexLock mu(self, weak_globals_lock_);
- weak_globals_.Dump(os);
- }
-}
-
-bool JavaVMExt::LoadNativeLibrary(const std::string& path,
- Handle<mirror::ClassLoader> class_loader,
- std::string* detail) {
- detail->clear();
-
- // See if we've already loaded this library. If we have, and the class loader
- // matches, return successfully without doing anything.
- // TODO: for better results we should canonicalize the pathname (or even compare
- // inodes). This implementation is fine if everybody is using System.loadLibrary.
- SharedLibrary* library;
- Thread* self = Thread::Current();
- {
- // TODO: move the locking (and more of this logic) into Libraries.
- MutexLock mu(self, libraries_lock);
- library = libraries->Get(path);
- }
- if (library != nullptr) {
- if (library->GetClassLoader() != class_loader.Get()) {
- // The library will be associated with class_loader. The JNI
- // spec says we can't load the same library into more than one
- // class loader.
- StringAppendF(detail, "Shared library \"%s\" already opened by "
- "ClassLoader %p; can't open in ClassLoader %p",
- path.c_str(), library->GetClassLoader(), class_loader.Get());
- LOG(WARNING) << detail;
- return false;
- }
- VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
- << "ClassLoader " << class_loader.Get() << "]";
- if (!library->CheckOnLoadResult()) {
- StringAppendF(detail, "JNI_OnLoad failed on a previous attempt "
- "to load \"%s\"", path.c_str());
- return false;
- }
- return true;
- }
-
- // Open the shared library. Because we're using a full path, the system
- // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
- // resolve this library's dependencies though.)
-
- // Failures here are expected when java.library.path has several entries
- // and we have to hunt for the lib.
-
- // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
- // class unloading. Libraries will only be unloaded when the reference count (incremented by
- // dlopen) becomes zero from dlclose.
-
- // This can execute slowly for a large library on a busy system, so we
- // want to switch from kRunnable while it executes. This allows the GC to ignore us.
- self->TransitionFromRunnableToSuspended(kWaitingForJniOnLoad);
- const char* path_str = path.empty() ? nullptr : path.c_str();
- void* handle = dlopen(path_str, RTLD_LAZY);
- bool needs_native_bridge = false;
- if (handle == nullptr) {
- if (android::NativeBridgeIsSupported(path_str)) {
- handle = android::NativeBridgeLoadLibrary(path_str, RTLD_LAZY);
- needs_native_bridge = true;
- }
- }
- self->TransitionFromSuspendedToRunnable();
-
- VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
-
- if (handle == nullptr) {
- *detail = dlerror();
- LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << *detail;
- return false;
- }
-
- // Create a new entry.
- // TODO: move the locking (and more of this logic) into Libraries.
- bool created_library = false;
- {
- MutexLock mu(self, libraries_lock);
- library = libraries->Get(path);
- if (library == nullptr) { // We won race to get libraries_lock
- library = new SharedLibrary(path, handle, class_loader.Get());
- libraries->Put(path, library);
- created_library = true;
- }
- }
- if (!created_library) {
- LOG(INFO) << "WOW: we lost a race to add shared library: "
- << "\"" << path << "\" ClassLoader=" << class_loader.Get();
- return library->CheckOnLoadResult();
- }
-
- VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader.Get()
- << "]";
-
- bool was_successful = false;
- void* sym = nullptr;
- if (UNLIKELY(needs_native_bridge)) {
- library->SetNeedsNativeBridge();
- sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
- } else {
- sym = dlsym(handle, "JNI_OnLoad");
- }
-
- if (sym == nullptr) {
- VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
- was_successful = true;
- } else {
- // Call JNI_OnLoad. We have to override the current class
- // loader, which will always be "null" since the stuff at the
- // top of the stack is around Runtime.loadLibrary(). (See
- // the comments in the JNI FindClass function.)
- typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
- JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
- StackHandleScope<1> hs(self);
- Handle<mirror::ClassLoader> old_class_loader(hs.NewHandle(self->GetClassLoaderOverride()));
- self->SetClassLoaderOverride(class_loader.Get());
-
- int version = 0;
- {
- ScopedThreadStateChange tsc(self, kNative);
- VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
- version = (*jni_on_load)(this, nullptr);
- }
-
- self->SetClassLoaderOverride(old_class_loader.Get());
-
- if (version == JNI_ERR) {
- StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
- } else if (IsBadJniVersion(version)) {
- StringAppendF(detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
- path.c_str(), version);
- // It's unwise to call dlclose() here, but we can mark it
- // as bad and ensure that future load attempts will fail.
- // We don't know how far JNI_OnLoad got, so there could
- // be some partially-initialized stuff accessible through
- // newly-registered native method calls. We could try to
- // unregister them, but that doesn't seem worthwhile.
- } else {
- was_successful = true;
- }
- VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
- << " from JNI_OnLoad in \"" << path << "\"]";
- }
-
- library->SetResult(was_successful);
- return was_successful;
-}
-
-void* JavaVMExt::FindCodeForNativeMethod(mirror::ArtMethod* m) {
- CHECK(m->IsNative());
- mirror::Class* c = m->GetDeclaringClass();
- // If this is a static method, it could be called before the class has been initialized.
- if (m->IsStatic()) {
- c = EnsureInitialized(Thread::Current(), c);
- if (c == nullptr) {
- return nullptr;
- }
- } else {
- CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
- }
- std::string detail;
- void* native_method;
- Thread* self = Thread::Current();
- {
- MutexLock mu(self, libraries_lock);
- native_method = libraries->FindNativeMethod(m, detail);
- }
- // Throwing can cause libraries_lock to be reacquired.
- if (native_method == nullptr) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
- }
- return native_method;
-}
-
-void JavaVMExt::SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg) {
- MutexLock mu(Thread::Current(), weak_globals_lock_);
- for (mirror::Object** entry : weak_globals_) {
- // Since this is called by the GC, we don't need a read barrier.
- mirror::Object* obj = *entry;
- mirror::Object* new_obj = callback(obj, arg);
- if (new_obj == nullptr) {
- new_obj = kClearedJniWeakGlobal;
- }
- *entry = new_obj;
- }
-}
-
-void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
- Thread* self = Thread::Current();
- {
- ReaderMutexLock mu(self, globals_lock);
- globals.VisitRoots(callback, arg, 0, kRootJNIGlobal);
- }
- {
- MutexLock mu(self, libraries_lock);
- // Libraries contains shared libraries which hold a pointer to a class loader.
- libraries->VisitRoots(callback, arg);
- }
- // The weak_globals table is visited by the GC itself (because it mutates the table).
+const JNINativeInterface* GetJniNativeInterface() {
+ return &gJniNativeInterface;
}
void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 25ed2f6..48b10f5 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -17,16 +17,8 @@
#ifndef ART_RUNTIME_JNI_INTERNAL_H_
#define ART_RUNTIME_JNI_INTERNAL_H_
-#include "jni.h"
-
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "indirect_reference_table.h"
-#include "object_callbacks.h"
-#include "reference_table.h"
-
+#include <jni.h>
#include <iosfwd>
-#include <string>
#ifndef NATIVE_METHOD
#define NATIVE_METHOD(className, functionName, signature) \
@@ -36,185 +28,18 @@
RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
namespace art {
-namespace mirror {
- class ArtField;
- class ArtMethod;
- class ClassLoader;
-} // namespace mirror
-union JValue;
-class Libraries;
-class ParsedOptions;
-class Runtime;
-class ScopedObjectAccess;
-template<class T> class Handle;
-class Thread;
-void JniAbortF(const char* jni_function_name, const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3)));
+const JNINativeInterface* GetJniNativeInterface();
+
+// Similar to RegisterNatives except its passed a descriptor for a class name and failures are
+// fatal.
void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINativeMethod* methods,
jint method_count);
int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause);
-class JavaVMExt : public JavaVM {
- public:
- JavaVMExt(Runtime* runtime, ParsedOptions* options);
- ~JavaVMExt();
-
- /**
- * Loads the given shared library. 'path' is an absolute pathname.
- *
- * Returns 'true' on success. On failure, sets 'detail' to a
- * human-readable description of the error.
- */
- bool LoadNativeLibrary(const std::string& path, Handle<mirror::ClassLoader> class_loader,
- std::string* detail)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- /**
- * Returns a pointer to the code for the native method 'm', found
- * using dlsym(3) on every native library that's been loaded so far.
- */
- void* FindCodeForNativeMethod(mirror::ArtMethod* m)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void DumpForSigQuit(std::ostream& os);
-
- void DumpReferenceTables(std::ostream& os)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void SetCheckJniEnabled(bool enabled);
-
- void VisitRoots(RootCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void DisallowNewWeakGlobals() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
- void AllowNewWeakGlobals() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- jweak AddWeakGlobalReference(Thread* self, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DeleteWeakGlobalRef(Thread* self, jweak obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepJniWeakGlobals(IsMarkedCallback* callback, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- Runtime* runtime;
-
- // Used for testing. By default, we'll LOG(FATAL) the reason.
- void (*check_jni_abort_hook)(void* data, const std::string& reason);
- void* check_jni_abort_hook_data;
-
- // Extra checking.
- bool check_jni;
- bool force_copy;
-
- // Extra diagnostics.
- std::string trace;
-
- // JNI global references.
- ReaderWriterMutex globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
- // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
- IndirectReferenceTable globals;
-
- Mutex libraries_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
- Libraries* libraries GUARDED_BY(libraries_lock);
-
- // Used by -Xcheck:jni.
- const JNIInvokeInterface* unchecked_functions;
-
- private:
- // TODO: Make the other members of this class also private.
- // JNI weak global references.
- Mutex weak_globals_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- // Since weak_globals_ contain weak roots, be careful not to
- // directly access the object references in it. Use Get() with the
- // read barrier enabled.
- IndirectReferenceTable weak_globals_ GUARDED_BY(weak_globals_lock_);
- bool allow_new_weak_globals_ GUARDED_BY(weak_globals_lock_);
- ConditionVariable weak_globals_add_condition_ GUARDED_BY(weak_globals_lock_);
-};
-
-struct JNIEnvExt : public JNIEnv {
- JNIEnvExt(Thread* self, JavaVMExt* vm);
- ~JNIEnvExt();
-
- void DumpReferenceTables(std::ostream& os)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- void SetCheckJniEnabled(bool enabled);
-
- void PushFrame(int capacity);
- void PopFrame();
-
- template<typename T>
- T AddLocalReference(mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static Offset SegmentStateOffset();
-
- static Offset LocalRefCookieOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie));
- }
-
- static Offset SelfOffset() {
- return Offset(OFFSETOF_MEMBER(JNIEnvExt, self));
- }
-
- jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- Thread* const self;
- JavaVMExt* vm;
-
- // Cookie used when using the local indirect reference table.
- uint32_t local_ref_cookie;
-
- // JNI local references.
- IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
-
- // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
- // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
- // to a native method.
- std::vector<uint32_t> stacked_local_ref_cookies;
-
- // Frequently-accessed fields cached from JavaVM.
- bool check_jni;
-
- // How many nested "critical" JNI calls are we in?
- int critical;
-
- // Entered JNI monitors, for bulk exit on thread detach.
- ReferenceTable monitors;
-
- // Used by -Xcheck:jni.
- const JNINativeInterface* unchecked_functions;
-};
-
-const JNINativeInterface* GetCheckJniNativeInterface();
-const JNIInvokeInterface* GetCheckJniInvokeInterface();
-
-// Used to save and restore the JNIEnvExt state when not going through code created by the JNI
-// compiler
-class ScopedJniEnvLocalRefState {
- public:
- explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) {
- saved_local_ref_cookie_ = env->local_ref_cookie;
- env->local_ref_cookie = env->locals.GetSegmentState();
- }
-
- ~ScopedJniEnvLocalRefState() {
- env_->locals.SetSegmentState(env_->local_ref_cookie);
- env_->local_ref_cookie = saved_local_ref_cookie_;
- }
-
- private:
- JNIEnvExt* env_;
- uint32_t saved_local_ref_cookie_;
- DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
-};
-
} // namespace art
std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs);
+
#endif // ART_RUNTIME_JNI_INTERNAL_H_
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index bb46321..20d031c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -17,6 +17,7 @@
#include "jni_internal.h"
#include "common_compiler_test.h"
+#include "java_vm_ext.h"
#include "mirror/art_method-inl.h"
#include "mirror/string-inl.h"
#include "scoped_thread_state_change.h"
@@ -53,24 +54,15 @@
}
void ExpectException(jclass exception_class) {
- EXPECT_TRUE(env_->ExceptionCheck());
+ ScopedObjectAccess soa(env_);
+ EXPECT_TRUE(env_->ExceptionCheck())
+ << PrettyDescriptor(soa.Decode<mirror::Class*>(exception_class));
jthrowable exception = env_->ExceptionOccurred();
EXPECT_NE(nullptr, exception);
env_->ExceptionClear();
EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class));
}
- void ExpectClassFound(const char* name) {
- EXPECT_NE(env_->FindClass(name), nullptr) << name;
- EXPECT_FALSE(env_->ExceptionCheck()) << name;
- }
-
- void ExpectClassNotFound(const char* name) {
- EXPECT_EQ(env_->FindClass(name), nullptr) << name;
- EXPECT_TRUE(env_->ExceptionCheck()) << name;
- env_->ExceptionClear();
- }
-
void CleanUpJniEnv() {
if (aioobe_ != nullptr) {
env_->DeleteGlobalRef(aioobe_);
@@ -98,6 +90,510 @@
return soa.AddLocalReference<jclass>(c);
}
+ void ExpectClassFound(const char* name) {
+ EXPECT_NE(env_->FindClass(name), nullptr) << name;
+ EXPECT_FALSE(env_->ExceptionCheck()) << name;
+ }
+
+ void ExpectClassNotFound(const char* name, bool check_jni, const char* check_jni_msg,
+ CheckJniAbortCatcher* abort_catcher) {
+ EXPECT_EQ(env_->FindClass(name), nullptr) << name;
+ if (!check_jni || check_jni_msg == nullptr) {
+ EXPECT_TRUE(env_->ExceptionCheck()) << name;
+ env_->ExceptionClear();
+ } else {
+ abort_catcher->Check(check_jni_msg);
+ }
+ }
+
+ void FindClassTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ // Null argument is always an abort.
+ env_->FindClass(nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+
+ // Reference types...
+ ExpectClassFound("java/lang/String");
+ // ...for arrays too, where you must include "L;".
+ ExpectClassFound("[Ljava/lang/String;");
+ // Primitive arrays are okay too, if the primitive type is valid.
+ ExpectClassFound("[C");
+
+ // But primitive types aren't allowed...
+ ExpectClassNotFound("C", check_jni, nullptr, &check_jni_abort_catcher);
+ ExpectClassNotFound("V", check_jni, nullptr, &check_jni_abort_catcher);
+ ExpectClassNotFound("K", check_jni, nullptr, &check_jni_abort_catcher);
+
+ if (check_jni) {
+ // Check JNI will reject invalid class names as aborts but without pending exceptions.
+ EXPECT_EQ(env_->FindClass("java.lang.String"), nullptr);
+ EXPECT_FALSE(env_->ExceptionCheck());
+ check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
+
+ EXPECT_EQ(env_->FindClass("[Ljava.lang.String;"), nullptr);
+ EXPECT_FALSE(env_->ExceptionCheck());
+ check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
+ } else {
+ // Without check JNI we're tolerant and replace '.' with '/'.
+ ExpectClassFound("java.lang.String");
+ ExpectClassFound("[Ljava.lang.String;");
+ }
+
+ ExpectClassNotFound("Ljava.lang.String;", check_jni, "illegal class name 'Ljava.lang.String;'",
+ &check_jni_abort_catcher);
+ ExpectClassNotFound("[java.lang.String", check_jni, "illegal class name '[java.lang.String'",
+ &check_jni_abort_catcher);
+
+ // You can't include the "L;" in a JNI class descriptor.
+ ExpectClassNotFound("Ljava/lang/String;", check_jni, "illegal class name 'Ljava/lang/String;'",
+ &check_jni_abort_catcher);
+
+ // But you must include it for an array of any reference type.
+ ExpectClassNotFound("[java/lang/String", check_jni, "illegal class name '[java/lang/String'",
+ &check_jni_abort_catcher);
+
+ ExpectClassNotFound("[K", check_jni, "illegal class name '[K'", &check_jni_abort_catcher);
+
+ // Void arrays aren't allowed.
+ ExpectClassNotFound("[V", check_jni, "illegal class name '[V'", &check_jni_abort_catcher);
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetFieldIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+
+ jfieldID fid = env_->GetFieldID(nullptr, "count", "I");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "GetFieldID received NULL jclass"
+ : "java_class == null");
+ fid = env_->GetFieldID(c, nullptr, "I");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ fid = env_->GetFieldID(c, "count", nullptr);
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetStaticFieldIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+
+ jfieldID fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "GetStaticFieldID received NULL jclass"
+ : "java_class == null");
+ fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
+ EXPECT_EQ(nullptr, fid);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetMethodIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jmethodID method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "GetMethodID received NULL jclass"
+ : "java_class == null");
+ jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
+ ASSERT_TRUE(jlnsme != nullptr);
+ method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ method = env_->GetMethodID(jlnsme, "<init>", nullptr);
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetStaticMethodIdBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jmethodID method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "GetStaticMethodID received NULL jclass"
+ : "java_class == null");
+ jclass jlstring = env_->FindClass("java/lang/String");
+ method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "name == null");
+ method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
+ EXPECT_EQ(nullptr, method);
+ check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL"
+ : "sig == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetFromReflectedField_ToReflectedFieldBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+ jfieldID fid = env_->GetFieldID(c, "count", "I");
+ ASSERT_NE(fid, nullptr);
+
+ // Check class argument for null argument, not checked in non-check JNI.
+ jobject field = env_->ToReflectedField(nullptr, fid, JNI_FALSE);
+ if (check_jni) {
+ EXPECT_EQ(field, nullptr);
+ check_jni_abort_catcher.Check("ToReflectedField received NULL jclass");
+ } else {
+ EXPECT_NE(field, nullptr);
+ }
+
+ field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
+ EXPECT_EQ(field, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "jfieldID was NULL"
+ : "fid == null");
+
+ fid = env_->FromReflectedField(nullptr);
+ ASSERT_EQ(fid, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "expected non-null java.lang.reflect.Field"
+ : "jlr_field == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+
+ jclass c = env_->FindClass("java/lang/String");
+ ASSERT_NE(c, nullptr);
+ jmethodID mid = env_->GetMethodID(c, "<init>", "()V");
+ ASSERT_NE(mid, nullptr);
+
+ // Check class argument for null argument, not checked in non-check JNI.
+ jobject method = env_->ToReflectedMethod(nullptr, mid, JNI_FALSE);
+ if (check_jni) {
+ EXPECT_EQ(method, nullptr);
+ check_jni_abort_catcher.Check("ToReflectedMethod received NULL jclass");
+ } else {
+ EXPECT_NE(method, nullptr);
+ }
+
+ method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
+ EXPECT_EQ(method, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "jmethodID was NULL"
+ : "mid == null");
+ mid = env_->FromReflectedMethod(method);
+ ASSERT_EQ(mid, nullptr);
+ check_jni_abort_catcher.Check(check_jni ? "expected non-null method" : "jlr_method == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void RegisterAndUnregisterNativesBadArguments(bool check_jni,
+ CheckJniAbortCatcher* check_jni_abort_catcher) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ // Passing a class of null is a failure.
+ {
+ JNINativeMethod methods[] = { };
+ EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
+ check_jni_abort_catcher->Check(check_jni ? "RegisterNatives received NULL jclass"
+ : "java_class == null");
+ }
+
+ // Passing methods as null is a failure.
+ jclass jlobject = env_->FindClass("java/lang/Object");
+ EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
+ check_jni_abort_catcher->Check("methods == null");
+
+ // Unregisters null is a failure.
+ EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
+ check_jni_abort_catcher->Check(check_jni ? "UnregisterNatives received NULL jclass"
+ : "java_class == null");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+
+ void GetPrimitiveArrayElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jbooleanArray array = env_->NewBooleanArray(10);
+ jboolean is_copy;
+ EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to get byte primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to get short primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to get char primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to get int primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to get long primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to get float primitive array elements with an object of type boolean[]");
+ EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to get double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
+ nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to get boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
+ nullptr);
+ jni_abort_catcher.Check(
+ check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to get boolean primitive array elements with an object of type java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jbooleanArray array = env_->NewBooleanArray(10);
+ ASSERT_TRUE(array != nullptr);
+ jboolean is_copy;
+ jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
+ ASSERT_TRUE(elements != nullptr);
+ env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
+ reinterpret_cast<jbyte*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to release byte primitive array elements with an object of type boolean[]");
+ env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
+ reinterpret_cast<jshort*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to release short primitive array elements with an object of type boolean[]");
+ env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
+ reinterpret_cast<jchar*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to release char primitive array elements with an object of type boolean[]");
+ env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
+ reinterpret_cast<jint*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to release int primitive array elements with an object of type boolean[]");
+ env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
+ reinterpret_cast<jlong*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to release long primitive array elements with an object of type boolean[]");
+ env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
+ reinterpret_cast<jfloat*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to release float primitive array elements with an object of type boolean[]");
+ env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
+ reinterpret_cast<jdouble*>(elements), 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to release double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to release boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
+ jni_abort_catcher.Check(
+ check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to release boolean primitive array elements with an object of type "
+ "java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetReleasePrimitiveArrayCriticalOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jobject object = env_->NewStringUTF("Test String");
+ jboolean is_copy;
+ void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "expected primitive array, given java.lang.String");
+ env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "expected primitive array, given java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void GetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+ constexpr size_t kLength = 10;
+ jbooleanArray array = env_->NewBooleanArray(kLength);
+ ASSERT_TRUE(array != nullptr);
+ jboolean elements[kLength];
+ env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+ reinterpret_cast<jbyte*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to get region of byte primitive array elements with an object of type boolean[]");
+ env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+ reinterpret_cast<jshort*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to get region of short primitive array elements with an object of type boolean[]");
+ env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+ reinterpret_cast<jchar*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to get region of char primitive array elements with an object of type boolean[]");
+ env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+ reinterpret_cast<jint*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to get region of int primitive array elements with an object of type boolean[]");
+ env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+ reinterpret_cast<jlong*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to get region of long primitive array elements with an object of type boolean[]");
+ env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+ reinterpret_cast<jfloat*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to get region of float primitive array elements with an object of type boolean[]");
+ env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+ reinterpret_cast<jdouble*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to get region of double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to get region of boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to get region of boolean primitive array elements with an object of type "
+ "java.lang.String");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void SetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+ constexpr size_t kLength = 10;
+ jbooleanArray array = env_->NewBooleanArray(kLength);
+ ASSERT_TRUE(array != nullptr);
+ jboolean elements[kLength];
+ env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
+ reinterpret_cast<jbyte*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected byte[]"
+ : "attempt to set region of byte primitive array elements with an object of type boolean[]");
+ env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
+ reinterpret_cast<jshort*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected short[]"
+ : "attempt to set region of short primitive array elements with an object of type boolean[]");
+ env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
+ reinterpret_cast<jchar*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected char[]"
+ : "attempt to set region of char primitive array elements with an object of type boolean[]");
+ env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
+ reinterpret_cast<jint*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected int[]"
+ : "attempt to set region of int primitive array elements with an object of type boolean[]");
+ env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
+ reinterpret_cast<jlong*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected long[]"
+ : "attempt to set region of long primitive array elements with an object of type boolean[]");
+ env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
+ reinterpret_cast<jfloat*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected float[]"
+ : "attempt to set region of float primitive array elements with an object of type boolean[]");
+ env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
+ reinterpret_cast<jdouble*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type boolean[] expected double[]"
+ : "attempt to set region of double primitive array elements with an object of type boolean[]");
+ jbyteArray array2 = env_->NewByteArray(10);
+ env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(
+ check_jni ? "incompatible array type byte[] expected boolean[]"
+ : "attempt to set region of boolean primitive array elements with an object of type byte[]");
+ jobject object = env_->NewStringUTF("Test String");
+ env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
+ reinterpret_cast<jboolean*>(elements));
+ jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String"
+ : "attempt to set region of boolean primitive array elements with an object of type "
+ "java.lang.String");
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
+ void NewObjectArrayBadArguments(bool check_jni) {
+ bool old_check_jni = vm_->SetCheckJniEnabled(check_jni);
+ CheckJniAbortCatcher jni_abort_catcher;
+
+ jclass element_class = env_->FindClass("java/lang/String");
+ ASSERT_NE(element_class, nullptr);
+
+ env_->NewObjectArray(-1, element_class, nullptr);
+ jni_abort_catcher.Check(check_jni ? "negative jsize: -1" : "negative array length: -1");
+
+ env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
+ jni_abort_catcher.Check(check_jni ? "negative jsize: -2147483648"
+ : "negative array length: -2147483648");
+
+ EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni));
+ }
+
JavaVMExt* vm_;
JNIEnv* env_;
jclass aioobe_;
@@ -125,48 +621,8 @@
}
TEST_F(JniInternalTest, FindClass) {
- // Reference types...
- ExpectClassFound("java/lang/String");
- // ...for arrays too, where you must include "L;".
- ExpectClassFound("[Ljava/lang/String;");
- // Primitive arrays are okay too, if the primitive type is valid.
- ExpectClassFound("[C");
-
- {
- CheckJniAbortCatcher check_jni_abort_catcher;
- env_->FindClass(nullptr);
- check_jni_abort_catcher.Check("name == null");
-
- // We support . as well as / for compatibility, if -Xcheck:jni is off.
- ExpectClassFound("java.lang.String");
- check_jni_abort_catcher.Check("illegal class name 'java.lang.String'");
- ExpectClassNotFound("Ljava.lang.String;");
- check_jni_abort_catcher.Check("illegal class name 'Ljava.lang.String;'");
- ExpectClassFound("[Ljava.lang.String;");
- check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'");
- ExpectClassNotFound("[java.lang.String");
- check_jni_abort_catcher.Check("illegal class name '[java.lang.String'");
-
- // You can't include the "L;" in a JNI class descriptor.
- ExpectClassNotFound("Ljava/lang/String;");
- check_jni_abort_catcher.Check("illegal class name 'Ljava/lang/String;'");
-
- // But you must include it for an array of any reference type.
- ExpectClassNotFound("[java/lang/String");
- check_jni_abort_catcher.Check("illegal class name '[java/lang/String'");
-
- ExpectClassNotFound("[K");
- check_jni_abort_catcher.Check("illegal class name '[K'");
-
- // Void arrays aren't allowed.
- ExpectClassNotFound("[V");
- check_jni_abort_catcher.Check("illegal class name '[V'");
- }
-
- // But primitive types aren't allowed...
- ExpectClassNotFound("C");
- ExpectClassNotFound("V");
- ExpectClassNotFound("K");
+ FindClassTest(false);
+ FindClassTest(true);
}
TEST_F(JniInternalTest, GetFieldID) {
@@ -208,16 +664,8 @@
ExpectException(jlnsfe);
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- fid = env_->GetFieldID(nullptr, "count", "I");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("java_class == null");
- fid = env_->GetFieldID(c, nullptr, "I");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("name == null");
- fid = env_->GetFieldID(c, "count", nullptr);
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("sig == null");
+ GetFieldIdBadArgumentTest(false);
+ GetFieldIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, GetStaticFieldID) {
@@ -253,16 +701,8 @@
ExpectException(jlnsfe);
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("java_class == null");
- fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;");
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("name == null");
- fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr);
- EXPECT_EQ(nullptr, fid);
- check_jni_abort_catcher.Check("sig == null");
+ GetStaticFieldIdBadArgumentTest(false);
+ GetStaticFieldIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, GetMethodID) {
@@ -302,16 +742,8 @@
EXPECT_FALSE(env_->ExceptionCheck());
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("java_class == null");
- method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("name == null");
- method = env_->GetMethodID(jlnsme, "<init>", nullptr);
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("sig == null");
+ GetMethodIdBadArgumentTest(false);
+ GetMethodIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, CallVoidMethodNullReceiver) {
@@ -325,7 +757,6 @@
// Null object to CallVoidMethod.
CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V");
env_->CallVoidMethod(nullptr, method);
check_jni_abort_catcher.Check("null");
}
@@ -356,16 +787,8 @@
EXPECT_FALSE(env_->ExceptionCheck());
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("java_class == null");
- method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;");
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("name == null");
- method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr);
- EXPECT_EQ(nullptr, method);
- check_jni_abort_catcher.Check("sig == null");
+ GetStaticMethodIdBadArgumentTest(false);
+ GetStaticMethodIdBadArgumentTest(true);
}
TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
@@ -386,13 +809,8 @@
ASSERT_EQ(4, env_->GetIntField(s, fid2));
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- field = env_->ToReflectedField(c, nullptr, JNI_FALSE);
- EXPECT_EQ(field, nullptr);
- check_jni_abort_catcher.Check("fid == null");
- fid2 = env_->FromReflectedField(nullptr);
- ASSERT_EQ(fid2, nullptr);
- check_jni_abort_catcher.Check("jlr_field == null");
+ GetFromReflectedField_ToReflectedFieldBadArgumentTest(false);
+ GetFromReflectedField_ToReflectedFieldBadArgumentTest(true);
}
TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
@@ -433,13 +851,8 @@
ASSERT_EQ(4, env_->CallIntMethod(s, mid2));
// Bad arguments.
- CheckJniAbortCatcher check_jni_abort_catcher;
- method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE);
- EXPECT_EQ(method, nullptr);
- check_jni_abort_catcher.Check("mid == null");
- mid2 = env_->FromReflectedMethod(method);
- ASSERT_EQ(mid2, nullptr);
- check_jni_abort_catcher.Check("jlr_method == null");
+ GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(false);
+ GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(true);
}
static void BogusMethod() {
@@ -514,23 +927,11 @@
}
EXPECT_FALSE(env_->ExceptionCheck());
- // Passing a class of null is a failure.
- {
- JNINativeMethod methods[] = { };
- EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR);
- check_jni_abort_catcher.Check("java_class == null");
- }
-
- // Passing methods as null is a failure.
- EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR);
- check_jni_abort_catcher.Check("methods == null");
-
- // Unregisters null is a failure.
- EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR);
- check_jni_abort_catcher.Check("java_class == null");
-
// Unregistering a class with no natives is a warning.
EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK);
+
+ RegisterAndUnregisterNativesBadArguments(false, &check_jni_abort_catcher);
+ RegisterAndUnregisterNativesBadArguments(true, &check_jni_abort_catcher);
}
#define EXPECT_PRIMITIVE_ARRAY(new_fn, \
@@ -544,6 +945,7 @@
\
{ \
CheckJniAbortCatcher jni_abort_catcher; \
+ down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(false); \
/* Allocate an negative sized array and check it has the right failure type. */ \
EXPECT_EQ(env_->new_fn(-1), nullptr); \
jni_abort_catcher.Check("negative array length: -1"); \
@@ -566,6 +968,7 @@
jni_abort_catcher.Check("buf == null"); \
env_->set_region_fn(a, 0, size, nullptr); \
jni_abort_catcher.Check("buf == null"); \
+ down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(true); \
} \
/* Allocate an array and check it has the right type and length. */ \
scalar_type ## Array a = env_->new_fn(size); \
@@ -670,189 +1073,28 @@
}
TEST_F(JniInternalTest, GetPrimitiveArrayElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- jbooleanArray array = env_->NewBooleanArray(10);
- jboolean is_copy;
- EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get byte primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get short primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get char primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get int primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get long primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get float primitive array elements with an object of type boolean[]");
- EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr);
- jni_abort_catcher.Check(
- "attempt to get double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy),
- nullptr);
- jni_abort_catcher.Check(
- "attempt to get boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy),
- nullptr);
- jni_abort_catcher.Check(
- "attempt to get boolean primitive array elements with an object of type java.lang.String");
+ GetPrimitiveArrayElementsOfWrongType(false);
+ GetPrimitiveArrayElementsOfWrongType(true);
}
TEST_F(JniInternalTest, ReleasePrimitiveArrayElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- jbooleanArray array = env_->NewBooleanArray(10);
- ASSERT_TRUE(array != nullptr);
- jboolean is_copy;
- jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy);
- ASSERT_TRUE(elements != nullptr);
- env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array),
- reinterpret_cast<jbyte*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release byte primitive array elements with an object of type boolean[]");
- env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array),
- reinterpret_cast<jshort*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release short primitive array elements with an object of type boolean[]");
- env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array),
- reinterpret_cast<jchar*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release char primitive array elements with an object of type boolean[]");
- env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array),
- reinterpret_cast<jint*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release int primitive array elements with an object of type boolean[]");
- env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array),
- reinterpret_cast<jlong*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release long primitive array elements with an object of type boolean[]");
- env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array),
- reinterpret_cast<jfloat*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release float primitive array elements with an object of type boolean[]");
- env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array),
- reinterpret_cast<jdouble*>(elements), 0);
- jni_abort_catcher.Check(
- "attempt to release double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0);
- jni_abort_catcher.Check(
- "attempt to release boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0);
- jni_abort_catcher.Check(
- "attempt to release boolean primitive array elements with an object of type "
- "java.lang.String");
+ ReleasePrimitiveArrayElementsOfWrongType(false);
+ ReleasePrimitiveArrayElementsOfWrongType(true);
}
+
TEST_F(JniInternalTest, GetReleasePrimitiveArrayCriticalOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- jobject object = env_->NewStringUTF("Test String");
- jboolean is_copy;
- void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy);
- jni_abort_catcher.Check("expected primitive array, given java.lang.String");
- env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0);
- jni_abort_catcher.Check("expected primitive array, given java.lang.String");
+ GetReleasePrimitiveArrayCriticalOfWrongType(false);
+ GetReleasePrimitiveArrayCriticalOfWrongType(true);
}
TEST_F(JniInternalTest, GetPrimitiveArrayRegionElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- constexpr size_t kLength = 10;
- jbooleanArray array = env_->NewBooleanArray(kLength);
- ASSERT_TRUE(array != nullptr);
- jboolean elements[kLength];
- env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
- reinterpret_cast<jbyte*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of byte primitive array elements with an object of type boolean[]");
- env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
- reinterpret_cast<jshort*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of short primitive array elements with an object of type boolean[]");
- env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
- reinterpret_cast<jchar*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of char primitive array elements with an object of type boolean[]");
- env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
- reinterpret_cast<jint*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of int primitive array elements with an object of type boolean[]");
- env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
- reinterpret_cast<jlong*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of long primitive array elements with an object of type boolean[]");
- env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
- reinterpret_cast<jfloat*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of float primitive array elements with an object of type boolean[]");
- env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
- reinterpret_cast<jdouble*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to get region of boolean primitive array elements with an object of type "
- "java.lang.String");
+ GetPrimitiveArrayRegionElementsOfWrongType(false);
+ GetPrimitiveArrayRegionElementsOfWrongType(true);
}
TEST_F(JniInternalTest, SetPrimitiveArrayRegionElementsOfWrongType) {
- CheckJniAbortCatcher jni_abort_catcher;
- constexpr size_t kLength = 10;
- jbooleanArray array = env_->NewBooleanArray(kLength);
- ASSERT_TRUE(array != nullptr);
- jboolean elements[kLength];
- env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength,
- reinterpret_cast<jbyte*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of byte primitive array elements with an object of type boolean[]");
- env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength,
- reinterpret_cast<jshort*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of short primitive array elements with an object of type boolean[]");
- env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength,
- reinterpret_cast<jchar*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of char primitive array elements with an object of type boolean[]");
- env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength,
- reinterpret_cast<jint*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of int primitive array elements with an object of type boolean[]");
- env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength,
- reinterpret_cast<jlong*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of long primitive array elements with an object of type boolean[]");
- env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength,
- reinterpret_cast<jfloat*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of float primitive array elements with an object of type boolean[]");
- env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength,
- reinterpret_cast<jdouble*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of double primitive array elements with an object of type boolean[]");
- jbyteArray array2 = env_->NewByteArray(10);
- env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of boolean primitive array elements with an object of type byte[]");
- jobject object = env_->NewStringUTF("Test String");
- env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength,
- reinterpret_cast<jboolean*>(elements));
- jni_abort_catcher.Check(
- "attempt to set region of boolean primitive array elements with an object of type "
- "java.lang.String");
+ SetPrimitiveArrayRegionElementsOfWrongType(false);
+ SetPrimitiveArrayRegionElementsOfWrongType(true);
}
TEST_F(JniInternalTest, NewObjectArray) {
@@ -873,12 +1115,8 @@
EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr));
// Negative array length checks.
- CheckJniAbortCatcher jni_abort_catcher;
- env_->NewObjectArray(-1, element_class, nullptr);
- jni_abort_catcher.Check("negative array length: -1");
-
- env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr);
- jni_abort_catcher.Check("negative array length: -2147483648");
+ NewObjectArrayBadArguments(false);
+ NewObjectArrayBadArguments(true);
}
TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) {
@@ -888,6 +1126,7 @@
};
ASSERT_EQ(strlen(primitive_descriptors), arraysize(primitive_names));
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
CheckJniAbortCatcher jni_abort_catcher;
for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
env_->NewObjectArray(0, nullptr, nullptr);
@@ -897,6 +1136,16 @@
std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
jni_abort_catcher.Check(error_msg.c_str());
}
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ for (size_t i = 0; i < strlen(primitive_descriptors); ++i) {
+ env_->NewObjectArray(0, nullptr, nullptr);
+ jni_abort_catcher.Check("NewObjectArray received NULL jclass");
+ jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]);
+ env_->NewObjectArray(1, primitive_class, nullptr);
+ std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i]));
+ jni_abort_catcher.Check(error_msg.c_str());
+ }
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) {
@@ -956,8 +1205,13 @@
// Null as class should fail.
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
jni_abort_catcher.Check("java_class == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr);
+ jni_abort_catcher.Check("GetSuperclass received NULL jclass");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, IsAssignableFrom) {
@@ -991,10 +1245,17 @@
// Null as either class should fail.
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
jni_abort_catcher.Check("java_class1 == null");
EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
jni_abort_catcher.Check("java_class2 == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE);
+ jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+ EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE);
+ jni_abort_catcher.Check("IsAssignableFrom received NULL jclass");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, GetObjectRefType) {
@@ -1008,13 +1269,14 @@
jweak weak_global = env_->NewWeakGlobalRef(local);
EXPECT_EQ(JNIWeakGlobalRefType, env_->GetObjectRefType(weak_global));
+ CheckJniAbortCatcher jni_abort_catcher;
jobject invalid = reinterpret_cast<jobject>(this);
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(invalid));
+ jni_abort_catcher.Check("use of invalid jobject");
// TODO: invoke a native method and test that its arguments are considered local references.
// Null as object should fail.
- CheckJniAbortCatcher jni_abort_catcher;
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(nullptr));
jni_abort_catcher.Check("java_object == null");
}
@@ -1079,10 +1341,17 @@
TEST_F(JniInternalTest, NewStringNegativeLength) {
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
env_->NewString(nullptr, -1);
jni_abort_catcher.Check("char_count < 0: -1");
env_->NewString(nullptr, std::numeric_limits<jint>::min());
jni_abort_catcher.Check("char_count < 0: -2147483648");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ env_->NewString(nullptr, -1);
+ jni_abort_catcher.Check("negative jsize: -1");
+ env_->NewString(nullptr, std::numeric_limits<jint>::min());
+ jni_abort_catcher.Check("negative jsize: -2147483648");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) {
@@ -1140,10 +1409,17 @@
TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
// Passing in a nullptr jstring is ignored normally, but caught by -Xcheck:jni.
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
{
CheckJniAbortCatcher check_jni_abort_catcher;
EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
- check_jni_abort_catcher.Check("GetStringUTFChars received null jstring");
+ }
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr);
+ check_jni_abort_catcher.Check("GetStringUTFChars received NULL jstring");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
jstring s = env_->NewStringUTF("hello");
@@ -1238,10 +1514,17 @@
// Null as array should fail.
CheckJniAbortCatcher jni_abort_catcher;
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
jni_abort_catcher.Check("java_array == null");
env_->SetObjectArrayElement(nullptr, 0, nullptr);
jni_abort_catcher.Check("java_array == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0));
+ jni_abort_catcher.Check("jarray was NULL");
+ env_->SetObjectArrayElement(nullptr, 0, nullptr);
+ jni_abort_catcher.Check("jarray was NULL");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
#define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \
@@ -1253,15 +1536,28 @@
env_->SetStatic ## type ## Field(c, fid, value2); \
EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \
\
+ bool old_check_jni = vm_->SetCheckJniEnabled(false); \
+ { \
+ CheckJniAbortCatcher jni_abort_catcher; \
+ env_->GetStatic ## type ## Field(nullptr, fid); \
+ env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+ } \
CheckJniAbortCatcher jni_abort_catcher; \
- env_->GetStatic ## type ## Field(nullptr, fid); \
- jni_abort_catcher.Check("received null jclass"); \
- env_->SetStatic ## type ## Field(nullptr, fid, value1); \
- jni_abort_catcher.Check("received null jclass"); \
env_->GetStatic ## type ## Field(c, nullptr); \
jni_abort_catcher.Check("fid == null"); \
env_->SetStatic ## type ## Field(c, nullptr, value1); \
jni_abort_catcher.Check("fid == null"); \
+ \
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+ env_->GetStatic ## type ## Field(nullptr, fid); \
+ jni_abort_catcher.Check("received NULL jclass"); \
+ env_->SetStatic ## type ## Field(nullptr, fid, value1); \
+ jni_abort_catcher.Check("received NULL jclass"); \
+ env_->GetStatic ## type ## Field(c, nullptr); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ env_->SetStatic ## type ## Field(c, nullptr, value1); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
} while (false)
#define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \
@@ -1273,6 +1569,7 @@
env_->Set ## type ## Field(instance, fid, value2); \
EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \
\
+ bool old_check_jni = vm_->SetCheckJniEnabled(false); \
CheckJniAbortCatcher jni_abort_catcher; \
env_->Get ## type ## Field(nullptr, fid); \
jni_abort_catcher.Check("obj == null"); \
@@ -1282,6 +1579,16 @@
jni_abort_catcher.Check("fid == null"); \
env_->Set ## type ## Field(instance, nullptr, value1); \
jni_abort_catcher.Check("fid == null"); \
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \
+ env_->Get ## type ## Field(nullptr, fid); \
+ jni_abort_catcher.Check("field operation on NULL object:"); \
+ env_->Set ## type ## Field(nullptr, fid, value1); \
+ jni_abort_catcher.Check("field operation on NULL object:"); \
+ env_->Get ## type ## Field(instance, nullptr); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ env_->Set ## type ## Field(instance, nullptr, value1); \
+ jni_abort_catcher.Check("jfieldID was NULL"); \
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \
} while (false)
@@ -1373,12 +1680,17 @@
// Currently, deleting an already-deleted reference is just a CheckJNI warning.
{
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteLocalRef(s);
+ }
CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
env_->DeleteLocalRef(s);
-
- std::string expected(StringPrintf("native code passing in reference to "
- "invalid local reference: %p", s));
+ std::string expected(StringPrintf("use of deleted local reference %p", s));
check_jni_abort_catcher.Check(expected.c_str());
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
s = env_->NewStringUTF("");
@@ -1412,7 +1724,6 @@
jobject outer;
jobject inner1, inner2;
ScopedObjectAccess soa(env_);
- mirror::Object* inner2_direct_pointer;
{
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(4));
outer = env_->NewLocalRef(original);
@@ -1421,24 +1732,35 @@
ASSERT_EQ(JNI_OK, env_->PushLocalFrame(4));
inner1 = env_->NewLocalRef(outer);
inner2 = env_->NewStringUTF("survivor");
- inner2_direct_pointer = soa.Decode<mirror::Object*>(inner2);
- env_->PopLocalFrame(inner2);
+ EXPECT_NE(env_->PopLocalFrame(inner2), nullptr);
}
EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(outer));
- EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+ check_jni_abort_catcher.Check("use of deleted local reference");
+ }
// Our local reference for the survivor is invalid because the survivor
// gets a new local reference...
- EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+ check_jni_abort_catcher.Check("use of deleted local reference");
+ }
- env_->PopLocalFrame(nullptr);
+ EXPECT_EQ(env_->PopLocalFrame(nullptr), nullptr);
}
EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
+ CheckJniAbortCatcher check_jni_abort_catcher;
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(outer));
+ check_jni_abort_catcher.Check("use of deleted local reference");
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+ check_jni_abort_catcher.Check("use of deleted local reference");
EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+ check_jni_abort_catcher.Check("use of deleted local reference");
}
TEST_F(JniInternalTest, NewGlobalRef_nullptr) {
@@ -1469,12 +1791,17 @@
// Currently, deleting an already-deleted reference is just a CheckJNI warning.
{
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteGlobalRef(o);
+ }
CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
env_->DeleteGlobalRef(o);
-
- std::string expected(StringPrintf("native code passing in reference to "
- "invalid global reference: %p", o));
+ std::string expected(StringPrintf("use of deleted global reference %p", o));
check_jni_abort_catcher.Check(expected.c_str());
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
jobject o1 = env_->NewGlobalRef(s);
@@ -1514,12 +1841,17 @@
// Currently, deleting an already-deleted reference is just a CheckJNI warning.
{
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ {
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ env_->DeleteWeakGlobalRef(o);
+ }
CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
env_->DeleteWeakGlobalRef(o);
-
- std::string expected(StringPrintf("native code passing in reference to "
- "invalid weak global reference: %p", o));
+ std::string expected(StringPrintf("use of deleted weak global reference %p", o));
check_jni_abort_catcher.Check(expected.c_str());
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
jobject o1 = env_->NewWeakGlobalRef(s);
@@ -1538,8 +1870,6 @@
}
TEST_F(JniInternalTest, Throw) {
- EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
jclass exception_class = env_->FindClass("java/lang/RuntimeException");
ASSERT_TRUE(exception_class != nullptr);
jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class));
@@ -1550,11 +1880,18 @@
jthrowable thrown_exception = env_->ExceptionOccurred();
env_->ExceptionClear();
EXPECT_TRUE(env_->IsSameObject(exception, thrown_exception));
+
+ // Bad argument.
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
+ check_jni_abort_catcher.Check("Throw received NULL jthrowable");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, ThrowNew) {
- EXPECT_EQ(JNI_ERR, env_->Throw(nullptr));
-
jclass exception_class = env_->FindClass("java/lang/RuntimeException");
ASSERT_TRUE(exception_class != nullptr);
@@ -1571,6 +1908,16 @@
thrown_exception = env_->ExceptionOccurred();
env_->ExceptionClear();
EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class));
+
+ // Bad argument.
+ bool old_check_jni = vm_->SetCheckJniEnabled(false);
+ CheckJniAbortCatcher check_jni_abort_catcher;
+ EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+ check_jni_abort_catcher.Check("c == null");
+ EXPECT_FALSE(vm_->SetCheckJniEnabled(true));
+ EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr));
+ check_jni_abort_catcher.Check("ThrowNew received NULL jclass");
+ EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni));
}
TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) {
diff --git a/runtime/leb128.h b/runtime/leb128.h
index 89de16e..dfb42b8 100644
--- a/runtime/leb128.h
+++ b/runtime/leb128.h
@@ -137,25 +137,26 @@
return dest;
}
-// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format.
-class Leb128EncodingVector {
+// An encoder that pushed uint32_t data onto the given std::vector.
+class Leb128Encoder {
public:
- Leb128EncodingVector() {
+ explicit Leb128Encoder(std::vector<uint8_t>* data) : data_(data) {
+ DCHECK(data != nullptr);
}
void Reserve(uint32_t size) {
- data_.reserve(size);
+ data_->reserve(size);
}
void PushBackUnsigned(uint32_t value) {
uint8_t out = value & 0x7f;
value >>= 7;
while (value != 0) {
- data_.push_back(out | 0x80);
+ data_->push_back(out | 0x80);
out = value & 0x7f;
value >>= 7;
}
- data_.push_back(out);
+ data_->push_back(out);
}
template<typename It>
@@ -169,12 +170,12 @@
uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6;
uint8_t out = value & 0x7f;
while (extra_bits != 0u) {
- data_.push_back(out | 0x80);
+ data_->push_back(out | 0x80);
value >>= 7;
out = value & 0x7f;
extra_bits >>= 7;
}
- data_.push_back(out);
+ data_->push_back(out);
}
template<typename It>
@@ -185,12 +186,23 @@
}
const std::vector<uint8_t>& GetData() const {
- return data_;
+ return *data_;
+ }
+
+ protected:
+ std::vector<uint8_t>* const data_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Leb128Encoder);
+};
+
+// An encoder with an API similar to vector<uint32_t> where the data is captured in ULEB128 format.
+class Leb128EncodingVector FINAL : private std::vector<uint8_t>, public Leb128Encoder {
+ public:
+ Leb128EncodingVector() : Leb128Encoder(this) {
}
private:
- std::vector<uint8_t> data_;
-
DISALLOW_COPY_AND_ASSIGN(Leb128EncodingVector);
};
diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h
index 414b3bb..cf6f83c 100644
--- a/runtime/lock_word-inl.h
+++ b/runtime/lock_word-inl.h
@@ -50,6 +50,7 @@
inline LockWord::LockWord(Monitor* mon)
: value_(mon->GetMonitorId() | (kStateFat << kStateShift)) {
DCHECK_EQ(FatLockMonitor(), mon);
+ DCHECK_LE(mon->GetMonitorId(), static_cast<uint32_t>(kMaxMonitorId));
}
inline int32_t LockWord::GetHashCode() const {
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index e585412..13cc3b0 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -63,6 +63,7 @@
kThinLockOwnerShift = 0,
kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1,
+ kThinLockMaxOwner = kThinLockOwnerMask,
// Count in higher bits.
kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift,
kThinLockCountMask = (1 << kThinLockCountSize) - 1,
@@ -80,10 +81,13 @@
kHashShift = 0,
kHashSize = 32 - kStateSize,
kHashMask = (1 << kHashSize) - 1,
+ kMaxHash = kHashMask,
+ kMaxMonitorId = kMaxHash
};
static LockWord FromThinLockId(uint32_t thread_id, uint32_t count) {
- CHECK_LE(thread_id, static_cast<uint32_t>(kThinLockOwnerMask));
+ CHECK_LE(thread_id, static_cast<uint32_t>(kThinLockMaxOwner));
+ CHECK_LE(count, static_cast<uint32_t>(kThinLockMaxCount));
return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift) |
(kStateThinOrUnlocked << kStateShift));
}
@@ -94,7 +98,7 @@
}
static LockWord FromHashCode(uint32_t hash_code) {
- CHECK_LE(hash_code, static_cast<uint32_t>(kHashMask));
+ CHECK_LE(hash_code, static_cast<uint32_t>(kMaxHash));
return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift));
}
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 0615abd..d755cb9 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -130,7 +130,6 @@
uintptr_t MemMap::next_mem_pos_ = GenerateNextMemPos();
#endif
-#if !defined(__APPLE__) // TODO: Reanable after b/16861075 BacktraceMap issue is addressed.
// Return true if the address range is contained in a single /proc/self/map entry.
static bool ContainedWithinExistingMap(uintptr_t begin,
uintptr_t end,
@@ -153,7 +152,6 @@
begin, end, maps.c_str());
return false;
}
-#endif
// Return true if the address range does not conflict with any /proc/self/maps entry.
static bool CheckNonOverlapping(uintptr_t begin,
@@ -388,6 +386,8 @@
std::string* error_msg) {
CHECK_NE(0, prot);
CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
+ uintptr_t expected = reinterpret_cast<uintptr_t>(expected_ptr);
+ uintptr_t limit = expected + byte_count;
// Note that we do not allow MAP_FIXED unless reuse == true, i.e we
// expect his mapping to be contained within an existing map.
@@ -396,11 +396,7 @@
// Only use this if you actually made the page reservation yourself.
CHECK(expected_ptr != nullptr);
-#if !defined(__APPLE__) // TODO: Reanable after b/16861075 BacktraceMap issue is addressed.
- uintptr_t expected = reinterpret_cast<uintptr_t>(expected_ptr);
- uintptr_t limit = expected + byte_count;
DCHECK(ContainedWithinExistingMap(expected, limit, error_msg));
-#endif
flags |= MAP_FIXED;
} else {
CHECK_EQ(0, flags & MAP_FIXED);
@@ -489,7 +485,7 @@
MutexLock mu(Thread::Current(), *Locks::mem_maps_lock_);
maps_.insert(std::pair<void*, MemMap*>(base_begin_, this));
}
-};
+}
MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
std::string* error_msg) {
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index 69f618c..e54d0e0 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -18,6 +18,8 @@
#include <memory>
+#include <valgrind.h>
+
#include "gtest/gtest.h"
namespace art {
@@ -198,17 +200,20 @@
#endif
TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) {
- uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000;
- std::string error_msg;
- std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
- reinterpret_cast<byte*>(start_addr),
- 0x21000000,
- PROT_READ | PROT_WRITE,
- true,
- &error_msg));
- ASSERT_TRUE(map.get() != nullptr) << error_msg;
- ASSERT_TRUE(error_msg.empty());
- ASSERT_EQ(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), start_addr);
+ // This test may not work under valgrind.
+ if (RUNNING_ON_VALGRIND == 0) {
+ uintptr_t start_addr = ART_BASE_ADDRESS + 0x1000000;
+ std::string error_msg;
+ std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousExactAddr32bitHighAddr",
+ reinterpret_cast<byte*>(start_addr),
+ 0x21000000,
+ PROT_READ | PROT_WRITE,
+ true,
+ &error_msg));
+ ASSERT_TRUE(map.get() != nullptr) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+ ASSERT_EQ(reinterpret_cast<uintptr_t>(BaseBegin(map.get())), start_addr);
+ }
}
TEST_F(MemMapTest, MapAnonymousOverflow) {
diff --git a/runtime/method_helper-inl.h b/runtime/method_helper-inl.h
index 9af835f..143f4bc 100644
--- a/runtime/method_helper-inl.h
+++ b/runtime/method_helper-inl.h
@@ -26,7 +26,9 @@
namespace art {
-inline bool MethodHelper::HasSameNameAndSignature(MethodHelper* other) {
+template <template <class T> class HandleKind>
+template <template <class T2> class HandleKind2>
+inline bool MethodHelperT<HandleKind>::HasSameNameAndSignature(MethodHelperT<HandleKind2>* other) {
const DexFile* dex_file = method_->GetDexFile();
const DexFile::MethodId& mid = dex_file->GetMethodId(GetMethod()->GetDexMethodIndex());
if (method_->GetDexCache() == other->method_->GetDexCache()) {
@@ -43,7 +45,9 @@
return dex_file->GetMethodSignature(mid) == other_dex_file->GetMethodSignature(other_mid);
}
-inline mirror::Class* MethodHelper::GetClassFromTypeIdx(uint16_t type_idx, bool resolve) {
+template <template <class T> class HandleKind>
+inline mirror::Class* MethodHelperT<HandleKind>::GetClassFromTypeIdx(uint16_t type_idx,
+ bool resolve) {
mirror::ArtMethod* method = GetMethod();
mirror::Class* type = method->GetDexCacheResolvedType(type_idx);
if (type == nullptr && resolve) {
@@ -53,7 +57,8 @@
return type;
}
-inline mirror::Class* MethodHelper::GetReturnType(bool resolve) {
+template <template <class T> class HandleKind>
+inline mirror::Class* MethodHelperT<HandleKind>::GetReturnType(bool resolve) {
mirror::ArtMethod* method = GetMethod();
const DexFile* dex_file = method->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
@@ -62,7 +67,8 @@
return GetClassFromTypeIdx(return_type_idx, resolve);
}
-inline mirror::String* MethodHelper::ResolveString(uint32_t string_idx) {
+template <template <class T> class HandleKind>
+inline mirror::String* MethodHelperT<HandleKind>::ResolveString(uint32_t string_idx) {
mirror::ArtMethod* method = GetMethod();
mirror::String* s = method->GetDexCacheStrings()->Get(string_idx);
if (UNLIKELY(s == nullptr)) {
diff --git a/runtime/method_helper.cc b/runtime/method_helper.cc
index d6f83a8..79c2b91 100644
--- a/runtime/method_helper.cc
+++ b/runtime/method_helper.cc
@@ -25,7 +25,8 @@
namespace art {
-mirror::String* MethodHelper::GetNameAsString(Thread* self) {
+template <template <class T> class HandleKind>
+mirror::String* MethodHelperT<HandleKind>::GetNameAsString(Thread* self) {
const DexFile* dex_file = method_->GetDexFile();
mirror::ArtMethod* method = method_->GetInterfaceMethodIfProxy();
uint32_t dex_method_idx = method->GetDexMethodIndex();
@@ -36,7 +37,10 @@
dex_cache);
}
-bool MethodHelper::HasSameSignatureWithDifferentClassLoaders(MethodHelper* other) {
+template <template <class T> class HandleKind>
+template <template <class T2> class HandleKind2>
+bool MethodHelperT<HandleKind>::HasSameSignatureWithDifferentClassLoaders(
+ MethodHelperT<HandleKind2>* other) {
if (UNLIKELY(GetReturnType() != other->GetReturnType())) {
return false;
}
@@ -62,7 +66,8 @@
return true;
}
-uint32_t MethodHelper::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
+template <template <class T> class HandleKind>
+uint32_t MethodHelperT<HandleKind>::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* method = GetMethod();
const DexFile* dexfile = method->GetDexFile();
@@ -102,8 +107,9 @@
return DexFile::kDexNoIndex;
}
-uint32_t MethodHelper::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile,
- uint32_t name_and_signature_idx)
+template <template <typename> class HandleKind>
+uint32_t MethodHelperT<HandleKind>::FindDexMethodIndexInOtherDexFile(
+ const DexFile& other_dexfile, uint32_t name_and_signature_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* method = GetMethod();
const DexFile* dexfile = method->GetDexFile();
@@ -133,4 +139,38 @@
return DexFile::kDexNoIndex;
}
+// Instantiate methods.
+template mirror::String* MethodHelperT<Handle>::GetNameAsString(Thread* self);
+
+template mirror::String* MethodHelperT<MutableHandle>::GetNameAsString(Thread* self);
+
+template
+uint32_t MethodHelperT<Handle>::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile);
+template
+uint32_t MethodHelperT<MutableHandle>::FindDexMethodIndexInOtherDexFile(
+ const DexFile& other_dexfile);
+
+template
+uint32_t MethodHelperT<Handle>::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile,
+ uint32_t name_and_signature_idx);
+template
+uint32_t MethodHelperT<MutableHandle>::FindDexMethodIndexInOtherDexFile(
+ const DexFile& other_dexfile, uint32_t name_and_signature_idx);
+
+template
+bool MethodHelperT<Handle>::HasSameSignatureWithDifferentClassLoaders<Handle>(
+ MethodHelperT<Handle>* other);
+
+template
+bool MethodHelperT<Handle>::HasSameSignatureWithDifferentClassLoaders<MutableHandle>(
+ MethodHelperT<MutableHandle>* other);
+
+template
+bool MethodHelperT<MutableHandle>::HasSameSignatureWithDifferentClassLoaders<Handle>(
+ MethodHelperT<Handle>* other);
+
+template
+bool MethodHelperT<MutableHandle>::HasSameSignatureWithDifferentClassLoaders<MutableHandle>(
+ MethodHelperT<MutableHandle>* other);
+
} // namespace art
diff --git a/runtime/method_helper.h b/runtime/method_helper.h
index f71d273..fe364d3 100644
--- a/runtime/method_helper.h
+++ b/runtime/method_helper.h
@@ -24,23 +24,22 @@
namespace art {
-class MethodHelper {
+template <template <class T> class HandleKind>
+class MethodHelperT {
public:
- explicit MethodHelper(Handle<mirror::ArtMethod> m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : method_(m), shorty_(nullptr), shorty_len_(0) {
- SetMethod(m.Get());
- }
-
- void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(new_m != nullptr);
- SetMethod(new_m);
- shorty_ = nullptr;
+ explicit MethodHelperT(HandleKind<mirror::ArtMethod> m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : method_(m), shorty_(nullptr), shorty_len_(0) {
}
mirror::ArtMethod* GetMethod() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return method_->GetInterfaceMethodIfProxy();
}
+ // GetMethod() != Get() for proxy methods.
+ mirror::ArtMethod* Get() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return method_.Get();
+ }
+
mirror::String* GetNameAsString(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -105,10 +104,12 @@
return GetParamPrimitiveType(param) == Primitive::kPrimNot;
}
- ALWAYS_INLINE bool HasSameNameAndSignature(MethodHelper* other)
+ template <template <class T> class HandleKind2>
+ ALWAYS_INLINE bool HasSameNameAndSignature(MethodHelperT<HandleKind2>* other)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool HasSameSignatureWithDifferentClassLoaders(MethodHelper* other)
+ template <template <class T> class HandleKind2>
+ bool HasSameSignatureWithDifferentClassLoaders(MethodHelperT<HandleKind2>* other)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* GetClassFromTypeIdx(uint16_t type_idx, bool resolve = true)
@@ -125,6 +126,33 @@
uint32_t name_and_signature_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ protected:
+ HandleKind<mirror::ArtMethod> method_;
+
+ const char* shorty_;
+ uint32_t shorty_len_;
+
+ private:
+ template <template <class T2> class HandleKind2> friend class MethodHelperT;
+
+ DISALLOW_COPY_AND_ASSIGN(MethodHelperT);
+};
+
+class MethodHelper : public MethodHelperT<Handle> {
+ using MethodHelperT<Handle>::MethodHelperT;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MethodHelper);
+};
+
+class MutableMethodHelper : public MethodHelperT<MutableHandle> {
+ using MethodHelperT<MutableHandle>::MethodHelperT;
+ public:
+ void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(new_m != nullptr);
+ SetMethod(new_m);
+ shorty_ = nullptr;
+ }
+
private:
// Set the method_ field, for proxy methods looking up the interface method via the resolved
// methods table.
@@ -132,11 +160,7 @@
method_.Assign(method);
}
- Handle<mirror::ArtMethod> method_;
- const char* shorty_;
- uint32_t shorty_len_;
-
- DISALLOW_COPY_AND_ASSIGN(MethodHelper);
+ DISALLOW_COPY_AND_ASSIGN(MutableMethodHelper);
};
} // namespace art
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 2c0ea36..6582226 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -29,19 +29,19 @@
inline uint32_t Array::ClassSize() {
uint32_t vtable_entries = Object::kVTableLength;
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
}
template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline size_t Array::SizeOf() {
// This is safe from overflow because the array was already allocated, so we know it's sane.
- size_t component_size =
- GetClass<kVerifyFlags, kReadBarrierOption>()->template GetComponentSize<kReadBarrierOption>();
+ size_t component_size_shift = GetClass<kVerifyFlags, kReadBarrierOption>()->
+ template GetComponentSizeShift<kReadBarrierOption>();
// Don't need to check this since we already check this in GetClass.
int32_t component_count =
GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
- size_t header_size = DataOffset(component_size).SizeValue();
- size_t data_size = component_count * component_size;
+ size_t header_size = DataOffset(1U << component_size_shift).SizeValue();
+ size_t data_size = component_count << component_size_shift;
return header_size + data_size;
}
@@ -56,24 +56,36 @@
}
static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size)
+ size_t component_size_shift)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(array_class != NULL);
DCHECK_GE(component_count, 0);
DCHECK(array_class->IsArrayClass());
+ size_t component_size = 1U << component_size_shift;
size_t header_size = Array::DataOffset(component_size).SizeValue();
- size_t data_size = component_count * component_size;
+ size_t data_size = static_cast<size_t>(component_count) << component_size_shift;
size_t size = header_size + data_size;
- // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
- size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size);
- if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) {
+ // Check for size_t overflow and throw OutOfMemoryError if this was
+ // an unreasonable request.
+#ifdef __LP64__
+ // 64-bit. No overflow as component_count is 32-bit and the maximum
+ // component size is 8.
+ DCHECK_LE((1U << component_size_shift), 8U);
+#else
+ // 32-bit.
+ DCHECK_NE(header_size, 0U);
+ DCHECK_EQ(RoundUp(header_size, component_size), header_size);
+ // The array length limit (exclusive).
+ const size_t length_limit = (0U - header_size) >> component_size_shift;
+ if (UNLIKELY(length_limit <= static_cast<size_t>(component_count))) {
self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
PrettyDescriptor(array_class).c_str(),
component_count).c_str());
return 0; // failure
}
+#endif
return size;
}
@@ -103,8 +115,10 @@
// array.
class SetLengthToUsableSizeVisitor {
public:
- SetLengthToUsableSizeVisitor(int32_t min_length, size_t header_size, size_t component_size) :
- minimum_length_(min_length), header_size_(header_size), component_size_(component_size) {
+ SetLengthToUsableSizeVisitor(int32_t min_length, size_t header_size,
+ size_t component_size_shift) :
+ minimum_length_(min_length), header_size_(header_size),
+ component_size_shift_(component_size_shift) {
}
void operator()(Object* obj, size_t usable_size) const
@@ -112,10 +126,12 @@
// Avoid AsArray as object is not yet in live bitmap or allocation stack.
Array* array = down_cast<Array*>(obj);
// DCHECK(array->IsArrayInstance());
- int32_t length = (usable_size - header_size_) / component_size_;
+ int32_t length = (usable_size - header_size_) >> component_size_shift_;
DCHECK_GE(length, minimum_length_);
- byte* old_end = reinterpret_cast<byte*>(array->GetRawData(component_size_, minimum_length_));
- byte* new_end = reinterpret_cast<byte*>(array->GetRawData(component_size_, length));
+ byte* old_end = reinterpret_cast<byte*>(array->GetRawData(1U << component_size_shift_,
+ minimum_length_));
+ byte* new_end = reinterpret_cast<byte*>(array->GetRawData(1U << component_size_shift_,
+ length));
// Ensure space beyond original allocation is zeroed.
memset(old_end, 0, new_end - old_end);
array->SetLength(length);
@@ -124,38 +140,46 @@
private:
const int32_t minimum_length_;
const size_t header_size_;
- const size_t component_size_;
+ const size_t component_size_shift_;
DISALLOW_COPY_AND_ASSIGN(SetLengthToUsableSizeVisitor);
};
-template <bool kIsInstrumented>
+template <bool kIsInstrumented, bool kFillUsable>
inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size, gc::AllocatorType allocator_type,
- bool fill_usable) {
+ size_t component_size_shift, gc::AllocatorType allocator_type) {
DCHECK(allocator_type != gc::kAllocatorTypeLOS);
- size_t size = ComputeArraySize(self, array_class, component_count, component_size);
+ DCHECK_EQ(array_class->GetComponentSizeShift(), component_size_shift);
+ DCHECK_EQ(array_class->GetComponentSize(), (1U << component_size_shift));
+ size_t size = ComputeArraySize(self, array_class, component_count, component_size_shift);
+#ifdef __LP64__
+ // 64-bit. No size_t overflow.
+ DCHECK_NE(size, 0U);
+#else
+ // 32-bit.
if (UNLIKELY(size == 0)) {
return nullptr;
}
+#endif
gc::Heap* heap = Runtime::Current()->GetHeap();
Array* result;
- if (!fill_usable) {
+ if (!kFillUsable) {
SetLengthVisitor visitor(component_count);
result = down_cast<Array*>(
heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
allocator_type, visitor));
} else {
- SetLengthToUsableSizeVisitor visitor(component_count, DataOffset(component_size).SizeValue(),
- component_size);
+ SetLengthToUsableSizeVisitor visitor(component_count,
+ DataOffset(1U << component_size_shift).SizeValue(),
+ component_size_shift);
result = down_cast<Array*>(
heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
allocator_type, visitor));
}
if (kIsDebugBuild && result != nullptr && Runtime::Current()->IsStarted()) {
array_class = result->GetClass(); // In case the array class moved.
- CHECK_EQ(array_class->GetComponentSize(), component_size);
- if (!fill_usable) {
+ CHECK_EQ(array_class->GetComponentSize(), 1U << component_size_shift);
+ if (!kFillUsable) {
CHECK_EQ(result->SizeOf(), size);
} else {
CHECK_GE(result->SizeOf(), size);
@@ -173,7 +197,8 @@
template<typename T>
inline PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
- Array* raw_array = Array::Alloc<true>(self, GetArrayClass(), length, sizeof(T),
+ Array* raw_array = Array::Alloc<true>(self, GetArrayClass(), length,
+ ComponentSizeShiftWidth<sizeof(T)>(),
Runtime::Current()->GetHeap()->GetCurrentAllocator());
return down_cast<PrimitiveArray<T>*>(raw_array);
}
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index f54af85..636be33 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -48,7 +48,8 @@
StackHandleScope<1> hs(self);
Handle<Array> new_array(
hs.NewHandle(
- Array::Alloc<true>(self, array_class.Get(), array_length, array_class->GetComponentSize(),
+ Array::Alloc<true>(self, array_class.Get(), array_length,
+ array_class->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
if (UNLIKELY(new_array.Get() == nullptr)) {
CHECK(self->IsExceptionPending());
@@ -94,7 +95,7 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
mirror::Class* element_class_ptr = element_class.Get();
StackHandleScope<1> hs(self);
- Handle<mirror::Class> array_class(
+ MutableHandle<mirror::Class> array_class(
hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr)));
if (UNLIKELY(array_class.Get() == nullptr)) {
CHECK(self->IsExceptionPending());
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7af88d6..521d7e7 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -33,13 +33,12 @@
// The size of a java.lang.Class representing an array.
static uint32_t ClassSize();
- // Allocates an array with the given properties, if fill_usable is true the array will be of at
+ // Allocates an array with the given properties, if kFillUsable is true the array will be of at
// least component_count size, however, if there's usable space at the end of the allocation the
// array will fill it.
- template <bool kIsInstrumented>
- static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size, gc::AllocatorType allocator_type,
- bool fill_usable = false)
+ template <bool kIsInstrumented, bool kFillUsable = false>
+ ALWAYS_INLINE static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size_shift, gc::AllocatorType allocator_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Array* CreateMultiArray(Thread* self, Handle<Class> element_class,
@@ -66,12 +65,11 @@
}
static MemberOffset DataOffset(size_t component_size) {
- if (component_size != sizeof(int64_t)) {
- return OFFSET_OF_OBJECT_MEMBER(Array, first_element_);
- } else {
- // Align longs and doubles.
- return MemberOffset(OFFSETOF_MEMBER(Array, first_element_) + 4);
- }
+ DCHECK(IsPowerOfTwo(component_size)) << component_size;
+ size_t data_offset = RoundUp(OFFSETOF_MEMBER(Array, first_element_), component_size);
+ DCHECK_EQ(RoundUp(data_offset, component_size), data_offset)
+ << "Array data offset isn't aligned with component size";
+ return MemberOffset(data_offset);
}
void* GetRawData(size_t component_size, int32_t index)
diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h
index 336d9ee..03425cc 100644
--- a/runtime/mirror/art_field-inl.h
+++ b/runtime/mirror/art_field-inl.h
@@ -25,6 +25,7 @@
#include "jvalue.h"
#include "object-inl.h"
#include "primitive.h"
+#include "thread-inl.h"
#include "scoped_thread_state_change.h"
#include "well_known_classes.h"
@@ -33,7 +34,7 @@
inline uint32_t ArtField::ClassSize() {
uint32_t vtable_entries = Object::kVTableLength + 6;
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
}
inline Class* ArtField::GetDeclaringClass() {
@@ -122,50 +123,64 @@
}
}
-inline bool ArtField::GetBoolean(Object* object) {
- DCHECK_EQ(Primitive::kPrimBoolean, GetTypeAsPrimitiveType()) << PrettyField(this);
- return Get32(object);
+#define FIELD_GET(object, type) \
+ DCHECK_EQ(Primitive::kPrim ## type, GetTypeAsPrimitiveType()) << PrettyField(this); \
+ DCHECK(object != nullptr) << PrettyField(this); \
+ DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); \
+ if (UNLIKELY(IsVolatile())) { \
+ return object->GetField ## type ## Volatile(GetOffset()); \
+ } \
+ return object->GetField ## type(GetOffset());
+
+#define FIELD_SET(object, type, value) \
+ DCHECK_EQ(Primitive::kPrim ## type, GetTypeAsPrimitiveType()) << PrettyField(this); \
+ DCHECK(object != nullptr) << PrettyField(this); \
+ DCHECK(!IsStatic() || (object == GetDeclaringClass()) || !Runtime::Current()->IsStarted()); \
+ if (UNLIKELY(IsVolatile())) { \
+ object->SetField ## type ## Volatile<kTransactionActive>(GetOffset(), value); \
+ } else { \
+ object->SetField ## type<kTransactionActive>(GetOffset(), value); \
+ }
+
+inline uint8_t ArtField::GetBoolean(Object* object) {
+ FIELD_GET(object, Boolean);
}
template<bool kTransactionActive>
-inline void ArtField::SetBoolean(Object* object, bool z) {
- DCHECK_EQ(Primitive::kPrimBoolean, GetTypeAsPrimitiveType()) << PrettyField(this);
- Set32<kTransactionActive>(object, z);
+inline void ArtField::SetBoolean(Object* object, uint8_t z) {
+ FIELD_SET(object, Boolean, z);
}
inline int8_t ArtField::GetByte(Object* object) {
- DCHECK_EQ(Primitive::kPrimByte, GetTypeAsPrimitiveType()) << PrettyField(this);
- return Get32(object);
+ FIELD_GET(object, Byte);
}
template<bool kTransactionActive>
inline void ArtField::SetByte(Object* object, int8_t b) {
- DCHECK_EQ(Primitive::kPrimByte, GetTypeAsPrimitiveType()) << PrettyField(this);
- Set32<kTransactionActive>(object, b);
+ FIELD_SET(object, Byte, b);
}
inline uint16_t ArtField::GetChar(Object* object) {
- DCHECK_EQ(Primitive::kPrimChar, GetTypeAsPrimitiveType()) << PrettyField(this);
- return Get32(object);
+ FIELD_GET(object, Char);
}
template<bool kTransactionActive>
inline void ArtField::SetChar(Object* object, uint16_t c) {
- DCHECK_EQ(Primitive::kPrimChar, GetTypeAsPrimitiveType()) << PrettyField(this);
- Set32<kTransactionActive>(object, c);
+ FIELD_SET(object, Char, c);
}
inline int16_t ArtField::GetShort(Object* object) {
- DCHECK_EQ(Primitive::kPrimShort, GetTypeAsPrimitiveType()) << PrettyField(this);
- return Get32(object);
+ FIELD_GET(object, Short);
}
template<bool kTransactionActive>
inline void ArtField::SetShort(Object* object, int16_t s) {
- DCHECK_EQ(Primitive::kPrimShort, GetTypeAsPrimitiveType()) << PrettyField(this);
- Set32<kTransactionActive>(object, s);
+ FIELD_SET(object, Short, s);
}
+#undef FIELD_GET
+#undef FIELD_SET
+
inline int32_t ArtField::GetInt(Object* object) {
if (kIsDebugBuild) {
Primitive::Type type = GetTypeAsPrimitiveType();
@@ -275,7 +290,7 @@
}
inline size_t ArtField::FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return Primitive::FieldSize(GetTypeAsPrimitiveType());
+ return Primitive::ComponentSize(GetTypeAsPrimitiveType());
}
inline mirror::DexCache* ArtField::GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h
index 82d0a64..50299b6 100644
--- a/runtime/mirror/art_field.h
+++ b/runtime/mirror/art_field.h
@@ -95,9 +95,9 @@
void SetOffset(MemberOffset num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// field access, null object for static fields
- bool GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint8_t GetBoolean(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive>
- void SetBoolean(Object* object, bool z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetBoolean(Object* object, uint8_t z) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int8_t GetByte(Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<bool kTransactionActive>
void SetByte(Object* object, int8_t b) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index 9782dde..8447616 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -38,7 +38,7 @@
inline uint32_t ArtMethod::ClassSize() {
uint32_t vtable_entries = Object::kVTableLength + 8;
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
}
template<ReadBarrierOption kReadBarrierOption>
@@ -207,25 +207,20 @@
return PointerToLowMemUInt32(GetEntryPointFromQuickCompiledCode());
}
-
-#if defined(ART_USE_PORTABLE_COMPILER)
inline uint32_t ArtMethod::GetPortableOatCodeOffset() {
DCHECK(!Runtime::Current()->IsStarted());
return PointerToLowMemUInt32(GetEntryPointFromPortableCompiledCode());
}
-#endif
inline void ArtMethod::SetQuickOatCodeOffset(uint32_t code_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(code_offset));
}
-#if defined(ART_USE_PORTABLE_COMPILER)
inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset));
}
-#endif
inline const void* ArtMethod::GetQuickOatEntryPoint() {
if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) {
@@ -276,6 +271,9 @@
}
inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) {
+ if (IsOptimized()) {
+ LOG(FATAL) << "Unimplemented vmap table for optimized compiler";
+ }
DCHECK(code_pointer != nullptr);
DCHECK(code_pointer == GetQuickOatCodePointer());
uint32_t offset =
@@ -286,6 +284,20 @@
return reinterpret_cast<const uint8_t*>(code_pointer) - offset;
}
+inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) {
+ return GetOptimizedCodeInfo().GetStackMapForNativePcOffset(native_pc_offset);
+}
+
+inline CodeInfo ArtMethod::GetOptimizedCodeInfo() {
+ DCHECK(IsOptimized());
+ const void* code_pointer = GetQuickOatCodePointer();
+ DCHECK(code_pointer != nullptr);
+ uint32_t offset =
+ reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_;
+ const void* data = reinterpret_cast<const void*>(reinterpret_cast<const uint8_t*>(code_pointer) - offset);
+ return CodeInfo(data);
+}
+
inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) {
DCHECK(!Runtime::Current()->IsStarted());
SetNativeGcMap(reinterpret_cast<uint8_t*>(gc_map_offset));
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 27499c2..787c767 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -105,9 +105,9 @@
}
size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
- CHECK_LE(1, shorty.length());
+ CHECK_LE(1U, shorty.length());
uint32_t num_registers = 0;
- for (int i = 1; i < shorty.length(); ++i) {
+ for (size_t i = 1; i < shorty.length(); ++i) {
char ch = shorty[i];
if (ch == 'D' || ch == 'J') {
num_registers += 2;
@@ -143,7 +143,7 @@
} else {
StackHandleScope<2> hs(Thread::Current());
MethodHelper mh(hs.NewHandle(this));
- MethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
+ MutableMethodHelper interface_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
IfTable* iftable = GetDeclaringClass()->GetIfTable();
for (size_t i = 0; i < iftable->Count() && result == NULL; i++) {
Class* interface = iftable->GetInterface(i);
@@ -281,6 +281,19 @@
return found_dex_pc;
}
+bool ArtMethod::IsEntrypointInterpreter() {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const void* oat_quick_code = class_linker->GetOatMethodQuickCodeFor(this);
+ const void* oat_portable_code = class_linker->GetOatMethodPortableCodeFor(this);
+ if (!IsPortableCompiled()) { // Quick.
+ return oat_quick_code == nullptr ||
+ oat_quick_code != GetEntryPointFromQuickCompiledCode();
+ } else { // Portable.
+ return oat_portable_code == nullptr ||
+ oat_portable_code != GetEntryPointFromPortableCompiledCode();
+ }
+}
+
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
@@ -310,22 +323,21 @@
} else {
const bool kLogInvocationStartAndReturn = false;
bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
-#if defined(ART_USE_PORTABLE_COMPILER)
bool have_portable_code = GetEntryPointFromPortableCompiledCode() != nullptr;
-#else
- bool have_portable_code = false;
-#endif
if (LIKELY(have_quick_code || have_portable_code)) {
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf("Invoking '%s' %s code=%p", PrettyMethod(this).c_str(),
have_quick_code ? "quick" : "portable",
have_quick_code ? GetEntryPointFromQuickCompiledCode()
-#if defined(ART_USE_PORTABLE_COMPILER)
: GetEntryPointFromPortableCompiledCode());
-#else
- : nullptr);
-#endif
}
+
+ // Ensure that we won't be accidentally calling quick/portable compiled code when -Xint.
+ if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly()) {
+ CHECK(IsEntrypointInterpreter())
+ << "Don't call compiled code when -Xint " << PrettyMethod(this);
+ }
+
if (!IsPortableCompiled()) {
#ifdef __LP64__
if (!IsStatic()) {
@@ -353,11 +365,7 @@
LOG(INFO) << StringPrintf("Returned '%s' %s code=%p", PrettyMethod(this).c_str(),
have_quick_code ? "quick" : "portable",
have_quick_code ? GetEntryPointFromQuickCompiledCode()
-#if defined(ART_USE_PORTABLE_COMPILER)
: GetEntryPointFromPortableCompiledCode());
-#else
- : nullptr);
-#endif
}
} else {
LOG(INFO) << "Not invoking '" << PrettyMethod(this) << "' code=null";
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index abfdd42..de6ec05 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -25,6 +25,7 @@
#include "object_callbacks.h"
#include "quick/quick_method_frame_info.h"
#include "read_barrier_option.h"
+#include "stack_map.h"
namespace art {
@@ -150,8 +151,17 @@
SetAccessFlags(GetAccessFlags() | kAccPreverified);
}
+ bool IsOptimized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Temporary solution for detecting if a method has been optimized: the compiler
+ // does not create a GC map. Instead, the vmap table contains the stack map
+ // (as in stack_map.h).
+ return (GetEntryPointFromQuickCompiledCode() != nullptr)
+ && (GetQuickOatCodePointer() != nullptr)
+ && (GetNativeGcMap() == nullptr);
+ }
+
bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return kUsePortableCompiler && ((GetAccessFlags() & kAccPortableCompiled) != 0);
+ return (GetAccessFlags() & kAccPortableCompiled) != 0;
}
void SetIsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -257,7 +267,6 @@
entry_point_from_interpreter);
}
-#if defined(ART_USE_PORTABLE_COMPILER)
static MemberOffset EntryPointFromPortableCompiledCodeOffset() {
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_));
}
@@ -274,7 +283,6 @@
SetFieldPtr<false, true, kVerifyFlags>(
EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code);
}
-#endif
static MemberOffset EntryPointFromQuickCompiledCodeOffset() {
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_));
@@ -311,12 +319,15 @@
void AssertPcIsWithinQuickCode(uintptr_t pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#if defined(ART_USE_PORTABLE_COMPILER)
- uint32_t GetPortableOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-#endif
+ // Returns true if the entrypoint points to the interpreter, as
+ // opposed to the compiled code, that is, this method will be
+ // interpretered on invocation.
+ bool IsEntrypointInterpreter() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
uint32_t GetQuickOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint32_t GetPortableOatCodeOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetQuickOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetPortableOatCodeOffset(uint32_t code_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static const void* EntryPointToCodePointer(const void* entry_point) ALWAYS_INLINE {
uintptr_t code = reinterpret_cast<uintptr_t>(entry_point);
@@ -339,6 +350,9 @@
const uint8_t* GetVmapTable(const void* code_pointer)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ CodeInfo GetOptimizedCodeInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_));
}
@@ -502,9 +516,7 @@
// Method dispatch from portable compiled code invokes this pointer which may cause bridging into
// quick compiled code or the interpreter.
-#if defined(ART_USE_PORTABLE_COMPILER)
uint64_t entry_point_from_portable_compiled_code_;
-#endif
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
// portable compiled code or the interpreter.
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 8e44471..3d3ae16 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -510,8 +510,19 @@
template<VerifyObjectFlags kVerifyFlags>
inline Primitive::Type Class::GetPrimitiveType() {
DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
- return static_cast<Primitive::Type>(
- GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_)));
+ int32_t v32 = GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_));
+ Primitive::Type type = static_cast<Primitive::Type>(v32 & 0xFFFF);
+ DCHECK_EQ(static_cast<size_t>(v32 >> 16), Primitive::ComponentSizeShift(type));
+ return type;
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline size_t Class::GetPrimitiveTypeSizeShift() {
+ DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
+ int32_t v32 = GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_));
+ size_t size_shift = static_cast<Primitive::Type>(v32 >> 16);
+ DCHECK_EQ(size_shift, Primitive::ComponentSizeShift(static_cast<Primitive::Type>(v32 & 0xFFFF)));
+ return size_shift;
}
inline void Class::CheckObjectAlloc() {
@@ -556,6 +567,8 @@
inline uint32_t Class::ComputeClassSize(bool has_embedded_tables,
uint32_t num_vtable_entries,
+ uint32_t num_8bit_static_fields,
+ uint32_t num_16bit_static_fields,
uint32_t num_32bit_static_fields,
uint32_t num_64bit_static_fields,
uint32_t num_ref_static_fields) {
@@ -569,18 +582,33 @@
sizeof(int32_t) /* vtable len */ +
embedded_vtable_size;
}
+
// Space used by reference statics.
size += num_ref_static_fields * sizeof(HeapReference<Object>);
- // Possible pad for alignment.
- if (((size & 7) != 0) && (num_64bit_static_fields > 0)) {
- size += sizeof(uint32_t);
- if (num_32bit_static_fields != 0) {
- // Shuffle one 32 bit static field forward.
- num_32bit_static_fields--;
+ if (!IsAligned<8>(size) && num_64bit_static_fields > 0) {
+ uint32_t gap = 8 - (size & 0x7);
+ size += gap; // will be padded
+ // Shuffle 4-byte fields forward.
+ while (gap >= sizeof(uint32_t) && num_32bit_static_fields != 0) {
+ --num_32bit_static_fields;
+ gap -= sizeof(uint32_t);
+ }
+ // Shuffle 2-byte fields forward.
+ while (gap >= sizeof(uint16_t) && num_16bit_static_fields != 0) {
+ --num_16bit_static_fields;
+ gap -= sizeof(uint16_t);
+ }
+ // Shuffle byte fields forward.
+ while (gap >= sizeof(uint8_t) && num_8bit_static_fields != 0) {
+ --num_8bit_static_fields;
+ gap -= sizeof(uint8_t);
}
}
+ // Guaranteed to be at least 4 byte aligned. No need for further alignments.
// Space used for primitive static fields.
- size += (num_32bit_static_fields * sizeof(uint32_t)) +
+ size += (num_8bit_static_fields * sizeof(uint8_t)) +
+ (num_16bit_static_fields * sizeof(uint16_t)) +
+ (num_32bit_static_fields * sizeof(uint32_t)) +
(num_64bit_static_fields * sizeof(uint64_t));
return size;
}
@@ -705,11 +733,11 @@
}
inline bool Class::GetSlowPathEnabled() {
- return GetField32(GetSlowPathFlagOffset());
+ return GetFieldBoolean(GetSlowPathFlagOffset());
}
inline void Class::SetSlowPath(bool enabled) {
- SetField32<false>(GetSlowPathFlagOffset(), enabled);
+ SetFieldBoolean<false>(GetSlowPathFlagOffset(), enabled);
}
inline void Class::InitializeClassVisitor::operator()(
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 5b8eb82..3fcb188 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -292,21 +292,10 @@
new_reference_offsets);
}
-void Class::SetReferenceStaticOffsets(uint32_t new_reference_offsets) {
- if (new_reference_offsets != CLASS_WALK_SUPER) {
- // Sanity check that the number of bits set in the reference offset bitmap
- // agrees with the number of references
- CHECK_EQ((size_t)POPCOUNT(new_reference_offsets),
- NumReferenceStaticFieldsDuringLinking());
- }
- // Not called within a transaction.
- SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_),
- new_reference_offsets);
-}
-
bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) {
size_t i = 0;
- while (descriptor1[i] != '\0' && descriptor1[i] == descriptor2[i]) {
+ size_t min_length = std::min(descriptor1.size(), descriptor2.size());
+ while (i < min_length && descriptor1[i] == descriptor2[i]) {
++i;
}
if (descriptor1.find('/', i) != StringPiece::npos ||
@@ -772,7 +761,8 @@
return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_;
}
-mirror::Class* Class::GetDirectInterface(Thread* self, Handle<mirror::Class> klass, uint32_t idx) {
+mirror::Class* Class::GetDirectInterface(Thread* self, Handle<mirror::Class> klass,
+ uint32_t idx) {
DCHECK(klass.Get() != nullptr);
DCHECK(!klass->IsPrimitive());
if (klass->IsArrayClass()) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 43ac98d..0acf695 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -65,6 +65,8 @@
namespace art {
struct ClassOffsets;
+template<class T> class Handle;
+template<class T> class Handle;
class Signature;
class StringPiece;
@@ -96,6 +98,12 @@
// Class Status
//
+ // kStatusRetired: Class that's temporarily used till class linking time
+ // has its (vtable) size figured out and has been cloned to one with the
+ // right size which will be the one used later. The old one is retired and
+ // will be gc'ed once all refs to the class point to the newly
+ // cloned version.
+ //
// kStatusNotReady: If a Class cannot be found in the class table by
// FindClass, it allocates an new one with AllocClass in the
// kStatusNotReady and calls LoadClass. Note if it does find a
@@ -131,7 +139,7 @@
//
// TODO: Explain the other states
enum Status {
- kStatusRetired = -2,
+ kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead.
kStatusError = -1,
kStatusNotReady = 0,
kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
@@ -258,6 +266,16 @@
return (GetAccessFlags() & kAccSynthetic) != 0;
}
+ // Returns true if the class can avoid access checks.
+ bool IsPreverified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccPreverified) != 0;
+ }
+
+ void SetPreverified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+ SetAccessFlags(flags | kAccPreverified);
+ }
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsTypeOfReferenceClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags<kVerifyFlags>() & kAccClassIsReference) != 0;
@@ -327,9 +345,16 @@
void SetPrimitiveType(Primitive::Type new_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_EQ(sizeof(Primitive::Type), sizeof(int32_t));
- SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), new_type);
+ int32_t v32 = static_cast<int32_t>(new_type);
+ DCHECK_EQ(v32 & 0xFFFF, v32) << "upper 16 bits aren't zero";
+ // Store the component size shift in the upper 16 bits.
+ v32 |= Primitive::ComponentSizeShift(new_type) << 16;
+ SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, primitive_type_), v32);
}
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ size_t GetPrimitiveTypeSizeShift() ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Returns true if the class is a primitive type.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsPrimitive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -439,15 +464,25 @@
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
size_t GetComponentSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return Primitive::ComponentSize(
- GetComponentType<kDefaultVerifyFlags, kReadBarrierOption>()->GetPrimitiveType());
+ return 1U << GetComponentSizeShift();
+ }
+
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ size_t GetComponentSizeShift() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetComponentType<kDefaultVerifyFlags, kReadBarrierOption>()->GetPrimitiveTypeSizeShift();
}
bool IsObjectClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return !IsPrimitive() && GetSuperClass() == NULL;
}
+
+ bool IsInstantiableNonArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return !IsPrimitive() && !IsInterface() && !IsAbstract() && !IsArrayClass();
+ }
+
bool IsInstantiable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return (!IsPrimitive() && !IsInterface() && !IsAbstract()) || ((IsAbstract()) && IsArrayClass());
+ return (!IsPrimitive() && !IsInterface() && !IsAbstract()) ||
+ ((IsAbstract()) && IsArrayClass());
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -491,6 +526,8 @@
// Compute how many bytes would be used a class with the given elements.
static uint32_t ComputeClassSize(bool has_embedded_tables,
uint32_t num_vtable_entries,
+ uint32_t num_8bit_static_fields,
+ uint32_t num_16bit_static_fields,
uint32_t num_32bit_static_fields,
uint32_t num_64bit_static_fields,
uint32_t num_ref_static_fields);
@@ -499,12 +536,12 @@
static uint32_t ClassClassSize() {
// The number of vtable entries in java.lang.Class.
uint32_t vtable_entries = Object::kVTableLength + 64;
- return ComputeClassSize(true, vtable_entries, 0, 1, 0);
+ return ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 0);
}
// The size of a java.lang.Class representing a primitive such as int.class.
static uint32_t PrimitiveClassSize() {
- return ComputeClassSize(false, 0, 0, 0, 0);
+ return ComputeClassSize(false, 0, 0, 0, 0, 0, 0);
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -858,14 +895,6 @@
// TODO: uint16_t
void SetStaticField(uint32_t i, ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- uint32_t GetReferenceStaticOffsets() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(Class, reference_static_offsets_));
- }
-
- void SetReferenceStaticOffsets(uint32_t new_reference_offsets)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Find a static or instance field using the JLS resolution order
static ArtField* FindField(Thread* self, Handle<Class> klass, const StringPiece& name,
const StringPiece& type)
@@ -972,7 +1001,8 @@
uint16_t GetDirectInterfaceTypeIdx(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static mirror::Class* GetDirectInterface(Thread* self, Handle<mirror::Class> klass, uint32_t idx)
+ static mirror::Class* GetDirectInterface(Thread* self, Handle<mirror::Class> klass,
+ uint32_t idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const char* GetSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -1130,15 +1160,13 @@
// See also class_size_.
uint32_t object_size_;
- // Primitive type value, or Primitive::kPrimNot (0); set for generated primitive classes.
- Primitive::Type primitive_type_;
+ // The lower 16 bits contains a Primitive::Type value. The upper 16
+ // bits contains the size shift of the primitive type.
+ uint32_t primitive_type_;
// Bitmap of offsets of ifields.
uint32_t reference_instance_offsets_;
- // Bitmap of offsets of sfields.
- uint32_t reference_static_offsets_;
-
// State of class initialization.
Status status_;
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index d3fcb55..288e88e 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -28,7 +28,7 @@
inline uint32_t DexCache::ClassSize() {
uint32_t vtable_entries = Object::kVTableLength + 1;
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
}
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx)
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 9dbfb56..166ea9c 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -37,7 +37,7 @@
inline uint32_t Object::ClassSize() {
uint32_t vtable_entries = kVTableLength;
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0);
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0);
}
template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
@@ -408,17 +408,157 @@
}
template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline uint8_t Object::GetFieldBoolean(MemberOffset field_offset) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return GetField<uint8_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline int8_t Object::GetFieldByte(MemberOffset field_offset) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return GetField<int8_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline uint8_t Object::GetFieldBooleanVolatile(MemberOffset field_offset) {
+ return GetFieldBoolean<kVerifyFlags, true>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline int8_t Object::GetFieldByteVolatile(MemberOffset field_offset) {
+ return GetFieldByte<kVerifyFlags, true>(field_offset);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+ bool kIsVolatile>
+inline void Object::SetFieldBoolean(MemberOffset field_offset, uint8_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldBoolean(this, field_offset,
+ GetFieldBoolean<kVerifyFlags, kIsVolatile>(field_offset),
+ kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ SetField<uint8_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+ bool kIsVolatile>
+inline void Object::SetFieldByte(MemberOffset field_offset, int8_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldByte(this, field_offset,
+ GetFieldByte<kVerifyFlags, kIsVolatile>(field_offset),
+ kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ SetField<int8_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldBooleanVolatile(MemberOffset field_offset, uint8_t new_value) {
+ return SetFieldBoolean<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+ field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldByteVolatile(MemberOffset field_offset, int8_t new_value) {
+ return SetFieldByte<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+ field_offset, new_value);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline uint16_t Object::GetFieldChar(MemberOffset field_offset) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return GetField<uint16_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
+inline int16_t Object::GetFieldShort(MemberOffset field_offset) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return GetField<int16_t, kIsVolatile>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline uint16_t Object::GetFieldCharVolatile(MemberOffset field_offset) {
+ return GetFieldChar<kVerifyFlags, true>(field_offset);
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline int16_t Object::GetFieldShortVolatile(MemberOffset field_offset) {
+ return GetFieldShort<kVerifyFlags, true>(field_offset);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+ bool kIsVolatile>
+inline void Object::SetFieldChar(MemberOffset field_offset, uint16_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldChar(this, field_offset,
+ GetFieldChar<kVerifyFlags, kIsVolatile>(field_offset),
+ kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ SetField<uint16_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags,
+ bool kIsVolatile>
+inline void Object::SetFieldShort(MemberOffset field_offset, int16_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldChar(this, field_offset,
+ GetFieldShort<kVerifyFlags, kIsVolatile>(field_offset),
+ kIsVolatile);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ SetField<int16_t, kIsVolatile>(field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldCharVolatile(MemberOffset field_offset, uint16_t new_value) {
+ return SetFieldChar<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+ field_offset, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline void Object::SetFieldShortVolatile(MemberOffset field_offset, int16_t new_value) {
+ return SetFieldShort<kTransactionActive, kCheckTransaction, kVerifyFlags, true>(
+ field_offset, new_value);
+}
+
+template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
inline int32_t Object::GetField32(MemberOffset field_offset) {
if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
- const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
- const int32_t* word_addr = reinterpret_cast<const int32_t*>(raw_addr);
- if (UNLIKELY(kIsVolatile)) {
- return reinterpret_cast<const Atomic<int32_t>*>(word_addr)->LoadSequentiallyConsistent();
- } else {
- return reinterpret_cast<const Atomic<int32_t>*>(word_addr)->LoadJavaData();
- }
+ return GetField<int32_t, kIsVolatile>(field_offset);
}
template<VerifyObjectFlags kVerifyFlags>
@@ -440,13 +580,7 @@
if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
- byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
- int32_t* word_addr = reinterpret_cast<int32_t*>(raw_addr);
- if (kIsVolatile) {
- reinterpret_cast<Atomic<int32_t>*>(word_addr)->StoreSequentiallyConsistent(new_value);
- } else {
- reinterpret_cast<Atomic<int32_t>*>(word_addr)->StoreJavaData(new_value);
- }
+ SetField<int32_t, kIsVolatile>(field_offset, new_value);
}
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
@@ -515,13 +649,7 @@
if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
- const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
- const int64_t* addr = reinterpret_cast<const int64_t*>(raw_addr);
- if (kIsVolatile) {
- return reinterpret_cast<const Atomic<int64_t>*>(addr)->LoadSequentiallyConsistent();
- } else {
- return reinterpret_cast<const Atomic<int64_t>*>(addr)->LoadJavaData();
- }
+ return GetField<int64_t, kIsVolatile>(field_offset);
}
template<VerifyObjectFlags kVerifyFlags>
@@ -543,13 +671,7 @@
if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
}
- byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
- int64_t* addr = reinterpret_cast<int64_t*>(raw_addr);
- if (kIsVolatile) {
- reinterpret_cast<Atomic<int64_t>*>(addr)->StoreSequentiallyConsistent(new_value);
- } else {
- reinterpret_cast<Atomic<int64_t>*>(addr)->StoreJavaData(new_value);
- }
+ SetField<int64_t, kIsVolatile>(field_offset, new_value);
}
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
@@ -558,6 +680,28 @@
new_value);
}
+template<typename kSize, bool kIsVolatile>
+inline void Object::SetField(MemberOffset field_offset, kSize new_value) {
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ kSize* addr = reinterpret_cast<kSize*>(raw_addr);
+ if (kIsVolatile) {
+ reinterpret_cast<Atomic<kSize>*>(addr)->StoreSequentiallyConsistent(new_value);
+ } else {
+ reinterpret_cast<Atomic<kSize>*>(addr)->StoreJavaData(new_value);
+ }
+}
+
+template<typename kSize, bool kIsVolatile>
+inline kSize Object::GetField(MemberOffset field_offset) {
+ const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
+ const kSize* addr = reinterpret_cast<const kSize*>(raw_addr);
+ if (kIsVolatile) {
+ return reinterpret_cast<const Atomic<kSize>*>(addr)->LoadSequentiallyConsistent();
+ } else {
+ return reinterpret_cast<const Atomic<kSize>*>(addr)->LoadJavaData();
+ }
+}
+
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline bool Object::CasFieldWeakSequentiallyConsistent64(MemberOffset field_offset,
int64_t old_value, int64_t new_value) {
@@ -744,9 +888,9 @@
template<bool kVisitClass, bool kIsStatic, typename Visitor>
inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) {
- if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
+ if (!kIsStatic && LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
if (!kVisitClass) {
- // Mask out the class from the reference offsets.
+ // Mask out the class from the reference offsets.
ref_offsets ^= kWordHighBitMask;
}
DCHECK_EQ(ClassOffset().Uint32Value(), 0U);
@@ -758,7 +902,7 @@
ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
}
} else {
- // There is no reference offset bitmap. In the non-static case, walk up the class
+ // There is no reference offset bitmap. In the non-static case, walk up the class
// inheritance hierarchy and find reference offsets the hard way. In the static case, just
// consider this class.
for (mirror::Class* klass = kIsStatic ? AsClass() : GetClass(); klass != nullptr;
@@ -786,8 +930,7 @@
template<bool kVisitClass, typename Visitor>
inline void Object::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) {
DCHECK(!klass->IsTemp());
- klass->VisitFieldsReferences<kVisitClass, true>(
- klass->GetReferenceStaticOffsets<kVerifyNone>(), visitor);
+ klass->VisitFieldsReferences<kVisitClass, true>(0, visitor);
}
template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor,
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index a1177d6..57069ab 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -187,8 +187,7 @@
}
}
}
- LOG(FATAL) << "Unreachable";
- return 0;
+ UNREACHABLE();
}
void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index a6b6227..6cd230b 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -19,6 +19,7 @@
#include "object_reference.h"
#include "offsets.h"
+#include "runtime.h"
#include "verify_object.h"
namespace art {
@@ -247,6 +248,78 @@
HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE uint8_t GetFieldBoolean(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE int8_t GetFieldByte(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE uint8_t GetFieldBooleanVolatile(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE int8_t GetFieldByteVolatile(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE void SetFieldBoolean(MemberOffset field_offset, uint8_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE void SetFieldByte(MemberOffset field_offset, int8_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetFieldBooleanVolatile(MemberOffset field_offset, uint8_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetFieldByteVolatile(MemberOffset field_offset, int8_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE uint16_t GetFieldChar(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE int16_t GetFieldShort(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE uint16_t GetFieldCharVolatile(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE int16_t GetFieldShortVolatile(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE void SetFieldChar(MemberOffset field_offset, uint16_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
+ ALWAYS_INLINE void SetFieldShort(MemberOffset field_offset, int16_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetFieldCharVolatile(MemberOffset field_offset, uint16_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE void SetFieldShortVolatile(MemberOffset field_offset, int16_t new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
ALWAYS_INLINE int32_t GetField32(MemberOffset field_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -356,6 +429,13 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
+ template<typename kSize, bool kIsVolatile>
+ ALWAYS_INLINE void SetField(MemberOffset field_offset, kSize new_value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<typename kSize, bool kIsVolatile>
+ ALWAYS_INLINE kSize GetField(MemberOffset field_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Verify the type correctness of stores to fields.
// TODO: This can cause thread suspension and isn't moving GC safe.
void CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_value)
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index c7540dc..0ca44f8 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -35,10 +35,13 @@
inline ObjectArray<T>* ObjectArray<T>::Alloc(Thread* self, Class* object_array_class,
int32_t length, gc::AllocatorType allocator_type) {
Array* array = Array::Alloc<true>(self, object_array_class, length,
- sizeof(HeapReference<Object>), allocator_type);
+ ComponentSizeShiftWidth<sizeof(HeapReference<Object>)>(),
+ allocator_type);
if (UNLIKELY(array == nullptr)) {
return nullptr;
} else {
+ DCHECK_EQ(array->GetClass()->GetComponentSizeShift(),
+ ComponentSizeShiftWidth<sizeof(HeapReference<Object>)>());
return array->AsObjectArray<T>();
}
}
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index ede3b64..7fa664d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -74,7 +74,12 @@
}
};
-// Keep the assembly code in sync
+// Keep constants in sync.
+TEST_F(ObjectTest, Constants) {
+ EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::HeapReference<mirror::Object>));
+}
+
+// Keep the assembly code constats in sync.
TEST_F(ObjectTest, AsmConstants) {
EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value());
EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value());
@@ -90,9 +95,7 @@
EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
-#if defined(ART_USE_PORTABLE_COMPILER)
EXPECT_EQ(METHOD_PORTABLE_CODE_OFFSET, ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value());
-#endif
EXPECT_EQ(METHOD_QUICK_CODE_OFFSET, ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
}
@@ -158,20 +161,20 @@
ScopedObjectAccess soa(Thread::Current());
Class* c = class_linker_->FindSystemClass(soa.Self(), "[I");
StackHandleScope<1> hs(soa.Self());
- Handle<Array> a(
- hs.NewHandle(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ MutableHandle<Array> a(
+ hs.NewHandle(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
EXPECT_TRUE(c == a->GetClass());
EXPECT_EQ(1, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
- a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator()));
EXPECT_TRUE(c == a->GetClass());
EXPECT_EQ(1, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
- a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
+ a.Assign(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator()));
EXPECT_TRUE(c == a->GetClass());
EXPECT_EQ(1, a->GetLength());
@@ -181,27 +184,27 @@
ScopedObjectAccess soa(Thread::Current());
Class* c = class_linker_->FindSystemClass(soa.Self(), "[B");
StackHandleScope<1> hs(soa.Self());
- Handle<Array> a(
- hs.NewHandle(Array::Alloc<true>(soa.Self(), c, 1, c->GetComponentSize(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator(), true)));
+ MutableHandle<Array> a(
+ hs.NewHandle(Array::Alloc<true, true>(soa.Self(), c, 1, c->GetComponentSizeShift(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator())));
EXPECT_TRUE(c == a->GetClass());
EXPECT_LE(1, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[I");
- a.Assign(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+ a.Assign(Array::Alloc<true, true>(soa.Self(), c, 2, c->GetComponentSizeShift(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
EXPECT_TRUE(c == a->GetClass());
EXPECT_LE(2, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;");
- a.Assign(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+ a.Assign(Array::Alloc<true, true>(soa.Self(), c, 2, c->GetComponentSizeShift(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
EXPECT_TRUE(c == a->GetClass());
EXPECT_LE(2, a->GetLength());
c = class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Object;");
- a.Assign(Array::Alloc<true>(soa.Self(), c, 2, c->GetComponentSize(),
- Runtime::Current()->GetHeap()->GetCurrentAllocator(), true));
+ a.Assign(Array::Alloc<true, true>(soa.Self(), c, 2, c->GetComponentSizeShift(),
+ Runtime::Current()->GetHeap()->GetCurrentAllocator()));
EXPECT_TRUE(c == a->GetClass());
EXPECT_LE(2, a->GetLength());
}
@@ -284,7 +287,7 @@
StackHandleScope<2> hs(soa.Self());
Handle<Class> c(hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "I")));
- Handle<IntArray> dims(hs.NewHandle(IntArray::Alloc(soa.Self(), 1)));
+ MutableHandle<IntArray> dims(hs.NewHandle(IntArray::Alloc(soa.Self(), 1)));
dims->Set<false>(0, 1);
Array* multi = Array::CreateMultiArray(soa.Self(), c, dims);
EXPECT_TRUE(multi->GetClass() == class_linker_->FindSystemClass(soa.Self(), "[I"));
@@ -482,8 +485,8 @@
ArtMethod* m4_2 = klass2->GetVirtualMethod(3);
EXPECT_STREQ(m4_2->GetName(), "m4");
- MethodHelper mh(hs.NewHandle(m1_1));
- MethodHelper mh2(hs.NewHandle(m1_2));
+ MutableMethodHelper mh(hs.NewHandle(m1_1));
+ MutableMethodHelper mh2(hs.NewHandle(m1_2));
EXPECT_TRUE(mh.HasSameNameAndSignature(&mh2));
EXPECT_TRUE(mh2.HasSameNameAndSignature(&mh));
diff --git a/runtime/mirror/reference-inl.h b/runtime/mirror/reference-inl.h
index b353402..d1d2a3a 100644
--- a/runtime/mirror/reference-inl.h
+++ b/runtime/mirror/reference-inl.h
@@ -24,7 +24,7 @@
inline uint32_t Reference::ClassSize() {
uint32_t vtable_entries = Object::kVTableLength + 5;
- return Class::ComputeClassSize(false, vtable_entries, 2, 0, 0);
+ return Class::ComputeClassSize(false, vtable_entries, 2, 0, 0, 0, 0);
}
inline bool Reference::IsEnqueuable() {
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index d924141..14d7de2 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -30,7 +30,7 @@
inline uint32_t String::ClassSize() {
uint32_t vtable_entries = Object::kVTableLength + 51;
- return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2);
+ return Class::ComputeClassSize(true, vtable_entries, 0, 1, 0, 1, 2);
}
inline CharArray* String::GetCharArray() {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 03f8687..6123934 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -176,10 +176,6 @@
// Deflated monitors have a null object.
}
-/*
- * Links a thread into a monitor's wait set. The monitor lock must be
- * held by the caller of this routine.
- */
void Monitor::AppendToWaitSet(Thread* thread) {
DCHECK(owner_ == Thread::Current());
DCHECK(thread != NULL);
@@ -197,10 +193,6 @@
t->SetWaitNext(thread);
}
-/*
- * Unlinks a thread from a monitor's wait set. The monitor lock must
- * be held by the caller of this routine.
- */
void Monitor::RemoveFromWaitSet(Thread *thread) {
DCHECK(owner_ == Thread::Current());
DCHECK(thread != NULL);
@@ -395,29 +387,6 @@
return true;
}
-/*
- * Wait on a monitor until timeout, interrupt, or notification. Used for
- * Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
- *
- * If another thread calls Thread.interrupt(), we throw InterruptedException
- * and return immediately if one of the following are true:
- * - blocked in wait(), wait(long), or wait(long, int) methods of Object
- * - blocked in join(), join(long), or join(long, int) methods of Thread
- * - blocked in sleep(long), or sleep(long, int) methods of Thread
- * Otherwise, we set the "interrupted" flag.
- *
- * Checks to make sure that "ns" is in the range 0-999999
- * (i.e. fractions of a millisecond) and throws the appropriate
- * exception if it isn't.
- *
- * The spec allows "spurious wakeups", and recommends that all code using
- * Object.wait() do so in a loop. This appears to derive from concerns
- * about pthread_cond_wait() on multiprocessor systems. Some commentary
- * on the web casts doubt on whether these can/should occur.
- *
- * Since we're allowed to wake up "early", we clamp extremely long durations
- * to return at the end of the 32-bit time epoch.
- */
void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why) {
DCHECK(self != NULL);
@@ -818,9 +787,6 @@
}
}
-/*
- * Object.wait(). Also called for class init.
- */
void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why) {
DCHECK(self != nullptr);
diff --git a/runtime/monitor.h b/runtime/monitor.h
index be9e6f9..8f97a40 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -75,6 +75,8 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DoNotify(self, obj, true);
}
+
+ // Object.wait(). Also called for class init.
static void Wait(Thread* self, mirror::Object* obj, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -139,15 +141,18 @@
LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Links a thread into a monitor's wait set. The monitor lock must be held by the caller of this
+ // routine.
void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
+
+ // Unlinks a thread from a monitor's wait set. The monitor lock must be held by the caller of
+ // this routine.
void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
- /*
- * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The
- * calling thread must own the lock or the owner must be suspended. There's a race with other
- * threads inflating the lock, installing hash codes and spurious failures. The caller should
- * re-read the lock word following the call.
- */
+ // Changes the shape of a monitor from thin to fat, preserving the internal lock state. The
+ // calling thread must own the lock or the owner must be suspended. There's a race with other
+ // threads inflating the lock, installing hash codes and spurious failures. The caller should
+ // re-read the lock word following the call.
static void Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -178,6 +183,25 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Wait on a monitor until timeout, interrupt, or notification. Used for Object.wait() and
+ // (somewhat indirectly) Thread.sleep() and Thread.join().
+ //
+ // If another thread calls Thread.interrupt(), we throw InterruptedException and return
+ // immediately if one of the following are true:
+ // - blocked in wait(), wait(long), or wait(long, int) methods of Object
+ // - blocked in join(), join(long), or join(long, int) methods of Thread
+ // - blocked in sleep(long), or sleep(long, int) methods of Thread
+ // Otherwise, we set the "interrupted" flag.
+ //
+ // Checks to make sure that "ns" is in the range 0-999999 (i.e. fractions of a millisecond) and
+ // throws the appropriate exception if it isn't.
+ //
+ // The spec allows "spurious wakeups", and recommends that all code using Object.wait() do so in
+ // a loop. This appears to derive from concerns about pthread_cond_wait() on multiprocessor
+ // systems. Some commentary on the web casts doubt on whether these can/should occur.
+ //
+ // Since we're allowed to wake up "early", we clamp extremely long durations to return at the end
+ // of the 32-bit time epoch.
void Wait(Thread* self, int64_t msec, int32_t nsec, bool interruptShouldThrow, ThreadState why)
LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index af24368..704e041 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -58,7 +58,7 @@
static const size_t kMaxHandles = 1000000; // Use arbitrary large amount for now.
static void FillHeap(Thread* self, ClassLinker* class_linker,
std::unique_ptr<StackHandleScope<kMaxHandles>>* hsp,
- std::vector<Handle<mirror::Object>>* handles)
+ std::vector<MutableHandle<mirror::Object>>* handles)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Runtime::Current()->GetHeap()->SetIdealFootprint(1 * GB);
@@ -73,7 +73,7 @@
// Start allocating with 128K
size_t length = 128 * KB / 4;
while (length > 10) {
- Handle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
+ MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>(
mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4)));
if (self->IsExceptionPending() || h.Get() == nullptr) {
self->ClearException();
@@ -92,7 +92,7 @@
// Allocate simple objects till it fails.
while (!self->IsExceptionPending()) {
- Handle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
+ MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self));
if (!self->IsExceptionPending() && h.Get() != nullptr) {
handles->push_back(h);
}
@@ -307,7 +307,7 @@
// Fill the heap.
std::unique_ptr<StackHandleScope<kMaxHandles>> hsp;
- std::vector<Handle<mirror::Object>> handles;
+ std::vector<MutableHandle<mirror::Object>> handles;
{
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 01c8978..65a7919 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -189,8 +189,8 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
- mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file,
- *dex_class_def);
+ mirror::Class* result = class_linker->DefineClass(soa.Self(), descriptor.c_str(),
+ class_loader, *dex_file, *dex_class_def);
if (result != nullptr) {
VLOG(class_linker) << "DexFile_defineClassNative returning " << result;
return soa.AddLocalReference<jclass>(result);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index fc1018a..23f46f4 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -76,7 +76,8 @@
}
gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentNonMovingAllocator();
mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
- array_class->GetComponentSize(), allocator);
+ array_class->GetComponentSizeShift(),
+ allocator);
return soa.AddLocalReference<jobject>(result);
}
@@ -99,9 +100,9 @@
return nullptr;
}
gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
- mirror::Array* result = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
- array_class->GetComponentSize(), allocator,
- true);
+ mirror::Array* result = mirror::Array::Alloc<true, true>(soa.Self(), array_class, length,
+ array_class->GetComponentSizeShift(),
+ allocator);
return soa.AddLocalReference<jobject>(result);
}
@@ -171,7 +172,7 @@
}
static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) {
- return Runtime::Current()->GetJavaVM()->check_jni ? JNI_TRUE : JNI_FALSE;
+ return Runtime::Current()->GetJavaVM()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
}
static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
@@ -242,7 +243,7 @@
}
// Based on ClassLinker::ResolveType.
-static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t type_idx)
+static void PreloadDexCachesResolveType(Thread* self, mirror::DexCache* dex_cache, uint32_t type_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
if (klass != NULL) {
@@ -254,7 +255,7 @@
if (class_name[1] == '\0') {
klass = linker->FindPrimitiveClass(class_name[0]);
} else {
- klass = linker->LookupClass(class_name, NULL);
+ klass = linker->LookupClass(self, class_name, NULL);
}
if (klass == NULL) {
return;
@@ -431,7 +432,6 @@
Runtime* runtime = Runtime::Current();
ClassLinker* linker = runtime->GetClassLinker();
- Thread* self = ThreadForEnv(env);
// We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
StringTable strings;
@@ -444,7 +444,7 @@
for (size_t i = 0; i< boot_class_path.size(); i++) {
const DexFile* dex_file = boot_class_path[i];
CHECK(dex_file != NULL);
- StackHandleScope<1> hs(self);
+ StackHandleScope<1> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(*dex_file)));
if (kPreloadDexCachesStrings) {
@@ -455,7 +455,7 @@
if (kPreloadDexCachesTypes) {
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
- PreloadDexCachesResolveType(dex_cache.Get(), i);
+ PreloadDexCachesResolveType(soa.Self(), dex_cache.Get(), i);
}
}
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a4ef839..e469126 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -18,6 +18,7 @@
#include "debugger.h"
#include "instruction_set.h"
+#include "java_vm_ext.h"
#include "jni_internal.h"
#include "JNIHelp.h"
#include "ScopedUtfChars.h"
@@ -49,7 +50,7 @@
}
static void EnableDebugFeatures(uint32_t debug_flags) {
- // Must match values in dalvik.system.Zygote.
+ // Must match values in com.android.internal.os.Zygote.
enum {
DEBUG_ENABLE_DEBUGGER = 1,
DEBUG_ENABLE_CHECKJNI = 1 << 1,
@@ -61,7 +62,7 @@
if ((debug_flags & DEBUG_ENABLE_CHECKJNI) != 0) {
Runtime* runtime = Runtime::Current();
JavaVMExt* vm = runtime->GetJavaVM();
- if (!vm->check_jni) {
+ if (!vm->IsCheckJniEnabled()) {
LOG(INFO) << "Late-enabling -Xcheck:jni";
vm->SetCheckJniEnabled(true);
// There's only one thread running at this point, so only one JNIEnv to fix up.
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 124bdf5..b11cbdf 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -78,7 +78,7 @@
return nullptr;
}
if (initialize) {
- class_linker->EnsureInitialized(c, true, true);
+ class_linker->EnsureInitialized(soa.Self(), c, true, true);
}
return soa.AddLocalReference<jclass>(c.Get());
}
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 51cd5b8..c1c6c26 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -15,6 +15,7 @@
*/
#include "dex_file.h"
+#include "jni_internal.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "scoped_fast_native_object_access.h"
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index fb708a2..a85eec7 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -43,7 +43,10 @@
exit(status);
}
-static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) {
+static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader,
+ jstring javaLdLibraryPath) {
+ // TODO: returns NULL on success or an error message describing the failure on failure. This
+ // should be refactored in terms of suppressed exceptions.
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
@@ -64,14 +67,10 @@
}
}
- std::string detail;
+ std::string error_msg;
{
- ScopedObjectAccess soa(env);
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> classLoader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(javaLoader)));
JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail);
+ bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, &error_msg);
if (success) {
return nullptr;
}
@@ -79,7 +78,7 @@
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
env->ExceptionClear();
- return env->NewStringUTF(detail.c_str());
+ return env->NewStringUTF(error_msg.c_str());
}
static jlong Runtime_maxMemory(JNIEnv*, jclass) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 761e800..f6a46bd 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -24,17 +24,18 @@
namespace art {
-static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) {
+static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader,
+ jstring javaName) {
ScopedFastNativeObjectAccess soa(env);
mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
ScopedUtfChars name(env, javaName);
- if (name.c_str() == NULL) {
- return NULL;
+ if (name.c_str() == nullptr) {
+ return nullptr;
}
ClassLinker* cl = Runtime::Current()->GetClassLinker();
std::string descriptor(DotToDescriptor(name.c_str()));
- mirror::Class* c = cl->LookupClass(descriptor.c_str(), loader);
- if (c != NULL && c->IsResolved()) {
+ mirror::Class* c = cl->LookupClass(soa.Self(), descriptor.c_str(), loader);
+ if (c != nullptr && c->IsResolved()) {
return soa.AddLocalReference<jclass>(c);
}
if (loader != nullptr) {
@@ -47,7 +48,7 @@
}
// Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
// the regular loadClass code.
- return NULL;
+ return nullptr;
}
static jint VMClassLoader_getBootClassPathSize(JNIEnv*, jclass) {
@@ -67,13 +68,15 @@
* with '/'); if it's not we'd need to make it absolute as part of forming
* the URL string.
*/
-static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName, jint index) {
+static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstring javaName,
+ jint index) {
ScopedUtfChars name(env, javaName);
if (name.c_str() == nullptr) {
return nullptr;
}
- const std::vector<const DexFile*>& path = Runtime::Current()->GetClassLinker()->GetBootClassPath();
+ const std::vector<const DexFile*>& path =
+ Runtime::Current()->GetClassLinker()->GetBootClassPath();
if (index < 0 || size_t(index) >= path.size()) {
return nullptr;
}
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 058458f..763a664 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -59,9 +59,10 @@
return NULL;
}
DCHECK(array_class->IsObjectArrayClass());
- mirror::Array* new_array = mirror::Array::Alloc<true>(soa.Self(), array_class, length,
- sizeof(mirror::HeapReference<mirror::Object>),
- runtime->GetHeap()->GetCurrentAllocator());
+ mirror::Array* new_array = mirror::Array::Alloc<true>(
+ soa.Self(), array_class, length,
+ ComponentSizeShiftWidth<sizeof(mirror::HeapReference<mirror::Object>)>(),
+ runtime->GetHeap()->GetCurrentAllocator());
return soa.AddLocalReference<jobject>(new_array);
}
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 34cb93a..0542aeb 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -48,7 +48,7 @@
return nullptr;
}
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), c, true, true)) {
DCHECK(soa.Self()->IsExceptionPending());
return nullptr;
}
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 8850df2..d166be0 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -33,13 +33,24 @@
mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (kIsSet && field->IsFinal()) {
- ThrowIllegalAccessException(nullptr, StringPrintf("Cannot set final field: %s",
- PrettyField(field).c_str()).c_str());
+ ThrowIllegalAccessException(nullptr,
+ StringPrintf("Cannot set %s field %s of class %s",
+ PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(),
+ PrettyField(field).c_str(),
+ field->GetDeclaringClass() == nullptr ? "null" :
+ PrettyClass(field->GetDeclaringClass()).c_str()).c_str());
return false;
}
- if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
- ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access field: %s",
- PrettyField(field).c_str()).c_str());
+ mirror::Class* calling_class = nullptr;
+ if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags(),
+ &calling_class)) {
+ ThrowIllegalAccessException(nullptr,
+ StringPrintf("Class %s cannot access %s field %s of class %s",
+ calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
+ PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(),
+ PrettyField(field).c_str(),
+ field->GetDeclaringClass() == nullptr ? "null" :
+ PrettyClass(field->GetDeclaringClass()).c_str()).c_str());
return false;
}
return true;
@@ -104,7 +115,7 @@
StackHandleScope<2> hs(soa.Self());
HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f));
HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass));
- if (UNLIKELY(!class_linker->EnsureInitialized(h_klass, true, true))) {
+ if (UNLIKELY(!class_linker->EnsureInitialized(soa.Self(), h_klass, true, true))) {
DCHECK(soa.Self()->IsExceptionPending());
return false;
}
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index 163ae20..8b2aecb 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,6 +16,7 @@
#include "base/logging.h"
#include "debugger.h"
+#include "jni_internal.h"
#include "scoped_fast_native_object_access.h"
#include "ScopedPrimitiveArray.h"
diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc
index cc44615..bc191b4 100644
--- a/runtime/native_bridge_art_interface.cc
+++ b/runtime/native_bridge_art_interface.cc
@@ -132,4 +132,4 @@
android::UnloadNativeBridge();
}
-}; // namespace art
+} // namespace art
diff --git a/runtime/native_bridge_art_interface.h b/runtime/native_bridge_art_interface.h
index 42f0ed2..026cd82 100644
--- a/runtime/native_bridge_art_interface.h
+++ b/runtime/native_bridge_art_interface.h
@@ -35,6 +35,6 @@
void UnloadNativeBridge();
-}; // namespace art
+} // namespace art
#endif // ART_RUNTIME_NATIVE_BRIDGE_ART_INTERFACE_H_
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ede108c..6810d73 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -23,7 +23,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '9', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '4', '2', '\0' };
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
diff --git a/runtime/oat.h b/runtime/oat.h
index 6d5fefe..6a32e3e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -152,7 +152,7 @@
enum OatClassType {
kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method.
kOatClassSomeCompiled = 1, // A bitmap of which OatMethodOffsets are present follows the OatClass.
- kOatClassNoneCompiled = 2, // All methods are interpretted so no OatMethodOffsets are necessary.
+ kOatClassNoneCompiled = 2, // All methods are interpreted so no OatMethodOffsets are necessary.
kOatClassMax = 3,
};
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 025f87d..a896f3e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -481,15 +481,17 @@
uint32_t bitmap_size = 0;
const byte* bitmap_pointer = nullptr;
const byte* methods_pointer = nullptr;
- if (type == kOatClassSomeCompiled) {
- bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer));
- bitmap_pointer = after_type_pointer + sizeof(bitmap_size);
- CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
- methods_pointer = bitmap_pointer + bitmap_size;
- } else {
- methods_pointer = after_type_pointer;
+ if (type != kOatClassNoneCompiled) {
+ if (type == kOatClassSomeCompiled) {
+ bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(after_type_pointer));
+ bitmap_pointer = after_type_pointer + sizeof(bitmap_size);
+ CHECK_LE(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ methods_pointer = bitmap_pointer + bitmap_size;
+ } else {
+ methods_pointer = after_type_pointer;
+ }
+ CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
}
- CHECK_LE(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
return OatClass(oat_file_,
status,
@@ -507,22 +509,23 @@
const OatMethodOffsets* methods_pointer)
: oat_file_(oat_file), status_(status), type_(type),
bitmap_(bitmap_pointer), methods_pointer_(methods_pointer) {
- CHECK(methods_pointer != nullptr);
switch (type_) {
case kOatClassAllCompiled: {
CHECK_EQ(0U, bitmap_size);
CHECK(bitmap_pointer == nullptr);
+ CHECK(methods_pointer != nullptr);
break;
}
case kOatClassSomeCompiled: {
CHECK_NE(0U, bitmap_size);
CHECK(bitmap_pointer != nullptr);
+ CHECK(methods_pointer != nullptr);
break;
}
case kOatClassNoneCompiled: {
CHECK_EQ(0U, bitmap_size);
CHECK(bitmap_pointer == nullptr);
- methods_pointer_ = nullptr;
+ CHECK(methods_pointer_ == nullptr);
break;
}
case kOatClassMax: {
@@ -580,23 +583,11 @@
}
}
-OatFile::OatMethod::OatMethod(const byte* base,
- const uint32_t code_offset,
- const uint32_t gc_map_offset)
- : begin_(base),
- code_offset_(code_offset),
- native_gc_map_offset_(gc_map_offset) {
-}
-
-OatFile::OatMethod::~OatMethod() {}
-
void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const {
CHECK(method != NULL);
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(GetPortableCode());
-#endif
method->SetEntryPointFromQuickCompiledCode(GetQuickCode());
- method->SetNativeGcMap(GetNativeGcMap());
+ method->SetNativeGcMap(GetNativeGcMap()); // Used by native methods in work around JNI mode.
}
} // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index e5cd6ec..b9d5702 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -147,14 +147,19 @@
uint32_t GetVmapTableOffset() const;
uint32_t GetVmapTableOffsetOffset() const;
- ~OatMethod();
-
// Create an OatMethod with offsets relative to the given base address
- OatMethod(const byte* base,
- const uint32_t code_offset,
- const uint32_t gc_map_offset);
+ OatMethod(const byte* base, const uint32_t code_offset, const uint32_t gc_map_offset)
+ : begin_(base),
+ code_offset_(code_offset),
+ native_gc_map_offset_(gc_map_offset) {
+ }
+ ~OatMethod() {}
- OatMethod() {}
+ // A representation of an invalid OatMethod, used when an OatMethod or OatClass can't be found.
+ // See ClassLinker::FindOatMethodFor.
+ static const OatMethod Invalid() {
+ return OatMethod(nullptr, -1, -1);
+ }
private:
template<class T>
@@ -165,10 +170,10 @@
return reinterpret_cast<T>(begin_ + offset);
}
- const byte* begin_;
+ const byte* const begin_;
- uint32_t code_offset_;
- uint32_t native_gc_map_offset_;
+ const uint32_t code_offset_;
+ const uint32_t native_gc_map_offset_;
friend class OatClass;
};
@@ -199,7 +204,12 @@
// is present. Note that most callers should use GetOatMethod.
uint32_t GetOatMethodOffsetsOffset(uint32_t method_index) const;
- OatClass() {}
+ // A representation of an invalid OatClass, used when an OatClass can't be found.
+ // See ClassLinker::FindOatClass.
+ static OatClass Invalid() {
+ return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr,
+ nullptr);
+ }
private:
OatClass(const OatFile* oat_file,
@@ -209,15 +219,15 @@
const uint32_t* bitmap_pointer,
const OatMethodOffsets* methods_pointer);
- const OatFile* oat_file_;
+ const OatFile* const oat_file_;
- mirror::Class::Status status_;
+ const mirror::Class::Status status_;
- OatClassType type_;
+ const OatClassType type_;
- const uint32_t* bitmap_;
+ const uint32_t* const bitmap_;
- const OatMethodOffsets* methods_pointer_;
+ const OatMethodOffsets* const methods_pointer_;
friend class OatDexFile;
};
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index bb2ad44..d820026 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -30,6 +30,87 @@
namespace art {
+ParsedOptions::ParsedOptions()
+ :
+ boot_class_path_(nullptr),
+ check_jni_(kIsDebugBuild), // -Xcheck:jni is off by default for regular
+ // builds but on by default in debug builds.
+ force_copy_(false),
+ compiler_callbacks_(nullptr),
+ is_zygote_(false),
+ must_relocate_(kDefaultMustRelocate),
+ dex2oat_enabled_(true),
+ image_dex2oat_enabled_(true),
+ interpreter_only_(kPoisonHeapReferences), // kPoisonHeapReferences currently works with
+ // the interpreter only.
+ // TODO: make it work with the compiler.
+ is_explicit_gc_disabled_(false),
+ use_tlab_(false),
+ verify_pre_gc_heap_(false),
+ verify_pre_sweeping_heap_(kIsDebugBuild), // Pre sweeping is the one that usually fails
+ // if the GC corrupted the heap.
+ verify_post_gc_heap_(false),
+ verify_pre_gc_rosalloc_(kIsDebugBuild),
+ verify_pre_sweeping_rosalloc_(false),
+ verify_post_gc_rosalloc_(false),
+ long_pause_log_threshold_(gc::Heap::kDefaultLongPauseLogThreshold),
+ long_gc_log_threshold_(gc::Heap::kDefaultLongGCLogThreshold),
+ dump_gc_performance_on_shutdown_(false),
+ ignore_max_footprint_(false),
+ heap_initial_size_(gc::Heap::kDefaultInitialSize),
+ heap_maximum_size_(gc::Heap::kDefaultMaximumSize),
+ heap_growth_limit_(0), // 0 means no growth limit.
+ heap_min_free_(gc::Heap::kDefaultMinFree),
+ heap_max_free_(gc::Heap::kDefaultMaxFree),
+ heap_non_moving_space_capacity_(gc::Heap::kDefaultNonMovingSpaceCapacity),
+ large_object_space_type_(gc::Heap::kDefaultLargeObjectSpaceType),
+ large_object_threshold_(gc::Heap::kDefaultLargeObjectThreshold),
+ heap_target_utilization_(gc::Heap::kDefaultTargetUtilization),
+ foreground_heap_growth_multiplier_(gc::Heap::kDefaultHeapGrowthMultiplier),
+ parallel_gc_threads_(1),
+ conc_gc_threads_(0), // Only the main GC thread, no workers.
+ collector_type_( // The default GC type is set in makefiles.
+#if ART_DEFAULT_GC_TYPE_IS_CMS
+ gc::kCollectorTypeCMS),
+#elif ART_DEFAULT_GC_TYPE_IS_SS
+ gc::kCollectorTypeSS),
+#elif ART_DEFAULT_GC_TYPE_IS_GSS
+ gc::kCollectorTypeGSS),
+#else
+ gc::kCollectorTypeCMS),
+#error "ART default GC type must be set"
+#endif
+ background_collector_type_(gc::kCollectorTypeNone),
+ // If background_collector_type_ is
+ // kCollectorTypeNone, it defaults to the
+ // collector_type_ after parsing options. If
+ // you set this to kCollectorTypeHSpaceCompact
+ // then we will do an hspace compaction when
+ // we transition to background instead of a
+ // normal collector transition.
+ stack_size_(0), // 0 means default.
+ max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
+ low_memory_mode_(false),
+ lock_profiling_threshold_(0),
+ method_trace_(false),
+ method_trace_file_("/data/method-trace-file.bin"),
+ method_trace_file_size_(10 * MB),
+ hook_is_sensitive_thread_(nullptr),
+ hook_vfprintf_(vfprintf),
+ hook_exit_(exit),
+ hook_abort_(nullptr), // We don't call abort(3) by default; see
+ // Runtime::Abort.
+ profile_clock_source_(kDefaultTraceClockSource),
+ verify_(true),
+ image_isa_(kRuntimeISA),
+ use_homogeneous_space_compaction_for_oom_(false), // If we are using homogeneous space
+ // compaction then default background
+ // compaction to off since homogeneous
+ // space compactions when we transition
+ // to not jank perceptible.
+ min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000)) // 100s.
+ {}
+
ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized) {
std::unique_ptr<ParsedOptions> parsed(new ParsedOptions());
if (parsed->Parse(options, ignore_unrecognized)) {
@@ -175,82 +256,9 @@
if (class_path_string != NULL) {
class_path_string_ = class_path_string;
}
- // -Xcheck:jni is off by default for regular builds but on by default in debug builds.
- check_jni_ = kIsDebugBuild;
- heap_initial_size_ = gc::Heap::kDefaultInitialSize;
- heap_maximum_size_ = gc::Heap::kDefaultMaximumSize;
- heap_min_free_ = gc::Heap::kDefaultMinFree;
- heap_max_free_ = gc::Heap::kDefaultMaxFree;
- heap_non_moving_space_capacity_ = gc::Heap::kDefaultNonMovingSpaceCapacity;
- heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization;
- foreground_heap_growth_multiplier_ = gc::Heap::kDefaultHeapGrowthMultiplier;
- heap_growth_limit_ = 0; // 0 means no growth limit .
// Default to number of processors minus one since the main GC thread also does work.
parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
- // Only the main GC thread, no workers.
- conc_gc_threads_ = 0;
- // The default GC type is set in makefiles.
-#if ART_DEFAULT_GC_TYPE_IS_CMS
- collector_type_ = gc::kCollectorTypeCMS;
-#elif ART_DEFAULT_GC_TYPE_IS_SS
- collector_type_ = gc::kCollectorTypeSS;
-#elif ART_DEFAULT_GC_TYPE_IS_GSS
- collector_type_ = gc::kCollectorTypeGSS;
-#else
-#error "ART default GC type must be set"
-#endif
- // If we are using homogeneous space compaction then default background compaction to off since
- // homogeneous space compactions when we transition to not jank perceptible.
- use_homogeneous_space_compaction_for_oom_ = false;
- // If background_collector_type_ is kCollectorTypeNone, it defaults to the collector_type_ after
- // parsing options. If you set this to kCollectorTypeHSpaceCompact then we will do an hspace
- // compaction when we transition to background instead of a normal collector transition.
- background_collector_type_ = gc::kCollectorTypeNone;
-#ifdef ART_USE_HSPACE_COMPACT
- background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
-#endif
-#ifdef ART_USE_BACKGROUND_COMPACT
- background_collector_type_ = gc::kCollectorTypeSS;
-#endif
- stack_size_ = 0; // 0 means default.
- max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
- low_memory_mode_ = false;
- use_tlab_ = false;
- min_interval_homogeneous_space_compaction_by_oom_ = MsToNs(100 * 1000); // 100s.
- verify_pre_gc_heap_ = false;
- // Pre sweeping is the one that usually fails if the GC corrupted the heap.
- verify_pre_sweeping_heap_ = kIsDebugBuild;
- verify_post_gc_heap_ = false;
- verify_pre_gc_rosalloc_ = kIsDebugBuild;
- verify_pre_sweeping_rosalloc_ = false;
- verify_post_gc_rosalloc_ = false;
-
- compiler_callbacks_ = nullptr;
- is_zygote_ = false;
- must_relocate_ = kDefaultMustRelocate;
- dex2oat_enabled_ = true;
- image_dex2oat_enabled_ = true;
- if (kPoisonHeapReferences) {
- // kPoisonHeapReferences currently works only with the interpreter only.
- // TODO: make it work with the compiler.
- interpreter_only_ = true;
- } else {
- interpreter_only_ = false;
- }
- is_explicit_gc_disabled_ = false;
-
- long_pause_log_threshold_ = gc::Heap::kDefaultLongPauseLogThreshold;
- long_gc_log_threshold_ = gc::Heap::kDefaultLongGCLogThreshold;
- dump_gc_performance_on_shutdown_ = false;
- ignore_max_footprint_ = false;
-
- lock_profiling_threshold_ = 0;
- hook_is_sensitive_thread_ = NULL;
-
- hook_vfprintf_ = vfprintf;
- hook_exit_ = exit;
- hook_abort_ = NULL; // We don't call abort(3) by default; see Runtime::Abort.
// gLogVerbosity.class_linker = true; // TODO: don't check this in!
// gLogVerbosity.compiler = true; // TODO: don't check this in!
@@ -266,15 +274,6 @@
// gLogVerbosity.threads = true; // TODO: don't check this in!
// gLogVerbosity.verifier = true; // TODO: don't check this in!
- method_trace_ = false;
- method_trace_file_ = "/data/method-trace-file.bin";
- method_trace_file_size_ = 10 * MB;
-
- profile_clock_source_ = kDefaultTraceClockSource;
-
- verify_ = true;
- image_isa_ = kRuntimeISA;
-
for (size_t i = 0; i < options.size(); ++i) {
if (true && options[0].first == "-Xzygote") {
LOG(INFO) << "option[" << i << "]=" << options[i].first;
@@ -309,6 +308,8 @@
}
} else if (StartsWith(option, "-Xcheck:jni")) {
check_jni_ = true;
+ } else if (StartsWith(option, "-Xjniopts:forcecopy")) {
+ force_copy_ = true;
} else if (StartsWith(option, "-Xrunjdwp:") || StartsWith(option, "-agentlib:jdwp=")) {
std::string tail(option.substr(option[1] == 'X' ? 10 : 15));
// TODO: move parsing logic out of Dbg
@@ -453,6 +454,32 @@
if (!ParseXGcOption(option)) {
return false;
}
+ } else if (StartsWith(option, "-XX:LargeObjectSpace=")) {
+ std::string substring;
+ if (!ParseStringAfterChar(option, '=', &substring)) {
+ return false;
+ }
+ if (substring == "disabled") {
+ large_object_space_type_ = gc::space::kLargeObjectSpaceTypeDisabled;
+ } else if (substring == "freelist") {
+ large_object_space_type_ = gc::space::kLargeObjectSpaceTypeFreeList;
+ } else if (substring == "map") {
+ large_object_space_type_ = gc::space::kLargeObjectSpaceTypeMap;
+ } else {
+ Usage("Unknown -XX:LargeObjectSpace= option %s\n", substring.c_str());
+ return false;
+ }
+ } else if (StartsWith(option, "-XX:LargeObjectThreshold=")) {
+ std::string substring;
+ if (!ParseStringAfterChar(option, '=', &substring)) {
+ return false;
+ }
+ size_t size = ParseMemoryOption(substring.c_str(), 1);
+ if (size == 0) {
+ Usage("Failed to parse memory option %s\n", option.c_str());
+ return false;
+ }
+ large_object_threshold_ = size;
} else if (StartsWith(option, "-XX:BackgroundGC=")) {
std::string substring;
if (!ParseStringAfterChar(option, '=', &substring)) {
@@ -642,7 +669,6 @@
StartsWith(option, "-Xint:") ||
StartsWith(option, "-Xdexopt:") ||
(option == "-Xnoquithandler") ||
- StartsWith(option, "-Xjniopts:") ||
StartsWith(option, "-Xjnigreflimit:") ||
(option == "-Xgenregmap") ||
(option == "-Xnogenregmap") ||
@@ -765,7 +791,6 @@
UsageMessage(stream, " -Xstacktracefile:<filename>\n");
UsageMessage(stream, " -Xgc:[no]preverify\n");
UsageMessage(stream, " -Xgc:[no]postverify\n");
- UsageMessage(stream, " -XX:+DisableExplicitGC\n");
UsageMessage(stream, " -XX:HeapGrowthLimit=N\n");
UsageMessage(stream, " -XX:HeapMinFree=N\n");
UsageMessage(stream, " -XX:HeapMaxFree=N\n");
@@ -782,6 +807,7 @@
UsageMessage(stream, " -Xgc:[no]postverify_rosalloc\n");
UsageMessage(stream, " -Xgc:[no]presweepingverify\n");
UsageMessage(stream, " -Ximage:filename\n");
+ UsageMessage(stream, " -XX:+DisableExplicitGC\n");
UsageMessage(stream, " -XX:ParallelGCThreads=integervalue\n");
UsageMessage(stream, " -XX:ConcGCThreads=integervalue\n");
UsageMessage(stream, " -XX:MaxSpinsBeforeThinLockInflation=integervalue\n");
@@ -791,6 +817,8 @@
UsageMessage(stream, " -XX:IgnoreMaxFootprint\n");
UsageMessage(stream, " -XX:UseTLAB\n");
UsageMessage(stream, " -XX:BackgroundGC=none\n");
+ UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n");
+ UsageMessage(stream, " -XX:LargeObjectThreshold=N\n");
UsageMessage(stream, " -Xmethod-trace\n");
UsageMessage(stream, " -Xmethod-trace-file:filename");
UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n");
@@ -904,7 +932,7 @@
}
bool sane_val = true;
double value;
- if (false) {
+ if ((false)) {
// TODO: this doesn't seem to work on the emulator. b/15114595
std::stringstream iss(substring);
iss >> value;
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 86056c5..26a2f31 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -24,6 +24,7 @@
#include "globals.h"
#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
#include "instruction_set.h"
#include "profiler_options.h"
@@ -44,6 +45,7 @@
std::string class_path_string_;
std::string image_;
bool check_jni_;
+ bool force_copy_;
std::string jni_trace_;
std::string native_bridge_library_filename_;
CompilerCallbacks* compiler_callbacks_;
@@ -71,6 +73,8 @@
size_t heap_min_free_;
size_t heap_max_free_;
size_t heap_non_moving_space_capacity_;
+ gc::space::LargeObjectSpaceType large_object_space_type_;
+ size_t large_object_threshold_;
double heap_target_utilization_;
double foreground_heap_growth_multiplier_;
unsigned int parallel_gc_threads_;
@@ -107,7 +111,7 @@
uint64_t min_interval_homogeneous_space_compaction_by_oom_;
private:
- ParsedOptions() {}
+ ParsedOptions();
void Usage(const char* fmt, ...);
void UsageMessage(FILE* stream, const char* fmt, ...);
diff --git a/runtime/primitive.cc b/runtime/primitive.cc
index 16ca0fe..a639f93 100644
--- a/runtime/primitive.cc
+++ b/runtime/primitive.cc
@@ -30,6 +30,7 @@
"PrimDouble",
"PrimVoid",
};
+
std::ostream& operator<<(std::ostream& os, const Primitive::Type& type) {
int32_t int_type = static_cast<int32_t>(type);
if (type >= Primitive::kPrimNot && type <= Primitive::kPrimVoid) {
diff --git a/runtime/primitive.h b/runtime/primitive.h
index b436bd2..afcc64d 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -21,12 +21,28 @@
#include "base/logging.h"
#include "base/macros.h"
-#include "mirror/object_reference.h"
namespace art {
-namespace mirror {
-class Object;
-} // namespace mirror
+
+static constexpr size_t kObjectReferenceSize = 4;
+
+
+template<size_t kComponentSize>
+size_t ComponentSizeShiftWidth() {
+ switch (kComponentSize) {
+ case 1:
+ return 0U;
+ case 2:
+ return 1U;
+ case 4:
+ return 2U;
+ case 8:
+ return 3U;
+ default:
+ LOG(FATAL) << "Unexpected component size : " << kComponentSize;
+ return 0U;
+ }
+}
class Primitive {
public:
@@ -68,6 +84,24 @@
}
}
+ static size_t ComponentSizeShift(Type type) {
+ switch (type) {
+ case kPrimVoid:
+ case kPrimBoolean:
+ case kPrimByte: return 0;
+ case kPrimChar:
+ case kPrimShort: return 1;
+ case kPrimInt:
+ case kPrimFloat: return 2;
+ case kPrimLong:
+ case kPrimDouble: return 3;
+ case kPrimNot: return ComponentSizeShiftWidth<kObjectReferenceSize>();
+ default:
+ LOG(FATAL) << "Invalid type " << static_cast<int>(type);
+ return 0;
+ }
+ }
+
static size_t ComponentSize(Type type) {
switch (type) {
case kPrimVoid: return 0;
@@ -79,17 +113,13 @@
case kPrimFloat: return 4;
case kPrimLong:
case kPrimDouble: return 8;
- case kPrimNot: return sizeof(mirror::HeapReference<mirror::Object>);
+ case kPrimNot: return kObjectReferenceSize;
default:
LOG(FATAL) << "Invalid type " << static_cast<int>(type);
return 0;
}
}
- static size_t FieldSize(Type type) {
- return ComponentSize(type) <= 4 ? 4 : 8;
- }
-
static const char* Descriptor(Type type) {
switch (type) {
case kPrimBoolean:
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index a6a2475..cde4177 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -119,12 +119,12 @@
}
// A closure that is called by the thread checkpoint code.
-class SampleCheckpoint : public Closure {
+class SampleCheckpoint FINAL : public Closure {
public:
explicit SampleCheckpoint(BackgroundMethodSamplingProfiler* const profiler) :
profiler_(profiler) {}
- virtual void Run(Thread* thread) NO_THREAD_SAFETY_ANALYSIS {
+ void Run(Thread* thread) OVERRIDE {
Thread* self = Thread::Current();
if (thread == nullptr) {
LOG(ERROR) << "Checkpoint with nullptr thread";
@@ -192,6 +192,7 @@
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);
@@ -238,17 +239,13 @@
// 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;
- const uint32_t kWaitTimeoutUs = kWaitTimeoutMs * 1000;
- uint64_t waitstart_us = MicroTime();
// Wait for all threads to pass the barrier.
- profiler->profiler_barrier_->Increment(self, barrier_count, kWaitTimeoutMs);
- uint64_t waitend_us = MicroTime();
- uint64_t waitdiff_us = waitend_us - waitstart_us;
+ 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_LT(waitdiff_us, kWaitTimeoutUs);
+ CHECK(!timed_out);
// Update the current time.
now_us = MicroTime();
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index d977ce9..1eded62 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -183,7 +183,8 @@
ASSERT_TRUE(throwsFieldClass.Get() != nullptr);
// Test "Class[] interfaces" field.
- FieldHelper fh(hs.NewHandle(static_fields->Get(0)));
+ MutableHandle<mirror::ArtField> fhandle = hs.NewHandle(static_fields->Get(0));
+ FieldHelper fh(fhandle);
EXPECT_EQ("interfaces", std::string(fh.GetField()->GetName()));
EXPECT_EQ("[Ljava/lang/Class;", std::string(fh.GetField()->GetTypeDescriptor()));
EXPECT_EQ(interfacesFieldClass.Get(), fh.GetType());
@@ -191,12 +192,13 @@
EXPECT_FALSE(fh.GetField()->IsPrimitiveType());
// Test "Class[][] throws" field.
- fh.ChangeField(static_fields->Get(1));
- EXPECT_EQ("throws", std::string(fh.GetField()->GetName()));
- EXPECT_EQ("[[Ljava/lang/Class;", std::string(fh.GetField()->GetTypeDescriptor()));
- EXPECT_EQ(throwsFieldClass.Get(), fh.GetType());
- EXPECT_EQ("L$Proxy1234;", std::string(fh.GetDeclaringClassDescriptor()));
- EXPECT_FALSE(fh.GetField()->IsPrimitiveType());
+ fhandle.Assign(static_fields->Get(1));
+ FieldHelper fh2(fhandle);
+ EXPECT_EQ("throws", std::string(fh2.GetField()->GetName()));
+ EXPECT_EQ("[[Ljava/lang/Class;", std::string(fh2.GetField()->GetTypeDescriptor()));
+ EXPECT_EQ(throwsFieldClass.Get(), fh2.GetType());
+ EXPECT_EQ("L$Proxy1234;", std::string(fh2.GetDeclaringClassDescriptor()));
+ EXPECT_FALSE(fh2.GetField()->IsPrimitiveType());
}
} // namespace art
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 98eeda7..2c158ba 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -206,13 +206,14 @@
const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
- StackHandleScope<2> hs(self_);
+ StackHandleScope<3> hs(self_);
mirror::Class* declaring_class = m->GetDeclaringClass();
Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- verifier::MethodVerifier verifier(h_dex_cache->GetDexFile(), &h_dex_cache, &h_class_loader,
- &m->GetClassDef(), code_item, m->GetDexMethodIndex(), m,
- m->GetAccessFlags(), false, true, true);
+ Handle<mirror::ArtMethod> h_method(hs.NewHandle(m));
+ verifier::MethodVerifier verifier(self_, h_dex_cache->GetDexFile(), h_dex_cache, h_class_loader,
+ &m->GetClassDef(), code_item, m->GetDexMethodIndex(),
+ h_method, m->GetAccessFlags(), false, true, true);
verifier.Verify();
const std::vector<int32_t> kinds(verifier.DescribeVRegs(dex_pc));
for (uint16_t reg = 0; reg < num_regs; ++reg) {
@@ -229,7 +230,7 @@
reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
break;
case kLongLoVReg:
- if (GetVRegKind(reg + 1, kinds), kLongHiVReg) {
+ if (GetVRegKind(reg + 1, kinds) == kLongHiVReg) {
// Treat it as a "long" register pair.
new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg));
} else {
@@ -237,14 +238,14 @@
}
break;
case kLongHiVReg:
- if (GetVRegKind(reg - 1, kinds), kLongLoVReg) {
+ if (GetVRegKind(reg - 1, kinds) == kLongLoVReg) {
// Nothing to do: we treated it as a "long" register pair.
} else {
new_frame->SetVReg(reg, GetVReg(m, reg, kind));
}
break;
case kDoubleLoVReg:
- if (GetVRegKind(reg + 1, kinds), kDoubleHiVReg) {
+ if (GetVRegKind(reg + 1, kinds) == kDoubleHiVReg) {
// Treat it as a "double" register pair.
new_frame->SetVRegLong(reg, GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg));
} else {
@@ -252,7 +253,7 @@
}
break;
case kDoubleHiVReg:
- if (GetVRegKind(reg - 1, kinds), kDoubleLoVReg) {
+ if (GetVRegKind(reg - 1, kinds) == kDoubleLoVReg) {
// Nothing to do: we treated it as a "double" register pair.
} else {
new_frame->SetVReg(reg, GetVReg(m, reg, kind));
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 70aba9b..01c5070 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -24,6 +24,7 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/string-inl.h"
+#include "runtime-inl.h"
#include "thread.h"
#include "utils.h"
@@ -62,7 +63,9 @@
// If "obj" is an array, return the number of elements in the array.
// Otherwise, return zero.
static size_t GetElementCount(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (obj == NULL || obj == kClearedJniWeakGlobal || !obj->IsArrayInstance()) {
+ // We assume the special cleared value isn't an array in the if statement below.
+ DCHECK(!Runtime::Current()->GetClearedJniWeakGlobal()->IsArrayInstance());
+ if (obj == nullptr || !obj->IsArrayInstance()) {
return 0;
}
return obj->AsArray()->GetLength();
@@ -81,9 +84,10 @@
} else if (obj2 == NULL) {
return false;
}
- if (obj1 == kClearedJniWeakGlobal) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsClearedJniWeakGlobal(obj1)) {
return true;
- } else if (obj2 == kClearedJniWeakGlobal) {
+ } else if (runtime->IsClearedJniWeakGlobal(obj2)) {
return false;
}
@@ -116,7 +120,7 @@
os << " NULL reference (count=" << equiv << ")\n";
return;
}
- if (obj == kClearedJniWeakGlobal) {
+ if (Runtime::Current()->IsClearedJniWeakGlobal(obj)) {
os << " cleared jweak (count=" << equiv << ")\n";
return;
}
@@ -167,7 +171,7 @@
if (ref == NULL) {
continue;
}
- if (ref == kClearedJniWeakGlobal) {
+ if (Runtime::Current()->IsClearedJniWeakGlobal(ref)) {
os << StringPrintf(" %5d: cleared jweak\n", idx);
continue;
}
@@ -209,7 +213,8 @@
sorted_entries.pop_back();
}
while (!sorted_entries.empty() &&
- sorted_entries.back().Read<kWithoutReadBarrier>() == kClearedJniWeakGlobal) {
+ Runtime::Current()->IsClearedJniWeakGlobal(
+ sorted_entries.back().Read<kWithoutReadBarrier>())) {
sorted_entries.pop_back();
}
if (sorted_entries.empty()) {
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 18fcee4..23f8076 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -348,7 +348,7 @@
std::unique_ptr<uint32_t[]> large_arg_array_;
};
-static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args)
+static void CheckMethodArguments(JavaVMExt* vm, mirror::ArtMethod* m, uint32_t* args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile::TypeList* params = m->GetParameterTypeList();
if (params == nullptr) {
@@ -376,11 +376,11 @@
self->ClearException();
++error_count;
} else if (!param_type->IsPrimitive()) {
- // TODO: check primitives are in range.
// TODO: There is a compaction bug here since GetClassFromTypeIdx can cause thread suspension,
// this is a hard to fix problem since the args can contain Object*, we need to save and
// restore them by using a visitor similar to the ones used in the trampoline entrypoints.
- mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]);
+ mirror::Object* argument =
+ (reinterpret_cast<StackReference<mirror::Object>*>(&args[i + offset]))->AsMirrorPtr();
if (argument != nullptr && !argument->InstanceOf(param_type)) {
LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of "
<< PrettyTypeOf(argument) << " as argument " << (i + 1)
@@ -389,13 +389,40 @@
}
} else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) {
offset++;
+ } else {
+ int32_t arg = static_cast<int32_t>(args[i + offset]);
+ if (param_type->IsPrimitiveBoolean()) {
+ if (arg != JNI_TRUE && arg != JNI_FALSE) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jboolean (0/1) but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ } else if (param_type->IsPrimitiveByte()) {
+ if (arg < -128 || arg > 127) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jbyte but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ } else if (param_type->IsPrimitiveChar()) {
+ if (args[i + offset] > 0xFFFF) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jchar but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ } else if (param_type->IsPrimitiveShort()) {
+ if (arg < -32768 || arg > 0x7FFF) {
+ LOG(ERROR) << "JNI ERROR (app bug): expected jshort but got value of "
+ << arg << " as argument " << (i + 1) << " to " << PrettyMethod(h_m.Get());
+ ++error_count;
+ }
+ }
}
}
- if (error_count > 0) {
+ if (UNLIKELY(error_count > 0)) {
// TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort
// with an argument.
- JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
- PrettyMethod(h_m.Get()).c_str());
+ vm->JniAbortF(nullptr, "bad arguments passed to %s (see above for details)",
+ PrettyMethod(h_m.Get()).c_str());
}
}
@@ -412,7 +439,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t* args = arg_array->GetArray();
if (UNLIKELY(soa.Env()->check_jni)) {
- CheckMethodArguments(method, args);
+ CheckMethodArguments(soa.Vm(), method, args);
}
method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
}
@@ -533,7 +560,7 @@
if (UNLIKELY(!declaring_class->IsInitialized())) {
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(h_class, true, true)) {
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), h_class, true, true)) {
return nullptr;
}
declaring_class = h_class.Get();
@@ -565,9 +592,16 @@
}
// If method is not set to be accessible, verify it can be accessed by the caller.
- if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags())) {
- ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access method: %s",
- PrettyMethod(m).c_str()).c_str());
+ mirror::Class* calling_class = nullptr;
+ if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags(),
+ &calling_class)) {
+ ThrowIllegalAccessException(nullptr,
+ StringPrintf("Class %s cannot access %s method %s of class %s",
+ calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
+ PrettyJavaAccessFlags(m->GetAccessFlags()).c_str(),
+ PrettyMethod(m).c_str(),
+ m->GetDeclaringClass() == nullptr ? "null" :
+ PrettyClass(m->GetDeclaringClass()).c_str()).c_str());
return nullptr;
}
@@ -788,7 +822,8 @@
return UnboxPrimitive(&throw_location, o, dst_class, nullptr, unboxed_value);
}
-bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
+bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
+ uint32_t access_flags, mirror::Class** calling_class) {
if ((access_flags & kAccPublic) != 0) {
return true;
}
@@ -802,6 +837,8 @@
if (caller_class == declaring_class) {
return true;
}
+ ScopedAssertNoThreadSuspension sants(self, "verify-access");
+ *calling_class = caller_class;
if ((access_flags & kAccPrivate) != 0) {
return false;
}
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 0f41aca..23d8e05 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_REFLECTION_H_
#define ART_RUNTIME_REFLECTION_H_
+#include "base/mutex.h"
#include "jni.h"
#include "primitive.h"
@@ -75,7 +76,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
- uint32_t access_flags)
+ uint32_t access_flags, mirror::Class** calling_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
} // namespace art
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 9d10daa..75211e0 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -117,7 +117,7 @@
// Ensure class is initialized before allocating object
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(c));
- bool initialized = class_linker_->EnsureInitialized(h_class, true, true);
+ bool initialized = class_linker_->EnsureInitialized(self, h_class, true, true);
CHECK(initialized);
*receiver = c->AllocObject(self);
}
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index ac9026b..fe05073 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -23,6 +23,16 @@
namespace art {
+inline bool Runtime::IsClearedJniWeakGlobal(mirror::Object* obj) {
+ return obj == GetClearedJniWeakGlobal();
+}
+
+inline mirror::Object* Runtime::GetClearedJniWeakGlobal() {
+ mirror::Object* obj = sentinel_.Read();
+ DCHECK(obj != nullptr);
+ return obj;
+}
+
inline QuickMethodFrameInfo Runtime::GetRuntimeMethodFrameInfo(mirror::ArtMethod* method) {
DCHECK(method != nullptr);
// Cannot be imt-conflict-method or resolution-method.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1544aa3..48439b6 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -72,6 +72,7 @@
#include "reflection.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
+#include "sigchain.h"
#include "signal_catcher.h"
#include "signal_set.h"
#include "handle_scope-inl.h"
@@ -139,9 +140,6 @@
system_class_loader_(nullptr),
dump_gc_performance_on_shutdown_(false),
preinitialization_transaction_(nullptr),
- null_pointer_handler_(nullptr),
- suspend_handler_(nullptr),
- stack_overflow_handler_(nullptr),
verify_(false),
target_sdk_version_(0),
implicit_null_checks_(false),
@@ -198,14 +196,10 @@
// TODO: acquire a static mutex on Runtime to avoid racing.
CHECK(instance_ == nullptr || instance_ == this);
instance_ = nullptr;
-
- delete null_pointer_handler_;
- delete suspend_handler_;
- delete stack_overflow_handler_;
}
struct AbortState {
- void Dump(std::ostream& os) NO_THREAD_SAFETY_ANALYSIS {
+ void Dump(std::ostream& os) {
if (gAborting > 1) {
os << "Runtime aborting --- recursively, so no thread-specific detail!\n";
return;
@@ -235,7 +229,9 @@
DumpAllThreads(os, self);
}
- void DumpThread(std::ostream& os, Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // No thread-safety analysis as we do explicitly test for holding the mutator lock.
+ void DumpThread(std::ostream& os, Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+ DCHECK(Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self));
self->Dump(os);
if (self->IsExceptionPending()) {
ThrowLocation throw_location;
@@ -246,7 +242,7 @@
}
}
- void DumpAllThreads(std::ostream& os, Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+ void DumpAllThreads(std::ostream& os, Thread* self) {
Runtime* runtime = Runtime::Current();
if (runtime != nullptr) {
ThreadList* thread_list = runtime->GetThreadList();
@@ -260,7 +256,7 @@
<< "\n";
}
os << "All threads:\n";
- thread_list->DumpLocked(os);
+ thread_list->Dump(os);
}
}
}
@@ -346,36 +342,34 @@
ScopedObjectAccess soa(Thread::Current());
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- StackHandleScope<3> hs(soa.Self());
+ StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> class_loader_class(
hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ClassLoader)));
- CHECK(cl->EnsureInitialized(class_loader_class, true, true));
+ CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
mirror::ArtMethod* getSystemClassLoader =
class_loader_class->FindDirectMethod("getSystemClassLoader", "()Ljava/lang/ClassLoader;");
CHECK(getSystemClassLoader != NULL);
JValue result = InvokeWithJValues(soa, nullptr, soa.EncodeMethod(getSystemClassLoader), nullptr);
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(down_cast<mirror::ClassLoader*>(result.GetL())));
- CHECK(class_loader.Get() != nullptr);
JNIEnv* env = soa.Self()->GetJniEnv();
ScopedLocalRef<jobject> system_class_loader(env,
- soa.AddLocalReference<jobject>(class_loader.Get()));
+ soa.AddLocalReference<jobject>(result.GetL()));
CHECK(system_class_loader.get() != nullptr);
- soa.Self()->SetClassLoaderOverride(class_loader.Get());
+ soa.Self()->SetClassLoaderOverride(system_class_loader.get());
Handle<mirror::Class> thread_class(
hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_Thread)));
- CHECK(cl->EnsureInitialized(thread_class, true, true));
+ CHECK(cl->EnsureInitialized(soa.Self(), thread_class, true, true));
mirror::ArtField* contextClassLoader =
thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
CHECK(contextClassLoader != NULL);
// We can't run in a transaction yet.
- contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), class_loader.Get());
+ contextClassLoader->SetObject<false>(soa.Self()->GetPeer(),
+ soa.Decode<mirror::ClassLoader*>(system_class_loader.get()));
return env->NewGlobalRef(system_class_loader.get());
}
@@ -412,7 +406,7 @@
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass()));
- class_linker_->EnsureInitialized(klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), klass, true, true);
}
// InitNativeMethods needs to be after started_ so that the classes
@@ -711,6 +705,8 @@
options->image_isa_,
options->collector_type_,
options->background_collector_type_,
+ options->large_object_space_type_,
+ options->large_object_threshold_,
options->parallel_gc_threads_,
options->conc_gc_threads_,
options->low_memory_mode_,
@@ -740,28 +736,37 @@
case kArm64:
case kX86_64:
implicit_null_checks_ = true;
- implicit_so_checks_ = true;
+ // Installing stack protection does not play well with valgrind.
+ implicit_so_checks_ = (RUNNING_ON_VALGRIND == 0);
break;
default:
// Keep the defaults.
break;
}
+ // Always initialize the signal chain so that any calls to sigaction get
+ // correctly routed to the next in the chain regardless of whether we
+ // have claimed the signal or not.
+ InitializeSignalChain();
+
if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {
fault_manager.Init();
// These need to be in a specific order. The null point check handler must be
// after the suspend check and stack overflow check handlers.
+ //
+ // Note: the instances attach themselves to the fault manager and are handled by it. The manager
+ // will delete the instance on Shutdown().
if (implicit_suspend_checks_) {
- suspend_handler_ = new SuspensionHandler(&fault_manager);
+ new SuspensionHandler(&fault_manager);
}
if (implicit_so_checks_) {
- stack_overflow_handler_ = new StackOverflowHandler(&fault_manager);
+ new StackOverflowHandler(&fault_manager);
}
if (implicit_null_checks_) {
- null_pointer_handler_ = new NullPointerHandler(&fault_manager);
+ new NullPointerHandler(&fault_manager);
}
if (kEnableJavaStackTraceHandler) {
@@ -813,6 +818,11 @@
class_linker_->InitWithoutImage(*options->boot_class_path_);
}
CHECK(class_linker_ != nullptr);
+
+ // Initialize the special sentinel_ value early.
+ sentinel_ = GcRoot<mirror::Object>(class_linker_->AllocObject(self));
+ CHECK(sentinel_.Read() != nullptr);
+
verifier::MethodVerifier::Init();
method_trace_ = options->method_trace_;
@@ -901,13 +911,9 @@
{
std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
std::string reason;
- self->TransitionFromSuspendedToRunnable();
- StackHandleScope<1> hs(self);
- auto class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
- if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, class_loader, &reason)) {
+ if (!instance_->java_vm_->LoadNativeLibrary(env, mapped_name, nullptr, &reason)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
}
- self->TransitionFromRunnableToSuspended(kNative);
}
// Initialize well known classes that may invoke runtime native methods.
@@ -1133,6 +1139,10 @@
void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
java_vm_->VisitRoots(callback, arg);
+ if (!sentinel_.IsNull()) {
+ sentinel_.VisitRoot(callback, arg, 0, kRootVMInternal);
+ DCHECK(!sentinel_.IsNull());
+ }
if (!pre_allocated_OutOfMemoryError_.IsNull()) {
pre_allocated_OutOfMemoryError_.VisitRoot(callback, arg, 0, kRootVMInternal);
DCHECK(!pre_allocated_OutOfMemoryError_.IsNull());
@@ -1200,14 +1210,10 @@
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
if (runtime->IsCompiler()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(nullptr);
-#endif
method->SetEntryPointFromQuickCompiledCode(nullptr);
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableImtConflictTrampoline());
-#endif
method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickImtConflictTrampoline());
}
return method.Get();
@@ -1224,14 +1230,10 @@
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
if (runtime->IsCompiler()) {
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(nullptr);
-#endif
method->SetEntryPointFromQuickCompiledCode(nullptr);
} else {
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(class_linker->GetPortableResolutionTrampoline());
-#endif
method->SetEntryPointFromQuickCompiledCode(class_linker->GetQuickResolutionTrampoline());
}
return method.Get();
@@ -1246,9 +1248,7 @@
method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod());
// TODO: use a special method for callee saves
method->SetDexMethodIndex(DexFile::kDexNoIndex);
-#if defined(ART_USE_PORTABLE_COMPILER)
method->SetEntryPointFromPortableCompiledCode(nullptr);
-#endif
method->SetEntryPointFromQuickCompiledCode(nullptr);
DCHECK_NE(instruction_set_, kNone);
return method.Get();
@@ -1354,6 +1354,34 @@
preinitialization_transaction_ = nullptr;
}
+void Runtime::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
+ uint8_t value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction_->RecordWriteFieldBoolean(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset,
+ int8_t value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction_->RecordWriteFieldByte(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset,
+ uint16_t value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction_->RecordWriteFieldChar(obj, field_offset, value, is_volatile);
+}
+
+void Runtime::RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset,
+ int16_t value, bool is_volatile) const {
+ DCHECK(IsCompiler());
+ DCHECK(IsActiveTransaction());
+ preinitialization_transaction_->RecordWriteFieldShort(obj, field_offset, value, is_volatile);
+}
+
void Runtime::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset,
uint32_t value, bool is_volatile) const {
DCHECK(IsCompiler());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8cfa8aa..1a6c6e0 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -200,8 +200,7 @@
// Detaches the current native thread from the runtime.
void DetachCurrentThread() LOCKS_EXCLUDED(Locks::mutator_lock_);
- void DumpForSigQuit(std::ostream& os)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void DumpForSigQuit(std::ostream& os);
void DumpLockHolders(std::ostream& os);
~Runtime();
@@ -247,6 +246,12 @@
return monitor_pool_;
}
+ // Is the given object the special object used to mark a cleared JNI weak global?
+ bool IsClearedJniWeakGlobal(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Get the special object used to mark a cleared JNI weak global.
+ mirror::Object* GetClearedJniWeakGlobal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
mirror::Throwable* GetPreAllocatedOutOfMemoryError() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Throwable* GetPreAllocatedNoClassDefFoundError()
@@ -427,6 +432,14 @@
}
void EnterTransactionMode(Transaction* transaction);
void ExitTransactionMode();
+ void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
+ bool is_volatile) const;
+ void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value,
+ bool is_volatile) const;
+ void RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset, uint16_t value,
+ bool is_volatile) const;
+ void RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset, int16_t value,
+ bool is_volatile) const;
void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
bool is_volatile) const;
void RecordWriteField64(mirror::Object* obj, MemberOffset field_offset, uint64_t value,
@@ -453,16 +466,8 @@
void AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::string>* arg_vector) const;
- bool ExplicitNullChecks() const {
- return null_pointer_handler_ == nullptr;
- }
-
- bool ExplicitSuspendChecks() const {
- return suspend_handler_ == nullptr;
- }
-
bool ExplicitStackOverflowChecks() const {
- return stack_overflow_handler_ == nullptr;
+ return !implicit_so_checks_;
}
bool IsVerificationEnabled() const {
@@ -517,6 +522,10 @@
GcRoot<mirror::ArtMethod> imt_conflict_method_;
GcRoot<mirror::ObjectArray<mirror::ArtMethod>> default_imt_;
+ // Special sentinel object used to invalid conditions in JNI (cleared weak references) and
+ // JDWP (invalid references).
+ GcRoot<mirror::Object> sentinel_;
+
InstructionSet instruction_set_;
QuickMethodFrameInfo callee_save_method_frame_infos_[kLastCalleeSaveType];
@@ -623,9 +632,6 @@
// Transaction used for pre-initializing classes at compilation time.
Transaction* preinitialization_transaction_;
- NullPointerHandler* null_pointer_handler_;
- SuspensionHandler* suspend_handler_;
- StackOverflowHandler* stack_overflow_handler_;
// If false, verification is disabled. True by default.
bool verify_;
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 23aca45..ae3eaf2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -18,7 +18,8 @@
#define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
#include "base/casts.h"
-#include "jni_internal-inl.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext-inl.h"
#include "read_barrier.h"
#include "thread-inl.h"
#include "verify_object.h"
@@ -114,6 +115,10 @@
return vm_;
}
+ bool ForceCopy() const {
+ return vm_->ForceCopy();
+ }
+
/*
* Add a local reference for an object to the indirect reference table associated with the
* current stack frame. When the native function returns, the reference will be discarded.
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 11e06fe..336340e 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -118,17 +118,6 @@
void SignalCatcher::HandleSigQuit() {
Runtime* runtime = Runtime::Current();
- ThreadList* thread_list = runtime->GetThreadList();
-
- // Grab exclusively the mutator lock, set state to Runnable without checking for a pending
- // suspend request as we're going to suspend soon anyway. We set the state to Runnable to avoid
- // giving away the mutator lock.
- thread_list->SuspendAll();
- Thread* self = Thread::Current();
- Locks::mutator_lock_->AssertExclusiveHeld(self);
- const char* old_cause = self->StartAssertNoThreadSuspension("Handling SIGQUIT");
- ThreadState old_state = self->SetStateUnsafe(kRunnable);
-
std::ostringstream os;
os << "\n"
<< "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
@@ -149,14 +138,6 @@
}
}
os << "----- end " << getpid() << " -----\n";
- CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
- self->EndAssertNoThreadSuspension(old_cause);
- thread_list->ResumeAll();
- // Run the checkpoints after resuming the threads to prevent deadlocks if the checkpoint function
- // acquires the mutator lock.
- if (self->ReadFlag(kCheckpointRequest)) {
- self->RunCheckpointFunction();
- }
Output(os.str());
}
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 2d0060e..b8b10d2 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -115,18 +115,22 @@
mirror::Object* StackVisitor::GetThisObject() const {
mirror::ArtMethod* m = GetMethod();
if (m->IsStatic()) {
- return NULL;
+ return nullptr;
} else if (m->IsNative()) {
- if (cur_quick_frame_ != NULL) {
+ if (cur_quick_frame_ != nullptr) {
HandleScope* hs = reinterpret_cast<HandleScope*>(
reinterpret_cast<char*>(cur_quick_frame_) + m->GetHandleScopeOffsetInBytes());
return hs->GetReference(0);
} else {
return cur_shadow_frame_->GetVRegReference(0);
}
+ } else if (m->IsOptimized()) {
+ // TODO: Implement, currently only used for exceptions when jdwp is enabled.
+ LOG(WARNING) << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
+ return nullptr;
} else {
const DexFile::CodeItem* code_item = m->GetCodeItem();
- if (code_item == NULL) {
+ if (code_item == nullptr) {
UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: "
<< PrettyMethod(m);
return nullptr;
diff --git a/runtime/stack.h b/runtime/stack.h
index e58caee..44e36c4 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -54,50 +54,6 @@
kUndefined,
};
-/**
- * @brief Represents the virtual register numbers that denote special meaning.
- * @details This is used to make some virtual register numbers to have specific
- * semantic meaning. This is done so that the compiler can treat all virtual
- * registers the same way and only special case when needed. For example,
- * calculating SSA does not care whether a virtual register is a normal one or
- * a compiler temporary, so it can deal with them in a consistent manner. But,
- * for example if backend cares about temporaries because it has custom spill
- * location, then it can special case them only then.
- */
-enum VRegBaseRegNum : int {
- /**
- * @brief Virtual registers originating from dex have number >= 0.
- */
- kVRegBaseReg = 0,
-
- /**
- * @brief Invalid virtual register number.
- */
- kVRegInvalid = -1,
-
- /**
- * @brief Used to denote the base register for compiler temporaries.
- * @details Compiler temporaries are virtual registers not originating
- * from dex but that are created by compiler. All virtual register numbers
- * that are <= kVRegTempBaseReg are categorized as compiler temporaries.
- */
- kVRegTempBaseReg = -2,
-
- /**
- * @brief Base register of temporary that holds the method pointer.
- * @details This is a special compiler temporary because it has a specific
- * location on stack.
- */
- kVRegMethodPtrBaseReg = kVRegTempBaseReg,
-
- /**
- * @brief Base register of non-special compiler temporary.
- * @details A non-special compiler temporary is one whose spill location
- * is flexible.
- */
- kVRegNonSpecialTempBaseReg = -3,
-};
-
// A reference from the shadow stack to a MirrorType object within the Java heap.
template<class MirrorType>
class MANAGED StackReference : public mirror::ObjectReference<false, MirrorType> {
@@ -612,75 +568,76 @@
/*
* Return sp-relative offset for a Dalvik virtual register, compiler
* spill or Method* in bytes using Method*.
- * Note that (reg >= 0) refers to a Dalvik register, (reg == -1)
- * denotes an invalid Dalvik register, (reg == -2) denotes Method*
- * and (reg <= -3) denotes a compiler temporary. A compiler temporary
- * can be thought of as a virtual register that does not exist in the
- * dex but holds intermediate values to help optimizations and code
- * generation. A special compiler temporary is one whose location
- * in frame is well known while non-special ones do not have a requirement
- * on location in frame as long as code generator itself knows how
- * to access them.
+ * Note that (reg == -1) denotes an invalid Dalvik register. For the
+ * positive values, the Dalvik registers come first, followed by the
+ * Method*, followed by other special temporaries if any, followed by
+ * regular compiler temporary. As of now we only have the Method* as
+ * as a special compiler temporary.
+ * A compiler temporary can be thought of as a virtual register that
+ * does not exist in the dex but holds intermediate values to help
+ * optimizations and code generation. A special compiler temporary is
+ * one whose location in frame is well known while non-special ones
+ * do not have a requirement on location in frame as long as code
+ * generator itself knows how to access them.
*
- * +---------------------------+
- * | IN[ins-1] | {Note: resides in caller's frame}
- * | . |
- * | IN[0] |
- * | caller's ArtMethod | ... StackReference<ArtMethod>
- * +===========================+ {Note: start of callee's frame}
- * | core callee-save spill | {variable sized}
- * +---------------------------+
- * | fp callee-save spill |
- * +---------------------------+
- * | filler word | {For compatibility, if V[locals-1] used as wide
- * +---------------------------+
- * | V[locals-1] |
- * | V[locals-2] |
- * | . |
- * | . | ... (reg == 2)
- * | V[1] | ... (reg == 1)
- * | V[0] | ... (reg == 0) <---- "locals_start"
- * +---------------------------+
- * | Compiler temp region | ... (reg <= -3)
- * | |
- * | |
- * +---------------------------+
- * | stack alignment padding | {0 to (kStackAlignWords-1) of padding}
- * +---------------------------+
- * | OUT[outs-1] |
- * | OUT[outs-2] |
- * | . |
- * | OUT[0] |
- * | StackReference<ArtMethod> | ... (reg == -2) <<== sp, 16-byte aligned
- * +===========================+
+ * +-------------------------------+
+ * | IN[ins-1] | {Note: resides in caller's frame}
+ * | . |
+ * | IN[0] |
+ * | caller's ArtMethod | ... StackReference<ArtMethod>
+ * +===============================+ {Note: start of callee's frame}
+ * | core callee-save spill | {variable sized}
+ * +-------------------------------+
+ * | fp callee-save spill |
+ * +-------------------------------+
+ * | filler word | {For compatibility, if V[locals-1] used as wide
+ * +-------------------------------+
+ * | V[locals-1] |
+ * | V[locals-2] |
+ * | . |
+ * | . | ... (reg == 2)
+ * | V[1] | ... (reg == 1)
+ * | V[0] | ... (reg == 0) <---- "locals_start"
+ * +-------------------------------+
+ * | stack alignment padding | {0 to (kStackAlignWords-1) of padding}
+ * +-------------------------------+
+ * | Compiler temp region | ... (reg >= max_num_special_temps)
+ * | . |
+ * | . |
+ * | V[max_num_special_temps + 1] |
+ * | V[max_num_special_temps + 0] |
+ * +-------------------------------+
+ * | OUT[outs-1] |
+ * | OUT[outs-2] |
+ * | . |
+ * | OUT[0] |
+ * | StackReference<ArtMethod> | ... (reg == num_total_code_regs == special_temp_value) <<== sp, 16-byte aligned
+ * +===============================+
*/
static int GetVRegOffset(const DexFile::CodeItem* code_item,
uint32_t core_spills, uint32_t fp_spills,
size_t frame_size, int reg, InstructionSet isa) {
DCHECK_EQ(frame_size & (kStackAlignment - 1), 0U);
- DCHECK_NE(reg, static_cast<int>(kVRegInvalid));
+ DCHECK_NE(reg, -1);
int spill_size = POPCOUNT(core_spills) * GetBytesPerGprSpillLocation(isa)
+ POPCOUNT(fp_spills) * GetBytesPerFprSpillLocation(isa)
+ sizeof(uint32_t); // Filler.
- int num_ins = code_item->ins_size_;
- int num_regs = code_item->registers_size_ - num_ins;
- int locals_start = frame_size - spill_size - num_regs * sizeof(uint32_t);
- if (reg == static_cast<int>(kVRegMethodPtrBaseReg)) {
+ int num_regs = code_item->registers_size_ - code_item->ins_size_;
+ int temp_threshold = code_item->registers_size_;
+ const int max_num_special_temps = 1;
+ if (reg == temp_threshold) {
// The current method pointer corresponds to special location on stack.
return 0;
- } else if (reg <= static_cast<int>(kVRegNonSpecialTempBaseReg)) {
+ } else if (reg >= temp_threshold + max_num_special_temps) {
/*
* Special temporaries may have custom locations and the logic above deals with that.
- * However, non-special temporaries are placed relative to the locals. Since the
- * virtual register numbers for temporaries "grow" in negative direction, reg number
- * will always be <= to the temp base reg. Thus, the logic ensures that the first
- * temp is at offset -4 bytes from locals, the second is at -8 bytes from locals,
- * and so on.
+ * However, non-special temporaries are placed relative to the outs.
*/
- int relative_offset =
- (reg + std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg)) - 1) * sizeof(uint32_t);
- return locals_start + relative_offset;
+ int temps_start = sizeof(StackReference<mirror::ArtMethod>) + code_item->outs_size_ * sizeof(uint32_t);
+ int relative_offset = (reg - (temp_threshold + max_num_special_temps)) * sizeof(uint32_t);
+ return temps_start + relative_offset;
} else if (reg < num_regs) {
+ int locals_start = frame_size - spill_size - num_regs * sizeof(uint32_t);
return locals_start + (reg * sizeof(uint32_t));
} else {
// Handle ins.
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7d3a48f..9b49d31 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -64,9 +64,9 @@
MemoryRegion region_;
- template<typename T> friend class CodeInfo;
- template<typename T> friend class StackMap;
- template<typename T> friend class StackMapStream;
+ friend class CodeInfo;
+ friend class StackMap;
+ friend class StackMapStream;
};
/**
@@ -77,13 +77,15 @@
* The location_kind for a Dex register can either be:
* - Constant: register_value holds the constant,
* - Stack: register_value holds the stack offset,
- * - Register: register_value holds the register number.
+ * - Register: register_value holds the physical register number.
+ * - None: the register has no location yet, meaning it has not been set.
*/
class DexRegisterMap {
public:
explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
enum LocationKind {
+ kNone,
kInStack,
kInRegister,
kConstant
@@ -114,8 +116,8 @@
MemoryRegion region_;
- template <typename T> friend class CodeInfo;
- template <typename T> friend class StackMapStream;
+ friend class CodeInfo;
+ friend class StackMapStream;
};
/**
@@ -127,12 +129,11 @@
* - Knowing the values of dex registers.
*
* The information is of the form:
- * [dex_pc, native_pc, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
+ * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
*
* Note that register_mask is fixed size, but stack_mask is variable size, depending on the
* stack size of a method.
*/
-template <typename T>
class StackMap {
public:
explicit StackMap(MemoryRegion region) : region_(region) {}
@@ -145,12 +146,12 @@
region_.Store<uint32_t>(kDexPcOffset, dex_pc);
}
- T GetNativePc() const {
- return region_.Load<T>(kNativePcOffset);
+ uint32_t GetNativePcOffset() const {
+ return region_.Load<uint32_t>(kNativePcOffsetOffset);
}
- void SetNativePc(T native_pc) {
- return region_.Store<T>(kNativePcOffset, native_pc);
+ void SetNativePcOffset(uint32_t native_pc_offset) {
+ return region_.Store<uint32_t>(kNativePcOffsetOffset, native_pc_offset);
}
uint32_t GetDexRegisterMapOffset() const {
@@ -199,8 +200,8 @@
private:
static constexpr int kDexPcOffset = 0;
- static constexpr int kNativePcOffset = kDexPcOffset + sizeof(uint32_t);
- static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffset + sizeof(T);
+ static constexpr int kNativePcOffsetOffset = kDexPcOffset + sizeof(uint32_t);
+ static constexpr int kDexRegisterMapOffsetOffset = kNativePcOffsetOffset + sizeof(uint32_t);
static constexpr int kInlineDescriptorOffsetOffset =
kDexRegisterMapOffsetOffset + sizeof(uint32_t);
static constexpr int kRegisterMaskOffset = kInlineDescriptorOffsetOffset + sizeof(uint32_t);
@@ -211,24 +212,36 @@
MemoryRegion region_;
- template <typename U> friend class CodeInfo;
- template <typename U> friend class StackMapStream;
+ friend class CodeInfo;
+ friend class StackMapStream;
};
/**
* Wrapper around all compiler information collected for a method.
* The information is of the form:
- * [number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
+ * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
*/
-template <typename T>
class CodeInfo {
public:
explicit CodeInfo(MemoryRegion region) : region_(region) {}
- StackMap<T> GetStackMapAt(size_t i) const {
+ explicit CodeInfo(const void* data) {
+ uint32_t size = reinterpret_cast<const uint32_t*>(data)[0];
+ region_ = MemoryRegion(const_cast<void*>(data), size);
+ }
+
+ StackMap GetStackMapAt(size_t i) const {
size_t size = StackMapSize();
- return StackMap<T>(GetStackMaps().Subregion(i * size, size));
+ return StackMap(GetStackMaps().Subregion(i * size, size));
+ }
+
+ uint32_t GetOverallSize() const {
+ return region_.Load<uint32_t>(kOverallSizeOffset);
+ }
+
+ void SetOverallSize(uint32_t size) {
+ region_.Store<uint32_t>(kOverallSizeOffset, size);
}
uint32_t GetStackMaskSize() const {
@@ -248,47 +261,48 @@
}
size_t StackMapSize() const {
- return StackMap<T>::kFixedSize + GetStackMaskSize();
+ return StackMap::kFixedSize + GetStackMaskSize();
}
- DexRegisterMap GetDexRegisterMapOf(StackMap<T> stack_map, uint32_t number_of_dex_registers) {
+ DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) {
uint32_t offset = stack_map.GetDexRegisterMapOffset();
return DexRegisterMap(region_.Subregion(offset,
DexRegisterMap::kFixedSize + number_of_dex_registers * DexRegisterMap::SingleEntrySize()));
}
- InlineInfo GetInlineInfoOf(StackMap<T> stack_map) {
+ InlineInfo GetInlineInfoOf(StackMap stack_map) {
uint32_t offset = stack_map.GetInlineDescriptorOffset();
uint8_t depth = region_.Load<uint8_t>(offset);
return InlineInfo(region_.Subregion(offset,
InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize()));
}
- StackMap<T> GetStackMapForDexPc(uint32_t dex_pc) {
+ StackMap GetStackMapForDexPc(uint32_t dex_pc) {
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
- StackMap<T> stack_map = GetStackMapAt(i);
+ StackMap stack_map = GetStackMapAt(i);
if (stack_map.GetDexPc() == dex_pc) {
return stack_map;
}
}
LOG(FATAL) << "Unreachable";
- return StackMap<T>(MemoryRegion());
+ return StackMap(MemoryRegion());
}
- StackMap<T> GetStackMapForNativePc(T native_pc) {
+ StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) {
// TODO: stack maps are sorted by native pc, we can do a binary search.
for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
- StackMap<T> stack_map = GetStackMapAt(i);
- if (stack_map.GetNativePc() == native_pc) {
+ StackMap stack_map = GetStackMapAt(i);
+ if (stack_map.GetNativePcOffset() == native_pc_offset) {
return stack_map;
}
}
LOG(FATAL) << "Unreachable";
- return StackMap<T>(MemoryRegion());
+ return StackMap(MemoryRegion());
}
private:
- static constexpr int kNumberOfStackMapsOffset = 0;
+ static constexpr int kOverallSizeOffset = 0;
+ static constexpr int kNumberOfStackMapsOffset = kOverallSizeOffset + sizeof(uint32_t);
static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t);
static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t);
@@ -299,7 +313,7 @@
}
MemoryRegion region_;
- template<typename U> friend class StackMapStream;
+ friend class StackMapStream;
};
} // namespace art
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index a5caa07..170cec6 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -24,7 +24,7 @@
#include "base/casts.h"
#include "base/mutex-inl.h"
#include "gc/heap.h"
-#include "jni_internal.h"
+#include "jni_env_ext.h"
namespace art {
@@ -45,6 +45,26 @@
}
}
+inline void Thread::AllowThreadSuspension() {
+ DCHECK_EQ(Thread::Current(), this);
+ if (UNLIKELY(TestAllFlags())) {
+ CheckSuspend();
+ }
+}
+
+inline void Thread::CheckSuspend() {
+ DCHECK_EQ(Thread::Current(), this);
+ for (;;) {
+ if (ReadFlag(kCheckpointRequest)) {
+ RunCheckpointFunction();
+ } else if (ReadFlag(kSuspendRequest)) {
+ FullSuspendCheck();
+ } else {
+ break;
+ }
+ }
+}
+
inline ThreadState Thread::SetState(ThreadState new_state) {
// Cannot use this code to change into Runnable as changing to Runnable should fail if
// old_state_and_flags.suspend_request is true.
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6b65f12..07657d1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -446,7 +446,7 @@
ScopedObjectAccess soa(self);
StackHandleScope<1> hs(self);
- Handle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName(soa)));
+ MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName(soa)));
if (peer_thread_name.Get() == nullptr) {
// The Thread constructor should have set the Thread.name to a
// non-null value. However, because we can run without code
@@ -507,40 +507,9 @@
+ 4 * KB;
if (read_stack_size <= min_stack) {
LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size
- << " bytes)";
+ << " bytes)";
}
- // TODO: move this into the Linux GetThreadStack implementation.
-#if !defined(__APPLE__)
- // If we're the main thread, check whether we were run with an unlimited stack. In that case,
- // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection
- // will be broken because we'll die long before we get close to 2GB.
- bool is_main_thread = (::art::GetTid() == getpid());
- if (is_main_thread) {
- rlimit stack_limit;
- if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) {
- PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed";
- }
- if (stack_limit.rlim_cur == RLIM_INFINITY) {
- // Find the default stack size for new threads...
- pthread_attr_t default_attributes;
- size_t default_stack_size;
- CHECK_PTHREAD_CALL(pthread_attr_init, (&default_attributes), "default stack size query");
- CHECK_PTHREAD_CALL(pthread_attr_getstacksize, (&default_attributes, &default_stack_size),
- "default stack size query");
- CHECK_PTHREAD_CALL(pthread_attr_destroy, (&default_attributes), "default stack size query");
-
- // ...and use that as our limit.
- size_t old_stack_size = read_stack_size;
- tlsPtr_.stack_size = default_stack_size;
- tlsPtr_.stack_begin += (old_stack_size - default_stack_size);
- VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")"
- << " to " << PrettySize(default_stack_size)
- << " with base " << reinterpret_cast<void*>(tlsPtr_.stack_begin);
- }
- }
-#endif
-
// Set stack_end_ to the bottom of the stack saving space of stack overflows
Runtime* runtime = Runtime::Current();
@@ -623,7 +592,7 @@
}
}
std::ostringstream ss;
- Runtime::Current()->GetThreadList()->DumpLocked(ss);
+ Runtime::Current()->GetThreadList()->Dump(ss);
LOG(FATAL) << ss.str();
}
@@ -1098,6 +1067,12 @@
(*tlsPtr_.name == kThreadNameDuringStartup);
}
+void Thread::AssertPendingException() const {
+ if (UNLIKELY(!IsExceptionPending())) {
+ LOG(FATAL) << "Pending exception expected.";
+ }
+}
+
void Thread::AssertNoPendingException() const {
if (UNLIKELY(IsExceptionPending())) {
ScopedObjectAccess soa(Thread::Current());
@@ -1133,6 +1108,21 @@
Thread* self = this;
DCHECK_EQ(self, Thread::Current());
+ if (tlsPtr_.jni_env != nullptr) {
+ // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
+ tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+ // Release locally held global references which releasing may require the mutator lock.
+ if (tlsPtr_.jpeer != nullptr) {
+ // If pthread_create fails we don't have a jni env here.
+ tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
+ tlsPtr_.jpeer = nullptr;
+ }
+ if (tlsPtr_.class_loader_override != nullptr) {
+ tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.class_loader_override);
+ tlsPtr_.class_loader_override = nullptr;
+ }
+ }
+
if (tlsPtr_.opeer != nullptr) {
ScopedObjectAccess soa(self);
// We may need to call user-supplied managed code, do this before final clean-up.
@@ -1160,22 +1150,16 @@
ObjectLock<mirror::Object> locker(self, h_obj);
locker.NotifyAll();
}
+ tlsPtr_.opeer = nullptr;
}
- // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
- if (tlsPtr_.jni_env != nullptr) {
- tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
- }
+ Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
}
Thread::~Thread() {
- if (tlsPtr_.jni_env != nullptr && tlsPtr_.jpeer != nullptr) {
- // If pthread_create fails we don't have a jni env here.
- tlsPtr_.jni_env->DeleteGlobalRef(tlsPtr_.jpeer);
- tlsPtr_.jpeer = nullptr;
- }
- tlsPtr_.opeer = nullptr;
-
+ CHECK(tlsPtr_.class_loader_override == nullptr);
+ CHECK(tlsPtr_.jpeer == nullptr);
+ CHECK(tlsPtr_.opeer == nullptr);
bool initialized = (tlsPtr_.jni_env != nullptr); // Did Thread::Init run?
if (initialized) {
delete tlsPtr_.jni_env;
@@ -1208,7 +1192,7 @@
delete tlsPtr_.stack_trace_sample;
free(tlsPtr_.nested_signal_state);
- Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
+ Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
TearDownAlternateSignalStack();
}
@@ -1236,7 +1220,7 @@
// Call the handler.
tlsPtr_.jni_env->CallVoidMethod(handler.get(),
- WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler_uncaughtException,
+ WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler_uncaughtException,
peer.get(), exception.get());
// If the handler threw, clear that exception too.
@@ -1260,7 +1244,7 @@
size_t Thread::NumHandleReferences() {
size_t count = 0;
- for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
+ for (HandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
count += cur->NumberOfReferences();
}
return count;
@@ -1269,7 +1253,7 @@
bool Thread::HandleScopeContains(jobject obj) const {
StackReference<mirror::Object>* hs_entry =
reinterpret_cast<StackReference<mirror::Object>*>(obj);
- for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
+ for (HandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) {
if (cur->Contains(hs_entry)) {
return true;
}
@@ -1302,6 +1286,7 @@
IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
IndirectRefKind kind = GetIndirectRefKind(ref);
mirror::Object* result;
+ bool expect_null = false;
// The "kinds" below are sorted by the frequency we expect to encounter them.
if (kind == kLocal) {
IndirectReferenceTable& locals = tlsPtr_.jni_env->locals;
@@ -1315,22 +1300,25 @@
result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr();
VerifyObject(result);
} else {
- result = kInvalidIndirectRefObject;
+ tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj);
+ expect_null = true;
+ result = nullptr;
}
} else if (kind == kGlobal) {
- JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
- result = vm->globals.SynchronizedGet(const_cast<Thread*>(this), &vm->globals_lock, ref);
+ result = tlsPtr_.jni_env->vm->DecodeGlobal(const_cast<Thread*>(this), ref);
} else {
DCHECK_EQ(kind, kWeakGlobal);
- result = Runtime::Current()->GetJavaVM()->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
- if (result == kClearedJniWeakGlobal) {
+ result = tlsPtr_.jni_env->vm->DecodeWeakGlobal(const_cast<Thread*>(this), ref);
+ if (Runtime::Current()->IsClearedJniWeakGlobal(result)) {
// This is a special case where it's okay to return nullptr.
- return nullptr;
+ expect_null = true;
+ result = nullptr;
}
}
- if (UNLIKELY(result == nullptr)) {
- JniAbortF(nullptr, "use of deleted %s %p", ToStr<IndirectRefKind>(kind).c_str(), obj);
+ if (UNLIKELY(!expect_null && result == nullptr)) {
+ tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p",
+ ToStr<IndirectRefKind>(kind).c_str(), obj);
}
return result;
}
@@ -1370,6 +1358,13 @@
}
}
+void Thread::SetClassLoaderOverride(jobject class_loader_override) {
+ if (tlsPtr_.class_loader_override != nullptr) {
+ GetJniEnv()->DeleteGlobalRef(tlsPtr_.class_loader_override);
+ }
+ tlsPtr_.class_loader_override = GetJniEnv()->NewGlobalRef(class_loader_override);
+}
+
class CountStackDepthVisitor : public StackVisitor {
public:
explicit CountStackDepthVisitor(Thread* thread)
@@ -1614,7 +1609,8 @@
ThrowNewException(throw_location, exception_class_descriptor, msg.c_str());
}
-void Thread::ThrowNewException(const ThrowLocation& throw_location, const char* exception_class_descriptor,
+void Thread::ThrowNewException(const ThrowLocation& throw_location,
+ const char* exception_class_descriptor,
const char* msg) {
// Callers should either clear or call ThrowNewWrappedException.
AssertNoPendingExceptionForNewException(msg);
@@ -1650,7 +1646,8 @@
return;
}
- if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(exception_class, true, true))) {
+ if (UNLIKELY(!runtime->GetClassLinker()->EnsureInitialized(soa.Self(), exception_class, true,
+ true))) {
DCHECK(IsExceptionPending());
return;
}
@@ -1842,12 +1839,24 @@
QUICK_ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccess)
QUICK_ENTRY_POINT_INFO(pInitializeType)
QUICK_ENTRY_POINT_INFO(pResolveString)
+ QUICK_ENTRY_POINT_INFO(pSet8Instance)
+ QUICK_ENTRY_POINT_INFO(pSet8Static)
+ QUICK_ENTRY_POINT_INFO(pSet16Instance)
+ QUICK_ENTRY_POINT_INFO(pSet16Static)
QUICK_ENTRY_POINT_INFO(pSet32Instance)
QUICK_ENTRY_POINT_INFO(pSet32Static)
QUICK_ENTRY_POINT_INFO(pSet64Instance)
QUICK_ENTRY_POINT_INFO(pSet64Static)
QUICK_ENTRY_POINT_INFO(pSetObjInstance)
QUICK_ENTRY_POINT_INFO(pSetObjStatic)
+ QUICK_ENTRY_POINT_INFO(pGetByteInstance)
+ QUICK_ENTRY_POINT_INFO(pGetBooleanInstance)
+ QUICK_ENTRY_POINT_INFO(pGetByteStatic)
+ QUICK_ENTRY_POINT_INFO(pGetBooleanStatic)
+ QUICK_ENTRY_POINT_INFO(pGetShortInstance)
+ QUICK_ENTRY_POINT_INFO(pGetCharInstance)
+ QUICK_ENTRY_POINT_INFO(pGetShortStatic)
+ QUICK_ENTRY_POINT_INFO(pGetCharStatic)
QUICK_ENTRY_POINT_INFO(pGet32Instance)
QUICK_ENTRY_POINT_INFO(pGet32Static)
QUICK_ENTRY_POINT_INFO(pGet64Instance)
@@ -2070,48 +2079,72 @@
// Process register map (which native and runtime methods don't have)
if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
- const uint8_t* native_gc_map = m->GetNativeGcMap();
- CHECK(native_gc_map != nullptr) << PrettyMethod(m);
- const DexFile::CodeItem* code_item = m->GetCodeItem();
- DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be nullptr or how would we compile its instructions?
- NativePcOffsetToReferenceMap map(native_gc_map);
- size_t num_regs = std::min(map.RegWidth() * 8,
- static_cast<size_t>(code_item->registers_size_));
- if (num_regs > 0) {
+ if (m->IsOptimized()) {
Runtime* runtime = Runtime::Current();
const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
- const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
- DCHECK(reg_bitmap != nullptr);
- const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
- const VmapTable vmap_table(m->GetVmapTable(code_pointer));
- QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
- // For all dex registers in the bitmap
- StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
- DCHECK(cur_quick_frame != nullptr);
- for (size_t reg = 0; reg < num_regs; ++reg) {
- // Does this register hold a reference?
- if (TestBitmap(reg, reg_bitmap)) {
- uint32_t vmap_offset;
- if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
- int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
- kReferenceVReg);
- // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
- mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
- if (*ref_addr != nullptr) {
- visitor_(ref_addr, reg, this);
+ StackMap map = m->GetStackMap(native_pc_offset);
+ MemoryRegion mask = map.GetStackMask();
+ for (size_t i = 0; i < mask.size_in_bits(); ++i) {
+ if (mask.LoadBit(i)) {
+ StackReference<mirror::Object>* ref_addr =
+ reinterpret_cast<StackReference<mirror::Object>*>(cur_quick_frame) + i;
+ mirror::Object* ref = ref_addr->AsMirrorPtr();
+ if (ref != nullptr) {
+ mirror::Object* new_ref = ref;
+ visitor_(&new_ref, -1, this);
+ if (ref != new_ref) {
+ ref_addr->Assign(new_ref);
}
- } else {
- StackReference<mirror::Object>* ref_addr =
- reinterpret_cast<StackReference<mirror::Object>*>(
- GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
- frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
- mirror::Object* ref = ref_addr->AsMirrorPtr();
- if (ref != nullptr) {
- mirror::Object* new_ref = ref;
- visitor_(&new_ref, reg, this);
- if (ref != new_ref) {
- ref_addr->Assign(new_ref);
+ }
+ }
+ }
+ } else {
+ const uint8_t* native_gc_map = m->GetNativeGcMap();
+ CHECK(native_gc_map != nullptr) << PrettyMethod(m);
+ const DexFile::CodeItem* code_item = m->GetCodeItem();
+ // Can't be nullptr or how would we compile its instructions?
+ DCHECK(code_item != nullptr) << PrettyMethod(m);
+ NativePcOffsetToReferenceMap map(native_gc_map);
+ size_t num_regs = std::min(map.RegWidth() * 8,
+ static_cast<size_t>(code_item->registers_size_));
+ if (num_regs > 0) {
+ Runtime* runtime = Runtime::Current();
+ const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m);
+ uintptr_t native_pc_offset = m->NativePcOffset(GetCurrentQuickFramePc(), entry_point);
+ const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
+ DCHECK(reg_bitmap != nullptr);
+ const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point);
+ const VmapTable vmap_table(m->GetVmapTable(code_pointer));
+ QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+ // For all dex registers in the bitmap
+ StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
+ DCHECK(cur_quick_frame != nullptr);
+ for (size_t reg = 0; reg < num_regs; ++reg) {
+ // Does this register hold a reference?
+ if (TestBitmap(reg, reg_bitmap)) {
+ uint32_t vmap_offset;
+ if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
+ int vmap_reg = vmap_table.ComputeRegister(frame_info.CoreSpillMask(), vmap_offset,
+ kReferenceVReg);
+ // This is sound as spilled GPRs will be word sized (ie 32 or 64bit).
+ mirror::Object** ref_addr =
+ reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg));
+ if (*ref_addr != nullptr) {
+ visitor_(ref_addr, reg, this);
+ }
+ } else {
+ StackReference<mirror::Object>* ref_addr =
+ reinterpret_cast<StackReference<mirror::Object>*>(
+ GetVRegAddr(cur_quick_frame, code_item, frame_info.CoreSpillMask(),
+ frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), reg));
+ mirror::Object* ref = ref_addr->AsMirrorPtr();
+ if (ref != nullptr) {
+ mirror::Object* new_ref = ref;
+ visitor_(&new_ref, reg, this);
+ if (ref != new_ref) {
+ ref_addr->Assign(new_ref);
+ }
}
}
}
@@ -2144,11 +2177,6 @@
const uint32_t tid_;
};
-void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) {
- VerifyObject(class_loader_override);
- tlsPtr_.class_loader_override = class_loader_override;
-}
-
void Thread::VisitRoots(RootCallback* visitor, void* arg) {
uint32_t thread_id = GetThreadId();
if (tlsPtr_.opeer != nullptr) {
@@ -2158,10 +2186,6 @@
visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
}
tlsPtr_.throw_location.VisitRoots(visitor, arg);
- if (tlsPtr_.class_loader_override != nullptr) {
- visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.class_loader_override), arg, thread_id,
- kRootNativeStack);
- }
if (tlsPtr_.monitor_enter_object != nullptr) {
visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 0c64f1f..6c427b8 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -146,6 +146,12 @@
static Thread* Current();
+ // On a runnable thread, check for pending thread suspension request and handle if pending.
+ void AllowThreadSuspension() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Process pending thread suspension request and handle if pending.
+ void CheckSuspend() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts,
mirror::Object* thread_peer)
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
@@ -322,6 +328,7 @@
return tlsPtr_.exception;
}
+ void AssertPendingException() const;
void AssertNoPendingException() const;
void AssertNoPendingExceptionForNewException(const char* msg) const;
@@ -457,12 +464,11 @@
tlsPtr_.wait_next = next;
}
- mirror::ClassLoader* GetClassLoaderOverride() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ jobject GetClassLoaderOverride() {
return tlsPtr_.class_loader_override;
}
- void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetClassLoaderOverride(jobject class_loader_override);
// Create the internal representation of a stack trace, that is more time
// and space efficient to compute than the StackTraceElement[].
@@ -673,7 +679,7 @@
// Number of references allocated in handle scopes & JNI shadow frames on this thread.
size_t NumStackReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return NumHandleReferences() + NumJniShadowFrameReferences();
- };
+ }
// Is the given obj in this thread's stack indirect reference table?
bool HandleScopeContains(jobject obj) const;
@@ -1029,7 +1035,11 @@
deoptimization_shadow_frame(nullptr), shadow_frame_under_construction(nullptr), name(nullptr),
pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
- thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr) {
+ thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
+ nested_signal_state(nullptr) {
+ for (size_t i = 0; i < kLockLevelCount; ++i) {
+ held_mutexes[i] = nullptr;
+ }
}
// The biased card table, see CardTable for details.
@@ -1085,7 +1095,7 @@
// Needed to get the right ClassLoader in JNI_OnLoad, but also
// useful for testing.
- mirror::ClassLoader* class_loader_override;
+ jobject class_loader_override;
// Thread local, lazily allocated, long jump context. Used to deliver exceptions.
Context* long_jump_context;
@@ -1162,7 +1172,6 @@
friend class Runtime; // For CreatePeer.
friend class QuickExceptionHandler; // For dumping the stack.
friend class ScopedThreadStateChange;
- friend class SignalCatcher; // For SetStateUnsafe.
friend class StubTest; // For accessing entrypoints.
friend class ThreadList; // For ~Thread and Destroy.
@@ -1171,6 +1180,23 @@
DISALLOW_COPY_AND_ASSIGN(Thread);
};
+class ScopedAssertNoThreadSuspension {
+ public:
+ ScopedAssertNoThreadSuspension(Thread* self, const char* cause)
+ : self_(self), old_cause_(self->StartAssertNoThreadSuspension(cause)) {
+ }
+ ~ScopedAssertNoThreadSuspension() {
+ self_->EndAssertNoThreadSuspension(old_cause_);
+ }
+ Thread* Self() {
+ return self_;
+ }
+
+ private:
+ Thread* const self_;
+ const char* old_cause_;
+};
+
std::ostream& operator<<(std::ostream& os, const Thread& thread);
std::ostream& operator<<(std::ostream& os, const ThreadState& state);
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index ceed254..ec5b775 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -88,10 +88,7 @@
}
void ThreadList::DumpForSigQuit(std::ostream& os) {
- {
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- DumpLocked(os);
- }
+ Dump(os);
DumpUnattachedThreads(os);
}
@@ -133,12 +130,54 @@
closedir(d);
}
-void ThreadList::DumpLocked(std::ostream& os) {
- os << "DALVIK THREADS (" << list_.size() << "):\n";
- for (const auto& thread : list_) {
- thread->Dump(os);
- os << "\n";
+// A closure used by Thread::Dump.
+class DumpCheckpoint FINAL : public Closure {
+ public:
+ explicit DumpCheckpoint(std::ostream* os) : os_(os), barrier_(0) {}
+
+ void Run(Thread* thread) OVERRIDE {
+ // Note thread and self may not be equal if thread was already suspended at the point of the
+ // request.
+ Thread* self = Thread::Current();
+ std::ostringstream local_os;
+ {
+ ScopedObjectAccess soa(self);
+ thread->Dump(local_os);
+ }
+ local_os << "\n";
+ {
+ // Use the logging lock to ensure serialization when writing to the common ostream.
+ MutexLock mu(self, *Locks::logging_lock_);
+ *os_ << local_os.str();
+ }
+ barrier_.Pass(self);
}
+
+ void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
+ Thread* self = Thread::Current();
+ ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
+ const uint32_t kWaitTimeoutMs = 10000;
+ bool timed_out = barrier_.Increment(self, threads_running_checkpoint, kWaitTimeoutMs);
+ if (timed_out) {
+ LOG(kIsDebugBuild ? FATAL : ERROR) << "Unexpected time out during dump checkpoint.";
+ }
+ }
+
+ private:
+ // The common stream that will accumulate all the dumps.
+ std::ostream* const os_;
+ // The barrier to be passed through and for the requestor to wait upon.
+ Barrier barrier_;
+};
+
+void ThreadList::Dump(std::ostream& os) {
+ {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ os << "DALVIK THREADS (" << list_.size() << "):\n";
+ }
+ DumpCheckpoint checkpoint(&os);
+ size_t threads_running_checkpoint = RunCheckpoint(&checkpoint);
+ checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
}
void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread* ignore2) {
@@ -155,12 +194,12 @@
#if HAVE_TIMED_RWLOCK
// Attempt to rectify locks so that we dump thread list with required locks before exiting.
-static void UnsafeLogFatalForThreadSuspendAllTimeout() NO_THREAD_SAFETY_ANALYSIS __attribute__((noreturn));
+static void UnsafeLogFatalForThreadSuspendAllTimeout() __attribute__((noreturn));
static void UnsafeLogFatalForThreadSuspendAllTimeout() {
Runtime* runtime = Runtime::Current();
std::ostringstream ss;
ss << "Thread suspend timeout\n";
- runtime->GetThreadList()->DumpLocked(ss);
+ runtime->GetThreadList()->Dump(ss);
LOG(FATAL) << ss.str();
exit(0);
}
@@ -266,12 +305,10 @@
// threads. Returns the number of successful requests.
size_t ThreadList::RunCheckpointOnRunnableThreads(Closure* checkpoint_function) {
Thread* self = Thread::Current();
- if (kIsDebugBuild) {
- Locks::mutator_lock_->AssertNotExclusiveHeld(self);
- Locks::thread_list_lock_->AssertNotHeld(self);
- Locks::thread_suspend_count_lock_->AssertNotHeld(self);
- CHECK_NE(self->GetState(), kRunnable);
- }
+ Locks::mutator_lock_->AssertNotExclusiveHeld(self);
+ Locks::thread_list_lock_->AssertNotHeld(self);
+ Locks::thread_suspend_count_lock_->AssertNotHeld(self);
+ CHECK_NE(self->GetState(), kRunnable);
size_t count = 0;
{
@@ -823,6 +860,8 @@
void ThreadList::Unregister(Thread* self) {
DCHECK_EQ(self, Thread::Current());
+ CHECK_NE(self->GetState(), kRunnable);
+ Locks::mutator_lock_->AssertNotHeld(self);
VLOG(threads) << "ThreadList::Unregister() " << *self;
@@ -836,21 +875,26 @@
// thread_suspend_count_lock_ so that the unregistering thread cannot be suspended.
// Note: deliberately not using MutexLock that could hold a stale self pointer.
Locks::thread_list_lock_->ExclusiveLock(self);
+ bool removed = true;
if (!Contains(self)) {
std::ostringstream os;
DumpNativeStack(os, GetTid(), " native: ", nullptr);
LOG(ERROR) << "Request to unregister unattached thread\n" << os.str();
- self = nullptr;
} else {
- // Note: we don't take the thread_suspend_count_lock_ here as to be suspending a thread other
- // than yourself you need to hold the thread_list_lock_ (see Thread::ModifySuspendCount).
+ Locks::thread_suspend_count_lock_->ExclusiveLock(self);
if (!self->IsSuspended()) {
list_.remove(self);
- delete self;
- self = nullptr;
+ } else {
+ // We failed to remove the thread due to a suspend request, loop and try again.
+ removed = false;
}
+ Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
}
Locks::thread_list_lock_->ExclusiveUnlock(self);
+ if (removed) {
+ delete self;
+ self = nullptr;
+ }
}
// Release the thread ID after the thread is finished and deleted to avoid cases where we can
// temporarily have multiple threads with the same thread id. When this occurs, it causes
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index bb4f775..9f47f9f 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -39,11 +39,11 @@
~ThreadList();
void DumpForSigQuit(std::ostream& os)
- LOCKS_EXCLUDED(Locks::thread_list_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void DumpLocked(std::ostream& os) // For thread suspend timeout dumps.
- EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ LOCKS_EXCLUDED(Locks::thread_list_lock_);
+ // For thread suspend timeout dumps.
+ void Dump(std::ostream& os)
+ LOCKS_EXCLUDED(Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
pid_t GetLockOwner(); // For SignalCatcher.
// Thread suspension support.
@@ -93,7 +93,8 @@
Locks::thread_suspend_count_lock_);
size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function)
- LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::thread_suspend_count_lock_);
+ LOCKS_EXCLUDED(Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
// Suspends all threads
void SuspendAllForDebugger()
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4bb388f..027f62d 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -560,7 +560,7 @@
mirror::ArtMethod* method, uint32_t new_dex_pc) {
// We're not recorded to listen to this kind of event, so complain.
LOG(ERROR) << "Unexpected dex PC event in tracing " << PrettyMethod(method) << " " << new_dex_pc;
-};
+}
void Trace::FieldRead(Thread* /*thread*/, mirror::Object* this_object,
mirror::ArtMethod* method, uint32_t dex_pc, mirror::ArtField* field)
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 50f1eca..b496f25 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -57,6 +57,40 @@
}
}
+void Transaction::RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset,
+ uint8_t value, bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.LogBooleanValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset,
+ int8_t value, bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.LogByteValue(field_offset, value, is_volatile);
+}
+
+void Transaction::RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset,
+ uint16_t value, bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.LogCharValue(field_offset, value, is_volatile);
+}
+
+
+void Transaction::RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset,
+ int16_t value, bool is_volatile) {
+ DCHECK(obj != nullptr);
+ MutexLock mu(Thread::Current(), log_lock_);
+ ObjectLog& object_log = object_logs_[obj];
+ object_log.LogShortValue(field_offset, value, is_volatile);
+}
+
+
void Transaction::RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
bool is_volatile) {
DCHECK(obj != nullptr);
@@ -223,35 +257,42 @@
}
}
+void Transaction::ObjectLog::LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile) {
+ LogValue(ObjectLog::kBoolean, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogByteValue(MemberOffset offset, int8_t value, bool is_volatile) {
+ LogValue(ObjectLog::kByte, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogCharValue(MemberOffset offset, uint16_t value, bool is_volatile) {
+ LogValue(ObjectLog::kChar, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogShortValue(MemberOffset offset, int16_t value, bool is_volatile) {
+ LogValue(ObjectLog::kShort, offset, value, is_volatile);
+}
+
void Transaction::ObjectLog::Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile) {
- auto it = field_values_.find(offset.Uint32Value());
- if (it == field_values_.end()) {
- ObjectLog::FieldValue field_value;
- field_value.value = value;
- field_value.is_volatile = is_volatile;
- field_value.kind = ObjectLog::k32Bits;
- field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
- }
+ LogValue(ObjectLog::k32Bits, offset, value, is_volatile);
}
void Transaction::ObjectLog::Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile) {
+ LogValue(ObjectLog::k64Bits, offset, value, is_volatile);
+}
+
+void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
+ LogValue(ObjectLog::kReference, offset, reinterpret_cast<uintptr_t>(obj), is_volatile);
+}
+
+void Transaction::ObjectLog::LogValue(ObjectLog::FieldValueKind kind,
+ MemberOffset offset, uint64_t value, bool is_volatile) {
auto it = field_values_.find(offset.Uint32Value());
if (it == field_values_.end()) {
ObjectLog::FieldValue field_value;
field_value.value = value;
field_value.is_volatile = is_volatile;
- field_value.kind = ObjectLog::k64Bits;
- field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
- }
-}
-
-void Transaction::ObjectLog::LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile) {
- auto it = field_values_.find(offset.Uint32Value());
- if (it == field_values_.end()) {
- ObjectLog::FieldValue field_value;
- field_value.value = reinterpret_cast<uintptr_t>(obj);
- field_value.is_volatile = is_volatile;
- field_value.kind = ObjectLog::kReference;
+ field_value.kind = kind;
field_values_.insert(std::make_pair(offset.Uint32Value(), field_value));
}
}
@@ -281,6 +322,42 @@
// we'd need to disable the check.
constexpr bool kCheckTransaction = true;
switch (field_value.kind) {
+ case kBoolean:
+ if (UNLIKELY(field_value.is_volatile)) {
+ obj->SetFieldBooleanVolatile<false, kCheckTransaction>(field_offset,
+ static_cast<bool>(field_value.value));
+ } else {
+ obj->SetFieldBoolean<false, kCheckTransaction>(field_offset,
+ static_cast<bool>(field_value.value));
+ }
+ break;
+ case kByte:
+ if (UNLIKELY(field_value.is_volatile)) {
+ obj->SetFieldByteVolatile<false, kCheckTransaction>(field_offset,
+ static_cast<int8_t>(field_value.value));
+ } else {
+ obj->SetFieldByte<false, kCheckTransaction>(field_offset,
+ static_cast<int8_t>(field_value.value));
+ }
+ break;
+ case kChar:
+ if (UNLIKELY(field_value.is_volatile)) {
+ obj->SetFieldCharVolatile<false, kCheckTransaction>(field_offset,
+ static_cast<uint16_t>(field_value.value));
+ } else {
+ obj->SetFieldChar<false, kCheckTransaction>(field_offset,
+ static_cast<uint16_t>(field_value.value));
+ }
+ break;
+ case kShort:
+ if (UNLIKELY(field_value.is_volatile)) {
+ obj->SetFieldShortVolatile<false, kCheckTransaction>(field_offset,
+ static_cast<int16_t>(field_value.value));
+ } else {
+ obj->SetFieldShort<false, kCheckTransaction>(field_offset,
+ static_cast<int16_t>(field_value.value));
+ }
+ break;
case k32Bits:
if (UNLIKELY(field_value.is_volatile)) {
obj->SetField32Volatile<false, kCheckTransaction>(field_offset,
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 6625390..21d3c98 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -41,6 +41,18 @@
~Transaction();
// Record object field changes.
+ void RecordWriteFieldBoolean(mirror::Object* obj, MemberOffset field_offset, uint8_t value,
+ bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWriteFieldByte(mirror::Object* obj, MemberOffset field_offset, int8_t value,
+ bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWriteFieldChar(mirror::Object* obj, MemberOffset field_offset, uint16_t value,
+ bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
+ void RecordWriteFieldShort(mirror::Object* obj, MemberOffset field_offset, int16_t value,
+ bool is_volatile)
+ LOCKS_EXCLUDED(log_lock_);
void RecordWriteField32(mirror::Object* obj, MemberOffset field_offset, uint32_t value,
bool is_volatile)
LOCKS_EXCLUDED(log_lock_);
@@ -82,6 +94,10 @@
private:
class ObjectLog {
public:
+ void LogBooleanValue(MemberOffset offset, uint8_t value, bool is_volatile);
+ void LogByteValue(MemberOffset offset, int8_t value, bool is_volatile);
+ void LogCharValue(MemberOffset offset, uint16_t value, bool is_volatile);
+ void LogShortValue(MemberOffset offset, int16_t value, bool is_volatile);
void Log32BitsValue(MemberOffset offset, uint32_t value, bool is_volatile);
void Log64BitsValue(MemberOffset offset, uint64_t value, bool is_volatile);
void LogReferenceValue(MemberOffset offset, mirror::Object* obj, bool is_volatile);
@@ -95,6 +111,10 @@
private:
enum FieldValueKind {
+ kBoolean,
+ kByte,
+ kChar,
+ kShort,
k32Bits,
k64Bits,
kReference
@@ -106,6 +126,7 @@
bool is_volatile;
};
+ void LogValue(FieldValueKind kind, MemberOffset offset, uint64_t value, bool is_volatile);
void UndoFieldWrite(mirror::Object* obj, MemberOffset field_offset,
const FieldValue& field_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc
index 691aec4..a14889c 100644
--- a/runtime/transaction_test.cc
+++ b/runtime/transaction_test.cc
@@ -89,7 +89,7 @@
Handle<mirror::Array> h_obj(
hs.NewHandle(
mirror::Array::Alloc<true>(soa.Self(), h_klass.Get(), kArraySize,
- h_klass->GetComponentSize(),
+ h_klass->GetComponentSizeShift(),
Runtime::Current()->GetHeap()->GetCurrentAllocator())));
ASSERT_TRUE(h_obj.Get() != nullptr);
ASSERT_EQ(h_obj->GetClass(), h_klass.Get());
@@ -110,7 +110,7 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->EnsureInitialized(h_klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
// Lookup fields.
@@ -205,7 +205,7 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->EnsureInitialized(h_klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
// Allocate an InstanceFieldTest object.
@@ -305,7 +305,7 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->EnsureInitialized(h_klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
ASSERT_TRUE(h_klass->IsInitialized());
// Lookup fields.
@@ -419,12 +419,12 @@
Handle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;", class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(h_klass);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
- class_linker_->EnsureInitialized(h_klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
ASSERT_FALSE(soa.Self()->IsExceptionPending());
}
@@ -440,12 +440,12 @@
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;",
class_loader)));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(h_klass);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
- class_linker_->EnsureInitialized(h_klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
ASSERT_FALSE(soa.Self()->IsExceptionPending());
}
@@ -460,33 +460,33 @@
// Load and verify java.lang.ExceptionInInitializerError and java.lang.InternalError which will
// be thrown by class initialization due to native call.
- Handle<mirror::Class> h_klass(
+ MutableHandle<mirror::Class> h_klass(
hs.NewHandle(class_linker_->FindSystemClass(soa.Self(),
"Ljava/lang/ExceptionInInitializerError;")));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(h_klass);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/InternalError;"));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(h_klass);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
// Load and verify Transaction$NativeSupport used in class initialization.
h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$NativeSupport;",
class_loader));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(h_klass);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$BlacklistedClass;",
class_loader));
ASSERT_TRUE(h_klass.Get() != nullptr);
- class_linker_->VerifyClass(h_klass);
+ class_linker_->VerifyClass(soa.Self(), h_klass);
ASSERT_TRUE(h_klass->IsVerified());
Transaction transaction;
Runtime::Current()->EnterTransactionMode(&transaction);
- class_linker_->EnsureInitialized(h_klass, true, true);
+ class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true);
Runtime::Current()->ExitTransactionMode();
ASSERT_TRUE(soa.Self()->IsExceptionPending());
}
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 3765759..0496d97 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -66,8 +66,9 @@
uint64_t owner;
CHECK_PTHREAD_CALL(pthread_threadid_np, (NULL, &owner), __FUNCTION__); // Requires Mac OS 10.6
return owner;
+#elif defined(__BIONIC__)
+ return gettid();
#else
- // Neither bionic nor glibc exposes gettid(2).
return syscall(__NR_gettid);
#endif
}
@@ -107,6 +108,31 @@
CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__);
+
+#if defined(__GLIBC__)
+ // If we're the main thread, check whether we were run with an unlimited stack. In that case,
+ // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection
+ // will be broken because we'll die long before we get close to 2GB.
+ bool is_main_thread = (::art::GetTid() == getpid());
+ if (is_main_thread) {
+ rlimit stack_limit;
+ if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) {
+ PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed";
+ }
+ if (stack_limit.rlim_cur == RLIM_INFINITY) {
+ size_t old_stack_size = *stack_size;
+
+ // Use the kernel default limit as our size, and adjust the base to match.
+ *stack_size = 8 * MB;
+ *stack_base = reinterpret_cast<uint8_t*>(*stack_base) + (old_stack_size - *stack_size);
+
+ VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")"
+ << " to " << PrettySize(*stack_size)
+ << " with base " << *stack_base;
+ }
+ }
+#endif
+
#endif
}
@@ -448,6 +474,35 @@
return result;
}
+std::string PrettyJavaAccessFlags(uint32_t access_flags) {
+ std::string result;
+ if ((access_flags & kAccPublic) != 0) {
+ result += "public ";
+ }
+ if ((access_flags & kAccProtected) != 0) {
+ result += "protected ";
+ }
+ if ((access_flags & kAccPrivate) != 0) {
+ result += "private ";
+ }
+ if ((access_flags & kAccFinal) != 0) {
+ result += "final ";
+ }
+ if ((access_flags & kAccStatic) != 0) {
+ result += "static ";
+ }
+ if ((access_flags & kAccTransient) != 0) {
+ result += "transient ";
+ }
+ if ((access_flags & kAccVolatile) != 0) {
+ result += "volatile ";
+ }
+ if ((access_flags & kAccSynchronized) != 0) {
+ result += "synchronized ";
+ }
+ return result;
+}
+
std::string PrettySize(int64_t byte_count) {
// The byte thresholds at which we display amounts. A byte count is displayed
// in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
@@ -793,7 +848,8 @@
}
enum ClassNameType { kName, kDescriptor };
-static bool IsValidClassName(const char* s, ClassNameType type, char separator) {
+template<ClassNameType kType, char kSeparator>
+static bool IsValidClassName(const char* s) {
int arrayCount = 0;
while (*s == '[') {
arrayCount++;
@@ -805,7 +861,8 @@
return false;
}
- if (arrayCount != 0) {
+ ClassNameType type = kType;
+ if (type != kDescriptor && arrayCount != 0) {
/*
* If we're looking at an array of some sort, then it doesn't
* matter if what is being asked for is a class name; the
@@ -873,7 +930,7 @@
return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0');
case '/':
case '.':
- if (c != separator) {
+ if (c != kSeparator) {
// The wrong separator character.
return false;
}
@@ -895,15 +952,15 @@
}
bool IsValidBinaryClassName(const char* s) {
- return IsValidClassName(s, kName, '.');
+ return IsValidClassName<kName, '.'>(s);
}
bool IsValidJniClassName(const char* s) {
- return IsValidClassName(s, kName, '/');
+ return IsValidClassName<kName, '/'>(s);
}
bool IsValidDescriptor(const char* s) {
- return IsValidClassName(s, kDescriptor, '/');
+ return IsValidClassName<kDescriptor, '/'>(s);
}
void Split(const std::string& s, char separator, std::vector<std::string>& result) {
@@ -1002,7 +1059,7 @@
} else {
s = thread_name + len - 15;
}
-#if defined(HAVE_ANDROID_PTHREAD_SETNAME_NP)
+#if defined(__BIONIC__)
// pthread_setname_np fails rather than truncating long strings.
char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
strncpy(buf, s, sizeof(buf)-1);
@@ -1064,10 +1121,6 @@
void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
mirror::ArtMethod* current_method) {
- // We may be called from contexts where current_method is not null, so we must assert this.
- if (current_method != nullptr) {
- Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
- }
#ifdef __linux__
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
if (!backtrace->Unwind(0)) {
@@ -1099,7 +1152,9 @@
if (it->func_offset != 0) {
os << "+" << it->func_offset;
}
- } else if (current_method != nullptr && current_method->IsWithinQuickCode(it->pc)) {
+ } else if (current_method != nullptr &&
+ Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
+ current_method->IsWithinQuickCode(it->pc)) {
const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode();
os << JniLongName(current_method) << "+"
<< (it->pc - reinterpret_cast<uintptr_t>(start_of_code));
@@ -1376,6 +1431,21 @@
return true;
}
+void EncodeUnsignedLeb128(uint32_t data, std::vector<uint8_t>* dst) {
+ Leb128Encoder(dst).PushBackUnsigned(data);
+}
+
+void EncodeSignedLeb128(int32_t data, std::vector<uint8_t>* dst) {
+ Leb128Encoder(dst).PushBackSigned(data);
+}
+
+void PushWord(std::vector<uint8_t>* buf, int data) {
+ buf->push_back(data & 0xff);
+ buf->push_back((data >> 8) & 0xff);
+ buf->push_back((data >> 16) & 0xff);
+ buf->push_back((data >> 24) & 0xff);
+}
+
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 5bdbba8..3f2d829 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -30,10 +30,6 @@
#include "instruction_set.h"
#include "primitive.h"
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
namespace art {
class DexFile;
@@ -315,6 +311,10 @@
std::string PrettyClassAndClassLoader(mirror::Class* c)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+// Returns a human-readable version of the Java part of the access flags, e.g., "private static "
+// (note the trailing whitespace).
+std::string PrettyJavaAccessFlags(uint32_t access_flags);
+
// Returns a human-readable size string such as "1MB".
std::string PrettySize(int64_t size_in_bytes);
@@ -503,6 +503,11 @@
}
};
+void PushWord(std::vector<uint8_t>* buf, int32_t data);
+
+void EncodeUnsignedLeb128(uint32_t data, std::vector<uint8_t>* buf);
+void EncodeSignedLeb128(int32_t data, std::vector<uint8_t>* buf);
+
// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
struct FreeDelete {
// NOTE: Deleting a const object is valid but free() takes a non-const pointer.
diff --git a/runtime/verifier/instruction_flags.cc b/runtime/verifier/instruction_flags.cc
index f76c226..ca3c687 100644
--- a/runtime/verifier/instruction_flags.cc
+++ b/runtime/verifier/instruction_flags.cc
@@ -22,13 +22,14 @@
namespace verifier {
std::string InstructionFlags::ToString() const {
- char encoding[7];
+ char encoding[8];
if (!IsOpcode()) {
- strncpy(encoding, "XXXXXX", sizeof(encoding));
+ strncpy(encoding, "XXXXXXX", sizeof(encoding));
} else {
- strncpy(encoding, "------", sizeof(encoding));
+ strncpy(encoding, "-------", sizeof(encoding));
if (IsVisited()) encoding[kVisited] = 'V';
if (IsChanged()) encoding[kChanged] = 'C';
+ if (IsOpcode()) encoding[kOpcode] = 'O';
if (IsInTry()) encoding[kInTry] = 'T';
if (IsBranchTarget()) encoding[kBranchTarget] = 'B';
if (IsCompileTimeInfoPoint()) encoding[kCompileTimeInfoPoint] = 'G';
diff --git a/runtime/verifier/instruction_flags.h b/runtime/verifier/instruction_flags.h
index f8abca0..36a6e55 100644
--- a/runtime/verifier/instruction_flags.h
+++ b/runtime/verifier/instruction_flags.h
@@ -20,24 +20,23 @@
#include <stdint.h>
#include <string>
-#include "base/logging.h"
+#include "base/macros.h"
namespace art {
namespace verifier {
-class InstructionFlags {
+class InstructionFlags FINAL {
public:
- InstructionFlags() : length_(0), flags_(0) {}
+ InstructionFlags() : flags_(0) {}
- void SetLengthInCodeUnits(size_t length) {
- DCHECK_LT(length, 65536u);
- length_ = length;
+ void SetIsOpcode() {
+ flags_ |= 1 << kOpcode;
}
- size_t GetLengthInCodeUnits() {
- return length_;
+ void ClearIsOpcode() {
+ flags_ &= ~(1 << kOpcode);
}
bool IsOpcode() const {
- return length_ != 0;
+ return (flags_ & (1 << kOpcode)) != 0;
}
void SetInTry() {
@@ -117,21 +116,22 @@
// Register type information flowing into the instruction changed and so the instruction must be
// reprocessed.
kChanged = 1,
+ // The item at this location is an opcode.
+ kOpcode = 2,
// Instruction is contained within a try region.
- kInTry = 2,
+ kInTry = 3,
// Instruction is the target of a branch (ie the start of a basic block).
- kBranchTarget = 3,
+ kBranchTarget = 4,
// Location of interest to the compiler for GC maps and verifier based method sharpening.
- kCompileTimeInfoPoint = 4,
+ kCompileTimeInfoPoint = 5,
// A return instruction.
- kReturn = 5,
+ kReturn = 6,
};
-
- // Size of instruction in code units.
- uint16_t length_;
uint8_t flags_;
};
+COMPILE_ASSERT(sizeof(InstructionFlags) == sizeof(uint8_t), err);
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
index d4fe106..2d9fd53 100644
--- a/runtime/verifier/method_verifier-inl.h
+++ b/runtime/verifier/method_verifier-inl.h
@@ -39,11 +39,11 @@
}
inline mirror::ClassLoader* MethodVerifier::GetClassLoader() {
- return class_loader_->Get();
+ return class_loader_.Get();
}
inline mirror::DexCache* MethodVerifier::GetDexCache() {
- return dex_cache_->Get();
+ return dex_cache_.Get();
}
inline MethodReference MethodVerifier::GetMethodReference() const {
@@ -66,9 +66,9 @@
return !failure_messages_.empty();
}
-inline RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
+inline const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
DCHECK(!HasFailures());
- RegType& result = ResolveClassAndCheckAccess(class_idx);
+ const RegType& result = ResolveClassAndCheckAccess(class_idx);
DCHECK(!HasFailures());
return result;
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1720e18..9747b4e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -38,6 +38,7 @@
#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "reg_type-inl.h"
#include "register_line-inl.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
@@ -87,7 +88,8 @@
}
}
-MethodVerifier::FailureKind MethodVerifier::VerifyClass(mirror::Class* klass,
+MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
+ mirror::Class* klass,
bool allow_soft_failures,
std::string* error) {
if (klass->IsVerified()) {
@@ -117,13 +119,14 @@
}
return kHardFailure;
}
- StackHandleScope<2> hs(Thread::Current());
+ StackHandleScope<2> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(klass->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(klass->GetClassLoader()));
- return VerifyClass(&dex_file, dex_cache, class_loader, class_def, allow_soft_failures, error);
+ return VerifyClass(self, &dex_file, dex_cache, class_loader, class_def, allow_soft_failures, error);
}
-MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file,
+MethodVerifier::FailureKind MethodVerifier::VerifyClass(Thread* self,
+ const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
@@ -144,6 +147,7 @@
ClassLinker* linker = Runtime::Current()->GetClassLinker();
int64_t previous_direct_method_idx = -1;
while (it.HasNextDirectMethod()) {
+ self->AllowThreadSuspension();
uint32_t method_idx = it.GetMemberIndex();
if (method_idx == previous_direct_method_idx) {
// smali can create dex files with two encoded_methods sharing the same method_idx
@@ -157,17 +161,20 @@
linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader,
NullHandle<mirror::ArtMethod>(), type);
if (method == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
+ DCHECK(self->IsExceptionPending());
// We couldn't resolve the method, but continue regardless.
- Thread::Current()->ClearException();
+ self->ClearException();
}
- MethodVerifier::FailureKind result = VerifyMethod(method_idx,
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ArtMethod> h_method(hs.NewHandle(method));
+ MethodVerifier::FailureKind result = VerifyMethod(self,
+ method_idx,
dex_file,
dex_cache,
class_loader,
class_def,
it.GetMethodCodeItem(),
- method,
+ h_method,
it.GetMethodAccessFlags(),
allow_soft_failures,
false);
@@ -188,6 +195,7 @@
}
int64_t previous_virtual_method_idx = -1;
while (it.HasNextVirtualMethod()) {
+ self->AllowThreadSuspension();
uint32_t method_idx = it.GetMemberIndex();
if (method_idx == previous_virtual_method_idx) {
// smali can create dex files with two encoded_methods sharing the same method_idx
@@ -201,17 +209,20 @@
linker->ResolveMethod(*dex_file, method_idx, dex_cache, class_loader,
NullHandle<mirror::ArtMethod>(), type);
if (method == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
+ DCHECK(self->IsExceptionPending());
// We couldn't resolve the method, but continue regardless.
- Thread::Current()->ClearException();
+ self->ClearException();
}
- MethodVerifier::FailureKind result = VerifyMethod(method_idx,
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ArtMethod> h_method(hs.NewHandle(method));
+ MethodVerifier::FailureKind result = VerifyMethod(self,
+ method_idx,
dex_file,
dex_cache,
class_loader,
class_def,
it.GetMethodCodeItem(),
- method,
+ h_method,
it.GetMethodAccessFlags(),
allow_soft_failures,
false);
@@ -237,22 +248,22 @@
}
}
-MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx,
+MethodVerifier::FailureKind MethodVerifier::VerifyMethod(Thread* self, uint32_t method_idx,
const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
const DexFile::CodeItem* code_item,
- mirror::ArtMethod* method,
+ Handle<mirror::ArtMethod> method,
uint32_t method_access_flags,
bool allow_soft_failures,
bool need_precise_constants) {
MethodVerifier::FailureKind result = kNoFailure;
uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
- MethodVerifier verifier(dex_file, &dex_cache, &class_loader, class_def, code_item,
- method_idx, method, method_access_flags, true, allow_soft_failures,
- need_precise_constants);
+ MethodVerifier verifier(self, dex_file, dex_cache, class_loader, class_def, code_item,
+ method_idx, method, method_access_flags, true, allow_soft_failures,
+ need_precise_constants);
if (verifier.Verify()) {
// Verification completed, however failures may be pending that didn't cause the verification
// to hard fail.
@@ -286,33 +297,41 @@
return result;
}
-MethodVerifier* MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx,
- const DexFile* dex_file,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- const DexFile::ClassDef* class_def,
- const DexFile::CodeItem* code_item,
- mirror::ArtMethod* method,
- uint32_t method_access_flags) {
- MethodVerifier* verifier = new MethodVerifier(dex_file, &dex_cache, &class_loader, class_def,
- code_item, dex_method_idx, method,
+MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self, std::ostream& os, uint32_t dex_method_idx,
+ const DexFile* dex_file,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexFile::ClassDef* class_def,
+ const DexFile::CodeItem* code_item,
+ Handle<mirror::ArtMethod> method,
+ uint32_t method_access_flags) {
+ MethodVerifier* verifier = new MethodVerifier(self, dex_file, dex_cache, class_loader,
+ class_def, code_item, dex_method_idx, method,
method_access_flags, true, true, true, true);
verifier->Verify();
verifier->DumpFailures(os);
os << verifier->info_messages_.str();
- verifier->Dump(os);
-
- return verifier;
+ // Only dump and return if no hard failures. Otherwise the verifier may be not fully initialized
+ // and querying any info is dangerous/can abort.
+ if (verifier->have_pending_hard_failure_) {
+ delete verifier;
+ return nullptr;
+ } else {
+ verifier->Dump(os);
+ return verifier;
+ }
}
-MethodVerifier::MethodVerifier(const DexFile* dex_file, Handle<mirror::DexCache>* dex_cache,
- Handle<mirror::ClassLoader>* class_loader,
+MethodVerifier::MethodVerifier(Thread* self,
+ const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
const DexFile::CodeItem* code_item, uint32_t dex_method_idx,
- mirror::ArtMethod* method, uint32_t method_access_flags,
+ Handle<mirror::ArtMethod> method, uint32_t method_access_flags,
bool can_load_classes, bool allow_soft_failures,
bool need_precise_constants, bool verify_to_dump)
- : reg_types_(can_load_classes),
+ : self_(self),
+ reg_types_(can_load_classes),
work_insn_idx_(-1),
dex_method_idx_(dex_method_idx),
mirror_method_(method),
@@ -347,21 +366,44 @@
void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc,
std::vector<uint32_t>* monitor_enter_dex_pcs) {
- StackHandleScope<2> hs(Thread::Current());
+ Thread* self = Thread::Current();
+ StackHandleScope<3> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
- MethodVerifier verifier(m->GetDexFile(), &dex_cache, &class_loader, &m->GetClassDef(),
- m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false,
- true, false);
+ Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+ MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+ m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+ false, true, false);
verifier.interesting_dex_pc_ = dex_pc;
verifier.monitor_enter_dex_pcs_ = monitor_enter_dex_pcs;
verifier.FindLocksAtDexPc();
}
+static bool HasMonitorEnterInstructions(const DexFile::CodeItem* const code_item) {
+ const Instruction* inst = Instruction::At(code_item->insns_);
+
+ uint32_t insns_size = code_item->insns_size_in_code_units_;
+ for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
+ if (inst->Opcode() == Instruction::MONITOR_ENTER) {
+ return true;
+ }
+
+ dex_pc += inst->SizeInCodeUnits();
+ inst = inst->Next();
+ }
+
+ return false;
+}
+
void MethodVerifier::FindLocksAtDexPc() {
CHECK(monitor_enter_dex_pcs_ != nullptr);
CHECK(code_item_ != nullptr); // This only makes sense for methods with code.
+ // Quick check whether there are any monitor_enter instructions at all.
+ if (!HasMonitorEnterInstructions(code_item_)) {
+ return;
+ }
+
// Strictly speaking, we ought to be able to get away with doing a subset of the full method
// verification. In practice, the phase we want relies on data structures set up by all the
// earlier passes, so we just run the full method verification and bail out early when we've
@@ -371,12 +413,14 @@
mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(mirror::ArtMethod* m,
uint32_t dex_pc) {
- StackHandleScope<2> hs(Thread::Current());
+ Thread* self = Thread::Current();
+ StackHandleScope<3> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
- MethodVerifier verifier(m->GetDexFile(), &dex_cache, &class_loader, &m->GetClassDef(),
- m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), true,
- true, false);
+ Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+ MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+ m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+ true, true, false);
return verifier.FindAccessedFieldAtDexPc(dex_pc);
}
@@ -401,12 +445,14 @@
mirror::ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::ArtMethod* m,
uint32_t dex_pc) {
- StackHandleScope<2> hs(Thread::Current());
+ Thread* self = Thread::Current();
+ StackHandleScope<3> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
- MethodVerifier verifier(m->GetDexFile(), &dex_cache, &class_loader, &m->GetClassDef(),
- m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), true,
- true, false);
+ Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+ MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+ m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+ true, true, false);
return verifier.FindInvokedMethodAtDexPc(dex_pc);
}
@@ -506,9 +552,9 @@
}
}
failures_.push_back(error);
- std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
+ std::string location(StringPrintf("%s: [0x%X] ", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
work_insn_idx_));
- std::ostringstream* failure_message = new std::ostringstream(location);
+ std::ostringstream* failure_message = new std::ostringstream(location, std::ostringstream::ate);
failure_messages_.push_back(failure_message);
return *failure_message;
}
@@ -523,7 +569,7 @@
DCHECK_NE(failure_num, 0U);
std::ostringstream* last_fail_message = failure_messages_[failure_num - 1];
prepend += last_fail_message->str();
- failure_messages_[failure_num - 1] = new std::ostringstream(prepend);
+ failure_messages_[failure_num - 1] = new std::ostringstream(prepend, std::ostringstream::ate);
delete last_fail_message;
}
@@ -565,9 +611,9 @@
break;
}
size_t inst_size = inst->SizeInCodeUnits();
- insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
+ insn_flags_[dex_pc].SetIsOpcode();
dex_pc += inst_size;
- inst = inst->Next();
+ inst = inst->RelativeAt(inst_size);
}
if (dex_pc != insns_size) {
@@ -603,9 +649,13 @@
<< "'try' block starts inside an instruction (" << start << ")";
return false;
}
- for (uint32_t dex_pc = start; dex_pc < end;
- dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
+ uint32_t dex_pc = start;
+ const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
+ while (dex_pc < end) {
insn_flags_[dex_pc].SetInTry();
+ size_t insn_size = inst->SizeInCodeUnits();
+ dex_pc += insn_size;
+ inst = inst->RelativeAt(insn_size);
}
}
// Iterate over each of the handlers to verify target addresses.
@@ -621,16 +671,21 @@
<< "exception handler starts at bad address (" << dex_pc << ")";
return false;
}
+ if (!CheckNotMoveResult(code_item_->insns_, dex_pc)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "exception handler begins with move-result* (" << dex_pc << ")";
+ return false;
+ }
insn_flags_[dex_pc].SetBranchTarget();
// Ensure exception types are resolved so that they don't need resolution to be delivered,
// unresolved exception types will be ignored by exception delivery
if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) {
mirror::Class* exception_type = linker->ResolveType(*dex_file_,
iterator.GetHandlerTypeIndex(),
- *dex_cache_, *class_loader_);
+ dex_cache_, class_loader_);
if (exception_type == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
+ DCHECK(self_->IsExceptionPending());
+ self_->ClearException();
}
}
}
@@ -762,7 +817,7 @@
return result;
}
-bool MethodVerifier::CheckRegisterIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckRegisterIndex(uint32_t idx) {
if (idx >= code_item_->registers_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register index out of range (" << idx << " >= "
<< code_item_->registers_size_ << ")";
@@ -771,7 +826,7 @@
return true;
}
-bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) {
if (idx + 1 >= code_item_->registers_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register index out of range (" << idx
<< "+1 >= " << code_item_->registers_size_ << ")";
@@ -780,7 +835,7 @@
return true;
}
-bool MethodVerifier::CheckFieldIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckFieldIndex(uint32_t idx) {
if (idx >= dex_file_->GetHeader().field_ids_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad field index " << idx << " (max "
<< dex_file_->GetHeader().field_ids_size_ << ")";
@@ -789,7 +844,7 @@
return true;
}
-bool MethodVerifier::CheckMethodIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckMethodIndex(uint32_t idx) {
if (idx >= dex_file_->GetHeader().method_ids_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad method index " << idx << " (max "
<< dex_file_->GetHeader().method_ids_size_ << ")";
@@ -798,7 +853,7 @@
return true;
}
-bool MethodVerifier::CheckNewInstance(uint32_t idx) {
+inline bool MethodVerifier::CheckNewInstance(uint32_t idx) {
if (idx >= dex_file_->GetHeader().type_ids_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
<< dex_file_->GetHeader().type_ids_size_ << ")";
@@ -813,7 +868,7 @@
return true;
}
-bool MethodVerifier::CheckStringIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckStringIndex(uint32_t idx) {
if (idx >= dex_file_->GetHeader().string_ids_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad string index " << idx << " (max "
<< dex_file_->GetHeader().string_ids_size_ << ")";
@@ -822,7 +877,7 @@
return true;
}
-bool MethodVerifier::CheckTypeIndex(uint32_t idx) {
+inline bool MethodVerifier::CheckTypeIndex(uint32_t idx) {
if (idx >= dex_file_->GetHeader().type_ids_size_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx << " (max "
<< dex_file_->GetHeader().type_ids_size_ << ")";
@@ -1140,10 +1195,10 @@
std::ostream indent_os(&indent_filter);
const Instruction* inst = Instruction::At(code_item_->insns_);
for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
- dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
+ dex_pc += inst->SizeInCodeUnits()) {
RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
if (reg_line != nullptr) {
- indent_os << reg_line->Dump() << "\n";
+ indent_os << reg_line->Dump(this) << "\n";
}
indent_os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].ToString() << " ";
const bool kDumpHexOfInstruction = false;
@@ -1183,12 +1238,12 @@
// If this is a constructor for a class other than java.lang.Object, mark the first ("this")
// argument as uninitialized. This restricts field access until the superclass constructor is
// called.
- RegType& declaring_class = GetDeclaringClass();
+ const RegType& declaring_class = GetDeclaringClass();
if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
- reg_line->SetRegisterType(arg_start + cur_arg,
+ reg_line->SetRegisterType(this, arg_start + cur_arg,
reg_types_.UninitializedThisArgument(declaring_class));
} else {
- reg_line->SetRegisterType(arg_start + cur_arg, declaring_class);
+ reg_line->SetRegisterType(this, arg_start + cur_arg, declaring_class);
}
cur_arg++;
}
@@ -1215,31 +1270,31 @@
// it's effectively considered initialized the instant we reach here (in the sense that we
// can return without doing anything or call virtual methods).
{
- RegType& reg_type = ResolveClassAndCheckAccess(iterator.GetTypeIdx());
+ const RegType& reg_type = ResolveClassAndCheckAccess(iterator.GetTypeIdx());
if (!reg_type.IsNonZeroReferenceTypes()) {
DCHECK(HasFailures());
return false;
}
- reg_line->SetRegisterType(arg_start + cur_arg, reg_type);
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_type);
}
break;
case 'Z':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Boolean());
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Boolean());
break;
case 'C':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Char());
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Char());
break;
case 'B':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Byte());
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Byte());
break;
case 'I':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Integer());
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Integer());
break;
case 'S':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Short());
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Short());
break;
case 'F':
- reg_line->SetRegisterType(arg_start + cur_arg, reg_types_.Float());
+ reg_line->SetRegisterType(this, arg_start + cur_arg, reg_types_.Float());
break;
case 'J':
case 'D': {
@@ -1249,9 +1304,16 @@
return false;
}
- RegType& lo_half = descriptor[0] == 'J' ? reg_types_.LongLo() : reg_types_.DoubleLo();
- RegType& hi_half = descriptor[0] == 'J' ? reg_types_.LongHi() : reg_types_.DoubleHi();
- reg_line->SetRegisterTypeWide(arg_start + cur_arg, lo_half, hi_half);
+ const RegType* lo_half;
+ const RegType* hi_half;
+ if (descriptor[0] == 'J') {
+ lo_half = ®_types_.LongLo();
+ hi_half = ®_types_.LongHi();
+ } else {
+ lo_half = ®_types_.DoubleLo();
+ hi_half = ®_types_.DoubleHi();
+ }
+ reg_line->SetRegisterTypeWide(this, arg_start + cur_arg, *lo_half, *hi_half);
cur_arg++;
break;
}
@@ -1313,6 +1375,7 @@
/* Continue until no instructions are marked "changed". */
while (true) {
+ self_->AllowThreadSuspension();
// Find the first marked one. Use "start_guess" as a way to find one quickly.
uint32_t insn_idx = start_guess;
for (; insn_idx < insns_size; insn_idx++) {
@@ -1350,8 +1413,8 @@
std::cout << info_messages_.str();
LOG(FATAL) << "work_line diverged in " << PrettyMethod(dex_method_idx_, *dex_file_)
<< "@" << reinterpret_cast<void*>(work_insn_idx_) << "\n"
- << " work_line=" << *work_line_ << "\n"
- << " expected=" << *register_line;
+ << " work_line=" << work_line_->Dump(this) << "\n"
+ << " expected=" << register_line->Dump(this);
}
}
}
@@ -1377,7 +1440,8 @@
*/
int dead_start = -1;
uint32_t insn_idx = 0;
- for (; insn_idx < insns_size; insn_idx += insn_flags_[insn_idx].GetLengthInCodeUnits()) {
+ for (; insn_idx < insns_size;
+ insn_idx += Instruction::At(code_item_->insns_ + insn_idx)->SizeInCodeUnits()) {
/*
* Switch-statement data doesn't get "visited" by scanner. It
* may or may not be preceded by a padding NOP (for alignment).
@@ -1453,7 +1517,7 @@
if (gDebugVerify) {
// Generate processing back trace to debug verifier
LogVerifyInfo() << "Processing " << inst->DumpString(dex_file_) << "\n"
- << *work_line_.get() << "\n";
+ << work_line_->Dump(this) << "\n";
}
/*
@@ -1489,31 +1553,31 @@
break;
case Instruction::MOVE:
- work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategory1nr);
+ work_line_->CopyRegister1(this, inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategory1nr);
break;
case Instruction::MOVE_FROM16:
- work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategory1nr);
+ work_line_->CopyRegister1(this, inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategory1nr);
break;
case Instruction::MOVE_16:
- work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategory1nr);
+ work_line_->CopyRegister1(this, inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategory1nr);
break;
case Instruction::MOVE_WIDE:
- work_line_->CopyRegister2(inst->VRegA_12x(), inst->VRegB_12x());
+ work_line_->CopyRegister2(this, inst->VRegA_12x(), inst->VRegB_12x());
break;
case Instruction::MOVE_WIDE_FROM16:
- work_line_->CopyRegister2(inst->VRegA_22x(), inst->VRegB_22x());
+ work_line_->CopyRegister2(this, inst->VRegA_22x(), inst->VRegB_22x());
break;
case Instruction::MOVE_WIDE_16:
- work_line_->CopyRegister2(inst->VRegA_32x(), inst->VRegB_32x());
+ work_line_->CopyRegister2(this, inst->VRegA_32x(), inst->VRegB_32x());
break;
case Instruction::MOVE_OBJECT:
- work_line_->CopyRegister1(inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategoryRef);
+ work_line_->CopyRegister1(this, inst->VRegA_12x(), inst->VRegB_12x(), kTypeCategoryRef);
break;
case Instruction::MOVE_OBJECT_FROM16:
- work_line_->CopyRegister1(inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategoryRef);
+ work_line_->CopyRegister1(this, inst->VRegA_22x(), inst->VRegB_22x(), kTypeCategoryRef);
break;
case Instruction::MOVE_OBJECT_16:
- work_line_->CopyRegister1(inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategoryRef);
+ work_line_->CopyRegister1(this, inst->VRegA_32x(), inst->VRegB_32x(), kTypeCategoryRef);
break;
/*
@@ -1528,13 +1592,13 @@
* easier to read in some cases.)
*/
case Instruction::MOVE_RESULT:
- work_line_->CopyResultRegister1(inst->VRegA_11x(), false);
+ work_line_->CopyResultRegister1(this, inst->VRegA_11x(), false);
break;
case Instruction::MOVE_RESULT_WIDE:
- work_line_->CopyResultRegister2(inst->VRegA_11x());
+ work_line_->CopyResultRegister2(this, inst->VRegA_11x());
break;
case Instruction::MOVE_RESULT_OBJECT:
- work_line_->CopyResultRegister1(inst->VRegA_11x(), true);
+ work_line_->CopyResultRegister1(this, inst->VRegA_11x(), true);
break;
case Instruction::MOVE_EXCEPTION: {
@@ -1542,21 +1606,21 @@
* This statement can only appear as the first instruction in an exception handler. We verify
* that as part of extracting the exception type from the catch block list.
*/
- RegType& res_type = GetCaughtExceptionType();
- work_line_->SetRegisterType(inst->VRegA_11x(), res_type);
+ const RegType& res_type = GetCaughtExceptionType();
+ work_line_->SetRegisterType(this, inst->VRegA_11x(), res_type);
break;
}
case Instruction::RETURN_VOID:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
if (!GetMethodReturnType().IsConflict()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected";
}
}
break;
case Instruction::RETURN:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
/* check the method signature */
- RegType& return_type = GetMethodReturnType();
+ const RegType& return_type = GetMethodReturnType();
if (!return_type.IsCategory1Types()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected non-category 1 return type "
<< return_type;
@@ -1564,14 +1628,14 @@
// Compilers may generate synthetic functions that write byte values into boolean fields.
// Also, it may use integer values for boolean, byte, short, and character return types.
const uint32_t vregA = inst->VRegA_11x();
- RegType& src_type = work_line_->GetRegisterType(vregA);
+ const RegType& src_type = work_line_->GetRegisterType(this, vregA);
bool use_src = ((return_type.IsBoolean() && src_type.IsByte()) ||
((return_type.IsBoolean() || return_type.IsByte() ||
return_type.IsShort() || return_type.IsChar()) &&
src_type.IsInteger()));
/* check the register contents */
bool success =
- work_line_->VerifyRegisterType(vregA, use_src ? src_type : return_type);
+ work_line_->VerifyRegisterType(this, vregA, use_src ? src_type : return_type);
if (!success) {
AppendToLastFailMessage(StringPrintf(" return-1nr on invalid register v%d", vregA));
}
@@ -1579,15 +1643,15 @@
}
break;
case Instruction::RETURN_WIDE:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
+ if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
/* check the method signature */
- RegType& return_type = GetMethodReturnType();
+ const RegType& return_type = GetMethodReturnType();
if (!return_type.IsCategory2Types()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-wide not expected";
} else {
/* check the register contents */
const uint32_t vregA = inst->VRegA_11x();
- bool success = work_line_->VerifyRegisterType(vregA, return_type);
+ bool success = work_line_->VerifyRegisterType(this, vregA, return_type);
if (!success) {
AppendToLastFailMessage(StringPrintf(" return-wide on invalid register v%d", vregA));
}
@@ -1595,8 +1659,8 @@
}
break;
case Instruction::RETURN_OBJECT:
- if (!IsConstructor() || work_line_->CheckConstructorReturn()) {
- RegType& return_type = GetMethodReturnType();
+ if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) {
+ const RegType& return_type = GetMethodReturnType();
if (!return_type.IsReferenceTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected";
} else {
@@ -1604,7 +1668,7 @@
DCHECK(!return_type.IsZero());
DCHECK(!return_type.IsUninitializedReference());
const uint32_t vregA = inst->VRegA_11x();
- RegType& reg_type = work_line_->GetRegisterType(vregA);
+ const RegType& reg_type = work_line_->GetRegisterType(this, vregA);
// Disallow returning uninitialized values and verify that the reference in vAA is an
// instance of the "return_type"
if (reg_type.IsUninitializedTypes()) {
@@ -1626,75 +1690,75 @@
/* could be boolean, int, float, or a null reference */
case Instruction::CONST_4: {
int32_t val = static_cast<int32_t>(inst->VRegB_11n() << 28) >> 28;
- work_line_->SetRegisterType(inst->VRegA_11n(),
+ work_line_->SetRegisterType(this, inst->VRegA_11n(),
DetermineCat1Constant(val, need_precise_constants_));
break;
}
case Instruction::CONST_16: {
int16_t val = static_cast<int16_t>(inst->VRegB_21s());
- work_line_->SetRegisterType(inst->VRegA_21s(),
+ work_line_->SetRegisterType(this, inst->VRegA_21s(),
DetermineCat1Constant(val, need_precise_constants_));
break;
}
case Instruction::CONST: {
int32_t val = inst->VRegB_31i();
- work_line_->SetRegisterType(inst->VRegA_31i(),
+ work_line_->SetRegisterType(this, inst->VRegA_31i(),
DetermineCat1Constant(val, need_precise_constants_));
break;
}
case Instruction::CONST_HIGH16: {
int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
- work_line_->SetRegisterType(inst->VRegA_21h(),
+ work_line_->SetRegisterType(this, inst->VRegA_21h(),
DetermineCat1Constant(val, need_precise_constants_));
break;
}
/* could be long or double; resolved upon use */
case Instruction::CONST_WIDE_16: {
int64_t val = static_cast<int16_t>(inst->VRegB_21s());
- RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_21s(), lo, hi);
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(this, inst->VRegA_21s(), lo, hi);
break;
}
case Instruction::CONST_WIDE_32: {
int64_t val = static_cast<int32_t>(inst->VRegB_31i());
- RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_31i(), lo, hi);
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(this, inst->VRegA_31i(), lo, hi);
break;
}
case Instruction::CONST_WIDE: {
int64_t val = inst->VRegB_51l();
- RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_51l(), lo, hi);
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(this, inst->VRegA_51l(), lo, hi);
break;
}
case Instruction::CONST_WIDE_HIGH16: {
int64_t val = static_cast<uint64_t>(inst->VRegB_21h()) << 48;
- RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
- RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- work_line_->SetRegisterTypeWide(inst->VRegA_21h(), lo, hi);
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(this, inst->VRegA_21h(), lo, hi);
break;
}
case Instruction::CONST_STRING:
- work_line_->SetRegisterType(inst->VRegA_21c(), reg_types_.JavaLangString());
+ work_line_->SetRegisterType(this, inst->VRegA_21c(), reg_types_.JavaLangString());
break;
case Instruction::CONST_STRING_JUMBO:
- work_line_->SetRegisterType(inst->VRegA_31c(), reg_types_.JavaLangString());
+ work_line_->SetRegisterType(this, inst->VRegA_31c(), reg_types_.JavaLangString());
break;
case Instruction::CONST_CLASS: {
// Get type from instruction if unresolved then we need an access check
// TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+ const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
// Register holds class, ie its type is class, on error it will hold Conflict.
- work_line_->SetRegisterType(inst->VRegA_21c(),
+ work_line_->SetRegisterType(this, inst->VRegA_21c(),
res_type.IsConflict() ? res_type
- : reg_types_.JavaLangClass(true));
+ : reg_types_.JavaLangClass());
break;
}
case Instruction::MONITOR_ENTER:
- work_line_->PushMonitor(inst->VRegA_11x(), work_insn_idx_);
+ work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_);
break;
case Instruction::MONITOR_EXIT:
/*
@@ -1718,7 +1782,7 @@
* "live" so we still need to check it.
*/
opcode_flags &= ~Instruction::kThrow;
- work_line_->PopMonitor(inst->VRegA_11x());
+ work_line_->PopMonitor(this, inst->VRegA_11x());
break;
case Instruction::CHECK_CAST:
@@ -1732,10 +1796,10 @@
*/
const bool is_checkcast = (inst->Opcode() == Instruction::CHECK_CAST);
const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c();
- RegType& res_type = ResolveClassAndCheckAccess(type_idx);
+ const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
if (res_type.IsConflict()) {
// If this is a primitive type, fail HARD.
- mirror::Class* klass = (*dex_cache_)->GetResolvedType(type_idx);
+ mirror::Class* klass = dex_cache_->GetResolvedType(type_idx);
if (klass != nullptr && klass->IsPrimitive()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type "
<< dex_file_->StringByTypeIdx(type_idx) << " in instanceof in "
@@ -1745,13 +1809,13 @@
DCHECK_NE(failures_.size(), 0U);
if (!is_checkcast) {
- work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
+ work_line_->SetRegisterType(this, inst->VRegA_22c(), reg_types_.Boolean());
}
break; // bad class
}
// TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
uint32_t orig_type_reg = (is_checkcast) ? inst->VRegA_21c() : inst->VRegB_22c();
- RegType& orig_type = work_line_->GetRegisterType(orig_type_reg);
+ const RegType& orig_type = work_line_->GetRegisterType(this, orig_type_reg);
if (!res_type.IsNonZeroReferenceTypes()) {
if (is_checkcast) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on unexpected class " << res_type;
@@ -1766,20 +1830,20 @@
}
} else {
if (is_checkcast) {
- work_line_->SetRegisterType(inst->VRegA_21c(), res_type);
+ work_line_->SetRegisterType(this, inst->VRegA_21c(), res_type);
} else {
- work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
+ work_line_->SetRegisterType(this, inst->VRegA_22c(), reg_types_.Boolean());
}
}
break;
}
case Instruction::ARRAY_LENGTH: {
- RegType& res_type = work_line_->GetRegisterType(inst->VRegB_12x());
+ const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegB_12x());
if (res_type.IsReferenceTypes()) {
if (!res_type.IsArrayTypes() && !res_type.IsZero()) { // ie not an array or null
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
} else {
- work_line_->SetRegisterType(inst->VRegA_12x(), reg_types_.Integer());
+ work_line_->SetRegisterType(this, inst->VRegA_12x(), reg_types_.Integer());
}
} else {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type;
@@ -1787,7 +1851,7 @@
break;
}
case Instruction::NEW_INSTANCE: {
- RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+ const RegType& res_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
if (res_type.IsConflict()) {
DCHECK_NE(failures_.size(), 0U);
break; // bad class
@@ -1799,12 +1863,12 @@
<< "new-instance on primitive, interface or abstract class" << res_type;
// Soft failure so carry on to set register type.
}
- RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
+ const RegType& uninit_type = reg_types_.Uninitialized(res_type, work_insn_idx_);
// Any registers holding previous allocations from this address that have not yet been
// initialized must be marked invalid.
- work_line_->MarkUninitRefsAsInvalid(uninit_type);
+ work_line_->MarkUninitRefsAsInvalid(this, uninit_type);
// add the new uninitialized reference to the register state
- work_line_->SetRegisterType(inst->VRegA_21c(), uninit_type);
+ work_line_->SetRegisterType(this, inst->VRegA_21c(), uninit_type);
break;
}
case Instruction::NEW_ARRAY:
@@ -1820,39 +1884,39 @@
break;
case Instruction::CMPL_FLOAT:
case Instruction::CMPG_FLOAT:
- if (!work_line_->VerifyRegisterType(inst->VRegB_23x(), reg_types_.Float())) {
+ if (!work_line_->VerifyRegisterType(this, inst->VRegB_23x(), reg_types_.Float())) {
break;
}
- if (!work_line_->VerifyRegisterType(inst->VRegC_23x(), reg_types_.Float())) {
+ if (!work_line_->VerifyRegisterType(this, inst->VRegC_23x(), reg_types_.Float())) {
break;
}
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
+ work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Integer());
break;
case Instruction::CMPL_DOUBLE:
case Instruction::CMPG_DOUBLE:
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.DoubleLo(),
+ if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegB_23x(), reg_types_.DoubleLo(),
reg_types_.DoubleHi())) {
break;
}
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.DoubleLo(),
+ if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegC_23x(), reg_types_.DoubleLo(),
reg_types_.DoubleHi())) {
break;
}
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
+ work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Integer());
break;
case Instruction::CMP_LONG:
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegB_23x(), reg_types_.LongLo(),
+ if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegB_23x(), reg_types_.LongLo(),
reg_types_.LongHi())) {
break;
}
- if (!work_line_->VerifyRegisterTypeWide(inst->VRegC_23x(), reg_types_.LongLo(),
+ if (!work_line_->VerifyRegisterTypeWide(this, inst->VRegC_23x(), reg_types_.LongLo(),
reg_types_.LongHi())) {
break;
}
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Integer());
+ work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Integer());
break;
case Instruction::THROW: {
- RegType& res_type = work_line_->GetRegisterType(inst->VRegA_11x());
+ const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegA_11x());
if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT)
<< "thrown class " << res_type << " not instanceof Throwable";
@@ -1868,20 +1932,19 @@
case Instruction::PACKED_SWITCH:
case Instruction::SPARSE_SWITCH:
/* verify that vAA is an integer, or can be converted to one */
- work_line_->VerifyRegisterType(inst->VRegA_31t(), reg_types_.Integer());
+ work_line_->VerifyRegisterType(this, inst->VRegA_31t(), reg_types_.Integer());
break;
case Instruction::FILL_ARRAY_DATA: {
/* Similar to the verification done for APUT */
- RegType& array_type = work_line_->GetRegisterType(inst->VRegA_31t());
+ const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegA_31t());
/* array_type can be null if the reg type is Zero */
if (!array_type.IsZero()) {
if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type "
<< array_type;
} else {
- RegType& component_type = reg_types_.GetComponentType(array_type,
- class_loader_->Get());
+ const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
DCHECK(!component_type.IsConflict());
if (component_type.IsNonZeroReferenceTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type "
@@ -1908,8 +1971,8 @@
}
case Instruction::IF_EQ:
case Instruction::IF_NE: {
- RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t());
- RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t());
+ const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t());
+ const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t());
bool mismatch = false;
if (reg_type1.IsZero()) { // zero then integral or reference expected
mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes();
@@ -1928,8 +1991,8 @@
case Instruction::IF_GE:
case Instruction::IF_GT:
case Instruction::IF_LE: {
- RegType& reg_type1 = work_line_->GetRegisterType(inst->VRegA_22t());
- RegType& reg_type2 = work_line_->GetRegisterType(inst->VRegB_22t());
+ const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t());
+ const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t());
if (!reg_type1.IsIntegralTypes() || !reg_type2.IsIntegralTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "args to 'if' (" << reg_type1 << ","
<< reg_type2 << ") must be integral";
@@ -1938,7 +2001,7 @@
}
case Instruction::IF_EQZ:
case Instruction::IF_NEZ: {
- RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
+ const RegType& reg_type = work_line_->GetRegisterType(this, inst->VRegA_21t());
if (!reg_type.IsReferenceTypes() && !reg_type.IsIntegralTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
<< " unexpected as arg to if-eqz/if-nez";
@@ -1984,8 +2047,8 @@
// type is assignable to the original then allow optimization. This check is performed to
// ensure that subsequent merges don't lose type information - such as becoming an
// interface from a class that would lose information relevant to field checks.
- RegType& orig_type = work_line_->GetRegisterType(instance_of_inst->VRegB_22c());
- RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
+ const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c());
+ const RegType& cast_type = ResolveClassAndCheckAccess(instance_of_inst->VRegC_22c());
if (!orig_type.Equals(cast_type) &&
!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
@@ -2000,7 +2063,7 @@
branch_line.reset(update_line);
}
update_line->CopyFromLine(work_line_.get());
- update_line->SetRegisterType(instance_of_inst->VRegB_22c(), cast_type);
+ update_line->SetRegisterType(this, instance_of_inst->VRegB_22c(), cast_type);
if (!insn_flags_[instance_of_idx].IsBranchTarget() && 0 != instance_of_idx) {
// See if instance-of was preceded by a move-object operation, common due to the small
// register encoding space of instance-of, and propagate type information to the source
@@ -2014,17 +2077,17 @@
switch (move_inst->Opcode()) {
case Instruction::MOVE_OBJECT:
if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
- update_line->SetRegisterType(move_inst->VRegB_12x(), cast_type);
+ update_line->SetRegisterType(this, move_inst->VRegB_12x(), cast_type);
}
break;
case Instruction::MOVE_OBJECT_FROM16:
if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) {
- update_line->SetRegisterType(move_inst->VRegB_22x(), cast_type);
+ update_line->SetRegisterType(this, move_inst->VRegB_22x(), cast_type);
}
break;
case Instruction::MOVE_OBJECT_16:
if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) {
- update_line->SetRegisterType(move_inst->VRegB_32x(), cast_type);
+ update_line->SetRegisterType(this, move_inst->VRegB_32x(), cast_type);
}
break;
default:
@@ -2040,7 +2103,7 @@
case Instruction::IF_GEZ:
case Instruction::IF_GTZ:
case Instruction::IF_LEZ: {
- RegType& reg_type = work_line_->GetRegisterType(inst->VRegA_21t());
+ const RegType& reg_type = work_line_->GetRegisterType(this, inst->VRegA_21t());
if (!reg_type.IsIntegralTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "type " << reg_type
<< " unexpected as arg to if-ltz/if-gez/if-gtz/if-lez";
@@ -2189,10 +2252,9 @@
inst->Opcode() == Instruction::INVOKE_SUPER_RANGE);
mirror::ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range,
is_super);
- RegType* return_type = nullptr;
+ const RegType* return_type = nullptr;
if (called_method != nullptr) {
- Thread* self = Thread::Current();
- StackHandleScope<1> hs(self);
+ StackHandleScope<1> hs(self_);
Handle<mirror::ArtMethod> h_called_method(hs.NewHandle(called_method));
MethodHelper mh(h_called_method);
mirror::Class* return_type_class = mh.GetReturnType(can_load_classes_);
@@ -2201,8 +2263,8 @@
return_type_class,
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
- DCHECK(!can_load_classes_ || self->IsExceptionPending());
- self->ClearException();
+ DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+ self_->ClearException();
}
}
if (return_type == nullptr) {
@@ -2210,10 +2272,10 @@
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- return_type = ®_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ return_type = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false);
}
if (!return_type->IsLowHalf()) {
- work_line_->SetResultRegisterType(*return_type);
+ work_line_->SetResultRegisterType(this, *return_type);
} else {
work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(®_types_));
}
@@ -2227,7 +2289,7 @@
is_range, false);
const char* return_type_descriptor;
bool is_constructor;
- RegType* return_type = nullptr;
+ const RegType* return_type = nullptr;
if (called_method == nullptr) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
@@ -2237,8 +2299,7 @@
} else {
is_constructor = called_method->IsConstructor();
return_type_descriptor = called_method->GetReturnTypeDescriptor();
- Thread* self = Thread::Current();
- StackHandleScope<1> hs(self);
+ StackHandleScope<1> hs(self_);
Handle<mirror::ArtMethod> h_called_method(hs.NewHandle(called_method));
MethodHelper mh(h_called_method);
mirror::Class* return_type_class = mh.GetReturnType(can_load_classes_);
@@ -2247,8 +2308,8 @@
return_type_class,
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
- DCHECK(!can_load_classes_ || self->IsExceptionPending());
- self->ClearException();
+ DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+ self_->ClearException();
}
}
if (is_constructor) {
@@ -2259,7 +2320,7 @@
* allowing the latter only if the "this" argument is the same as the "this" argument to
* this method (which implies that we're in a constructor ourselves).
*/
- RegType& this_type = work_line_->GetInvocationThis(inst, is_range);
+ const RegType& this_type = work_line_->GetInvocationThis(this, inst, is_range);
if (this_type.IsConflict()) // failure.
break;
@@ -2270,7 +2331,7 @@
}
/* must be in same class or in superclass */
- // RegType& this_super_klass = this_type.GetSuperClass(®_types_);
+ // const RegType& this_super_klass = this_type.GetSuperClass(®_types_);
// TODO: re-enable constructor type verification
// if (this_super_klass.IsConflict()) {
// Unknown super class, fail so we re-check at runtime.
@@ -2289,14 +2350,14 @@
* Replace the uninitialized reference with an initialized one. We need to do this for all
* registers that have the same object instance in them, not just the "this" register.
*/
- work_line_->MarkRefsAsInitialized(this_type);
+ work_line_->MarkRefsAsInitialized(this, this_type);
}
if (return_type == nullptr) {
- return_type = ®_types_.FromDescriptor(class_loader_->Get(),
- return_type_descriptor, false);
+ return_type = ®_types_.FromDescriptor(GetClassLoader(), return_type_descriptor,
+ false);
}
if (!return_type->IsLowHalf()) {
- work_line_->SetResultRegisterType(*return_type);
+ work_line_->SetResultRegisterType(this, *return_type);
} else {
work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(®_types_));
}
@@ -2319,10 +2380,9 @@
} else {
descriptor = called_method->GetReturnTypeDescriptor();
}
- RegType& return_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor,
- false);
+ const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
if (!return_type.IsLowHalf()) {
- work_line_->SetResultRegisterType(return_type);
+ work_line_->SetResultRegisterType(this, return_type);
} else {
work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
}
@@ -2347,7 +2407,7 @@
/* Get the type of the "this" arg, which should either be a sub-interface of called
* interface or Object (see comments in RegType::JoinClass).
*/
- RegType& this_type = work_line_->GetInvocationThis(inst, is_range);
+ const RegType& this_type = work_line_->GetInvocationThis(this, inst, is_range);
if (this_type.IsZero()) {
/* null pointer always passes (and always fails at runtime) */
} else {
@@ -2377,10 +2437,9 @@
} else {
descriptor = abs_method->GetReturnTypeDescriptor();
}
- RegType& return_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor,
- false);
+ const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
if (!return_type.IsLowHalf()) {
- work_line_->SetResultRegisterType(return_type);
+ work_line_->SetResultRegisterType(this, return_type);
} else {
work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
}
@@ -2389,74 +2448,74 @@
}
case Instruction::NEG_INT:
case Instruction::NOT_INT:
- work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Integer());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer());
break;
case Instruction::NEG_LONG:
case Instruction::NOT_LONG:
- work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckUnaryOpWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::NEG_FLOAT:
- work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Float());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Float(), reg_types_.Float());
break;
case Instruction::NEG_DOUBLE:
- work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ work_line_->CheckUnaryOpWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::INT_TO_LONG:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckUnaryOpToWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.Integer());
break;
case Instruction::INT_TO_FLOAT:
- work_line_->CheckUnaryOp(inst, reg_types_.Float(), reg_types_.Integer());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Float(), reg_types_.Integer());
break;
case Instruction::INT_TO_DOUBLE:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ work_line_->CheckUnaryOpToWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.Integer());
break;
case Instruction::LONG_TO_INT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(),
+ work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Integer(),
reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::LONG_TO_FLOAT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(),
+ work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Float(),
reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::LONG_TO_DOUBLE:
- work_line_->CheckUnaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ work_line_->CheckUnaryOpWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::FLOAT_TO_INT:
- work_line_->CheckUnaryOp(inst, reg_types_.Integer(), reg_types_.Float());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Float());
break;
case Instruction::FLOAT_TO_LONG:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckUnaryOpToWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.Float());
break;
case Instruction::FLOAT_TO_DOUBLE:
- work_line_->CheckUnaryOpToWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ work_line_->CheckUnaryOpToWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.Float());
break;
case Instruction::DOUBLE_TO_INT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Integer(),
+ work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Integer(),
reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::DOUBLE_TO_LONG:
- work_line_->CheckUnaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckUnaryOpWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::DOUBLE_TO_FLOAT:
- work_line_->CheckUnaryOpFromWide(inst, reg_types_.Float(),
+ work_line_->CheckUnaryOpFromWide(this, inst, reg_types_.Float(),
reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::INT_TO_BYTE:
- work_line_->CheckUnaryOp(inst, reg_types_.Byte(), reg_types_.Integer());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Byte(), reg_types_.Integer());
break;
case Instruction::INT_TO_CHAR:
- work_line_->CheckUnaryOp(inst, reg_types_.Char(), reg_types_.Integer());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Char(), reg_types_.Integer());
break;
case Instruction::INT_TO_SHORT:
- work_line_->CheckUnaryOp(inst, reg_types_.Short(), reg_types_.Integer());
+ work_line_->CheckUnaryOp(this, inst, reg_types_.Short(), reg_types_.Integer());
break;
case Instruction::ADD_INT:
@@ -2467,13 +2526,13 @@
case Instruction::SHL_INT:
case Instruction::SHR_INT:
case Instruction::USHR_INT:
- work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(),
+ work_line_->CheckBinaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer(),
reg_types_.Integer(), false);
break;
case Instruction::AND_INT:
case Instruction::OR_INT:
case Instruction::XOR_INT:
- work_line_->CheckBinaryOp(inst, reg_types_.Integer(), reg_types_.Integer(),
+ work_line_->CheckBinaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer(),
reg_types_.Integer(), true);
break;
case Instruction::ADD_LONG:
@@ -2484,7 +2543,7 @@
case Instruction::AND_LONG:
case Instruction::OR_LONG:
case Instruction::XOR_LONG:
- work_line_->CheckBinaryOpWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckBinaryOpWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.LongLo(), reg_types_.LongHi());
break;
@@ -2492,7 +2551,7 @@
case Instruction::SHR_LONG:
case Instruction::USHR_LONG:
/* shift distance is Int, making these different from other binary operations */
- work_line_->CheckBinaryOpWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckBinaryOpWideShift(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.Integer());
break;
case Instruction::ADD_FLOAT:
@@ -2500,18 +2559,15 @@
case Instruction::MUL_FLOAT:
case Instruction::DIV_FLOAT:
case Instruction::REM_FLOAT:
- work_line_->CheckBinaryOp(inst,
- reg_types_.Float(),
- reg_types_.Float(),
- reg_types_.Float(),
- false);
+ work_line_->CheckBinaryOp(this, inst, reg_types_.Float(), reg_types_.Float(),
+ reg_types_.Float(), false);
break;
case Instruction::ADD_DOUBLE:
case Instruction::SUB_DOUBLE:
case Instruction::MUL_DOUBLE:
case Instruction::DIV_DOUBLE:
case Instruction::REM_DOUBLE:
- work_line_->CheckBinaryOpWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ work_line_->CheckBinaryOpWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
@@ -2522,27 +2578,18 @@
case Instruction::SHL_INT_2ADDR:
case Instruction::SHR_INT_2ADDR:
case Instruction::USHR_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst,
- reg_types_.Integer(),
- reg_types_.Integer(),
- reg_types_.Integer(),
- false);
+ work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Integer(), reg_types_.Integer(),
+ reg_types_.Integer(), false);
break;
case Instruction::AND_INT_2ADDR:
case Instruction::OR_INT_2ADDR:
case Instruction::XOR_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst,
- reg_types_.Integer(),
- reg_types_.Integer(),
- reg_types_.Integer(),
- true);
+ work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Integer(), reg_types_.Integer(),
+ reg_types_.Integer(), true);
break;
case Instruction::DIV_INT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst,
- reg_types_.Integer(),
- reg_types_.Integer(),
- reg_types_.Integer(),
- false);
+ work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Integer(), reg_types_.Integer(),
+ reg_types_.Integer(), false);
break;
case Instruction::ADD_LONG_2ADDR:
case Instruction::SUB_LONG_2ADDR:
@@ -2552,14 +2599,14 @@
case Instruction::AND_LONG_2ADDR:
case Instruction::OR_LONG_2ADDR:
case Instruction::XOR_LONG_2ADDR:
- work_line_->CheckBinaryOp2addrWide(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckBinaryOp2addrWide(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::SHL_LONG_2ADDR:
case Instruction::SHR_LONG_2ADDR:
case Instruction::USHR_LONG_2ADDR:
- work_line_->CheckBinaryOp2addrWideShift(inst, reg_types_.LongLo(), reg_types_.LongHi(),
+ work_line_->CheckBinaryOp2addrWideShift(this, inst, reg_types_.LongLo(), reg_types_.LongHi(),
reg_types_.Integer());
break;
case Instruction::ADD_FLOAT_2ADDR:
@@ -2567,18 +2614,15 @@
case Instruction::MUL_FLOAT_2ADDR:
case Instruction::DIV_FLOAT_2ADDR:
case Instruction::REM_FLOAT_2ADDR:
- work_line_->CheckBinaryOp2addr(inst,
- reg_types_.Float(),
- reg_types_.Float(),
- reg_types_.Float(),
- false);
+ work_line_->CheckBinaryOp2addr(this, inst, reg_types_.Float(), reg_types_.Float(),
+ reg_types_.Float(), false);
break;
case Instruction::ADD_DOUBLE_2ADDR:
case Instruction::SUB_DOUBLE_2ADDR:
case Instruction::MUL_DOUBLE_2ADDR:
case Instruction::DIV_DOUBLE_2ADDR:
case Instruction::REM_DOUBLE_2ADDR:
- work_line_->CheckBinaryOp2addrWide(inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ work_line_->CheckBinaryOp2addrWide(this, inst, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.DoubleLo(), reg_types_.DoubleHi(),
reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
@@ -2587,12 +2631,14 @@
case Instruction::MUL_INT_LIT16:
case Instruction::DIV_INT_LIT16:
case Instruction::REM_INT_LIT16:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, true);
+ work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), false,
+ true);
break;
case Instruction::AND_INT_LIT16:
case Instruction::OR_INT_LIT16:
case Instruction::XOR_INT_LIT16:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, true);
+ work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), true,
+ true);
break;
case Instruction::ADD_INT_LIT8:
case Instruction::RSUB_INT_LIT8:
@@ -2602,12 +2648,14 @@
case Instruction::SHL_INT_LIT8:
case Instruction::SHR_INT_LIT8:
case Instruction::USHR_INT_LIT8:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), false, false);
+ work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), false,
+ false);
break;
case Instruction::AND_INT_LIT8:
case Instruction::OR_INT_LIT8:
case Instruction::XOR_INT_LIT8:
- work_line_->CheckLiteralOp(inst, reg_types_.Integer(), reg_types_.Integer(), true, false);
+ work_line_->CheckLiteralOp(this, inst, reg_types_.Integer(), reg_types_.Integer(), true,
+ false);
break;
// Special instructions.
@@ -2631,6 +2679,18 @@
case Instruction::IPUT_QUICK:
VerifyIPutQuick(inst, reg_types_.Integer(), true);
break;
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ VerifyIPutQuick(inst, reg_types_.Boolean(), true);
+ break;
+ case Instruction::IPUT_BYTE_QUICK:
+ VerifyIPutQuick(inst, reg_types_.Byte(), true);
+ break;
+ case Instruction::IPUT_CHAR_QUICK:
+ VerifyIPutQuick(inst, reg_types_.Char(), true);
+ break;
+ case Instruction::IPUT_SHORT_QUICK:
+ VerifyIPutQuick(inst, reg_types_.Short(), true);
+ break;
case Instruction::IPUT_WIDE_QUICK:
VerifyIPutQuick(inst, reg_types_.LongLo(), true);
break;
@@ -2643,10 +2703,9 @@
mirror::ArtMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range);
if (called_method != nullptr) {
const char* descriptor = called_method->GetReturnTypeDescriptor();
- RegType& return_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor,
- false);
+ const RegType& return_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
if (!return_type.IsLowHalf()) {
- work_line_->SetResultRegisterType(return_type);
+ work_line_->SetResultRegisterType(this, return_type);
} else {
work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
}
@@ -2664,10 +2723,6 @@
case Instruction::UNUSED_43:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
- case Instruction::UNUSED_EB:
- case Instruction::UNUSED_EC:
- case Instruction::UNUSED_ED:
- case Instruction::UNUSED_EE:
case Instruction::UNUSED_EF:
case Instruction::UNUSED_F0:
case Instruction::UNUSED_F1:
@@ -2712,7 +2767,7 @@
* not expensive and it makes our debugging output cleaner.)
*/
if (!just_set_result) {
- work_line_->SetResultTypeToUnknown();
+ work_line_->SetResultTypeToUnknown(this);
}
@@ -2737,7 +2792,7 @@
return false;
}
DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0);
- if (!CheckNotMoveException(code_item_->insns_, work_insn_idx_ + branch_target)) {
+ if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, work_insn_idx_ + branch_target)) {
return false;
}
/* update branch target, set "changed" if appropriate */
@@ -2783,7 +2838,7 @@
(((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
abs_offset = work_insn_idx_ + offset;
DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_);
- if (!CheckNotMoveException(code_item_->insns_, abs_offset)) {
+ if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, abs_offset)) {
return false;
}
if (!UpdateRegisters(abs_offset, work_line_.get(), false)) {
@@ -2809,17 +2864,16 @@
has_catch_all_handler = true;
} else {
// It is also a catch-all if it is java.lang.Throwable.
- mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, *dex_cache_,
- *class_loader_);
+ mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_,
+ class_loader_);
if (klass != nullptr) {
if (klass == mirror::Throwable::GetJavaLangThrowable()) {
has_catch_all_handler = true;
}
} else {
// Clear exception.
- Thread* self = Thread::Current();
- DCHECK(self->IsExceptionPending());
- self->ClearException();
+ DCHECK(self_->IsExceptionPending());
+ self_->ClearException();
}
}
/*
@@ -2856,7 +2910,8 @@
* and this change should not be used in those cases.
*/
if ((opcode_flags & Instruction::kContinue) != 0) {
- uint32_t next_insn_idx = work_insn_idx_ + CurrentInsnFlags()->GetLengthInCodeUnits();
+ DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst);
+ uint32_t next_insn_idx = work_insn_idx_ + inst->SizeInCodeUnits();
if (next_insn_idx >= code_item_->insns_size_in_code_units_) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area";
return false;
@@ -2875,12 +2930,12 @@
const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx);
Instruction::Code opcode = ret_inst->Opcode();
if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
- work_line_->MarkAllRegistersAsConflicts();
+ work_line_->MarkAllRegistersAsConflicts(this);
} else {
if (opcode == Instruction::RETURN_WIDE) {
- work_line_->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+ work_line_->MarkAllRegistersAsConflictsExceptWide(this, ret_inst->VRegA_11x());
} else {
- work_line_->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+ work_line_->MarkAllRegistersAsConflictsExcept(this, ret_inst->VRegA_11x());
}
}
}
@@ -2903,7 +2958,7 @@
/* If we're returning from the method, make sure monitor stack is empty. */
if ((opcode_flags & Instruction::kReturn) != 0) {
- if (!work_line_->VerifyMonitorStackEmpty()) {
+ if (!work_line_->VerifyMonitorStackEmpty(this)) {
return false;
}
}
@@ -2915,7 +2970,8 @@
* alone and let the caller sort it out.
*/
if ((opcode_flags & Instruction::kContinue) != 0) {
- *start_guess = work_insn_idx_ + insn_flags_[work_insn_idx_].GetLengthInCodeUnits();
+ DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst);
+ *start_guess = work_insn_idx_ + inst->SizeInCodeUnits();
} else if ((opcode_flags & Instruction::kBranch) != 0) {
/* we're still okay if branch_target is zero */
*start_guess = work_insn_idx_ + branch_target;
@@ -2927,21 +2983,20 @@
return true;
} // NOLINT(readability/fn_size)
-RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
+const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) {
const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
- RegType& referrer = GetDeclaringClass();
- mirror::Class* klass = (*dex_cache_)->GetResolvedType(class_idx);
- RegType& result =
- klass != nullptr ? reg_types_.FromClass(descriptor, klass,
- klass->CannotBeAssignedFromOtherTypes())
- : reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ const RegType& referrer = GetDeclaringClass();
+ mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
+ const RegType& result = klass != nullptr ?
+ reg_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) :
+ reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
if (result.IsConflict()) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
<< "' in " << referrer;
return result;
}
if (klass == nullptr && !result.IsUnresolvedTypes()) {
- (*dex_cache_)->SetResolvedType(class_idx, result.GetClass());
+ dex_cache_->SetResolvedType(class_idx, result.GetClass());
}
// Check if access is allowed. Unresolved types use xxxWithAccessCheck to
// check at runtime if access is allowed and so pass here. If result is
@@ -2954,8 +3009,8 @@
return result;
}
-RegType& MethodVerifier::GetCaughtExceptionType() {
- RegType* common_super = nullptr;
+const RegType& MethodVerifier::GetCaughtExceptionType() {
+ const RegType* common_super = nullptr;
if (code_item_->tries_size_ != 0) {
const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
@@ -2966,7 +3021,7 @@
if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
common_super = ®_types_.JavaLangThrowable(false);
} else {
- RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
+ const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
if (exception.IsUnresolvedTypes()) {
// We don't know enough about the type. Fail here and let runtime handle it.
@@ -3001,7 +3056,7 @@
mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx,
MethodType method_type) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
- RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
+ const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
if (klass_type.IsConflict()) {
std::string append(" in attempt to access method ");
append += dex_file_->GetMethodName(method_id);
@@ -3012,8 +3067,8 @@
return nullptr; // Can't resolve Class so no more to do here
}
mirror::Class* klass = klass_type.GetClass();
- RegType& referrer = GetDeclaringClass();
- mirror::ArtMethod* res_method = (*dex_cache_)->GetResolvedMethod(dex_method_idx);
+ const RegType& referrer = GetDeclaringClass();
+ mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
if (res_method == nullptr) {
const char* name = dex_file_->GetMethodName(method_id);
const Signature signature = dex_file_->GetMethodSignature(method_id);
@@ -3026,7 +3081,7 @@
res_method = klass->FindVirtualMethod(name, signature);
}
if (res_method != nullptr) {
- (*dex_cache_)->SetResolvedMethod(dex_method_idx, res_method);
+ dex_cache_->SetResolvedMethod(dex_method_idx, res_method);
} else {
// If a virtual or interface method wasn't found with the expected type, look in
// the direct methods. This can happen when the wrong invoke type is used or when
@@ -3119,7 +3174,7 @@
* rigorous check here (which is okay since we have to do it at runtime).
*/
if (method_type != METHOD_STATIC) {
- RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+ const RegType& actual_arg_type = work_line_->GetInvocationThis(this, inst, is_range);
if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
CHECK(have_pending_hard_failure_);
return nullptr;
@@ -3140,7 +3195,7 @@
}
}
if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
- RegType* res_method_class;
+ const RegType* res_method_class;
if (res_method != nullptr) {
mirror::Class* klass = res_method->GetDeclaringClass();
std::string temp;
@@ -3149,7 +3204,7 @@
} else {
const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
- res_method_class = ®_types_.FromDescriptor(class_loader_->Get(),
+ res_method_class = ®_types_.FromDescriptor(GetClassLoader(),
dex_file_->StringByTypeIdx(class_idx),
false);
}
@@ -3182,18 +3237,17 @@
return nullptr;
}
- RegType& reg_type = reg_types_.FromDescriptor(class_loader_->Get(), param_descriptor,
- false);
+ const RegType& reg_type = reg_types_.FromDescriptor(GetClassLoader(), param_descriptor, false);
uint32_t get_reg = is_range ? inst->VRegC_3rc() + static_cast<uint32_t>(sig_registers) :
arg[sig_registers];
if (reg_type.IsIntegralTypes()) {
- RegType& src_type = work_line_->GetRegisterType(get_reg);
+ const RegType& src_type = work_line_->GetRegisterType(this, get_reg);
if (!src_type.IsIntegralTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << get_reg << " has type " << src_type
<< " but expected " << reg_type;
return res_method;
}
- } else if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+ } else if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
// Continue on soft failures. We need to find possible hard failures to avoid problems in the
// compiler.
if (have_pending_hard_failure_) {
@@ -3270,7 +3324,7 @@
// has a vtable entry for the target method.
if (is_super) {
DCHECK(method_type == METHOD_VIRTUAL);
- RegType& super = GetDeclaringClass().GetSuperClass(®_types_);
+ const RegType& super = GetDeclaringClass().GetSuperClass(®_types_);
if (super.IsUnresolvedTypes()) {
Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from "
<< PrettyMethod(dex_method_idx_, *dex_file_)
@@ -3298,7 +3352,7 @@
RegisterLine* reg_line, bool is_range) {
DCHECK(inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK ||
inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
- RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range);
+ const RegType& actual_arg_type = reg_line->GetInvocationThis(this, inst, is_range);
if (!actual_arg_type.HasClass()) {
VLOG(verifier) << "Failed to get mirror::Class* from '" << actual_arg_type << "'";
return nullptr;
@@ -3316,9 +3370,10 @@
CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class);
uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
CHECK_LT(static_cast<int32_t>(vtable_index), dispatch_class->GetVTableLength())
- << PrettyDescriptor(klass);
+ << PrettyDescriptor(klass) << " in method "
+ << PrettyMethod(dex_method_idx_, *dex_file_, true);
mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
- CHECK(!Thread::Current()->IsExceptionPending());
+ CHECK(!self_->IsExceptionPending());
return res_method;
}
@@ -3336,7 +3391,7 @@
// We use vAA as our expected arg count, rather than res_method->insSize, because we need to
// match the call to the signature. Also, we might be calling through an abstract method
// definition (which doesn't have register count values).
- RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+ const RegType& actual_arg_type = work_line_->GetInvocationThis(this, inst, is_range);
if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
return nullptr;
}
@@ -3361,7 +3416,7 @@
if (!actual_arg_type.IsZero()) {
mirror::Class* klass = res_method->GetDeclaringClass();
std::string temp;
- RegType& res_method_class =
+ const RegType& res_method_class =
reg_types_.FromClass(klass->GetDescriptor(&temp), klass,
klass->CannotBeAssignedFromOtherTypes());
if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
@@ -3397,9 +3452,9 @@
<< " missing signature component";
return nullptr;
}
- RegType& reg_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ const RegType& reg_type = reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args];
- if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+ if (!work_line_->VerifyRegisterType(this, get_reg, reg_type)) {
return res_method;
}
actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1;
@@ -3425,7 +3480,7 @@
DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE);
type_idx = inst->VRegB_3rc();
}
- RegType& res_type = ResolveClassAndCheckAccess(type_idx);
+ const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
if (res_type.IsConflict()) { // bad class
DCHECK_NE(failures_.size(), 0U);
} else {
@@ -3434,14 +3489,14 @@
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type;
} else if (!is_filled) {
/* make sure "size" register is valid type */
- work_line_->VerifyRegisterType(inst->VRegB_22c(), reg_types_.Integer());
+ work_line_->VerifyRegisterType(this, inst->VRegB_22c(), reg_types_.Integer());
/* set register type to array class */
- RegType& precise_type = reg_types_.FromUninitialized(res_type);
- work_line_->SetRegisterType(inst->VRegA_22c(), precise_type);
+ const RegType& precise_type = reg_types_.FromUninitialized(res_type);
+ work_line_->SetRegisterType(this, inst->VRegA_22c(), precise_type);
} else {
// Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of
// the list and fail. It's legal, if silly, for arg_count to be zero.
- RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_->Get());
+ const RegType& expected_type = reg_types_.GetComponentType(res_type, GetClassLoader());
uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
uint32_t arg[5];
if (!is_range) {
@@ -3449,41 +3504,42 @@
}
for (size_t ui = 0; ui < arg_count; ui++) {
uint32_t get_reg = is_range ? inst->VRegC_3rc() + ui : arg[ui];
- if (!work_line_->VerifyRegisterType(get_reg, expected_type)) {
- work_line_->SetResultRegisterType(reg_types_.Conflict());
+ if (!work_line_->VerifyRegisterType(this, get_reg, expected_type)) {
+ work_line_->SetResultRegisterType(this, reg_types_.Conflict());
return;
}
}
// filled-array result goes into "result" register
- RegType& precise_type = reg_types_.FromUninitialized(res_type);
- work_line_->SetResultRegisterType(precise_type);
+ const RegType& precise_type = reg_types_.FromUninitialized(res_type);
+ work_line_->SetResultRegisterType(this, precise_type);
}
}
}
void MethodVerifier::VerifyAGet(const Instruction* inst,
- RegType& insn_type, bool is_primitive) {
- RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
+ const RegType& insn_type, bool is_primitive) {
+ const RegType& index_type = work_line_->GetRegisterType(this, inst->VRegC_23x());
if (!index_type.IsArrayIndexTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
} else {
- RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x());
+ const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x());
if (array_type.IsZero()) {
// Null array class; this code path will fail at runtime. Infer a merge-able type from the
// instruction type. TODO: have a proper notion of bottom here.
if (!is_primitive || insn_type.IsCategory1Types()) {
// Reference or category 1
- work_line_->SetRegisterType(inst->VRegA_23x(), reg_types_.Zero());
+ work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Zero());
} else {
// Category 2
- work_line_->SetRegisterTypeWide(inst->VRegA_23x(), reg_types_.FromCat2ConstLo(0, false),
+ work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(),
+ reg_types_.FromCat2ConstLo(0, false),
reg_types_.FromCat2ConstHi(0, false));
}
} else if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
} else {
/* verify the class */
- RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->Get());
+ const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
if (!component_type.IsReferenceTypes() && !is_primitive) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
<< " source for aget-object";
@@ -3500,9 +3556,9 @@
// instruction, which can't differentiate object types and ints from floats, longs from
// doubles.
if (!component_type.IsLowHalf()) {
- work_line_->SetRegisterType(inst->VRegA_23x(), component_type);
+ work_line_->SetRegisterType(this, inst->VRegA_23x(), component_type);
} else {
- work_line_->SetRegisterTypeWide(inst->VRegA_23x(), component_type,
+ work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(), component_type,
component_type.HighHalf(®_types_));
}
}
@@ -3510,12 +3566,12 @@
}
}
-void MethodVerifier::VerifyPrimitivePut(RegType& target_type, RegType& insn_type,
+void MethodVerifier::VerifyPrimitivePut(const RegType& target_type, const RegType& insn_type,
const uint32_t vregA) {
// Primitive assignability rules are weaker than regular assignability rules.
bool instruction_compatible;
bool value_compatible;
- RegType& value_type = work_line_->GetRegisterType(vregA);
+ const RegType& value_type = work_line_->GetRegisterType(this, vregA);
if (target_type.IsIntegralTypes()) {
instruction_compatible = target_type.Equals(insn_type);
value_compatible = value_type.IsIntegralTypes();
@@ -3527,7 +3583,7 @@
// Additional register check: this is not checked statically (as part of VerifyInstructions),
// as target_type depends on the resolved type of the field.
if (instruction_compatible && work_line_->NumRegs() > vregA + 1) {
- RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+ const RegType& value_type_hi = work_line_->GetRegisterType(this, vregA + 1);
value_compatible = value_type.IsLongTypes() && value_type.CheckWidePair(value_type_hi);
} else {
value_compatible = false;
@@ -3537,7 +3593,7 @@
// Additional register check: this is not checked statically (as part of VerifyInstructions),
// as target_type depends on the resolved type of the field.
if (instruction_compatible && work_line_->NumRegs() > vregA + 1) {
- RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+ const RegType& value_type_hi = work_line_->GetRegisterType(this, vregA + 1);
value_compatible = value_type.IsDoubleTypes() && value_type.CheckWidePair(value_type_hi);
} else {
value_compatible = false;
@@ -3562,19 +3618,19 @@
}
void MethodVerifier::VerifyAPut(const Instruction* inst,
- RegType& insn_type, bool is_primitive) {
- RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
+ const RegType& insn_type, bool is_primitive) {
+ const RegType& index_type = work_line_->GetRegisterType(this, inst->VRegC_23x());
if (!index_type.IsArrayIndexTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
} else {
- RegType& array_type = work_line_->GetRegisterType(inst->VRegB_23x());
+ const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x());
if (array_type.IsZero()) {
// Null array type; this code path will fail at runtime. Infer a merge-able type from the
// instruction type.
} else if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
} else {
- RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->Get());
+ const RegType& component_type = reg_types_.GetComponentType(array_type, GetClassLoader());
const uint32_t vregA = inst->VRegA_23x();
if (is_primitive) {
VerifyPrimitivePut(component_type, insn_type, vregA);
@@ -3586,7 +3642,7 @@
// The instruction agrees with the type of array, confirm the value to be stored does too
// Note: we use the instruction type (rather than the component type) for aput-object as
// incompatible classes will be caught at runtime as an array store exception
- work_line_->VerifyRegisterType(vregA, insn_type);
+ work_line_->VerifyRegisterType(this, vregA, insn_type);
}
}
}
@@ -3596,7 +3652,7 @@
mirror::ArtField* MethodVerifier::GetStaticField(int field_idx) {
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
// Check access to class
- RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
+ const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
if (klass_type.IsConflict()) { // bad class
AppendToLastFailMessage(StringPrintf(" in attempt to access static field %d (%s) in %s",
field_idx, dex_file_->GetFieldName(field_id),
@@ -3607,14 +3663,14 @@
return nullptr; // Can't resolve Class so no more to do here, will do checking at runtime.
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_,
- *class_loader_);
+ mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_,
+ class_loader_);
if (field == nullptr) {
VLOG(verifier) << "Unable to resolve static field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
<< dex_file_->GetFieldDeclaringClassDescriptor(field_id);
- DCHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
+ DCHECK(self_->IsExceptionPending());
+ self_->ClearException();
return nullptr;
} else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
field->GetAccessFlags())) {
@@ -3628,10 +3684,10 @@
return field;
}
-mirror::ArtField* MethodVerifier::GetInstanceField(RegType& obj_type, int field_idx) {
+mirror::ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) {
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
// Check access to class
- RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
+ const RegType& klass_type = ResolveClassAndCheckAccess(field_id.class_idx_);
if (klass_type.IsConflict()) {
AppendToLastFailMessage(StringPrintf(" in attempt to access instance field %d (%s) in %s",
field_idx, dex_file_->GetFieldName(field_id),
@@ -3642,14 +3698,14 @@
return nullptr; // Can't resolve Class so no more to do here
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_,
- *class_loader_);
+ mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_,
+ class_loader_);
if (field == nullptr) {
VLOG(verifier) << "Unable to resolve instance field " << field_idx << " ("
<< dex_file_->GetFieldName(field_id) << ") in "
<< dex_file_->GetFieldDeclaringClassDescriptor(field_id);
- DCHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
+ DCHECK(self_->IsExceptionPending());
+ self_->ClearException();
return nullptr;
} else if (!GetDeclaringClass().CanAccessMember(field->GetDeclaringClass(),
field->GetAccessFlags())) {
@@ -3670,7 +3726,7 @@
return nullptr;
} else {
mirror::Class* klass = field->GetDeclaringClass();
- RegType& field_klass =
+ const RegType& field_klass =
reg_types_.FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id),
klass, klass->CannotBeAssignedFromOtherTypes());
if (obj_type.IsUninitializedTypes() &&
@@ -3695,22 +3751,21 @@
}
}
-void MethodVerifier::VerifyISGet(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_type,
bool is_primitive, bool is_static) {
uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
mirror::ArtField* field;
if (is_static) {
field = GetStaticField(field_idx);
} else {
- RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+ const RegType& object_type = work_line_->GetRegisterType(this, inst->VRegB_22c());
field = GetInstanceField(object_type, field_idx);
}
- RegType* field_type = nullptr;
+ const RegType* field_type = nullptr;
if (field != nullptr) {
- Thread* self = Thread::Current();
mirror::Class* field_type_class;
{
- StackHandleScope<1> hs(self);
+ StackHandleScope<1> hs(self_);
HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
field_type_class = FieldHelper(h_field).GetType(can_load_classes_);
}
@@ -3718,14 +3773,14 @@
field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
field_type_class->CannotBeAssignedFromOtherTypes());
} else {
- DCHECK(!can_load_classes_ || self->IsExceptionPending());
- self->ClearException();
+ DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+ self_->ClearException();
}
}
if (field_type == nullptr) {
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- field_type = ®_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ field_type = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false);
}
DCHECK(field_type != nullptr);
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
@@ -3750,28 +3805,28 @@
<< " to be compatible with type '" << insn_type
<< "' but found type '" << *field_type
<< "' in Get-object";
- work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+ work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
return;
}
}
if (!field_type->IsLowHalf()) {
- work_line_->SetRegisterType(vregA, *field_type);
+ work_line_->SetRegisterType(this, vregA, *field_type);
} else {
- work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_));
+ work_line_->SetRegisterTypeWide(this, vregA, *field_type, field_type->HighHalf(®_types_));
}
}
-void MethodVerifier::VerifyISPut(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_type,
bool is_primitive, bool is_static) {
uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
mirror::ArtField* field;
if (is_static) {
field = GetStaticField(field_idx);
} else {
- RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
+ const RegType& object_type = work_line_->GetRegisterType(this, inst->VRegB_22c());
field = GetInstanceField(object_type, field_idx);
}
- RegType* field_type = nullptr;
+ const RegType* field_type = nullptr;
if (field != nullptr) {
if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
@@ -3780,7 +3835,7 @@
}
mirror::Class* field_type_class;
{
- StackHandleScope<1> hs(Thread::Current());
+ StackHandleScope<1> hs(self_);
HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
FieldHelper fh(h_field);
field_type_class = fh.GetType(can_load_classes_);
@@ -3789,15 +3844,14 @@
field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
field_type_class->CannotBeAssignedFromOtherTypes());
} else {
- Thread* self = Thread::Current();
- DCHECK(!can_load_classes_ || self->IsExceptionPending());
- self->ClearException();
+ DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+ self_->ClearException();
}
}
if (field_type == nullptr) {
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- field_type = ®_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ field_type = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false);
}
DCHECK(field_type != nullptr);
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
@@ -3811,7 +3865,7 @@
<< "' in put-object";
return;
}
- work_line_->VerifyRegisterType(vregA, *field_type);
+ work_line_->VerifyRegisterType(this, vregA, *field_type);
}
}
@@ -3822,8 +3876,12 @@
inst->Opcode() == Instruction::IGET_OBJECT_QUICK ||
inst->Opcode() == Instruction::IPUT_QUICK ||
inst->Opcode() == Instruction::IPUT_WIDE_QUICK ||
- inst->Opcode() == Instruction::IPUT_OBJECT_QUICK);
- RegType& object_type = reg_line->GetRegisterType(inst->VRegB_22c());
+ inst->Opcode() == Instruction::IPUT_OBJECT_QUICK ||
+ inst->Opcode() == Instruction::IPUT_BOOLEAN_QUICK ||
+ inst->Opcode() == Instruction::IPUT_BYTE_QUICK ||
+ inst->Opcode() == Instruction::IPUT_CHAR_QUICK ||
+ inst->Opcode() == Instruction::IPUT_SHORT_QUICK);
+ const RegType& object_type = reg_line->GetRegisterType(this, inst->VRegB_22c());
if (!object_type.HasClass()) {
VLOG(verifier) << "Failed to get mirror::Class* from '" << object_type << "'";
return nullptr;
@@ -3838,7 +3896,7 @@
return f;
}
-void MethodVerifier::VerifyIGetQuick(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
bool is_primitive) {
DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get());
@@ -3848,19 +3906,18 @@
}
mirror::Class* field_type_class;
{
- StackHandleScope<1> hs(Thread::Current());
+ StackHandleScope<1> hs(self_);
HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field));
FieldHelper fh(h_field);
field_type_class = fh.GetType(can_load_classes_);
}
- RegType* field_type;
+ const RegType* field_type;
if (field_type_class != nullptr) {
field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
field_type_class->CannotBeAssignedFromOtherTypes());
} else {
- Thread* self = Thread::Current();
- DCHECK(!can_load_classes_ || self->IsExceptionPending());
- self->ClearException();
+ DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+ self_->ClearException();
field_type = ®_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(),
field->GetTypeDescriptor(), false);
}
@@ -3887,18 +3944,18 @@
<< " to be compatible with type '" << insn_type
<< "' but found type '" << *field_type
<< "' in get-object";
- work_line_->SetRegisterType(vregA, reg_types_.Conflict());
+ work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
return;
}
}
if (!field_type->IsLowHalf()) {
- work_line_->SetRegisterType(vregA, *field_type);
+ work_line_->SetRegisterType(this, vregA, *field_type);
} else {
- work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_));
+ work_line_->SetRegisterTypeWide(this, vregA, *field_type, field_type->HighHalf(®_types_));
}
}
-void MethodVerifier::VerifyIPutQuick(const Instruction* inst, RegType& insn_type,
+void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
bool is_primitive) {
DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_);
mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get());
@@ -3908,7 +3965,7 @@
}
const char* descriptor = field->GetTypeDescriptor();
mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
- RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
if (field != nullptr) {
if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
@@ -3921,7 +3978,7 @@
// Primitive field assignability rules are weaker than regular assignability rules
bool instruction_compatible;
bool value_compatible;
- RegType& value_type = work_line_->GetRegisterType(vregA);
+ const RegType& value_type = work_line_->GetRegisterType(this, vregA);
if (field_type.IsIntegralTypes()) {
instruction_compatible = insn_type.IsIntegralTypes();
value_compatible = value_type.IsIntegralTypes();
@@ -3963,7 +4020,7 @@
<< "' in put-object";
return;
}
- work_line_->VerifyRegisterType(vregA, field_type);
+ work_line_->VerifyRegisterType(this, vregA, field_type);
}
}
@@ -3975,6 +4032,19 @@
return true;
}
+bool MethodVerifier::CheckNotMoveResult(const uint16_t* insns, int insn_idx) {
+ if (((insns[insn_idx] & 0xff) >= Instruction::MOVE_RESULT) &&
+ ((insns[insn_idx] & 0xff) <= Instruction::MOVE_RESULT_OBJECT)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid use of move-result*";
+ return false;
+ }
+ return true;
+}
+
+bool MethodVerifier::CheckNotMoveExceptionOrMoveResult(const uint16_t* insns, int insn_idx) {
+ return (CheckNotMoveException(insns, insn_idx) && CheckNotMoveResult(insns, insn_idx));
+}
+
bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_line,
bool update_merge_line) {
bool changed = true;
@@ -3989,7 +4059,7 @@
target_line->CopyFromLine(merge_line);
} else {
// Verify that the monitor stack is empty on return.
- if (!merge_line->VerifyMonitorStackEmpty()) {
+ if (!merge_line->VerifyMonitorStackEmpty(this)) {
return false;
}
// For returns we only care about the operand to the return, all other registers are dead.
@@ -3997,33 +4067,33 @@
const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
Instruction::Code opcode = ret_inst->Opcode();
if ((opcode == Instruction::RETURN_VOID) || (opcode == Instruction::RETURN_VOID_BARRIER)) {
- target_line->MarkAllRegistersAsConflicts();
+ target_line->MarkAllRegistersAsConflicts(this);
} else {
target_line->CopyFromLine(merge_line);
if (opcode == Instruction::RETURN_WIDE) {
- target_line->MarkAllRegistersAsConflictsExceptWide(ret_inst->VRegA_11x());
+ target_line->MarkAllRegistersAsConflictsExceptWide(this, ret_inst->VRegA_11x());
} else {
- target_line->MarkAllRegistersAsConflictsExcept(ret_inst->VRegA_11x());
+ target_line->MarkAllRegistersAsConflictsExcept(this, ret_inst->VRegA_11x());
}
}
}
} else {
std::unique_ptr<RegisterLine> copy(gDebugVerify ?
- RegisterLine::Create(target_line->NumRegs(), this) :
- nullptr);
+ RegisterLine::Create(target_line->NumRegs(), this) :
+ nullptr);
if (gDebugVerify) {
copy->CopyFromLine(target_line);
}
- changed = target_line->MergeRegisters(merge_line);
+ changed = target_line->MergeRegisters(this, merge_line);
if (have_pending_hard_failure_) {
return false;
}
if (gDebugVerify && changed) {
LogVerifyInfo() << "Merging at [" << reinterpret_cast<void*>(work_insn_idx_) << "]"
<< " to [" << reinterpret_cast<void*>(next_insn) << "]: " << "\n"
- << *copy.get() << " MERGE\n"
- << *merge_line << " ==\n"
- << *target_line << "\n";
+ << copy->Dump(this) << " MERGE\n"
+ << merge_line->Dump(this) << " ==\n"
+ << target_line->Dump(this) << "\n";
}
if (update_merge_line && changed) {
merge_line->CopyFromLine(target_line);
@@ -4039,23 +4109,19 @@
return &insn_flags_[work_insn_idx_];
}
-RegType& MethodVerifier::GetMethodReturnType() {
+const RegType& MethodVerifier::GetMethodReturnType() {
if (return_type_ == nullptr) {
- if (mirror_method_ != nullptr) {
- Thread* self = Thread::Current();
- StackHandleScope<1> hs(self);
- mirror::Class* return_type_class;
- {
- HandleWrapper<mirror::ArtMethod> h_mirror_method(hs.NewHandleWrapper(&mirror_method_));
- return_type_class = MethodHelper(h_mirror_method).GetReturnType(can_load_classes_);
- }
+ if (mirror_method_.Get() != nullptr) {
+ StackHandleScope<1> hs(self_);
+ mirror::Class* return_type_class =
+ MethodHelper(hs.NewHandle(mirror_method_.Get())).GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type_ = ®_types_.FromClass(mirror_method_->GetReturnTypeDescriptor(),
return_type_class,
return_type_class->CannotBeAssignedFromOtherTypes());
} else {
- DCHECK(!can_load_classes_ || self->IsExceptionPending());
- self->ClearException();
+ DCHECK(!can_load_classes_ || self_->IsExceptionPending());
+ self_->ClearException();
}
}
if (return_type_ == nullptr) {
@@ -4063,23 +4129,23 @@
const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
uint16_t return_type_idx = proto_id.return_type_idx_;
const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
- return_type_ = ®_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ return_type_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false);
}
}
return *return_type_;
}
-RegType& MethodVerifier::GetDeclaringClass() {
+const RegType& MethodVerifier::GetDeclaringClass() {
if (declaring_class_ == nullptr) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
const char* descriptor
= dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
- if (mirror_method_ != nullptr) {
+ if (mirror_method_.Get() != nullptr) {
mirror::Class* klass = mirror_method_->GetDeclaringClass();
declaring_class_ = ®_types_.FromClass(descriptor, klass,
klass->CannotBeAssignedFromOtherTypes());
} else {
- declaring_class_ = ®_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
+ declaring_class_ = ®_types_.FromDescriptor(GetClassLoader(), descriptor, false);
}
}
return *declaring_class_;
@@ -4090,16 +4156,19 @@
DCHECK(line != nullptr) << "No register line at DEX pc " << StringPrintf("0x%x", dex_pc);
std::vector<int32_t> result;
for (size_t i = 0; i < line->NumRegs(); ++i) {
- RegType& type = line->GetRegisterType(i);
+ const RegType& type = line->GetRegisterType(this, i);
if (type.IsConstant()) {
result.push_back(type.IsPreciseConstant() ? kConstant : kImpreciseConstant);
- result.push_back(type.ConstantValue());
+ const ConstantType* const_val = down_cast<const ConstantType*>(&type);
+ result.push_back(const_val->ConstantValue());
} else if (type.IsConstantLo()) {
result.push_back(type.IsPreciseConstantLo() ? kConstant : kImpreciseConstant);
- result.push_back(type.ConstantValueLo());
+ const ConstantType* const_val = down_cast<const ConstantType*>(&type);
+ result.push_back(const_val->ConstantValueLo());
} else if (type.IsConstantHi()) {
result.push_back(type.IsPreciseConstantHi() ? kConstant : kImpreciseConstant);
- result.push_back(type.ConstantValueHi());
+ const ConstantType* const_val = down_cast<const ConstantType*>(&type);
+ result.push_back(const_val->ConstantValueHi());
} else if (type.IsIntegralTypes()) {
result.push_back(kIntVReg);
result.push_back(0);
@@ -4130,7 +4199,7 @@
return result;
}
-RegType& MethodVerifier::DetermineCat1Constant(int32_t value, bool precise) {
+const RegType& MethodVerifier::DetermineCat1Constant(int32_t value, bool precise) {
if (precise) {
// Precise constant type.
return reg_types_.FromCat1Const(value, true);
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 17713d2..9f5efe8 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -27,6 +27,7 @@
#include "class_reference.h"
#include "dex_file.h"
#include "dex_instruction.h"
+#include "handle.h"
#include "instruction_flags.h"
#include "method_reference.h"
#include "reg_type.h"
@@ -139,21 +140,23 @@
};
/* Verify a class. Returns "kNoFailure" on success. */
- static FailureKind VerifyClass(mirror::Class* klass, bool allow_soft_failures, std::string* error)
+ static FailureKind VerifyClass(Thread* self, mirror::Class* klass, bool allow_soft_failures,
+ std::string* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static FailureKind VerifyClass(const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+ static FailureKind VerifyClass(Thread* self, const DexFile* dex_file,
+ Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
bool allow_soft_failures, std::string* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static MethodVerifier* VerifyMethodAndDump(std::ostream& os, uint32_t method_idx,
+ static MethodVerifier* VerifyMethodAndDump(Thread* self, std::ostream& os, uint32_t method_idx,
const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def,
const DexFile::CodeItem* code_item,
- mirror::ArtMethod* method,
+ Handle<mirror::ArtMethod> method,
uint32_t method_access_flags)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -203,14 +206,15 @@
return can_load_classes_;
}
- MethodVerifier(const DexFile* dex_file, Handle<mirror::DexCache>* dex_cache,
- Handle<mirror::ClassLoader>* class_loader, const DexFile::ClassDef* class_def,
- const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method,
+ MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
+ const DexFile::CodeItem* code_item, uint32_t method_idx,
+ Handle<mirror::ArtMethod> method,
uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
bool need_precise_constants) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : MethodVerifier(dex_file, dex_cache, class_loader, class_def, code_item, method_idx, method,
- access_flags, can_load_classes, allow_soft_failures, need_precise_constants,
- false) {}
+ : MethodVerifier(self, dex_file, dex_cache, class_loader, class_def, code_item, method_idx,
+ method, access_flags, can_load_classes, allow_soft_failures,
+ need_precise_constants, false) {}
~MethodVerifier();
@@ -236,16 +240,17 @@
bool HasCheckCasts() const;
bool HasVirtualOrInterfaceInvokes() const;
bool HasFailures() const;
- RegType& ResolveCheckedClass(uint32_t class_idx)
+ const RegType& ResolveCheckedClass(uint32_t class_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
// Private constructor for dumping.
- MethodVerifier(const DexFile* dex_file, Handle<mirror::DexCache>* dex_cache,
- Handle<mirror::ClassLoader>* class_loader, const DexFile::ClassDef* class_def,
- const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method,
- uint32_t access_flags, bool can_load_classes, bool allow_soft_failures,
- bool need_precise_constants, bool verify_to_dump)
+ MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader, const DexFile::ClassDef* class_def,
+ const DexFile::CodeItem* code_item, uint32_t method_idx,
+ Handle<mirror::ArtMethod> method, uint32_t access_flags,
+ bool can_load_classes, bool allow_soft_failures, bool need_precise_constants,
+ bool verify_to_dump)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Adds the given string to the beginning of the last failure message.
@@ -265,12 +270,12 @@
* (3) Iterate through the method, checking type safety and looking
* for code flow problems.
*/
- static FailureKind VerifyMethod(uint32_t method_idx, const DexFile* dex_file,
+ static FailureKind VerifyMethod(Thread* self, uint32_t method_idx, const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
const DexFile::ClassDef* class_def_idx,
const DexFile::CodeItem* code_item,
- mirror::ArtMethod* method, uint32_t method_access_flags,
+ Handle<mirror::ArtMethod> method, uint32_t method_access_flags,
bool allow_soft_failures, bool need_precise_constants)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -485,34 +490,34 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Helper to perform verification on puts of primitive type.
- void VerifyPrimitivePut(RegType& target_type, RegType& insn_type,
+ void VerifyPrimitivePut(const RegType& target_type, const RegType& insn_type,
const uint32_t vregA) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Perform verification of an aget instruction. The destination register's type will be set to
// be that of component type of the array unless the array type is unknown, in which case a
// bottom type inferred from the type of instruction is used. is_primitive is false for an
// aget-object.
- void VerifyAGet(const Instruction* inst, RegType& insn_type,
+ void VerifyAGet(const Instruction* inst, const RegType& insn_type,
bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Perform verification of an aput instruction.
- void VerifyAPut(const Instruction* inst, RegType& insn_type,
+ void VerifyAPut(const Instruction* inst, const RegType& insn_type,
bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Lookup instance field and fail for resolution violations
- mirror::ArtField* GetInstanceField(RegType& obj_type, int field_idx)
+ mirror::ArtField* GetInstanceField(const RegType& obj_type, int field_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Lookup static field and fail for resolution violations
mirror::ArtField* GetStaticField(int field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Perform verification of an iget or sget instruction.
- void VerifyISGet(const Instruction* inst, RegType& insn_type,
+ void VerifyISGet(const Instruction* inst, const RegType& insn_type,
bool is_primitive, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Perform verification of an iput or sput instruction.
- void VerifyISPut(const Instruction* inst, RegType& insn_type,
+ void VerifyISPut(const Instruction* inst, const RegType& insn_type,
bool is_primitive, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -522,18 +527,18 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Perform verification of an iget-quick instruction.
- void VerifyIGetQuick(const Instruction* inst, RegType& insn_type,
+ void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
bool is_primitive)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Perform verification of an iput-quick instruction.
- void VerifyIPutQuick(const Instruction* inst, RegType& insn_type,
+ void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
bool is_primitive)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Resolves a class based on an index and performs access checks to ensure the referrer can
// access the resolved class.
- RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
+ const RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -541,7 +546,7 @@
* address, determine the Join of all exceptions that can land here. Fails if no matching
* exception handler can be found or if the Join of exception types fails.
*/
- RegType& GetCaughtExceptionType()
+ const RegType& GetCaughtExceptionType()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -607,6 +612,21 @@
bool CheckNotMoveException(const uint16_t* insns, int insn_idx);
/*
+ * Verify that the target instruction is not "move-result". It is important that we cannot
+ * branch to move-result instructions, but we have to make this a distinct check instead of
+ * adding it to CheckNotMoveException, because it is legal to continue into "move-result"
+ * instructions - as long as the previous instruction was an invoke, which is checked elsewhere.
+ */
+ bool CheckNotMoveResult(const uint16_t* insns, int insn_idx);
+
+ /*
+ * Verify that the target instruction is not "move-result" or "move-exception". This is to
+ * be used when checking branch and switch instructions, but not instructions that can
+ * continue.
+ */
+ bool CheckNotMoveExceptionOrMoveResult(const uint16_t* insns, int insn_idx);
+
+ /*
* Control can transfer to "next_insn". Merge the registers from merge_line into the table at
* next_insn, and set the changed flag on the target address if any of the registers were changed.
* In the case of fall-through, update the merge line on a change as its the working line for the
@@ -627,16 +647,19 @@
}
// Return the register type for the method.
- RegType& GetMethodReturnType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& GetMethodReturnType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get a type representing the declaring class of the method.
- RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
InstructionFlags* CurrentInsnFlags();
- RegType& DetermineCat1Constant(int32_t value, bool precise)
+ const RegType& DetermineCat1Constant(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // The thread we're verifying on.
+ Thread* const self_;
+
RegTypeCache reg_types_;
PcToRegisterLineTable reg_table_;
@@ -653,17 +676,17 @@
const uint32_t dex_method_idx_; // The method we're working on.
// Its object representation if known.
- mirror::ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_);
+ Handle<mirror::ArtMethod> mirror_method_ GUARDED_BY(Locks::mutator_lock_);
const uint32_t method_access_flags_; // Method's access flags.
- RegType* return_type_; // Lazily computed return type of the method.
+ const RegType* return_type_; // Lazily computed return type of the method.
const DexFile* const dex_file_; // The dex file containing the method.
// The dex_cache for the declaring class of the method.
- Handle<mirror::DexCache>* dex_cache_ GUARDED_BY(Locks::mutator_lock_);
+ Handle<mirror::DexCache> dex_cache_ GUARDED_BY(Locks::mutator_lock_);
// The class loader for the declaring class of the method.
- Handle<mirror::ClassLoader>* class_loader_ GUARDED_BY(Locks::mutator_lock_);
+ Handle<mirror::ClassLoader> class_loader_ GUARDED_BY(Locks::mutator_lock_);
const DexFile::ClassDef* const class_def_; // The class def of the declaring class of the method.
const DexFile::CodeItem* const code_item_; // The code item containing the code for the method.
- RegType* declaring_class_; // Lazily computed reg type of the method's declaring class.
+ const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class.
// Instruction widths and flags, one entry per code unit.
std::unique_ptr<InstructionFlags[]> insn_flags_;
// The dex PC of a FindLocksAtDexPc request, -1 otherwise.
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index a5895e6..770ca7e 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -32,11 +32,12 @@
void VerifyClass(const std::string& descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ASSERT_TRUE(descriptor != NULL);
- mirror::Class* klass = class_linker_->FindSystemClass(Thread::Current(), descriptor.c_str());
+ Thread* self = Thread::Current();
+ mirror::Class* klass = class_linker_->FindSystemClass(self, descriptor.c_str());
// Verify the class
std::string error_msg;
- ASSERT_TRUE(MethodVerifier::VerifyClass(klass, true, &error_msg) == MethodVerifier::kNoFailure)
+ ASSERT_TRUE(MethodVerifier::VerifyClass(self, klass, true, &error_msg) == MethodVerifier::kNoFailure)
<< error_msg;
}
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
new file mode 100644
index 0000000..480ed40
--- /dev/null
+++ b/runtime/verifier/reg_type-inl.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 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_VERIFIER_REG_TYPE_INL_H_
+#define ART_RUNTIME_VERIFIER_REG_TYPE_INL_H_
+
+#include "reg_type.h"
+
+#include "base/casts.h"
+#include "mirror/class.h"
+
+namespace art {
+namespace verifier {
+
+inline bool RegType::CanAccess(const RegType& other) const {
+ if (Equals(other)) {
+ return true; // Trivial accessibility.
+ } else {
+ bool this_unresolved = IsUnresolvedTypes();
+ bool other_unresolved = other.IsUnresolvedTypes();
+ if (!this_unresolved && !other_unresolved) {
+ return GetClass()->CanAccess(other.GetClass());
+ } else if (!other_unresolved) {
+ return other.GetClass()->IsPublic(); // Be conservative, only allow if other is public.
+ } else {
+ return false; // More complicated test not possible on unresolved types, be conservative.
+ }
+ }
+}
+
+inline bool RegType::CanAccessMember(mirror::Class* klass, uint32_t access_flags) const {
+ if ((access_flags & kAccPublic) != 0) {
+ return true;
+ }
+ if (!IsUnresolvedTypes()) {
+ return GetClass()->CanAccessMember(klass, access_flags);
+ } else {
+ return false; // More complicated test not possible on unresolved types, be conservative.
+ }
+}
+
+inline bool RegType::IsConstantBoolean() const {
+ if (!IsConstant()) {
+ return false;
+ } else {
+ const ConstantType* const_val = down_cast<const ConstantType*>(this);
+ return const_val->ConstantValue() >= 0 && const_val->ConstantValue() <= 1;
+ }
+}
+
+inline bool RegType::AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict) {
+ if (lhs.Equals(rhs)) {
+ return true;
+ } else {
+ if (lhs.IsBoolean()) {
+ return rhs.IsBooleanTypes();
+ } else if (lhs.IsByte()) {
+ return rhs.IsByteTypes();
+ } else if (lhs.IsShort()) {
+ return rhs.IsShortTypes();
+ } else if (lhs.IsChar()) {
+ return rhs.IsCharTypes();
+ } else if (lhs.IsInteger()) {
+ return rhs.IsIntegralTypes();
+ } else if (lhs.IsFloat()) {
+ return rhs.IsFloatTypes();
+ } else if (lhs.IsLongLo()) {
+ return rhs.IsLongTypes();
+ } else if (lhs.IsDoubleLo()) {
+ return rhs.IsDoubleTypes();
+ } else {
+ CHECK(lhs.IsReferenceTypes())
+ << "Unexpected register type in IsAssignableFrom: '"
+ << lhs << "' := '" << rhs << "'";
+ if (rhs.IsZero()) {
+ return true; // All reference types can be assigned null.
+ } else if (!rhs.IsReferenceTypes()) {
+ return false; // Expect rhs to be a reference type.
+ } else if (lhs.IsJavaLangObject()) {
+ return true; // All reference types can be assigned to Object.
+ } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
+ // If we're not strict allow assignment to any interface, see comment in ClassJoin.
+ return true;
+ } else if (lhs.IsJavaLangObjectArray()) {
+ return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
+ } else if (lhs.HasClass() && rhs.HasClass() &&
+ lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) {
+ // We're assignable from the Class point-of-view.
+ return true;
+ } else {
+ // Unresolved types are only assignable for null and equality.
+ return false;
+ }
+ }
+ }
+}
+
+inline bool RegType::IsAssignableFrom(const RegType& src) const {
+ return AssignableFrom(*this, src, false);
+}
+
+inline bool RegType::IsStrictlyAssignableFrom(const RegType& src) const {
+ return AssignableFrom(*this, src, true);
+}
+
+inline const DoubleHiType* DoubleHiType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const DoubleLoType* DoubleLoType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const LongHiType* LongHiType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const LongLoType* LongLoType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const FloatType* FloatType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const CharType* CharType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const ShortType* ShortType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const ByteType* ByteType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+
+inline const IntegerType* IntegerType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const BooleanType* BooleanType::GetInstance() {
+ DCHECK(BooleanType::instance_ != nullptr);
+ return BooleanType::instance_;
+}
+
+inline const ConflictType* ConflictType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+inline const UndefinedType* UndefinedType::GetInstance() {
+ DCHECK(instance_ != nullptr);
+ return instance_;
+}
+
+} // namespace verifier
+} // namespace art
+
+#endif // ART_RUNTIME_VERIFIER_REG_TYPE_INL_H_
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 30be82f..41541b5 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#include "reg_type.h"
-
+#include "reg_type-inl.h"
#include "base/casts.h"
#include "class_linker-inl.h"
@@ -33,41 +32,23 @@
namespace art {
namespace verifier {
-UndefinedType* UndefinedType::instance_ = NULL;
-ConflictType* ConflictType::instance_ = NULL;
-BooleanType* BooleanType::instance = NULL;
-ByteType* ByteType::instance_ = NULL;
-ShortType* ShortType::instance_ = NULL;
-CharType* CharType::instance_ = NULL;
-FloatType* FloatType::instance_ = NULL;
-LongLoType* LongLoType::instance_ = NULL;
-LongHiType* LongHiType::instance_ = NULL;
-DoubleLoType* DoubleLoType::instance_ = NULL;
-DoubleHiType* DoubleHiType::instance_ = NULL;
-IntegerType* IntegerType::instance_ = NULL;
-
-int32_t RegType::ConstantValue() const {
- ScopedObjectAccess soa(Thread::Current());
- LOG(FATAL) << "Unexpected call to ConstantValue: " << *this;
- return 0;
-}
-
-int32_t RegType::ConstantValueLo() const {
- ScopedObjectAccess soa(Thread::Current());
- LOG(FATAL) << "Unexpected call to ConstantValueLo: " << *this;
- return 0;
-}
-
-int32_t RegType::ConstantValueHi() const {
- ScopedObjectAccess soa(Thread::Current());
- LOG(FATAL) << "Unexpected call to ConstantValueHi: " << *this;
- return 0;
-}
+const UndefinedType* UndefinedType::instance_ = nullptr;
+const ConflictType* ConflictType::instance_ = nullptr;
+const BooleanType* BooleanType::instance_ = nullptr;
+const ByteType* ByteType::instance_ = nullptr;
+const ShortType* ShortType::instance_ = nullptr;
+const CharType* CharType::instance_ = nullptr;
+const FloatType* FloatType::instance_ = nullptr;
+const LongLoType* LongLoType::instance_ = nullptr;
+const LongHiType* LongHiType::instance_ = nullptr;
+const DoubleLoType* DoubleLoType::instance_ = nullptr;
+const DoubleHiType* DoubleHiType::instance_ = nullptr;
+const IntegerType* IntegerType::instance_ = nullptr;
PrimitiveType::PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: RegType(klass, descriptor, cache_id) {
- CHECK(klass != NULL);
+ CHECK(klass != nullptr);
CHECK(!descriptor.empty());
}
@@ -81,7 +62,7 @@
: PrimitiveType(klass, descriptor, cache_id) {
}
-std::string PreciseConstType::Dump() {
+std::string PreciseConstType::Dump() const {
std::stringstream result;
uint32_t val = ConstantValue();
if (val == 0) {
@@ -98,290 +79,223 @@
return result.str();
}
-std::string BooleanType::Dump() {
+std::string BooleanType::Dump() const {
return "Boolean";
}
-std::string ConflictType::Dump() {
+std::string ConflictType::Dump() const {
return "Conflict";
}
-std::string ByteType::Dump() {
+std::string ByteType::Dump() const {
return "Byte";
}
-std::string ShortType::Dump() {
+std::string ShortType::Dump() const {
return "Short";
}
-std::string CharType::Dump() {
+std::string CharType::Dump() const {
return "Char";
}
-std::string FloatType::Dump() {
+std::string FloatType::Dump() const {
return "Float";
}
-std::string LongLoType::Dump() {
+std::string LongLoType::Dump() const {
return "Long (Low Half)";
}
-std::string LongHiType::Dump() {
+std::string LongHiType::Dump() const {
return "Long (High Half)";
}
-std::string DoubleLoType::Dump() {
+std::string DoubleLoType::Dump() const {
return "Double (Low Half)";
}
-std::string DoubleHiType::Dump() {
+std::string DoubleHiType::Dump() const {
return "Double (High Half)";
}
-std::string IntegerType::Dump() {
+std::string IntegerType::Dump() const {
return "Integer";
}
-DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new DoubleHiType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-DoubleHiType* DoubleHiType::GetInstance() {
- CHECK(instance_ != NULL);
+const DoubleHiType* DoubleHiType::CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new DoubleHiType(klass, descriptor, cache_id);
return instance_;
}
void DoubleHiType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new DoubleLoType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-DoubleLoType* DoubleLoType::GetInstance() {
- CHECK(instance_ != NULL);
+const DoubleLoType* DoubleLoType::CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new DoubleLoType(klass, descriptor, cache_id);
return instance_;
}
void DoubleLoType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new LongLoType(klass, descriptor, cache_id);
- }
+const LongLoType* LongLoType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new LongLoType(klass, descriptor, cache_id);
return instance_;
}
-LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new LongHiType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-LongHiType* LongHiType::GetInstance() {
- CHECK(instance_ != NULL);
+const LongHiType* LongHiType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new LongHiType(klass, descriptor, cache_id);
return instance_;
}
void LongHiType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-LongLoType* LongLoType::GetInstance() {
- CHECK(instance_ != NULL);
- return instance_;
-}
-
void LongLoType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new FloatType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-FloatType* FloatType::GetInstance() {
- CHECK(instance_ != NULL);
+const FloatType* FloatType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new FloatType(klass, descriptor, cache_id);
return instance_;
}
void FloatType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new CharType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-CharType* CharType::GetInstance() {
- CHECK(instance_ != NULL);
+const CharType* CharType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new CharType(klass, descriptor, cache_id);
return instance_;
}
void CharType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new ShortType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-ShortType* ShortType::GetInstance() {
- CHECK(instance_ != NULL);
+const ShortType* ShortType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new ShortType(klass, descriptor, cache_id);
return instance_;
}
void ShortType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new ByteType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-ByteType* ByteType::GetInstance() {
- CHECK(instance_ != NULL);
+const ByteType* ByteType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new ByteType(klass, descriptor, cache_id);
return instance_;
}
void ByteType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new IntegerType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-IntegerType* IntegerType::GetInstance() {
- CHECK(instance_ != NULL);
+const IntegerType* IntegerType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new IntegerType(klass, descriptor, cache_id);
return instance_;
}
void IntegerType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-ConflictType* ConflictType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new ConflictType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-ConflictType* ConflictType::GetInstance() {
- CHECK(instance_ != NULL);
+const ConflictType* ConflictType::CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new ConflictType(klass, descriptor, cache_id);
return instance_;
}
void ConflictType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
-BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
+const BooleanType* BooleanType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
uint16_t cache_id) {
- if (BooleanType::instance == NULL) {
- instance = new BooleanType(klass, descriptor, cache_id);
- }
- return BooleanType::instance;
-}
-
-BooleanType* BooleanType::GetInstance() {
- CHECK(BooleanType::instance != NULL);
- return BooleanType::instance;
+ CHECK(BooleanType::instance_ == nullptr);
+ instance_ = new BooleanType(klass, descriptor, cache_id);
+ return BooleanType::instance_;
}
void BooleanType::Destroy() {
- if (BooleanType::instance != NULL) {
- delete instance;
- instance = NULL;
+ if (BooleanType::instance_ != nullptr) {
+ delete instance_;
+ instance_ = nullptr;
}
}
-std::string UndefinedType::Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+std::string UndefinedType::Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return "Undefined";
}
-UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id) {
- if (instance_ == NULL) {
- instance_ = new UndefinedType(klass, descriptor, cache_id);
- }
- return instance_;
-}
-
-UndefinedType* UndefinedType::GetInstance() {
- CHECK(instance_ != NULL);
+const UndefinedType* UndefinedType::CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id) {
+ CHECK(instance_ == nullptr);
+ instance_ = new UndefinedType(klass, descriptor, cache_id);
return instance_;
}
void UndefinedType::Destroy() {
- if (instance_ != NULL) {
+ if (instance_ != nullptr) {
delete instance_;
- instance_ = NULL;
+ instance_ = nullptr;
}
}
@@ -391,7 +305,7 @@
DCHECK(klass->IsInstantiable());
}
-std::string UnresolvedMergedType::Dump() {
+std::string UnresolvedMergedType::Dump() const {
std::stringstream result;
std::set<uint16_t> types = GetMergedTypes();
result << "UnresolvedMergedReferences(";
@@ -405,20 +319,20 @@
return result.str();
}
-std::string UnresolvedSuperClass::Dump() {
+std::string UnresolvedSuperClass::Dump() const {
std::stringstream result;
uint16_t super_type_id = GetUnresolvedSuperClassChildId();
result << "UnresolvedSuperClass(" << reg_type_cache_->GetFromId(super_type_id).Dump() << ")";
return result.str();
}
-std::string UnresolvedReferenceType::Dump() {
+std::string UnresolvedReferenceType::Dump() const {
std::stringstream result;
result << "Unresolved Reference" << ": " << PrettyDescriptor(GetDescriptor().c_str());
return result.str();
}
-std::string UnresolvedUninitializedRefType::Dump() {
+std::string UnresolvedUninitializedRefType::Dump() const {
std::stringstream result;
result << "Unresolved And Uninitialized Reference" << ": "
<< PrettyDescriptor(GetDescriptor().c_str())
@@ -426,40 +340,40 @@
return result.str();
}
-std::string UnresolvedUninitializedThisRefType::Dump() {
+std::string UnresolvedUninitializedThisRefType::Dump() const {
std::stringstream result;
result << "Unresolved And Uninitialized This Reference"
<< PrettyDescriptor(GetDescriptor().c_str());
return result.str();
}
-std::string ReferenceType::Dump() {
+std::string ReferenceType::Dump() const {
std::stringstream result;
result << "Reference" << ": " << PrettyDescriptor(GetClass());
return result.str();
}
-std::string PreciseReferenceType::Dump() {
+std::string PreciseReferenceType::Dump() const {
std::stringstream result;
result << "Precise Reference" << ": "<< PrettyDescriptor(GetClass());
return result.str();
}
-std::string UninitializedReferenceType::Dump() {
+std::string UninitializedReferenceType::Dump() const {
std::stringstream result;
result << "Uninitialized Reference" << ": " << PrettyDescriptor(GetClass());
result << " Allocation PC: " << GetAllocationPc();
return result.str();
}
-std::string UninitializedThisReferenceType::Dump() {
+std::string UninitializedThisReferenceType::Dump() const {
std::stringstream result;
result << "Uninitialized This Reference" << ": " << PrettyDescriptor(GetClass());
result << "Allocation PC: " << GetAllocationPc();
return result.str();
}
-std::string ImpreciseConstType::Dump() {
+std::string ImpreciseConstType::Dump() const {
std::stringstream result;
uint32_t val = ConstantValue();
if (val == 0) {
@@ -474,7 +388,7 @@
}
return result.str();
}
-std::string PreciseConstLoType::Dump() {
+std::string PreciseConstLoType::Dump() const {
std::stringstream result;
int32_t val = ConstantValueLo();
@@ -488,7 +402,7 @@
return result.str();
}
-std::string ImpreciseConstLoType::Dump() {
+std::string ImpreciseConstLoType::Dump() const {
std::stringstream result;
int32_t val = ConstantValueLo();
@@ -502,7 +416,7 @@
return result.str();
}
-std::string PreciseConstHiType::Dump() {
+std::string PreciseConstHiType::Dump() const {
std::stringstream result;
int32_t val = ConstantValueHi();
result << "Precise ";
@@ -515,7 +429,7 @@
return result.str();
}
-std::string ImpreciseConstHiType::Dump() {
+std::string ImpreciseConstHiType::Dump() const {
std::stringstream result;
int32_t val = ConstantValueHi();
result << "Imprecise ";
@@ -528,19 +442,7 @@
return result.str();
}
-ConstantType::ConstantType(uint32_t constant, uint16_t cache_id)
- : RegType(NULL, "", cache_id), constant_(constant) {
-}
-
-RegType& UndefinedType::Merge(RegType& incoming_type, RegTypeCache* reg_types)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (incoming_type.IsUndefined()) {
- return *this; // Undefined MERGE Undefined => Undefined
- }
- return reg_types->Conflict();
-}
-
-RegType& RegType::HighHalf(RegTypeCache* cache) const {
+const RegType& RegType::HighHalf(RegTypeCache* cache) const {
DCHECK(IsLowHalf());
if (IsLongLo()) {
return cache->LongHi();
@@ -548,7 +450,8 @@
return cache->DoubleHi();
} else {
DCHECK(IsImpreciseConstantLo());
- return cache->FromCat2ConstHi(ConstantValue(), false);
+ const ConstantType* const_val = down_cast<const ConstantType*>(this);
+ return cache->FromCat2ConstHi(const_val->ConstantValue(), false);
}
}
@@ -586,22 +489,21 @@
bool UnresolvedType::IsNonZeroReferenceTypes() const {
return true;
}
+
std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const {
std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes();
- RegType& _left(reg_type_cache_->GetFromId(refs.first));
- UnresolvedMergedType* left = down_cast<UnresolvedMergedType*>(&_left);
-
- RegType& _right(reg_type_cache_->GetFromId(refs.second));
- UnresolvedMergedType* right = down_cast<UnresolvedMergedType*>(&_right);
+ const RegType& left = reg_type_cache_->GetFromId(refs.first);
+ const RegType& right = reg_type_cache_->GetFromId(refs.second);
std::set<uint16_t> types;
- if (left->IsUnresolvedMergedReference()) {
- types = left->GetMergedTypes();
+ if (left.IsUnresolvedMergedReference()) {
+ types = down_cast<const UnresolvedMergedType*>(&left)->GetMergedTypes();
} else {
types.insert(refs.first);
}
- if (right->IsUnresolvedMergedReference()) {
- std::set<uint16_t> right_types = right->GetMergedTypes();
+ if (right.IsUnresolvedMergedReference()) {
+ std::set<uint16_t> right_types =
+ down_cast<const UnresolvedMergedType*>(&right)->GetMergedTypes();
types.insert(right_types.begin(), right_types.end());
} else {
types.insert(refs.second);
@@ -614,10 +516,10 @@
return types;
}
-RegType& RegType::GetSuperClass(RegTypeCache* cache) {
+const RegType& RegType::GetSuperClass(RegTypeCache* cache) const {
if (!IsUnresolvedTypes()) {
mirror::Class* super_klass = GetClass()->GetSuperClass();
- if (super_klass != NULL) {
+ if (super_klass != nullptr) {
// A super class of a precise type isn't precise as a precise type indicates the register
// holds exactly that type.
std::string temp;
@@ -636,34 +538,7 @@
}
}
-bool RegType::CanAccess(RegType& other) {
- if (Equals(other)) {
- return true; // Trivial accessibility.
- } else {
- bool this_unresolved = IsUnresolvedTypes();
- bool other_unresolved = other.IsUnresolvedTypes();
- if (!this_unresolved && !other_unresolved) {
- return GetClass()->CanAccess(other.GetClass());
- } else if (!other_unresolved) {
- return other.GetClass()->IsPublic(); // Be conservative, only allow if other is public.
- } else {
- return false; // More complicated test not possible on unresolved types, be conservative.
- }
- }
-}
-
-bool RegType::CanAccessMember(mirror::Class* klass, uint32_t access_flags) {
- if ((access_flags & kAccPublic) != 0) {
- return true;
- }
- if (!IsUnresolvedTypes()) {
- return GetClass()->CanAccessMember(klass, access_flags);
- } else {
- return false; // More complicated test not possible on unresolved types, be conservative.
- }
-}
-
-bool RegType::IsObjectArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+bool RegType::IsObjectArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
// Primitive arrays will always resolve
DCHECK(descriptor_[1] == 'L' || descriptor_[1] == '[');
@@ -676,11 +551,11 @@
}
}
-bool RegType::IsJavaLangObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+bool RegType::IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return IsReference() && GetClass()->IsObjectClass();
}
-bool RegType::IsArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+bool RegType::IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
return descriptor_[0] == '[';
} else if (HasClass()) {
@@ -690,7 +565,7 @@
}
}
-bool RegType::IsJavaLangObjectArray() {
+bool RegType::IsJavaLangObjectArray() const {
if (HasClass()) {
mirror::Class* type = GetClass();
return type->IsArrayClass() && type->GetComponentType()->IsObjectClass();
@@ -698,110 +573,42 @@
return false;
}
-bool RegType::IsInstantiableTypes() {
+bool RegType::IsInstantiableTypes() const {
return IsUnresolvedTypes() || (IsNonZeroReferenceTypes() && GetClass()->IsInstantiable());
}
-ImpreciseConstType::ImpreciseConstType(uint32_t constat, uint16_t cache_id)
- : ConstantType(constat, cache_id) {
-}
-
-static bool AssignableFrom(RegType& lhs, RegType& rhs, bool strict)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (lhs.Equals(rhs)) {
- return true;
- } else {
- if (lhs.IsBoolean()) {
- return rhs.IsBooleanTypes();
- } else if (lhs.IsByte()) {
- return rhs.IsByteTypes();
- } else if (lhs.IsShort()) {
- return rhs.IsShortTypes();
- } else if (lhs.IsChar()) {
- return rhs.IsCharTypes();
- } else if (lhs.IsInteger()) {
- return rhs.IsIntegralTypes();
- } else if (lhs.IsFloat()) {
- return rhs.IsFloatTypes();
- } else if (lhs.IsLongLo()) {
- return rhs.IsLongTypes();
- } else if (lhs.IsDoubleLo()) {
- return rhs.IsDoubleTypes();
- } else {
- CHECK(lhs.IsReferenceTypes())
- << "Unexpected register type in IsAssignableFrom: '"
- << lhs << "' := '" << rhs << "'";
- if (rhs.IsZero()) {
- return true; // All reference types can be assigned null.
- } else if (!rhs.IsReferenceTypes()) {
- return false; // Expect rhs to be a reference type.
- } else if (lhs.IsJavaLangObject()) {
- return true; // All reference types can be assigned to Object.
- } else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
- // If we're not strict allow assignment to any interface, see comment in ClassJoin.
- return true;
- } else if (lhs.IsJavaLangObjectArray()) {
- return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
- } else if (lhs.HasClass() && rhs.HasClass() &&
- lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) {
- // We're assignable from the Class point-of-view.
- return true;
- } else {
- // Unresolved types are only assignable for null and equality.
- return false;
- }
- }
- }
-}
-
-bool RegType::IsAssignableFrom(RegType& src) {
- return AssignableFrom(*this, src, false);
-}
-
-bool RegType::IsStrictlyAssignableFrom(RegType& src) {
- return AssignableFrom(*this, src, true);
-}
-
-int32_t ConstantType::ConstantValueLo() const {
- DCHECK(IsConstantLo());
- return constant_;
-}
-
-int32_t ConstantType::ConstantValueHi() const {
- if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) {
- return constant_;
- } else {
- DCHECK(false);
- return 0;
- }
-}
-
-static RegType& SelectNonConstant(RegType& a, RegType& b) {
+static const RegType& SelectNonConstant(const RegType& a, const RegType& b) {
return a.IsConstantTypes() ? b : a;
}
-RegType& RegType::Merge(RegType& incoming_type, RegTypeCache* reg_types) {
+const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types) const {
DCHECK(!Equals(incoming_type)); // Trivial equality handled by caller
- if (IsConflict()) {
+ // Perform pointer equality tests for conflict to avoid virtual method dispatch.
+ const ConflictType& conflict = reg_types->Conflict();
+ if (this == &conflict) {
+ DCHECK(IsConflict());
return *this; // Conflict MERGE * => Conflict
- } else if (incoming_type.IsConflict()) {
+ } else if (&incoming_type == &conflict) {
+ DCHECK(incoming_type.IsConflict());
return incoming_type; // * MERGE Conflict => Conflict
} else if (IsUndefined() || incoming_type.IsUndefined()) {
- return reg_types->Conflict(); // Unknown MERGE * => Conflict
+ return conflict; // Unknown MERGE * => Conflict
} else if (IsConstant() && incoming_type.IsConstant()) {
- int32_t val1 = ConstantValue();
- int32_t val2 = incoming_type.ConstantValue();
+ const ConstantType& type1 = *down_cast<const ConstantType*>(this);
+ const ConstantType& type2 = *down_cast<const ConstantType*>(&incoming_type);
+ int32_t val1 = type1.ConstantValue();
+ int32_t val2 = type2.ConstantValue();
if (val1 >= 0 && val2 >= 0) {
// +ve1 MERGE +ve2 => MAX(+ve1, +ve2)
if (val1 >= val2) {
- if (!IsPreciseConstant()) {
+ if (!type1.IsPreciseConstant()) {
return *this;
} else {
return reg_types->FromCat1Const(val1, false);
}
} else {
- if (!incoming_type.IsPreciseConstant()) {
- return incoming_type;
+ if (!type2.IsPreciseConstant()) {
+ return type2;
} else {
return reg_types->FromCat1Const(val2, false);
}
@@ -809,30 +616,30 @@
} else if (val1 < 0 && val2 < 0) {
// -ve1 MERGE -ve2 => MIN(-ve1, -ve2)
if (val1 <= val2) {
- if (!IsPreciseConstant()) {
+ if (!type1.IsPreciseConstant()) {
return *this;
} else {
return reg_types->FromCat1Const(val1, false);
}
} else {
- if (!incoming_type.IsPreciseConstant()) {
- return incoming_type;
+ if (!type2.IsPreciseConstant()) {
+ return type2;
} else {
return reg_types->FromCat1Const(val2, false);
}
}
} else {
// Values are +ve and -ve, choose smallest signed type in which they both fit
- if (IsConstantByte()) {
- if (incoming_type.IsConstantByte()) {
+ if (type1.IsConstantByte()) {
+ if (type2.IsConstantByte()) {
return reg_types->ByteConstant();
- } else if (incoming_type.IsConstantShort()) {
+ } else if (type2.IsConstantShort()) {
return reg_types->ShortConstant();
} else {
return reg_types->IntConstant();
}
- } else if (IsConstantShort()) {
- if (incoming_type.IsConstantShort()) {
+ } else if (type1.IsConstantShort()) {
+ if (type2.IsConstantShort()) {
return reg_types->ShortConstant();
} else {
return reg_types->IntConstant();
@@ -842,12 +649,16 @@
}
}
} else if (IsConstantLo() && incoming_type.IsConstantLo()) {
- int32_t val1 = ConstantValueLo();
- int32_t val2 = incoming_type.ConstantValueLo();
+ const ConstantType& type1 = *down_cast<const ConstantType*>(this);
+ const ConstantType& type2 = *down_cast<const ConstantType*>(&incoming_type);
+ int32_t val1 = type1.ConstantValueLo();
+ int32_t val2 = type2.ConstantValueLo();
return reg_types->FromCat2ConstLo(val1 | val2, false);
} else if (IsConstantHi() && incoming_type.IsConstantHi()) {
- int32_t val1 = ConstantValueHi();
- int32_t val2 = incoming_type.ConstantValueHi();
+ const ConstantType& type1 = *down_cast<const ConstantType*>(this);
+ const ConstantType& type2 = *down_cast<const ConstantType*>(&incoming_type);
+ int32_t val1 = type1.ConstantValueHi();
+ int32_t val2 = type2.ConstantValueHi();
return reg_types->FromCat2ConstHi(val1 | val2, false);
} else if (IsIntegralTypes() && incoming_type.IsIntegralTypes()) {
if (IsBooleanTypes() && incoming_type.IsBooleanTypes()) {
@@ -887,12 +698,12 @@
// Something that is uninitialized hasn't had its constructor called. Mark any merge
// of this type with something that is initialized as conflicting. The cases of a merge
// with itself, 0 or Object are handled above.
- return reg_types->Conflict();
+ return conflict;
} else { // Two reference types, compute Join
mirror::Class* c1 = GetClass();
mirror::Class* c2 = incoming_type.GetClass();
- DCHECK(c1 != NULL && !c1->IsPrimitive());
- DCHECK(c2 != NULL && !c2->IsPrimitive());
+ DCHECK(c1 != nullptr && !c1->IsPrimitive());
+ DCHECK(c2 != nullptr && !c2->IsPrimitive());
mirror::Class* join_class = ClassJoin(c1, c2);
if (c1 == join_class && !IsPreciseReference()) {
return *this;
@@ -904,7 +715,7 @@
}
}
} else {
- return reg_types->Conflict(); // Unexpected types => Conflict
+ return conflict; // Unexpected types => Conflict
}
}
@@ -931,7 +742,7 @@
mirror::Class* common_elem = ClassJoin(s_ct, t_ct);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
mirror::Class* array_class = class_linker->FindArrayClass(Thread::Current(), &common_elem);
- DCHECK(array_class != NULL);
+ DCHECK(array_class != nullptr);
return array_class;
} else {
size_t s_depth = s->Depth();
@@ -967,9 +778,9 @@
}
}
-void RegType::VisitRoots(RootCallback* callback, void* arg) {
+void RegType::VisitRoots(RootCallback* callback, void* arg) const {
if (!klass_.IsNull()) {
- klass_.VisitRoot(callback, arg, 0, kRootUnknown);
+ callback(reinterpret_cast<mirror::Object**>(&klass_), arg, 0, kRootUnknown);
}
}
@@ -1009,8 +820,7 @@
}
std::ostream& operator<<(std::ostream& os, const RegType& rhs) {
- RegType& rhs_non_const = const_cast<RegType&>(rhs);
- os << rhs_non_const.Dump();
+ os << rhs.Dump();
return os;
}
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index d508fb5..34d6caa 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -25,6 +25,7 @@
#include "jni.h"
#include "base/macros.h"
+#include "base/mutex.h"
#include "gc_root.h"
#include "globals.h"
#include "object_callbacks.h"
@@ -59,7 +60,9 @@
virtual bool IsUninitializedReference() const { return false; }
virtual bool IsUninitializedThisReference() const { return false; }
virtual bool IsUnresolvedAndUninitializedReference() const { return false; }
- virtual bool IsUnresolvedAndUninitializedThisReference() const { return false; }
+ virtual bool IsUnresolvedAndUninitializedThisReference() const {
+ return false;
+ }
virtual bool IsUnresolvedMergedReference() const { return false; }
virtual bool IsUnresolvedSuperClass() const { return false; }
virtual bool IsReference() const { return false; }
@@ -72,90 +75,64 @@
virtual bool IsImpreciseConstant() const { return false; }
virtual bool IsConstantTypes() const { return false; }
bool IsConstant() const {
- return IsPreciseConstant() || IsImpreciseConstant();
+ return IsImpreciseConstant() || IsPreciseConstant();
}
bool IsConstantLo() const {
- return IsPreciseConstantLo() || IsImpreciseConstantLo();
+ return IsImpreciseConstantLo() || IsPreciseConstantLo();
}
bool IsPrecise() const {
- return IsPreciseConstantLo() || IsPreciseConstant() || IsPreciseConstantHi();
+ return IsPreciseConstantLo() || IsPreciseConstant() ||
+ IsPreciseConstantHi();
}
- bool IsLongConstant() const {
- return IsConstantLo();
- }
+ bool IsLongConstant() const { return IsConstantLo(); }
bool IsConstantHi() const {
return (IsPreciseConstantHi() || IsImpreciseConstantHi());
}
- bool IsLongConstantHigh() const {
- return IsConstantHi();
- }
+ bool IsLongConstantHigh() const { return IsConstantHi(); }
virtual bool IsUninitializedTypes() const { return false; }
- bool IsUnresolvedTypes() const {
- return IsUnresolvedReference() || IsUnresolvedAndUninitializedReference() ||
- IsUnresolvedAndUninitializedThisReference() ||
- IsUnresolvedMergedReference() || IsUnresolvedSuperClass();
- }
+ virtual bool IsUnresolvedTypes() const { return false; }
bool IsLowHalf() const {
- return (IsLongLo() || IsDoubleLo() || IsPreciseConstantLo() ||
- IsImpreciseConstantLo());
+ return (IsLongLo() || IsDoubleLo() || IsPreciseConstantLo() || IsImpreciseConstantLo());
}
bool IsHighHalf() const {
- return (IsLongHi() || IsDoubleHi() || IsPreciseConstantHi() ||
- IsImpreciseConstantHi());
+ return (IsLongHi() || IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
}
- bool IsLongOrDoubleTypes() const {
- return IsLowHalf();
- }
+ bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
// Check this is the low half, and that type_h is its matching high-half.
- inline bool CheckWidePair(RegType& type_h) const {
+ inline bool CheckWidePair(const RegType& type_h) const {
if (IsLowHalf()) {
- return ((IsPreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
- (IsPreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
- (IsImpreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
+ return ((IsImpreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
(IsImpreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
+ (IsPreciseConstantLo() && type_h.IsPreciseConstantHi()) ||
+ (IsPreciseConstantLo() && type_h.IsImpreciseConstantHi()) ||
(IsDoubleLo() && type_h.IsDoubleHi()) ||
(IsLongLo() && type_h.IsLongHi()));
}
return false;
}
// The high half that corresponds to this low half
- RegType& HighHalf(RegTypeCache* cache) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& HighHalf(RegTypeCache* cache) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsConstantBoolean() const {
- return IsConstant() && (ConstantValue() >= 0) && (ConstantValue() <= 1);
- }
- virtual bool IsConstantChar() const {
- return false;
- }
- virtual bool IsConstantByte() const {
- return false;
- }
- virtual bool IsConstantShort() const {
- return false;
- }
- virtual bool IsOne() const {
- return false;
- }
- virtual bool IsZero() const {
- return false;
- }
+ bool IsConstantBoolean() const;
+ virtual bool IsConstantChar() const { return false; }
+ virtual bool IsConstantByte() const { return false; }
+ virtual bool IsConstantShort() const { return false; }
+ virtual bool IsOne() const { return false; }
+ virtual bool IsZero() const { return false; }
bool IsReferenceTypes() const {
return IsNonZeroReferenceTypes() || IsZero();
}
- virtual bool IsNonZeroReferenceTypes() const {
- return false;
- }
+ virtual bool IsNonZeroReferenceTypes() const { return false; }
bool IsCategory1Types() const {
- return IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() || IsShort() ||
- IsBoolean();
+ return IsChar() || IsInteger() || IsFloat() || IsConstant() || IsByte() ||
+ IsShort() || IsBoolean();
}
bool IsCategory2Types() const {
return IsLowHalf(); // Don't expect explicit testing of high halves
}
- bool IsBooleanTypes() const {
- return IsBoolean() || IsConstantBoolean();
- }
+ bool IsBooleanTypes() const { return IsBoolean() || IsConstantBoolean(); }
bool IsByteTypes() const {
return IsConstantByte() || IsByte() || IsBoolean();
}
@@ -166,102 +143,107 @@
return IsChar() || IsBooleanTypes() || IsConstantChar();
}
bool IsIntegralTypes() const {
- return IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() || IsBoolean();
+ return IsInteger() || IsConstant() || IsByte() || IsShort() || IsChar() ||
+ IsBoolean();
}
- // Give the constant value encoded, but this shouldn't be called in the general case.
- virtual int32_t ConstantValue() const;
- virtual int32_t ConstantValueLo() const;
- virtual int32_t ConstantValueHi() const;
- bool IsArrayIndexTypes() const {
- return IsIntegralTypes();
- }
+ // Give the constant value encoded, but this shouldn't be called in the
+ // general case.
+ bool IsArrayIndexTypes() const { return IsIntegralTypes(); }
// Float type may be derived from any constant type
- bool IsFloatTypes() const {
- return IsFloat() || IsConstant();
- }
- bool IsLongTypes() const {
- return IsLongLo() || IsLongConstant();
- }
+ bool IsFloatTypes() const { return IsFloat() || IsConstant(); }
+ bool IsLongTypes() const { return IsLongLo() || IsLongConstant(); }
bool IsLongHighTypes() const {
- return (IsLongHi() ||
- IsPreciseConstantHi() ||
- IsImpreciseConstantHi());
+ return (IsLongHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
}
- bool IsDoubleTypes() const {
- return IsDoubleLo() || IsLongConstant();
- }
+ bool IsDoubleTypes() const { return IsDoubleLo() || IsLongConstant(); }
bool IsDoubleHighTypes() const {
return (IsDoubleHi() || IsPreciseConstantHi() || IsImpreciseConstantHi());
}
- virtual bool IsLong() const {
- return false;
+ virtual bool IsLong() const { return false; }
+ bool HasClass() const {
+ bool result = !klass_.IsNull();
+ DCHECK_EQ(result, HasClassVirtual());
+ return result;
}
- virtual bool HasClass() const {
- return false;
- }
- bool IsJavaLangObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsObjectArrayTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ virtual bool HasClassVirtual() const { return false; }
+ bool IsJavaLangObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsObjectArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Primitive::Type GetPrimitiveType() const;
- bool IsJavaLangObjectArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsInstantiableTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsJavaLangObjectArray() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsInstantiableTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::string& GetDescriptor() const {
- DCHECK(HasClass() || (IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
- !IsUnresolvedSuperClass()));
+ DCHECK(HasClass() ||
+ (IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
+ !IsUnresolvedSuperClass()));
return descriptor_;
}
- mirror::Class* GetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* GetClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(!IsUnresolvedReference());
DCHECK(!klass_.IsNull()) << Dump();
DCHECK(HasClass());
return klass_.Read();
}
- uint16_t GetId() const {
- return cache_id_;
- }
- RegType& GetSuperClass(RegTypeCache* cache)
+ uint16_t GetId() const { return cache_id_; }
+ const RegType& GetSuperClass(RegTypeCache* cache) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- virtual std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+ virtual std::string Dump() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
// Can this type access other?
- bool CanAccess(RegType& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool CanAccess(const RegType& other) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Can this type access a member with the given properties?
- bool CanAccessMember(mirror::Class* klass, uint32_t access_flags)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool CanAccessMember(mirror::Class* klass, uint32_t access_flags) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Can this type be assigned by src?
- // Note: Object and interface types may always be assigned to one another, see comment on
+ // Note: Object and interface types may always be assigned to one another, see
+ // comment on
// ClassJoin.
- bool IsAssignableFrom(RegType& src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsAssignableFrom(const RegType& src) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't allow assignment to
+ // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't
+ // allow assignment to
// an interface from an Object.
- bool IsStrictlyAssignableFrom(RegType& src) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsStrictlyAssignableFrom(const RegType& src) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Are these RegTypes the same?
- bool Equals(RegType& other) const {
- return GetId() == other.GetId();
- }
+ bool Equals(const RegType& other) const { return GetId() == other.GetId(); }
- // Compute the merge of this register from one edge (path) with incoming_type from another.
- virtual RegType& Merge(RegType& incoming_type, RegTypeCache* reg_types)
+ // Compute the merge of this register from one edge (path) with incoming_type
+ // from another.
+ const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
- * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
- * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
- * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
+ * A basic Join operation on classes. For a pair of types S and T the Join,
+ *written S v T = J, is
+ * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is
+ *J is the parent of
+ * S and T such that there isn't a parent of both S and T that isn't also the
+ *parent of J (ie J
* is the deepest (lowest upper bound) parent of S and T).
*
- * This operation applies for regular classes and arrays, however, for interface types there
- * needn't be a partial ordering on the types. We could solve the problem of a lack of a partial
- * order by introducing sets of types, however, the only operation permissible on an interface is
- * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
- * types until an invoke-interface call on the interface typed reference at runtime and allow
- * the perversion of Object being assignable to an interface type (note, however, that we don't
- * allow assignment of Object or Interface to any concrete class and are therefore type safe).
+ * This operation applies for regular classes and arrays, however, for
+ *interface types there
+ * needn't be a partial ordering on the types. We could solve the problem of a
+ *lack of a partial
+ * order by introducing sets of types, however, the only operation permissible
+ *on an interface is
+ * invoke-interface. In the tradition of Java verifiers [1] we defer the
+ *verification of interface
+ * types until an invoke-interface call on the interface typed reference at
+ *runtime and allow
+ * the perversion of Object being assignable to an interface type (note,
+ *however, that we don't
+ * allow assignment of Object or Interface to any concrete class and are
+ *therefore type safe).
*
* [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
*/
@@ -270,12 +252,13 @@
virtual ~RegType() {}
- void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void VisitRoots(RootCallback* callback, void* arg) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
protected:
- RegType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : descriptor_(descriptor), klass_(GcRoot<mirror::Class>(klass)), cache_id_(cache_id) {
+ RegType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : descriptor_(descriptor), klass_(klass), cache_id_(cache_id) {
if (kIsDebugBuild) {
CheckInvariants();
}
@@ -283,414 +266,404 @@
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
const std::string descriptor_;
- GcRoot<mirror::Class> klass_;
+ mutable GcRoot<mirror::Class>
+ klass_; // Non-const only due to moving classes.
const uint16_t cache_id_;
friend class RegTypeCache;
private:
+ static bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
DISALLOW_COPY_AND_ASSIGN(RegType);
};
// Bottom type.
-class ConflictType : public RegType {
+class ConflictType FINAL : public RegType {
public:
- bool IsConflict() const {
- return true;
- }
+ bool IsConflict() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get the singleton Conflict instance.
- static ConflictType* GetInstance();
+ static const ConflictType* GetInstance() PURE;
// Create the singleton instance.
- static ConflictType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ static const ConflictType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Destroy the singleton instance.
static void Destroy();
private:
- ConflictType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : RegType(klass, descriptor, cache_id) {
- }
+ ConflictType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : RegType(klass, descriptor, cache_id) {}
- static ConflictType* instance_;
+ static const ConflictType* instance_;
};
-// A variant of the bottom type used to specify an undefined value in the incoming registers.
+// A variant of the bottom type used to specify an undefined value in the
+// incoming registers.
// Merging with UndefinedType yields ConflictType which is the true bottom.
-class UndefinedType : public RegType {
+class UndefinedType FINAL : public RegType {
public:
- bool IsUndefined() const {
- return true;
- }
+ bool IsUndefined() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get the singleton Undefined instance.
- static UndefinedType* GetInstance();
+ static const UndefinedType* GetInstance() PURE;
// Create the singleton instance.
- static UndefinedType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ static const UndefinedType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Destroy the singleton instance.
static void Destroy();
private:
- UndefinedType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : RegType(klass, descriptor, cache_id) {
- }
+ UndefinedType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : RegType(klass, descriptor, cache_id) {}
- virtual RegType& Merge(RegType& incoming_type, RegTypeCache* reg_types)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static UndefinedType* instance_;
+ static const UndefinedType* instance_;
};
class PrimitiveType : public RegType {
public:
- PrimitiveType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ PrimitiveType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool HasClassVirtual() const OVERRIDE { return true; }
};
class Cat1Type : public PrimitiveType {
public:
- Cat1Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Cat1Type(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
class IntegerType : public Cat1Type {
public:
- bool IsInteger() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static IntegerType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ bool IsInteger() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const IntegerType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static IntegerType* GetInstance();
+ static const IntegerType* GetInstance() PURE;
static void Destroy();
+
private:
- IntegerType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {
- }
- static IntegerType* instance_;
+ IntegerType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat1Type(klass, descriptor, cache_id) {}
+ static const IntegerType* instance_;
};
-class BooleanType : public Cat1Type {
+class BooleanType FINAL : public Cat1Type {
public:
- bool IsBoolean() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static BooleanType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ bool IsBoolean() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const BooleanType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static BooleanType* GetInstance();
+ static const BooleanType* GetInstance() PURE;
static void Destroy();
- private:
- BooleanType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {
- }
- static BooleanType* instance;
+ private:
+ BooleanType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat1Type(klass, descriptor, cache_id) {}
+
+ static const BooleanType* instance_;
};
-class ByteType : public Cat1Type {
+class ByteType FINAL : public Cat1Type {
public:
- bool IsByte() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static ByteType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ bool IsByte() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const ByteType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static ByteType* GetInstance();
+ static const ByteType* GetInstance() PURE;
static void Destroy();
+
private:
- ByteType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {
- }
- static ByteType* instance_;
+ ByteType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat1Type(klass, descriptor, cache_id) {}
+ static const ByteType* instance_;
};
-class ShortType : public Cat1Type {
+class ShortType FINAL : public Cat1Type {
public:
- bool IsShort() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static ShortType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ bool IsShort() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const ShortType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static ShortType* GetInstance();
+ static const ShortType* GetInstance() PURE;
static void Destroy();
+
private:
- ShortType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {
- }
- static ShortType* instance_;
+ ShortType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat1Type(klass, descriptor, cache_id) {}
+ static const ShortType* instance_;
};
-class CharType : public Cat1Type {
+class CharType FINAL : public Cat1Type {
public:
- bool IsChar() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static CharType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ bool IsChar() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const CharType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static CharType* GetInstance();
+ static const CharType* GetInstance() PURE;
static void Destroy();
+
private:
- CharType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {
- }
- static CharType* instance_;
+ CharType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat1Type(klass, descriptor, cache_id) {}
+ static const CharType* instance_;
};
-class FloatType : public Cat1Type {
+class FloatType FINAL : public Cat1Type {
public:
- bool IsFloat() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static FloatType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ bool IsFloat() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const FloatType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static FloatType* GetInstance();
+ static const FloatType* GetInstance() PURE;
static void Destroy();
+
private:
- FloatType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat1Type(klass, descriptor, cache_id) {
- }
- static FloatType* instance_;
+ FloatType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat1Type(klass, descriptor, cache_id) {}
+ static const FloatType* instance_;
};
class Cat2Type : public PrimitiveType {
public:
- Cat2Type(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Cat2Type(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class LongLoType : public Cat2Type {
+class LongLoType FINAL : public Cat2Type {
public:
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsLongLo() const {
- return true;
- }
- bool IsLong() const {
- return true;
- }
- static LongLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsLongLo() const OVERRIDE { return true; }
+ bool IsLong() const OVERRIDE { return true; }
+ static const LongLoType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static LongLoType* GetInstance();
+ static const LongLoType* GetInstance() PURE;
static void Destroy();
+
private:
- LongLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {
- }
- static LongLoType* instance_;
+ LongLoType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat2Type(klass, descriptor, cache_id) {}
+ static const LongLoType* instance_;
};
-class LongHiType : public Cat2Type {
+class LongHiType FINAL : public Cat2Type {
public:
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsLongHi() const {
- return true;
- }
- static LongHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsLongHi() const OVERRIDE { return true; }
+ static const LongHiType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static LongHiType* GetInstance();
+ static const LongHiType* GetInstance() PURE;
static void Destroy();
+
private:
- LongHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {
- }
- static LongHiType* instance_;
+ LongHiType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat2Type(klass, descriptor, cache_id) {}
+ static const LongHiType* instance_;
};
-class DoubleLoType : public Cat2Type {
+class DoubleLoType FINAL : public Cat2Type {
public:
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsDoubleLo() const {
- return true;
- }
- bool IsDouble() const {
- return true;
- }
- static DoubleLoType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsDoubleLo() const OVERRIDE { return true; }
+ bool IsDouble() const OVERRIDE { return true; }
+ static const DoubleLoType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
+ uint16_t cache_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static const DoubleLoType* GetInstance() PURE;
+ static void Destroy();
+
+ private:
+ DoubleLoType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat2Type(klass, descriptor, cache_id) {}
+ static const DoubleLoType* instance_;
+};
+
+class DoubleHiType FINAL : public Cat2Type {
+ public:
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ virtual bool IsDoubleHi() const OVERRIDE { return true; }
+ static const DoubleHiType* CreateInstance(mirror::Class* klass,
+ const std::string& descriptor,
uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static DoubleLoType* GetInstance();
+ static const DoubleHiType* GetInstance() PURE;
static void Destroy();
- private:
- DoubleLoType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {
- }
- static DoubleLoType* instance_;
-};
-class DoubleHiType : public Cat2Type {
- public:
- std::string Dump();
- virtual bool IsDoubleHi() const {
- return true;
- }
- static DoubleHiType* CreateInstance(mirror::Class* klass, const std::string& descriptor,
- uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static DoubleHiType* GetInstance();
- static void Destroy();
private:
- DoubleHiType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : Cat2Type(klass, descriptor, cache_id) {
- }
- static DoubleHiType* instance_;
+ DoubleHiType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : Cat2Type(klass, descriptor, cache_id) {}
+ static const DoubleHiType* instance_;
};
class ConstantType : public RegType {
public:
- ConstantType(uint32_t constat, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ConstantType(uint32_t constant, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : RegType(nullptr, "", cache_id), constant_(constant) {
+ }
- // If this is a 32-bit constant, what is the value? This value may be imprecise in which case
- // the value represents part of the integer range of values that may be held in the register.
+
+ // If this is a 32-bit constant, what is the value? This value may be
+ // imprecise in which case
+ // the value represents part of the integer range of values that may be held
+ // in the register.
int32_t ConstantValue() const {
DCHECK(IsConstantTypes());
return constant_;
}
- int32_t ConstantValueLo() const;
- int32_t ConstantValueHi() const;
- bool IsZero() const {
+ int32_t ConstantValueLo() const {
+ DCHECK(IsConstantLo());
+ return constant_;
+ }
+
+ int32_t ConstantValueHi() const {
+ if (IsConstantHi() || IsPreciseConstantHi() || IsImpreciseConstantHi()) {
+ return constant_;
+ } else {
+ DCHECK(false);
+ return 0;
+ }
+ }
+
+ bool IsZero() const OVERRIDE {
return IsPreciseConstant() && ConstantValue() == 0;
}
- bool IsOne() const {
+ bool IsOne() const OVERRIDE {
return IsPreciseConstant() && ConstantValue() == 1;
}
- bool IsConstantChar() const {
+ bool IsConstantChar() const OVERRIDE {
return IsConstant() && ConstantValue() >= 0 &&
ConstantValue() <= std::numeric_limits<jchar>::max();
}
- bool IsConstantByte() const {
+ bool IsConstantByte() const OVERRIDE {
return IsConstant() &&
ConstantValue() >= std::numeric_limits<jbyte>::min() &&
ConstantValue() <= std::numeric_limits<jbyte>::max();
}
- bool IsConstantShort() const {
+ bool IsConstantShort() const OVERRIDE {
return IsConstant() &&
ConstantValue() >= std::numeric_limits<jshort>::min() &&
ConstantValue() <= std::numeric_limits<jshort>::max();
}
- virtual bool IsConstantTypes() const { return true; }
+ virtual bool IsConstantTypes() const OVERRIDE { return true; }
private:
const uint32_t constant_;
};
-class PreciseConstType : public ConstantType {
+class PreciseConstType FINAL : public ConstantType {
public:
- PreciseConstType(uint32_t constat, uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstantType(constat, cache_id) {
- }
+ PreciseConstType(uint32_t constant, uint16_t cache_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : ConstantType(constant, cache_id) {}
- bool IsPreciseConstant() const {
- return true;
- }
+ bool IsPreciseConstant() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class PreciseConstLoType : public ConstantType {
+class PreciseConstLoType FINAL : public ConstantType {
public:
- PreciseConstLoType(uint32_t constat, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstantType(constat, cache_id) {
- }
- bool IsPreciseConstantLo() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ PreciseConstLoType(uint32_t constant, uint16_t cache_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : ConstantType(constant, cache_id) {}
+ bool IsPreciseConstantLo() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class PreciseConstHiType : public ConstantType {
+class PreciseConstHiType FINAL : public ConstantType {
public:
- PreciseConstHiType(uint32_t constat, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : ConstantType(constat, cache_id) {
- }
- bool IsPreciseConstantHi() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ PreciseConstHiType(uint32_t constant, uint16_t cache_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : ConstantType(constant, cache_id) {}
+ bool IsPreciseConstantHi() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class ImpreciseConstType : public ConstantType {
+class ImpreciseConstType FINAL : public ConstantType {
public:
ImpreciseConstType(uint32_t constat, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsImpreciseConstant() const {
- return true;
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : ConstantType(constat, cache_id) {
}
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsImpreciseConstant() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class ImpreciseConstLoType : public ConstantType {
+class ImpreciseConstLoType FINAL : public ConstantType {
public:
- ImpreciseConstLoType(uint32_t constat, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : ConstantType(constat, cache_id) {
- }
- bool IsImpreciseConstantLo() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ImpreciseConstLoType(uint32_t constant, uint16_t cache_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : ConstantType(constant, cache_id) {}
+ bool IsImpreciseConstantLo() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class ImpreciseConstHiType : public ConstantType {
+class ImpreciseConstHiType FINAL : public ConstantType {
public:
- ImpreciseConstHiType(uint32_t constat, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : ConstantType(constat, cache_id) {
- }
- bool IsImpreciseConstantHi() const {
- return true;
- }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ImpreciseConstHiType(uint32_t constant, uint16_t cache_id)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : ConstantType(constant, cache_id) {}
+ bool IsImpreciseConstantHi() const OVERRIDE { return true; }
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-// Common parent of all uninitialized types. Uninitialized types are created by "new" dex
+// Common parent of all uninitialized types. Uninitialized types are created by
+// "new" dex
// instructions and must be passed to a constructor.
class UninitializedType : public RegType {
public:
- UninitializedType(mirror::Class* klass, const std::string& descriptor, uint32_t allocation_pc,
- uint16_t cache_id)
- : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {
- }
+ UninitializedType(mirror::Class* klass, const std::string& descriptor,
+ uint32_t allocation_pc, uint16_t cache_id)
+ : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {}
- bool IsUninitializedTypes() const;
- bool IsNonZeroReferenceTypes() const;
+ bool IsUninitializedTypes() const OVERRIDE;
+ bool IsNonZeroReferenceTypes() const OVERRIDE;
uint32_t GetAllocationPc() const {
DCHECK(IsUninitializedTypes());
@@ -702,30 +675,27 @@
};
// Similar to ReferenceType but not yet having been passed to a constructor.
-class UninitializedReferenceType : public UninitializedType {
+class UninitializedReferenceType FINAL : public UninitializedType {
public:
- UninitializedReferenceType(mirror::Class* klass, const std::string& descriptor,
+ UninitializedReferenceType(mirror::Class* klass,
+ const std::string& descriptor,
uint32_t allocation_pc, uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : UninitializedType(klass, descriptor, allocation_pc, cache_id) {
- }
+ : UninitializedType(klass, descriptor, allocation_pc, cache_id) {}
- bool IsUninitializedReference() const {
- return true;
- }
+ bool IsUninitializedReference() const OVERRIDE { return true; }
- bool HasClass() const {
- return true;
- }
+ bool HasClassVirtual() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-// Similar to UnresolvedReferenceType but not yet having been passed to a constructor.
-class UnresolvedUninitializedRefType : public UninitializedType {
+// Similar to UnresolvedReferenceType but not yet having been passed to a
+// constructor.
+class UnresolvedUninitializedRefType FINAL : public UninitializedType {
public:
- UnresolvedUninitializedRefType(const std::string& descriptor, uint32_t allocation_pc,
- uint16_t cache_id)
+ UnresolvedUninitializedRefType(const std::string& descriptor,
+ uint32_t allocation_pc, uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: UninitializedType(NULL, descriptor, allocation_pc, cache_id) {
if (kIsDebugBuild) {
@@ -733,19 +703,22 @@
}
}
- bool IsUnresolvedAndUninitializedReference() const {
- return true;
- }
+ bool IsUnresolvedAndUninitializedReference() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-// Similar to UninitializedReferenceType but special case for the this argument of a constructor.
-class UninitializedThisReferenceType : public UninitializedType {
+// Similar to UninitializedReferenceType but special case for the this argument
+// of a constructor.
+class UninitializedThisReferenceType FINAL : public UninitializedType {
public:
- UninitializedThisReferenceType(mirror::Class* klass, const std::string& descriptor,
+ UninitializedThisReferenceType(mirror::Class* klass,
+ const std::string& descriptor,
uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: UninitializedType(klass, descriptor, 0, cache_id) {
@@ -754,23 +727,20 @@
}
}
- virtual bool IsUninitializedThisReference() const {
- return true;
- }
+ virtual bool IsUninitializedThisReference() const OVERRIDE { return true; }
- bool HasClass() const {
- return true;
- }
+ bool HasClassVirtual() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-class UnresolvedUninitializedThisRefType : public UninitializedType {
+class UnresolvedUninitializedThisRefType FINAL : public UninitializedType {
public:
- UnresolvedUninitializedThisRefType(const std::string& descriptor, uint16_t cache_id)
+ UnresolvedUninitializedThisRefType(const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: UninitializedType(NULL, descriptor, 0, cache_id) {
if (kIsDebugBuild) {
@@ -778,112 +748,108 @@
}
}
- bool IsUnresolvedAndUninitializedThisReference() const {
- return true;
- }
+ bool IsUnresolvedAndUninitializedThisReference() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-// A type of register holding a reference to an Object of type GetClass or a sub-class.
-class ReferenceType : public RegType {
+// A type of register holding a reference to an Object of type GetClass or a
+// sub-class.
+class ReferenceType FINAL : public RegType {
public:
- ReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : RegType(klass, descriptor, cache_id) {
- }
+ ReferenceType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : RegType(klass, descriptor, cache_id) {}
- bool IsReference() const {
- return true;
- }
+ bool IsReference() const OVERRIDE { return true; }
- bool IsNonZeroReferenceTypes() const {
- return true;
- }
+ bool IsNonZeroReferenceTypes() const OVERRIDE { return true; }
- bool HasClass() const {
- return true;
- }
+ bool HasClassVirtual() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
-// A type of register holding a reference to an Object of type GetClass and only an object of that
+// A type of register holding a reference to an Object of type GetClass and only
+// an object of that
// type.
-class PreciseReferenceType : public RegType {
+class PreciseReferenceType FINAL : public RegType {
public:
- PreciseReferenceType(mirror::Class* klass, const std::string& descriptor, uint16_t cache_id)
+ PreciseReferenceType(mirror::Class* klass, const std::string& descriptor,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsPreciseReference() const {
- return true;
- }
+ bool IsPreciseReference() const OVERRIDE { return true; }
- bool IsNonZeroReferenceTypes() const {
- return true;
- }
+ bool IsNonZeroReferenceTypes() const OVERRIDE { return true; }
- bool HasClass() const {
- return true;
- }
+ bool HasClassVirtual() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
// Common parent of unresolved types.
class UnresolvedType : public RegType {
public:
UnresolvedType(const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : RegType(NULL, descriptor, cache_id) {
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : RegType(NULL, descriptor, cache_id) {}
- bool IsNonZeroReferenceTypes() const;
+ bool IsNonZeroReferenceTypes() const OVERRIDE;
};
-// Similar to ReferenceType except the Class couldn't be loaded. Assignability and other tests made
+// Similar to ReferenceType except the Class couldn't be loaded. Assignability
+// and other tests made
// of this type must be conservative.
-class UnresolvedReferenceType : public UnresolvedType {
+class UnresolvedReferenceType FINAL : public UnresolvedType {
public:
UnresolvedReferenceType(const std::string& descriptor, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : UnresolvedType(descriptor, cache_id) {
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : UnresolvedType(descriptor, cache_id) {
if (kIsDebugBuild) {
CheckInvariants();
}
}
- bool IsUnresolvedReference() const {
- return true;
- }
+ bool IsUnresolvedReference() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
};
// Type representing the super-class of an unresolved type.
-class UnresolvedSuperClass : public UnresolvedType {
+class UnresolvedSuperClass FINAL : public UnresolvedType {
public:
- UnresolvedSuperClass(uint16_t child_id, RegTypeCache* reg_type_cache, uint16_t cache_id)
+ UnresolvedSuperClass(uint16_t child_id, RegTypeCache* reg_type_cache,
+ uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : UnresolvedType("", cache_id), unresolved_child_id_(child_id),
+ : UnresolvedType("", cache_id),
+ unresolved_child_id_(child_id),
reg_type_cache_(reg_type_cache) {
if (kIsDebugBuild) {
CheckInvariants();
}
}
- bool IsUnresolvedSuperClass() const {
- return true;
- }
+ bool IsUnresolvedSuperClass() const OVERRIDE { return true; }
+
+ bool IsUnresolvedTypes() const OVERRIDE { return true; }
uint16_t GetUnresolvedSuperClassChildId() const {
DCHECK(IsUnresolvedSuperClass());
return static_cast<uint16_t>(unresolved_child_id_ & 0xFFFF);
}
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -892,14 +858,17 @@
const RegTypeCache* const reg_type_cache_;
};
-// A merge of two unresolved types. If the types were resolved this may be Conflict or another
+// A merge of two unresolved types. If the types were resolved this may be
+// Conflict or another
// known ReferenceType.
-class UnresolvedMergedType : public UnresolvedType {
+class UnresolvedMergedType FINAL : public UnresolvedType {
public:
- UnresolvedMergedType(uint16_t left_id, uint16_t right_id, const RegTypeCache* reg_type_cache,
- uint16_t cache_id)
+ UnresolvedMergedType(uint16_t left_id, uint16_t right_id,
+ const RegTypeCache* reg_type_cache, uint16_t cache_id)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : UnresolvedType("", cache_id), reg_type_cache_(reg_type_cache), merged_types_(left_id, right_id) {
+ : UnresolvedType("", cache_id),
+ reg_type_cache_(reg_type_cache),
+ merged_types_(left_id, right_id) {
if (kIsDebugBuild) {
CheckInvariants();
}
@@ -914,11 +883,11 @@
// The complete set of merged types.
std::set<uint16_t> GetMergedTypes() const;
- bool IsUnresolvedMergedReference() const {
- return true;
- }
+ bool IsUnresolvedMergedReference() const OVERRIDE { return true; }
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsUnresolvedTypes() const OVERRIDE { return true; }
+
+ std::string Dump() const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h
index fdf96a8..9024a7d 100644
--- a/runtime/verifier/reg_type_cache-inl.h
+++ b/runtime/verifier/reg_type_cache-inl.h
@@ -17,21 +17,24 @@
#ifndef ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
#define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
+#include "class_linker.h"
+#include "mirror/class-inl.h"
+#include "mirror/string.h"
+#include "mirror/throwable.h"
#include "reg_type.h"
#include "reg_type_cache.h"
-#include "class_linker.h"
namespace art {
namespace verifier {
-inline RegType& RegTypeCache::GetFromId(uint16_t id) const {
+inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const {
DCHECK_LT(id, entries_.size());
- RegType* result = entries_[id];
+ const RegType* result = entries_[id];
DCHECK(result != NULL);
return *result;
}
-inline ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
+inline const ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
// We only expect 0 to be a precise constant.
DCHECK(value != 0 || precise);
if (precise && (value >= kMinSmallConstant) && (value <= kMaxSmallConstant)) {
@@ -40,6 +43,81 @@
return FromCat1NonSmallConstant(value, precise);
}
+inline const ImpreciseConstType& RegTypeCache::ByteConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::CharConstant() {
+ int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
+ const ConstantType& result = FromCat1Const(jchar_max, false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::ShortConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jshort>::min(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::IntConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::PosByteConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const ImpreciseConstType& RegTypeCache::PosShortConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jshort>::max(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
+}
+
+inline const PreciseReferenceType& RegTypeCache::JavaLangClass() {
+ const RegType* result = &FromClass("Ljava/lang/Class;", mirror::Class::GetJavaLangClass(), true);
+ DCHECK(result->IsPreciseReference());
+ return *down_cast<const PreciseReferenceType*>(result);
+}
+
+inline const PreciseReferenceType& RegTypeCache::JavaLangString() {
+ // String is final and therefore always precise.
+ const RegType* result = &FromClass("Ljava/lang/String;", mirror::String::GetJavaLangString(),
+ true);
+ DCHECK(result->IsPreciseReference());
+ return *down_cast<const PreciseReferenceType*>(result);
+}
+
+inline const RegType& RegTypeCache::JavaLangThrowable(bool precise) {
+ const RegType* result = &FromClass("Ljava/lang/Throwable;",
+ mirror::Throwable::GetJavaLangThrowable(), precise);
+ if (precise) {
+ DCHECK(result->IsPreciseReference());
+ return *down_cast<const PreciseReferenceType*>(result);
+ } else {
+ DCHECK(result->IsReference());
+ return *down_cast<const ReferenceType*>(result);
+ }
+}
+
+inline const RegType& RegTypeCache::JavaLangObject(bool precise) {
+ const RegType* result = &FromClass("Ljava/lang/Object;",
+ mirror::Class::GetJavaLangClass()->GetSuperClass(), precise);
+ if (precise) {
+ DCHECK(result->IsPreciseReference());
+ return *down_cast<const PreciseReferenceType*>(result);
+ } else {
+ DCHECK(result->IsReference());
+ return *down_cast<const ReferenceType*>(result);
+ }
+}
+
} // namespace verifier
} // namespace art
#endif // ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 63fb2d9..7c40945 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -21,15 +21,16 @@
#include "dex_file-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
+#include "reg_type-inl.h"
namespace art {
namespace verifier {
bool RegTypeCache::primitive_initialized_ = false;
uint16_t RegTypeCache::primitive_count_ = 0;
-PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
+const PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
-static bool MatchingPrecisionForClass(RegType* entry, bool precise)
+static bool MatchingPrecisionForClass(const RegType* entry, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (entry->IsPreciseReference() == precise) {
// We were or weren't looking for a precise reference and we found what we need.
@@ -65,8 +66,8 @@
DCHECK_EQ(entries_.size(), primitive_count_);
}
-RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor,
- bool precise) {
+const RegType& RegTypeCache::FromDescriptor(mirror::ClassLoader* loader, const char* descriptor,
+ bool precise) {
DCHECK(RegTypeCache::primitive_initialized_);
if (descriptor[1] == '\0') {
switch (descriptor[0]) {
@@ -95,10 +96,10 @@
} else {
return Conflict();
}
-};
+}
-RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const {
- CHECK(RegTypeCache::primitive_initialized_);
+const RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const {
+ DCHECK(RegTypeCache::primitive_initialized_);
switch (prim_type) {
case Primitive::kPrimBoolean:
return *BooleanType::GetInstance();
@@ -123,7 +124,7 @@
}
bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) {
- RegType* entry = entries_[idx];
+ const RegType* entry = entries_[idx];
if (descriptor != entry->descriptor_) {
return false;
}
@@ -143,11 +144,11 @@
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(loader));
- mirror::Class* klass = NULL;
+ mirror::Class* klass = nullptr;
if (can_load_classes_) {
klass = class_linker->FindClass(self, descriptor, class_loader);
} else {
- klass = class_linker->LookupClass(descriptor, loader);
+ klass = class_linker->LookupClass(self, descriptor, loader);
if (klass != nullptr && !klass->IsLoaded()) {
// We found the class but without it being loaded its not safe for use.
klass = nullptr;
@@ -156,8 +157,8 @@
return klass;
}
-RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor,
- bool precise) {
+const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descriptor,
+ bool precise) {
// Try looking up the class in the cache first. We use a StringPiece to avoid continual strlen
// operations on the descriptor.
StringPiece descriptor_sp(descriptor);
@@ -169,7 +170,7 @@
// Class not found in the cache, will create a new type for that.
// Try resolving class.
mirror::Class* klass = ResolveClass(descriptor, loader);
- if (klass != NULL) {
+ if (klass != nullptr) {
// Class resolved, first look for the class in the list of entries
// Class was not found, must create new type.
// To pass the verification, the type should be imprecise,
@@ -210,7 +211,7 @@
}
}
-RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
+const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
DCHECK(klass != nullptr);
if (klass->IsPrimitive()) {
// Note: precise isn't used for primitive classes. A char is assignable to an int. All
@@ -219,7 +220,7 @@
} else {
// Look for the reference in the list of entries to have.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->klass_.Read() == klass && MatchingPrecisionForClass(cur_entry, precise)) {
return *cur_entry;
}
@@ -237,8 +238,8 @@
}
RegTypeCache::RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
- if (kIsDebugBuild && can_load_classes) {
- Thread::Current()->AssertThreadSuspensionIsAllowable();
+ if (kIsDebugBuild) {
+ Thread::Current()->AssertThreadSuspensionIsAllowable(gAborting == 0);
}
entries_.reserve(64);
FillPrimitiveAndSmallConstantTypes();
@@ -251,7 +252,7 @@
// All entries are from the global pool, nothing to delete.
return;
}
- std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
+ std::vector<const RegType*>::iterator non_primitive_begin = entries_.begin();
std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants);
STLDeleteContainerPointers(non_primitive_begin, entries_.end());
}
@@ -271,7 +272,7 @@
DoubleLoType::Destroy();
DoubleHiType::Destroy();
for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
- PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
+ const PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
delete type;
small_precise_constants_[value - kMinSmallConstant] = nullptr;
}
@@ -281,14 +282,15 @@
}
template <class Type>
-Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
- mirror::Class* klass = NULL;
+const Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
+ mirror::Class* klass = nullptr;
// Try loading the class from linker.
if (!descriptor.empty()) {
klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(),
descriptor.c_str());
+ DCHECK(klass != nullptr);
}
- Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
+ const Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
RegTypeCache::primitive_count_++;
return entry;
}
@@ -313,25 +315,27 @@
}
}
-RegType& RegTypeCache::FromUnresolvedMerge(RegType& left, RegType& right) {
+const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) {
std::set<uint16_t> types;
if (left.IsUnresolvedMergedReference()) {
- types = (down_cast<UnresolvedMergedType*>(&left))->GetMergedTypes();
+ RegType& non_const(const_cast<RegType&>(left));
+ types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
} else {
types.insert(left.GetId());
}
if (right.IsUnresolvedMergedReference()) {
- std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&right))->GetMergedTypes();
+ RegType& non_const(const_cast<RegType&>(right));
+ std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
types.insert(right_types.begin(), right_types.end());
} else {
types.insert(right.GetId());
}
// Check if entry already exists.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedMergedReference()) {
std::set<uint16_t> cur_entry_types =
- (down_cast<UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
+ (down_cast<const UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
if (cur_entry_types == types) {
return *cur_entry;
}
@@ -348,13 +352,13 @@
return *entry;
}
-RegType& RegTypeCache::FromUnresolvedSuperClass(RegType& child) {
+const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) {
// Check if entry already exists.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedSuperClass()) {
- UnresolvedSuperClass* tmp_entry =
- down_cast<UnresolvedSuperClass*>(cur_entry);
+ const UnresolvedSuperClass* tmp_entry =
+ down_cast<const UnresolvedSuperClass*>(cur_entry);
uint16_t unresolved_super_child_id =
tmp_entry->GetUnresolvedSuperClassChildId();
if (unresolved_super_child_id == child.GetId()) {
@@ -367,28 +371,29 @@
return *entry;
}
-UninitializedType& RegTypeCache::Uninitialized(RegType& type, uint32_t allocation_pc) {
- UninitializedType* entry = NULL;
+const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
+ UninitializedType* entry = nullptr;
const std::string& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedAndUninitializedReference() &&
- down_cast<UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc() == allocation_pc &&
+ down_cast<const UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc()
+ == allocation_pc &&
(cur_entry->GetDescriptor() == descriptor)) {
- return *down_cast<UnresolvedUninitializedRefType*>(cur_entry);
+ return *down_cast<const UnresolvedUninitializedRefType*>(cur_entry);
}
}
entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size());
} else {
mirror::Class* klass = type.GetClass();
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUninitializedReference() &&
- down_cast<UninitializedReferenceType*>(cur_entry)
+ down_cast<const UninitializedReferenceType*>(cur_entry)
->GetAllocationPc() == allocation_pc &&
cur_entry->GetClass() == klass) {
- return *down_cast<UninitializedReferenceType*>(cur_entry);
+ return *down_cast<const UninitializedReferenceType*>(cur_entry);
}
}
entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size());
@@ -397,13 +402,13 @@
return *entry;
}
-RegType& RegTypeCache::FromUninitialized(RegType& uninit_type) {
+const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) {
RegType* entry;
if (uninit_type.IsUnresolvedTypes()) {
const std::string& descriptor(uninit_type.GetDescriptor());
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedReference() &&
cur_entry->GetDescriptor() == descriptor) {
return *cur_entry;
@@ -415,7 +420,7 @@
if (uninit_type.IsUninitializedThisReference() && !klass->IsFinal()) {
// For uninitialized "this reference" look for reference types that are not precise.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsReference() && cur_entry->GetClass() == klass) {
return *cur_entry;
}
@@ -425,7 +430,7 @@
// We're uninitialized because of allocation, look or create a precise type as allocations
// may only create objects of that type.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) {
return *cur_entry;
}
@@ -439,61 +444,24 @@
return *entry;
}
-ImpreciseConstType& RegTypeCache::ByteConstant() {
- ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
- DCHECK(result.IsImpreciseConstant());
- return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::CharConstant() {
- int32_t jchar_max = static_cast<int32_t>(std::numeric_limits<jchar>::max());
- ConstantType& result = FromCat1Const(jchar_max, false);
- DCHECK(result.IsImpreciseConstant());
- return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::ShortConstant() {
- ConstantType& result = FromCat1Const(std::numeric_limits<jshort>::min(), false);
- DCHECK(result.IsImpreciseConstant());
- return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::IntConstant() {
- ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
- DCHECK(result.IsImpreciseConstant());
- return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::PosByteConstant() {
- ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::max(), false);
- DCHECK(result.IsImpreciseConstant());
- return *down_cast<ImpreciseConstType*>(&result);
-}
-
-ImpreciseConstType& RegTypeCache::PosShortConstant() {
- ConstantType& result = FromCat1Const(std::numeric_limits<jshort>::max(), false);
- DCHECK(result.IsImpreciseConstant());
- return *down_cast<ImpreciseConstType*>(&result);
-}
-
-UninitializedType& RegTypeCache::UninitializedThisArgument(RegType& type) {
+const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
UninitializedType* entry;
const std::string& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
cur_entry->GetDescriptor() == descriptor) {
- return *down_cast<UninitializedType*>(cur_entry);
+ return *down_cast<const UninitializedType*>(cur_entry);
}
}
entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size());
} else {
mirror::Class* klass = type.GetClass();
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
- return *down_cast<UninitializedType*>(cur_entry);
+ return *down_cast<const UninitializedType*>(cur_entry);
}
}
entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size());
@@ -502,13 +470,13 @@
return *entry;
}
-ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->klass_.IsNull() && cur_entry->IsConstant() &&
cur_entry->IsPreciseConstant() == precise &&
- (down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) {
- return *down_cast<ConstantType*>(cur_entry);
+ (down_cast<const ConstantType*>(cur_entry))->ConstantValue() == value) {
+ return *down_cast<const ConstantType*>(cur_entry);
}
}
ConstantType* entry;
@@ -521,12 +489,12 @@
return *entry;
}
-ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) &&
- (down_cast<ConstantType*>(cur_entry))->ConstantValueLo() == value) {
- return *down_cast<ConstantType*>(cur_entry);
+ (down_cast<const ConstantType*>(cur_entry))->ConstantValueLo() == value) {
+ return *down_cast<const ConstantType*>(cur_entry);
}
}
ConstantType* entry;
@@ -539,12 +507,12 @@
return *entry;
}
-ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
+ const RegType* cur_entry = entries_[i];
if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) &&
- (down_cast<ConstantType*>(cur_entry))->ConstantValueHi() == value) {
- return *down_cast<ConstantType*>(cur_entry);
+ (down_cast<const ConstantType*>(cur_entry))->ConstantValueHi() == value) {
+ return *down_cast<const ConstantType*>(cur_entry);
}
}
ConstantType* entry;
@@ -557,7 +525,7 @@
return *entry;
}
-RegType& RegTypeCache::GetComponentType(RegType& array, mirror::ClassLoader* loader) {
+const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) {
if (!array.IsArrayTypes()) {
return Conflict();
} else if (array.IsUnresolvedTypes()) {
@@ -581,8 +549,8 @@
void RegTypeCache::Dump(std::ostream& os) {
for (size_t i = 0; i < entries_.size(); i++) {
- RegType* cur_entry = entries_[i];
- if (cur_entry != NULL) {
+ const RegType* cur_entry = entries_[i];
+ if (cur_entry != nullptr) {
os << i << ": " << cur_entry->Dump() << "\n";
}
}
@@ -592,18 +560,18 @@
// Visit the primitive types, this is required since if there are no active verifiers they wont
// be in the entries array, and therefore not visited as roots.
if (primitive_initialized_) {
- Undefined().VisitRoots(callback, arg);
- Conflict().VisitRoots(callback, arg);
- Boolean().VisitRoots(callback, arg);
- Byte().VisitRoots(callback, arg);
- Short().VisitRoots(callback, arg);
- Char().VisitRoots(callback, arg);
- Integer().VisitRoots(callback, arg);
- LongLo().VisitRoots(callback, arg);
- LongHi().VisitRoots(callback, arg);
- Float().VisitRoots(callback, arg);
- DoubleLo().VisitRoots(callback, arg);
- DoubleHi().VisitRoots(callback, arg);
+ UndefinedType::GetInstance()->VisitRoots(callback, arg);
+ ConflictType::GetInstance()->VisitRoots(callback, arg);
+ BooleanType::GetInstance()->VisitRoots(callback, arg);
+ ByteType::GetInstance()->VisitRoots(callback, arg);
+ ShortType::GetInstance()->VisitRoots(callback, arg);
+ CharType::GetInstance()->VisitRoots(callback, arg);
+ IntegerType::GetInstance()->VisitRoots(callback, arg);
+ LongLoType::GetInstance()->VisitRoots(callback, arg);
+ LongHiType::GetInstance()->VisitRoots(callback, arg);
+ FloatType::GetInstance()->VisitRoots(callback, arg);
+ DoubleLoType::GetInstance()->VisitRoots(callback, arg);
+ DoubleHiType::GetInstance()->VisitRoots(callback, arg);
for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
small_precise_constants_[value - kMinSmallConstant]->VisitRoots(callback, arg);
}
@@ -611,7 +579,7 @@
}
void RegTypeCache::VisitRoots(RootCallback* callback, void* arg) {
- for (RegType* entry : entries_) {
+ for (const RegType* entry : entries_) {
entry->VisitRoots(callback, arg);
}
}
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index a75aaab..ff7b1f3 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -51,99 +51,91 @@
}
}
static void ShutDown();
- RegType& GetFromId(uint16_t id) const;
- RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise)
+ const art::verifier::RegType& GetFromId(uint16_t id) const;
+ const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
+ const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ConstantType& FromCat1Const(int32_t value, bool precise)
+ const ConstantType& FromCat1Const(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ConstantType& FromCat2ConstLo(int32_t value, bool precise)
+ const ConstantType& FromCat2ConstLo(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ConstantType& FromCat2ConstHi(int32_t value, bool precise)
+ const ConstantType& FromCat2ConstHi(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise)
+ const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& FromUnresolvedMerge(RegType& left, RegType& right)
+ const RegType& FromUnresolvedMerge(const RegType& left, const RegType& right)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& FromUnresolvedSuperClass(RegType& child)
+ const RegType& FromUnresolvedSuperClass(const RegType& child)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& JavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // String is final and therefore always precise.
- return From(NULL, "Ljava/lang/String;", true);
- }
- RegType& JavaLangThrowable(bool precise)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return From(NULL, "Ljava/lang/Throwable;", precise);
- }
- ConstantType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const ConstantType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return FromCat1Const(0, true);
}
- ConstantType& One() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const ConstantType& One() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return FromCat1Const(1, true);
}
size_t GetCacheSize() {
return entries_.size();
}
- static RegType& Boolean() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const BooleanType& Boolean() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *BooleanType::GetInstance();
}
- static RegType& Byte() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const ByteType& Byte() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *ByteType::GetInstance();
}
- static RegType& Char() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const CharType& Char() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *CharType::GetInstance();
}
- static RegType& Short() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const ShortType& Short() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *ShortType::GetInstance();
}
- static RegType& Integer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const IntegerType& Integer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *IntegerType::GetInstance();
}
- static RegType& Float() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const FloatType& Float() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *FloatType::GetInstance();
}
- static RegType& LongLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const LongLoType& LongLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *LongLoType::GetInstance();
}
- static RegType& LongHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const LongHiType& LongHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *LongHiType::GetInstance();
}
- static RegType& DoubleLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DoubleLoType& DoubleLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *DoubleLoType::GetInstance();
}
- static RegType& DoubleHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DoubleHiType& DoubleHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *DoubleHiType::GetInstance();
}
- static RegType& Undefined() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const UndefinedType& Undefined() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return *UndefinedType::GetInstance();
}
- static RegType& Conflict() {
+ const ConflictType& Conflict() {
return *ConflictType::GetInstance();
}
- RegType& JavaLangClass(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return From(NULL, "Ljava/lang/Class;", precise);
- }
- RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return From(NULL, "Ljava/lang/Object;", precise);
- }
- UninitializedType& Uninitialized(RegType& type, uint32_t allocation_pc)
+
+ const PreciseReferenceType& JavaLangClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const PreciseReferenceType& JavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& JavaLangThrowable(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ const UninitializedType& Uninitialized(const RegType& type, uint32_t allocation_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Create an uninitialized 'this' argument for the given type.
- UninitializedType& UninitializedThisArgument(RegType& type)
+ const UninitializedType& UninitializedThisArgument(const RegType& type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& FromUninitialized(RegType& uninit_type)
+ const RegType& FromUninitialized(const RegType& uninit_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ImpreciseConstType& CharConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ImpreciseConstType& PosByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ImpreciseConstType& PosShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& GetComponentType(RegType& array, mirror::ClassLoader* loader)
+ const ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& CharConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& PosByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& PosShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
+ const RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
void VisitRoots(RootCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void VisitStaticRoots(RootCallback* callback, void* arg)
@@ -155,23 +147,20 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
+ const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AddEntry(RegType* new_entry);
template <class Type>
- static Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
+ static const Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void CreatePrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // The actual storage for the RegTypes.
- std::vector<RegType*> entries_;
-
// A quick look up for popular small constants.
static constexpr int32_t kMinSmallConstant = -1;
static constexpr int32_t kMaxSmallConstant = 4;
- static PreciseConstType* small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
+ static const PreciseConstType* small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
static constexpr size_t kNumPrimitivesAndSmallConstants =
12 + (kMaxSmallConstant - kMinSmallConstant + 1);
@@ -182,6 +171,9 @@
// Number of well known primitives that will be copied into a RegTypeCache upon construction.
static uint16_t primitive_count_;
+ // The actual storage for the RegTypes.
+ std::vector<const RegType*> entries_;
+
// Whether or not we're allowed to load classes.
const bool can_load_classes_;
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index e27558a..2fecc8b 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -21,6 +21,7 @@
#include "base/casts.h"
#include "common_runtime_test.h"
#include "reg_type_cache-inl.h"
+#include "reg_type-inl.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -33,21 +34,21 @@
// Tests creating primitive types types.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& ref_type_const_0 = cache.FromCat1Const(10, true);
- RegType& ref_type_const_1 = cache.FromCat1Const(10, true);
- RegType& ref_type_const_2 = cache.FromCat1Const(30, true);
- RegType& ref_type_const_3 = cache.FromCat1Const(30, false);
+ const RegType& ref_type_const_0 = cache.FromCat1Const(10, true);
+ const RegType& ref_type_const_1 = cache.FromCat1Const(10, true);
+ const RegType& ref_type_const_2 = cache.FromCat1Const(30, true);
+ const RegType& ref_type_const_3 = cache.FromCat1Const(30, false);
EXPECT_TRUE(ref_type_const_0.Equals(ref_type_const_1));
EXPECT_FALSE(ref_type_const_0.Equals(ref_type_const_2));
EXPECT_FALSE(ref_type_const_0.Equals(ref_type_const_3));
- RegType& ref_type_const_wide_0 = cache.FromCat2ConstHi(50, true);
- RegType& ref_type_const_wide_1 = cache.FromCat2ConstHi(50, true);
+ const RegType& ref_type_const_wide_0 = cache.FromCat2ConstHi(50, true);
+ const RegType& ref_type_const_wide_1 = cache.FromCat2ConstHi(50, true);
EXPECT_TRUE(ref_type_const_wide_0.Equals(ref_type_const_wide_1));
- RegType& ref_type_const_wide_2 = cache.FromCat2ConstLo(50, true);
- RegType& ref_type_const_wide_3 = cache.FromCat2ConstLo(50, true);
- RegType& ref_type_const_wide_4 = cache.FromCat2ConstLo(55, true);
+ const RegType& ref_type_const_wide_2 = cache.FromCat2ConstLo(50, true);
+ const RegType& ref_type_const_wide_3 = cache.FromCat2ConstLo(50, true);
+ const RegType& ref_type_const_wide_4 = cache.FromCat2ConstLo(55, true);
EXPECT_TRUE(ref_type_const_wide_2.Equals(ref_type_const_wide_3));
EXPECT_FALSE(ref_type_const_wide_2.Equals(ref_type_const_wide_4));
}
@@ -56,11 +57,11 @@
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
int64_t val = static_cast<int32_t>(1234);
- RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true);
- RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
- RegType& precise_const = cache.FromCat1Const(static_cast<int32_t>(val >> 32), true);
- RegType& long_lo = cache.LongLo();
- RegType& long_hi = cache.LongHi();
+ const RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ const RegType& precise_const = cache.FromCat1Const(static_cast<int32_t>(val >> 32), true);
+ const RegType& long_lo = cache.LongLo();
+ const RegType& long_hi = cache.LongHi();
// Check sanity of types.
EXPECT_TRUE(precise_lo.IsLowHalf());
EXPECT_FALSE(precise_hi.IsLowHalf());
@@ -80,7 +81,7 @@
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& bool_reg_type = cache.Boolean();
+ const RegType& bool_reg_type = cache.Boolean();
EXPECT_FALSE(bool_reg_type.IsUndefined());
EXPECT_FALSE(bool_reg_type.IsConflict());
EXPECT_FALSE(bool_reg_type.IsZero());
@@ -111,8 +112,9 @@
EXPECT_FALSE(bool_reg_type.IsDoubleTypes());
EXPECT_TRUE(bool_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(bool_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(bool_reg_type.HasClass());
- RegType& byte_reg_type = cache.Byte();
+ const RegType& byte_reg_type = cache.Byte();
EXPECT_FALSE(byte_reg_type.IsUndefined());
EXPECT_FALSE(byte_reg_type.IsConflict());
EXPECT_FALSE(byte_reg_type.IsZero());
@@ -143,8 +145,9 @@
EXPECT_FALSE(byte_reg_type.IsDoubleTypes());
EXPECT_TRUE(byte_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(byte_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(byte_reg_type.HasClass());
- RegType& char_reg_type = cache.Char();
+ const RegType& char_reg_type = cache.Char();
EXPECT_FALSE(char_reg_type.IsUndefined());
EXPECT_FALSE(char_reg_type.IsConflict());
EXPECT_FALSE(char_reg_type.IsZero());
@@ -175,8 +178,9 @@
EXPECT_FALSE(char_reg_type.IsDoubleTypes());
EXPECT_TRUE(char_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(char_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(char_reg_type.HasClass());
- RegType& short_reg_type = cache.Short();
+ const RegType& short_reg_type = cache.Short();
EXPECT_FALSE(short_reg_type.IsUndefined());
EXPECT_FALSE(short_reg_type.IsConflict());
EXPECT_FALSE(short_reg_type.IsZero());
@@ -207,8 +211,9 @@
EXPECT_FALSE(short_reg_type.IsDoubleTypes());
EXPECT_TRUE(short_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(short_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(short_reg_type.HasClass());
- RegType& int_reg_type = cache.Integer();
+ const RegType& int_reg_type = cache.Integer();
EXPECT_FALSE(int_reg_type.IsUndefined());
EXPECT_FALSE(int_reg_type.IsConflict());
EXPECT_FALSE(int_reg_type.IsZero());
@@ -239,8 +244,9 @@
EXPECT_FALSE(int_reg_type.IsDoubleTypes());
EXPECT_TRUE(int_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(int_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(int_reg_type.HasClass());
- RegType& long_reg_type = cache.LongLo();
+ const RegType& long_reg_type = cache.LongLo();
EXPECT_FALSE(long_reg_type.IsUndefined());
EXPECT_FALSE(long_reg_type.IsConflict());
EXPECT_FALSE(long_reg_type.IsZero());
@@ -271,8 +277,9 @@
EXPECT_FALSE(long_reg_type.IsDoubleTypes());
EXPECT_FALSE(long_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(long_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(long_reg_type.HasClass());
- RegType& float_reg_type = cache.Float();
+ const RegType& float_reg_type = cache.Float();
EXPECT_FALSE(float_reg_type.IsUndefined());
EXPECT_FALSE(float_reg_type.IsConflict());
EXPECT_FALSE(float_reg_type.IsZero());
@@ -303,8 +310,9 @@
EXPECT_FALSE(float_reg_type.IsDoubleTypes());
EXPECT_FALSE(float_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(float_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(float_reg_type.HasClass());
- RegType& double_reg_type = cache.DoubleLo();
+ const RegType& double_reg_type = cache.DoubleLo();
EXPECT_FALSE(double_reg_type.IsUndefined());
EXPECT_FALSE(double_reg_type.IsConflict());
EXPECT_FALSE(double_reg_type.IsZero());
@@ -335,6 +343,7 @@
EXPECT_TRUE(double_reg_type.IsDoubleTypes());
EXPECT_FALSE(double_reg_type.IsArrayIndexTypes());
EXPECT_FALSE(double_reg_type.IsNonZeroReferenceTypes());
+ EXPECT_TRUE(double_reg_type.HasClass());
}
class RegTypeReferenceTest : public CommonRuntimeTest {};
@@ -344,9 +353,9 @@
// match the one that is imprecise.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& imprecise_obj = cache.JavaLangObject(false);
- RegType& precise_obj = cache.JavaLangObject(true);
- RegType& precise_obj_2 = cache.FromDescriptor(NULL, "Ljava/lang/Object;", true);
+ const RegType& imprecise_obj = cache.JavaLangObject(false);
+ const RegType& precise_obj = cache.JavaLangObject(true);
+ const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
EXPECT_TRUE(precise_obj.Equals(precise_obj_2));
EXPECT_FALSE(imprecise_obj.Equals(precise_obj));
@@ -359,14 +368,14 @@
// a hit second time.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& ref_type_0 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+ const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes());
- RegType& ref_type_1 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+ const RegType& ref_type_1 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.Equals(ref_type_1));
- RegType& unresolved_super_class = cache.FromUnresolvedSuperClass(ref_type_0);
+ const RegType& unresolved_super_class = cache.FromUnresolvedSuperClass(ref_type_0);
EXPECT_TRUE(unresolved_super_class.IsUnresolvedSuperClass());
EXPECT_TRUE(unresolved_super_class.IsNonZeroReferenceTypes());
}
@@ -375,21 +384,21 @@
// Tests creating types uninitialized types from unresolved types.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& ref_type_0 = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+ const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
- RegType& ref_type = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+ const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.Equals(ref_type));
// Create an uninitialized type of this unresolved type
- RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull);
+ const RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull);
EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference());
EXPECT_TRUE(unresolved_unintialised.IsUninitializedTypes());
EXPECT_TRUE(unresolved_unintialised.IsNonZeroReferenceTypes());
// Create an uninitialized type of this unresolved type with different PC
- RegType& ref_type_unresolved_unintialised_1 = cache.Uninitialized(ref_type, 1102ull);
+ const RegType& ref_type_unresolved_unintialised_1 = cache.Uninitialized(ref_type, 1102ull);
EXPECT_TRUE(unresolved_unintialised.IsUnresolvedAndUninitializedReference());
EXPECT_FALSE(unresolved_unintialised.Equals(ref_type_unresolved_unintialised_1));
// Create an uninitialized type of this unresolved type with the same PC
- RegType& unresolved_unintialised_2 = cache.Uninitialized(ref_type, 1101ull);
+ const RegType& unresolved_unintialised_2 = cache.Uninitialized(ref_type, 1101ull);
EXPECT_TRUE(unresolved_unintialised.Equals(unresolved_unintialised_2));
}
@@ -397,12 +406,12 @@
// Tests types for proper Dump messages.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& unresolved_ref = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
- RegType& unresolved_ref_another = cache.FromDescriptor(NULL, "Ljava/lang/DoesNotExistEither;", true);
- RegType& resolved_ref = cache.JavaLangString();
- RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10);
- RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12);
- RegType& unresolved_merged = cache.FromUnresolvedMerge(unresolved_ref, unresolved_ref_another);
+ const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
+ const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true);
+ const RegType& resolved_ref = cache.JavaLangString();
+ const RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10);
+ const RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12);
+ const RegType& unresolved_merged = cache.FromUnresolvedMerge(unresolved_ref, unresolved_ref_another);
std::string expected = "Unresolved Reference: java.lang.DoesNotExist";
EXPECT_EQ(expected, unresolved_ref.Dump());
@@ -422,16 +431,16 @@
// The JavaLangObject method instead of FromDescriptor. String class is final.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& ref_type = cache.JavaLangString();
- RegType& ref_type_2 = cache.JavaLangString();
- RegType& ref_type_3 = cache.FromDescriptor(NULL, "Ljava/lang/String;", true);
+ const RegType& ref_type = cache.JavaLangString();
+ const RegType& ref_type_2 = cache.JavaLangString();
+ const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true);
EXPECT_TRUE(ref_type.Equals(ref_type_2));
EXPECT_TRUE(ref_type_2.Equals(ref_type_3));
EXPECT_TRUE(ref_type.IsPreciseReference());
// Create an uninitialized type out of this:
- RegType& ref_type_unintialized = cache.Uninitialized(ref_type, 0110ull);
+ const RegType& ref_type_unintialized = cache.Uninitialized(ref_type, 0110ull);
EXPECT_TRUE(ref_type_unintialized.IsUninitializedReference());
EXPECT_FALSE(ref_type_unintialized.IsUnresolvedAndUninitializedReference());
}
@@ -442,9 +451,9 @@
// The JavaLangObject method instead of FromDescriptor. Object Class in not final.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache(true);
- RegType& ref_type = cache.JavaLangObject(true);
- RegType& ref_type_2 = cache.JavaLangObject(true);
- RegType& ref_type_3 = cache.FromDescriptor(NULL, "Ljava/lang/Object;", true);
+ const RegType& ref_type = cache.JavaLangObject(true);
+ const RegType& ref_type_2 = cache.JavaLangObject(true);
+ const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true);
EXPECT_TRUE(ref_type.Equals(ref_type_2));
EXPECT_TRUE(ref_type_3.Equals(ref_type_2));
@@ -455,19 +464,20 @@
// String and object , LUB is object.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache_new(true);
- RegType& string = cache_new.JavaLangString();
- RegType& Object = cache_new.JavaLangObject(true);
+ const RegType& string = cache_new.JavaLangString();
+ const RegType& Object = cache_new.JavaLangObject(true);
EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject());
// Merge two unresolved types.
- RegType& ref_type_0 = cache_new.FromDescriptor(NULL, "Ljava/lang/DoesNotExist;", true);
+ const RegType& ref_type_0 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true);
EXPECT_TRUE(ref_type_0.IsUnresolvedReference());
- RegType& ref_type_1 = cache_new.FromDescriptor(NULL, "Ljava/lang/DoesNotExistToo;", true);
+ const RegType& ref_type_1 = cache_new.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistToo;", true);
EXPECT_FALSE(ref_type_0.Equals(ref_type_1));
- RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new);
+ const RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new);
EXPECT_TRUE(merged.IsUnresolvedMergedReference());
+ RegType& merged_nonconst = const_cast<RegType&>(merged);
- std::set<uint16_t> merged_ids = (down_cast<UnresolvedMergedType*>(&merged))->GetMergedTypes();
+ std::set<uint16_t> merged_ids = (down_cast<UnresolvedMergedType*>(&merged_nonconst))->GetMergedTypes();
EXPECT_EQ(ref_type_0.GetId(), *(merged_ids.begin()));
EXPECT_EQ(ref_type_1.GetId(), *((++merged_ids.begin())));
}
@@ -478,27 +488,27 @@
RegTypeCache cache_new(true);
constexpr int32_t kTestConstantValue = 10;
- RegType& float_type = cache_new.Float();
- RegType& precise_cst = cache_new.FromCat1Const(kTestConstantValue, true);
- RegType& imprecise_cst = cache_new.FromCat1Const(kTestConstantValue, false);
+ const RegType& float_type = cache_new.Float();
+ const RegType& precise_cst = cache_new.FromCat1Const(kTestConstantValue, true);
+ const RegType& imprecise_cst = cache_new.FromCat1Const(kTestConstantValue, false);
{
// float MERGE precise cst => float.
- RegType& merged = float_type.Merge(precise_cst, &cache_new);
+ const RegType& merged = float_type.Merge(precise_cst, &cache_new);
EXPECT_TRUE(merged.IsFloat());
}
{
// precise cst MERGE float => float.
- RegType& merged = precise_cst.Merge(float_type, &cache_new);
+ const RegType& merged = precise_cst.Merge(float_type, &cache_new);
EXPECT_TRUE(merged.IsFloat());
}
{
// float MERGE imprecise cst => float.
- RegType& merged = float_type.Merge(imprecise_cst, &cache_new);
+ const RegType& merged = float_type.Merge(imprecise_cst, &cache_new);
EXPECT_TRUE(merged.IsFloat());
}
{
// imprecise cst MERGE float => float.
- RegType& merged = imprecise_cst.Merge(float_type, &cache_new);
+ const RegType& merged = imprecise_cst.Merge(float_type, &cache_new);
EXPECT_TRUE(merged.IsFloat());
}
}
@@ -509,50 +519,50 @@
RegTypeCache cache_new(true);
constexpr int32_t kTestConstantValue = 10;
- RegType& long_lo_type = cache_new.LongLo();
- RegType& long_hi_type = cache_new.LongHi();
- RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
- RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
- RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
- RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
+ const RegType& long_lo_type = cache_new.LongLo();
+ const RegType& long_hi_type = cache_new.LongHi();
+ const RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
+ const RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
+ const RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
+ const RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
{
// lo MERGE precise cst lo => lo.
- RegType& merged = long_lo_type.Merge(precise_cst_lo, &cache_new);
+ const RegType& merged = long_lo_type.Merge(precise_cst_lo, &cache_new);
EXPECT_TRUE(merged.IsLongLo());
}
{
// precise cst lo MERGE lo => lo.
- RegType& merged = precise_cst_lo.Merge(long_lo_type, &cache_new);
+ const RegType& merged = precise_cst_lo.Merge(long_lo_type, &cache_new);
EXPECT_TRUE(merged.IsLongLo());
}
{
// lo MERGE imprecise cst lo => lo.
- RegType& merged = long_lo_type.Merge(imprecise_cst_lo, &cache_new);
+ const RegType& merged = long_lo_type.Merge(imprecise_cst_lo, &cache_new);
EXPECT_TRUE(merged.IsLongLo());
}
{
// imprecise cst lo MERGE lo => lo.
- RegType& merged = imprecise_cst_lo.Merge(long_lo_type, &cache_new);
+ const RegType& merged = imprecise_cst_lo.Merge(long_lo_type, &cache_new);
EXPECT_TRUE(merged.IsLongLo());
}
{
// hi MERGE precise cst hi => hi.
- RegType& merged = long_hi_type.Merge(precise_cst_hi, &cache_new);
+ const RegType& merged = long_hi_type.Merge(precise_cst_hi, &cache_new);
EXPECT_TRUE(merged.IsLongHi());
}
{
// precise cst hi MERGE hi => hi.
- RegType& merged = precise_cst_hi.Merge(long_hi_type, &cache_new);
+ const RegType& merged = precise_cst_hi.Merge(long_hi_type, &cache_new);
EXPECT_TRUE(merged.IsLongHi());
}
{
// hi MERGE imprecise cst hi => hi.
- RegType& merged = long_hi_type.Merge(imprecise_cst_hi, &cache_new);
+ const RegType& merged = long_hi_type.Merge(imprecise_cst_hi, &cache_new);
EXPECT_TRUE(merged.IsLongHi());
}
{
// imprecise cst hi MERGE hi => hi.
- RegType& merged = imprecise_cst_hi.Merge(long_hi_type, &cache_new);
+ const RegType& merged = imprecise_cst_hi.Merge(long_hi_type, &cache_new);
EXPECT_TRUE(merged.IsLongHi());
}
}
@@ -563,50 +573,50 @@
RegTypeCache cache_new(true);
constexpr int32_t kTestConstantValue = 10;
- RegType& double_lo_type = cache_new.DoubleLo();
- RegType& double_hi_type = cache_new.DoubleHi();
- RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
- RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
- RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
- RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
+ const RegType& double_lo_type = cache_new.DoubleLo();
+ const RegType& double_hi_type = cache_new.DoubleHi();
+ const RegType& precise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, true);
+ const RegType& imprecise_cst_lo = cache_new.FromCat2ConstLo(kTestConstantValue, false);
+ const RegType& precise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, true);
+ const RegType& imprecise_cst_hi = cache_new.FromCat2ConstHi(kTestConstantValue, false);
{
// lo MERGE precise cst lo => lo.
- RegType& merged = double_lo_type.Merge(precise_cst_lo, &cache_new);
+ const RegType& merged = double_lo_type.Merge(precise_cst_lo, &cache_new);
EXPECT_TRUE(merged.IsDoubleLo());
}
{
// precise cst lo MERGE lo => lo.
- RegType& merged = precise_cst_lo.Merge(double_lo_type, &cache_new);
+ const RegType& merged = precise_cst_lo.Merge(double_lo_type, &cache_new);
EXPECT_TRUE(merged.IsDoubleLo());
}
{
// lo MERGE imprecise cst lo => lo.
- RegType& merged = double_lo_type.Merge(imprecise_cst_lo, &cache_new);
+ const RegType& merged = double_lo_type.Merge(imprecise_cst_lo, &cache_new);
EXPECT_TRUE(merged.IsDoubleLo());
}
{
// imprecise cst lo MERGE lo => lo.
- RegType& merged = imprecise_cst_lo.Merge(double_lo_type, &cache_new);
+ const RegType& merged = imprecise_cst_lo.Merge(double_lo_type, &cache_new);
EXPECT_TRUE(merged.IsDoubleLo());
}
{
// hi MERGE precise cst hi => hi.
- RegType& merged = double_hi_type.Merge(precise_cst_hi, &cache_new);
+ const RegType& merged = double_hi_type.Merge(precise_cst_hi, &cache_new);
EXPECT_TRUE(merged.IsDoubleHi());
}
{
// precise cst hi MERGE hi => hi.
- RegType& merged = precise_cst_hi.Merge(double_hi_type, &cache_new);
+ const RegType& merged = precise_cst_hi.Merge(double_hi_type, &cache_new);
EXPECT_TRUE(merged.IsDoubleHi());
}
{
// hi MERGE imprecise cst hi => hi.
- RegType& merged = double_hi_type.Merge(imprecise_cst_hi, &cache_new);
+ const RegType& merged = double_hi_type.Merge(imprecise_cst_hi, &cache_new);
EXPECT_TRUE(merged.IsDoubleHi());
}
{
// imprecise cst hi MERGE hi => hi.
- RegType& merged = imprecise_cst_hi.Merge(double_hi_type, &cache_new);
+ const RegType& merged = imprecise_cst_hi.Merge(double_hi_type, &cache_new);
EXPECT_TRUE(merged.IsDoubleHi());
}
}
@@ -615,8 +625,8 @@
// Tests creating primitive types types.
ScopedObjectAccess soa(Thread::Current());
RegTypeCache cache_new(true);
- RegType& imprecise_const = cache_new.FromCat1Const(10, false);
- RegType& precise_const = cache_new.FromCat1Const(10, true);
+ const RegType& imprecise_const = cache_new.FromCat1Const(10, false);
+ const RegType& precise_const = cache_new.FromCat1Const(10, true);
EXPECT_TRUE(imprecise_const.IsImpreciseConstant());
EXPECT_TRUE(precise_const.IsPreciseConstant());
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 378c6d3..219e687 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -25,10 +25,135 @@
namespace art {
namespace verifier {
-inline RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const {
+inline const RegType& RegisterLine::GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const {
// The register index was validated during the static pass, so we don't need to check it here.
DCHECK_LT(vsrc, num_regs_);
- return verifier_->GetRegTypeCache()->GetFromId(line_[vsrc]);
+ return verifier->GetRegTypeCache()->GetFromId(line_[vsrc]);
+}
+
+inline bool RegisterLine::SetRegisterType(MethodVerifier* verifier, uint32_t vdst,
+ const RegType& new_type) {
+ DCHECK_LT(vdst, num_regs_);
+ if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
+ << new_type << "'";
+ return false;
+ } else if (new_type.IsConflict()) { // should only be set during a merge
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
+ return false;
+ } else {
+ line_[vdst] = new_type.GetId();
+ }
+ // Clear the monitor entry bits for this register.
+ ClearAllRegToLockDepths(vdst);
+ return true;
+}
+
+inline bool RegisterLine::SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst,
+ const RegType& new_type1,
+ const RegType& new_type2) {
+ DCHECK_LT(vdst + 1, num_regs_);
+ if (!new_type1.CheckWidePair(new_type2)) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
+ << new_type1 << "' '" << new_type2 << "'";
+ return false;
+ } else {
+ line_[vdst] = new_type1.GetId();
+ line_[vdst + 1] = new_type2.GetId();
+ }
+ // Clear the monitor entry bits for this register.
+ ClearAllRegToLockDepths(vdst);
+ ClearAllRegToLockDepths(vdst + 1);
+ return true;
+}
+
+inline void RegisterLine::SetResultTypeToUnknown(MethodVerifier* verifier) {
+ result_[0] = verifier->GetRegTypeCache()->Undefined().GetId();
+ result_[1] = result_[0];
+}
+
+inline void RegisterLine::SetResultRegisterType(MethodVerifier* verifier, const RegType& new_type) {
+ DCHECK(!new_type.IsLowHalf());
+ DCHECK(!new_type.IsHighHalf());
+ result_[0] = new_type.GetId();
+ result_[1] = verifier->GetRegTypeCache()->Undefined().GetId();
+}
+
+inline void RegisterLine::SetResultRegisterTypeWide(const RegType& new_type1,
+ const RegType& new_type2) {
+ DCHECK(new_type1.CheckWidePair(new_type2));
+ result_[0] = new_type1.GetId();
+ result_[1] = new_type2.GetId();
+}
+
+inline void RegisterLine::CopyRegister1(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc,
+ TypeCategory cat) {
+ DCHECK(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
+ const RegType& type = GetRegisterType(verifier, vsrc);
+ if (!SetRegisterType(verifier, vdst, type)) {
+ return;
+ }
+ if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
+ (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
+ << " cat=" << static_cast<int>(cat);
+ } else if (cat == kTypeCategoryRef) {
+ CopyRegToLockDepth(vdst, vsrc);
+ }
+}
+
+inline void RegisterLine::CopyRegister2(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc) {
+ const RegType& type_l = GetRegisterType(verifier, vsrc);
+ const RegType& type_h = GetRegisterType(verifier, vsrc + 1);
+
+ if (!type_l.CheckWidePair(type_h)) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
+ << " type=" << type_l << "/" << type_h;
+ } else {
+ SetRegisterTypeWide(verifier, vdst, type_l, type_h);
+ }
+}
+
+inline bool RegisterLine::VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
+ const RegType& check_type) {
+ // Verify the src register type against the check type refining the type of the register
+ const RegType& src_type = GetRegisterType(verifier, vsrc);
+ if (UNLIKELY(!check_type.IsAssignableFrom(src_type))) {
+ enum VerifyError fail_type;
+ if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
+ // Hard fail if one of the types is primitive, since they are concretely known.
+ fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
+ } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) {
+ fail_type = VERIFY_ERROR_NO_CLASS;
+ } else {
+ fail_type = VERIFY_ERROR_BAD_CLASS_SOFT;
+ }
+ verifier->Fail(fail_type) << "register v" << vsrc << " has type "
+ << src_type << " but expected " << check_type;
+ return false;
+ }
+ if (check_type.IsLowHalf()) {
+ const RegType& src_type_h = GetRegisterType(verifier, vsrc + 1);
+ if (UNLIKELY(!src_type.CheckWidePair(src_type_h))) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
+ << src_type << "/" << src_type_h;
+ return false;
+ }
+ }
+ // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
+ // precise than the subtype in vsrc so leave it for reference types. For primitive types
+ // if they are a defined type then they are as precise as we can get, however, for constant
+ // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
+ return true;
+}
+
+inline bool RegisterLine::VerifyMonitorStackEmpty(MethodVerifier* verifier) const {
+ if (MonitorStackDepth() != 0) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected empty monitor stack";
+ return false;
+ } else {
+ return true;
+ }
}
} // namespace verifier
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 4d67cfb..3139204 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -20,15 +20,16 @@
#include "dex_instruction-inl.h"
#include "method_verifier.h"
#include "register_line-inl.h"
+#include "reg_type-inl.h"
namespace art {
namespace verifier {
-bool RegisterLine::CheckConstructorReturn() const {
+bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
for (size_t i = 0; i < num_regs_; i++) {
- if (GetRegisterType(i).IsUninitializedThisReference() ||
- GetRegisterType(i).IsUnresolvedAndUninitializedThisReference()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
+ if (GetRegisterType(verifier, i).IsUninitializedThisReference() ||
+ GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) {
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
<< "Constructor returning without calling superclass constructor";
return false;
}
@@ -36,122 +37,38 @@
return true;
}
-bool RegisterLine::SetRegisterType(uint32_t vdst, RegType& new_type) {
- DCHECK_LT(vdst, num_regs_);
- if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
- << new_type << "'";
- return false;
- } else if (new_type.IsConflict()) { // should only be set during a merge
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
- return false;
- } else {
- line_[vdst] = new_type.GetId();
- }
- // Clear the monitor entry bits for this register.
- ClearAllRegToLockDepths(vdst);
- return true;
-}
-
-bool RegisterLine::SetRegisterTypeWide(uint32_t vdst, RegType& new_type1,
- RegType& new_type2) {
- DCHECK_LT(vdst + 1, num_regs_);
- if (!new_type1.CheckWidePair(new_type2)) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
- << new_type1 << "' '" << new_type2 << "'";
- return false;
- } else {
- line_[vdst] = new_type1.GetId();
- line_[vdst + 1] = new_type2.GetId();
- }
- // Clear the monitor entry bits for this register.
- ClearAllRegToLockDepths(vdst);
- ClearAllRegToLockDepths(vdst + 1);
- return true;
-}
-
-void RegisterLine::SetResultTypeToUnknown() {
- result_[0] = verifier_->GetRegTypeCache()->Undefined().GetId();
- result_[1] = result_[0];
-}
-
-void RegisterLine::SetResultRegisterType(RegType& new_type) {
- DCHECK(!new_type.IsLowHalf());
- DCHECK(!new_type.IsHighHalf());
- result_[0] = new_type.GetId();
- result_[1] = verifier_->GetRegTypeCache()->Undefined().GetId();
-}
-
-void RegisterLine::SetResultRegisterTypeWide(RegType& new_type1,
- RegType& new_type2) {
- DCHECK(new_type1.CheckWidePair(new_type2));
- result_[0] = new_type1.GetId();
- result_[1] = new_type2.GetId();
-}
-
-RegType& RegisterLine::GetInvocationThis(const Instruction* inst, bool is_range) {
+const RegType& RegisterLine::GetInvocationThis(MethodVerifier* verifier, const Instruction* inst,
+ bool is_range) {
const size_t args_count = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
if (args_count < 1) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
- return verifier_->GetRegTypeCache()->Conflict();
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke lacks 'this'";
+ return verifier->GetRegTypeCache()->Conflict();
}
/* Get the element type of the array held in vsrc */
const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- RegType& this_type = GetRegisterType(this_reg);
+ const RegType& this_type = GetRegisterType(verifier, this_reg);
if (!this_type.IsReferenceTypes()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "tried to get class from non-reference register v"
<< this_reg << " (type=" << this_type << ")";
- return verifier_->GetRegTypeCache()->Conflict();
+ return verifier->GetRegTypeCache()->Conflict();
}
return this_type;
}
-bool RegisterLine::VerifyRegisterType(uint32_t vsrc,
- RegType& check_type) {
- // Verify the src register type against the check type refining the type of the register
- RegType& src_type = GetRegisterType(vsrc);
- if (!(check_type.IsAssignableFrom(src_type))) {
- enum VerifyError fail_type;
- if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
- // Hard fail if one of the types is primitive, since they are concretely known.
- fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
- } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) {
- fail_type = VERIFY_ERROR_NO_CLASS;
- } else {
- fail_type = VERIFY_ERROR_BAD_CLASS_SOFT;
- }
- verifier_->Fail(fail_type) << "register v" << vsrc << " has type "
- << src_type << " but expected " << check_type;
- return false;
- }
- if (check_type.IsLowHalf()) {
- RegType& src_type_h = GetRegisterType(vsrc + 1);
- if (!src_type.CheckWidePair(src_type_h)) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
- << src_type << "/" << src_type_h;
- return false;
- }
- }
- // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
- // precise than the subtype in vsrc so leave it for reference types. For primitive types
- // if they are a defined type then they are as precise as we can get, however, for constant
- // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
- return true;
-}
-
-bool RegisterLine::VerifyRegisterTypeWide(uint32_t vsrc, RegType& check_type1,
- RegType& check_type2) {
+bool RegisterLine::VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc,
+ const RegType& check_type1,
+ const RegType& check_type2) {
DCHECK(check_type1.CheckWidePair(check_type2));
// Verify the src register type against the check type refining the type of the register
- RegType& src_type = GetRegisterType(vsrc);
+ const RegType& src_type = GetRegisterType(verifier, vsrc);
if (!check_type1.IsAssignableFrom(src_type)) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << vsrc << " has type " << src_type
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << vsrc << " has type " << src_type
<< " but expected " << check_type1;
return false;
}
- RegType& src_type_h = GetRegisterType(vsrc + 1);
+ const RegType& src_type_h = GetRegisterType(verifier, vsrc + 1);
if (!src_type.CheckWidePair(src_type_h)) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
<< src_type << "/" << src_type_h;
return false;
}
@@ -162,12 +79,12 @@
return true;
}
-void RegisterLine::MarkRefsAsInitialized(RegType& uninit_type) {
+void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) {
DCHECK(uninit_type.IsUninitializedTypes());
- RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
+ const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type);
size_t changed = 0;
for (uint32_t i = 0; i < num_regs_; i++) {
- if (GetRegisterType(i).Equals(uninit_type)) {
+ if (GetRegisterType(verifier, i).Equals(uninit_type)) {
line_[i] = init_type.GetId();
changed++;
}
@@ -175,15 +92,15 @@
DCHECK_GT(changed, 0u);
}
-void RegisterLine::MarkAllRegistersAsConflicts() {
- uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+void RegisterLine::MarkAllRegistersAsConflicts(MethodVerifier* verifier) {
+ uint16_t conflict_type_id = verifier->GetRegTypeCache()->Conflict().GetId();
for (uint32_t i = 0; i < num_regs_; i++) {
line_[i] = conflict_type_id;
}
}
-void RegisterLine::MarkAllRegistersAsConflictsExcept(uint32_t vsrc) {
- uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+void RegisterLine::MarkAllRegistersAsConflictsExcept(MethodVerifier* verifier, uint32_t vsrc) {
+ uint16_t conflict_type_id = verifier->GetRegTypeCache()->Conflict().GetId();
for (uint32_t i = 0; i < num_regs_; i++) {
if (i != vsrc) {
line_[i] = conflict_type_id;
@@ -191,8 +108,8 @@
}
}
-void RegisterLine::MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc) {
- uint16_t conflict_type_id = verifier_->GetRegTypeCache()->Conflict().GetId();
+void RegisterLine::MarkAllRegistersAsConflictsExceptWide(MethodVerifier* verifier, uint32_t vsrc) {
+ uint16_t conflict_type_id = verifier->GetRegTypeCache()->Conflict().GetId();
for (uint32_t i = 0; i < num_regs_; i++) {
if ((i != vsrc) && (i != (vsrc + 1))) {
line_[i] = conflict_type_id;
@@ -200,11 +117,11 @@
}
}
-std::string RegisterLine::Dump() {
+std::string RegisterLine::Dump(MethodVerifier* verifier) const {
std::string result;
for (size_t i = 0; i < num_regs_; i++) {
result += StringPrintf("%zd:[", i);
- result += GetRegisterType(i).Dump();
+ result += GetRegisterType(verifier, i).Dump();
result += "],";
}
for (const auto& monitor : monitors_) {
@@ -213,52 +130,25 @@
return result;
}
-void RegisterLine::MarkUninitRefsAsInvalid(RegType& uninit_type) {
+void RegisterLine::MarkUninitRefsAsInvalid(MethodVerifier* verifier, const RegType& uninit_type) {
for (size_t i = 0; i < num_regs_; i++) {
- if (GetRegisterType(i).Equals(uninit_type)) {
- line_[i] = verifier_->GetRegTypeCache()->Conflict().GetId();
+ if (GetRegisterType(verifier, i).Equals(uninit_type)) {
+ line_[i] = verifier->GetRegTypeCache()->Conflict().GetId();
ClearAllRegToLockDepths(i);
}
}
}
-void RegisterLine::CopyRegister1(uint32_t vdst, uint32_t vsrc, TypeCategory cat) {
- DCHECK(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
- RegType& type = GetRegisterType(vsrc);
- if (!SetRegisterType(vdst, type)) {
- return;
- }
- if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
- (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
- << " cat=" << static_cast<int>(cat);
- } else if (cat == kTypeCategoryRef) {
- CopyRegToLockDepth(vdst, vsrc);
- }
-}
-
-void RegisterLine::CopyRegister2(uint32_t vdst, uint32_t vsrc) {
- RegType& type_l = GetRegisterType(vsrc);
- RegType& type_h = GetRegisterType(vsrc + 1);
-
- if (!type_l.CheckWidePair(type_h)) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
- << " type=" << type_l << "/" << type_h;
- } else {
- SetRegisterTypeWide(vdst, type_l, type_h);
- }
-}
-
-void RegisterLine::CopyResultRegister1(uint32_t vdst, bool is_reference) {
- RegType& type = verifier_->GetRegTypeCache()->GetFromId(result_[0]);
+void RegisterLine::CopyResultRegister1(MethodVerifier* verifier, uint32_t vdst, bool is_reference) {
+ const RegType& type = verifier->GetRegTypeCache()->GetFromId(result_[0]);
if ((!is_reference && !type.IsCategory1Types()) ||
(is_reference && !type.IsReferenceTypes())) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
<< "copyRes1 v" << vdst << "<- result0" << " type=" << type;
} else {
- DCHECK(verifier_->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined());
- SetRegisterType(vdst, type);
- result_[0] = verifier_->GetRegTypeCache()->Undefined().GetId();
+ DCHECK(verifier->GetRegTypeCache()->GetFromId(result_[1]).IsUndefined());
+ SetRegisterType(verifier, vdst, type);
+ result_[0] = verifier->GetRegTypeCache()->Undefined().GetId();
}
}
@@ -266,178 +156,179 @@
* Implement "move-result-wide". Copy the category-2 value from the result
* register to another register, and reset the result register.
*/
-void RegisterLine::CopyResultRegister2(uint32_t vdst) {
- RegType& type_l = verifier_->GetRegTypeCache()->GetFromId(result_[0]);
- RegType& type_h = verifier_->GetRegTypeCache()->GetFromId(result_[1]);
+void RegisterLine::CopyResultRegister2(MethodVerifier* verifier, uint32_t vdst) {
+ const RegType& type_l = verifier->GetRegTypeCache()->GetFromId(result_[0]);
+ const RegType& type_h = verifier->GetRegTypeCache()->GetFromId(result_[1]);
if (!type_l.IsCategory2Types()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD)
<< "copyRes2 v" << vdst << "<- result0" << " type=" << type_l;
} else {
DCHECK(type_l.CheckWidePair(type_h)); // Set should never allow this case
- SetRegisterTypeWide(vdst, type_l, type_h); // also sets the high
- result_[0] = verifier_->GetRegTypeCache()->Undefined().GetId();
- result_[1] = verifier_->GetRegTypeCache()->Undefined().GetId();
+ SetRegisterTypeWide(verifier, vdst, type_l, type_h); // also sets the high
+ result_[0] = verifier->GetRegTypeCache()->Undefined().GetId();
+ result_[1] = verifier->GetRegTypeCache()->Undefined().GetId();
}
}
-void RegisterLine::CheckUnaryOp(const Instruction* inst,
- RegType& dst_type,
- RegType& src_type) {
- if (VerifyRegisterType(inst->VRegB_12x(), src_type)) {
- SetRegisterType(inst->VRegA_12x(), dst_type);
+void RegisterLine::CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type, const RegType& src_type) {
+ if (VerifyRegisterType(verifier, inst->VRegB_12x(), src_type)) {
+ SetRegisterType(verifier, inst->VRegA_12x(), dst_type);
}
}
-void RegisterLine::CheckUnaryOpWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type1, RegType& src_type2) {
- if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) {
- SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2);
+void RegisterLine::CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1, const RegType& src_type2) {
+ if (VerifyRegisterTypeWide(verifier, inst->VRegB_12x(), src_type1, src_type2)) {
+ SetRegisterTypeWide(verifier, inst->VRegA_12x(), dst_type1, dst_type2);
}
}
-void RegisterLine::CheckUnaryOpToWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type) {
- if (VerifyRegisterType(inst->VRegB_12x(), src_type)) {
- SetRegisterTypeWide(inst->VRegA_12x(), dst_type1, dst_type2);
+void RegisterLine::CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type) {
+ if (VerifyRegisterType(verifier, inst->VRegB_12x(), src_type)) {
+ SetRegisterTypeWide(verifier, inst->VRegA_12x(), dst_type1, dst_type2);
}
}
-void RegisterLine::CheckUnaryOpFromWide(const Instruction* inst,
- RegType& dst_type,
- RegType& src_type1, RegType& src_type2) {
- if (VerifyRegisterTypeWide(inst->VRegB_12x(), src_type1, src_type2)) {
- SetRegisterType(inst->VRegA_12x(), dst_type);
+void RegisterLine::CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type,
+ const RegType& src_type1, const RegType& src_type2) {
+ if (VerifyRegisterTypeWide(verifier, inst->VRegB_12x(), src_type1, src_type2)) {
+ SetRegisterType(verifier, inst->VRegA_12x(), dst_type);
}
}
-void RegisterLine::CheckBinaryOp(const Instruction* inst,
- RegType& dst_type,
- RegType& src_type1, RegType& src_type2,
+void RegisterLine::CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type,
+ const RegType& src_type1, const RegType& src_type2,
bool check_boolean_op) {
const uint32_t vregB = inst->VRegB_23x();
const uint32_t vregC = inst->VRegC_23x();
- if (VerifyRegisterType(vregB, src_type1) &&
- VerifyRegisterType(vregC, src_type2)) {
+ if (VerifyRegisterType(verifier, vregB, src_type1) &&
+ VerifyRegisterType(verifier, vregC, src_type2)) {
if (check_boolean_op) {
DCHECK(dst_type.IsInteger());
- if (GetRegisterType(vregB).IsBooleanTypes() &&
- GetRegisterType(vregC).IsBooleanTypes()) {
- SetRegisterType(inst->VRegA_23x(), verifier_->GetRegTypeCache()->Boolean());
+ if (GetRegisterType(verifier, vregB).IsBooleanTypes() &&
+ GetRegisterType(verifier, vregC).IsBooleanTypes()) {
+ SetRegisterType(verifier, inst->VRegA_23x(), verifier->GetRegTypeCache()->Boolean());
return;
}
}
- SetRegisterType(inst->VRegA_23x(), dst_type);
+ SetRegisterType(verifier, inst->VRegA_23x(), dst_type);
}
}
-void RegisterLine::CheckBinaryOpWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type1_1, RegType& src_type1_2,
- RegType& src_type2_1, RegType& src_type2_2) {
- if (VerifyRegisterTypeWide(inst->VRegB_23x(), src_type1_1, src_type1_2) &&
- VerifyRegisterTypeWide(inst->VRegC_23x(), src_type2_1, src_type2_2)) {
- SetRegisterTypeWide(inst->VRegA_23x(), dst_type1, dst_type2);
+void RegisterLine::CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2) {
+ if (VerifyRegisterTypeWide(verifier, inst->VRegB_23x(), src_type1_1, src_type1_2) &&
+ VerifyRegisterTypeWide(verifier, inst->VRegC_23x(), src_type2_1, src_type2_2)) {
+ SetRegisterTypeWide(verifier, inst->VRegA_23x(), dst_type1, dst_type2);
}
}
-void RegisterLine::CheckBinaryOpWideShift(const Instruction* inst,
- RegType& long_lo_type, RegType& long_hi_type,
- RegType& int_type) {
- if (VerifyRegisterTypeWide(inst->VRegB_23x(), long_lo_type, long_hi_type) &&
- VerifyRegisterType(inst->VRegC_23x(), int_type)) {
- SetRegisterTypeWide(inst->VRegA_23x(), long_lo_type, long_hi_type);
+void RegisterLine::CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type) {
+ if (VerifyRegisterTypeWide(verifier, inst->VRegB_23x(), long_lo_type, long_hi_type) &&
+ VerifyRegisterType(verifier, inst->VRegC_23x(), int_type)) {
+ SetRegisterTypeWide(verifier, inst->VRegA_23x(), long_lo_type, long_hi_type);
}
}
-void RegisterLine::CheckBinaryOp2addr(const Instruction* inst,
- RegType& dst_type, RegType& src_type1,
- RegType& src_type2, bool check_boolean_op) {
+void RegisterLine::CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type, const RegType& src_type1,
+ const RegType& src_type2, bool check_boolean_op) {
const uint32_t vregA = inst->VRegA_12x();
const uint32_t vregB = inst->VRegB_12x();
- if (VerifyRegisterType(vregA, src_type1) &&
- VerifyRegisterType(vregB, src_type2)) {
+ if (VerifyRegisterType(verifier, vregA, src_type1) &&
+ VerifyRegisterType(verifier, vregB, src_type2)) {
if (check_boolean_op) {
DCHECK(dst_type.IsInteger());
- if (GetRegisterType(vregA).IsBooleanTypes() &&
- GetRegisterType(vregB).IsBooleanTypes()) {
- SetRegisterType(vregA, verifier_->GetRegTypeCache()->Boolean());
+ if (GetRegisterType(verifier, vregA).IsBooleanTypes() &&
+ GetRegisterType(verifier, vregB).IsBooleanTypes()) {
+ SetRegisterType(verifier, vregA, verifier->GetRegTypeCache()->Boolean());
return;
}
}
- SetRegisterType(vregA, dst_type);
+ SetRegisterType(verifier, vregA, dst_type);
}
}
-void RegisterLine::CheckBinaryOp2addrWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type1_1, RegType& src_type1_2,
- RegType& src_type2_1, RegType& src_type2_2) {
+void RegisterLine::CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2) {
const uint32_t vregA = inst->VRegA_12x();
const uint32_t vregB = inst->VRegB_12x();
- if (VerifyRegisterTypeWide(vregA, src_type1_1, src_type1_2) &&
- VerifyRegisterTypeWide(vregB, src_type2_1, src_type2_2)) {
- SetRegisterTypeWide(vregA, dst_type1, dst_type2);
+ if (VerifyRegisterTypeWide(verifier, vregA, src_type1_1, src_type1_2) &&
+ VerifyRegisterTypeWide(verifier, vregB, src_type2_1, src_type2_2)) {
+ SetRegisterTypeWide(verifier, vregA, dst_type1, dst_type2);
}
}
-void RegisterLine::CheckBinaryOp2addrWideShift(const Instruction* inst,
- RegType& long_lo_type, RegType& long_hi_type,
- RegType& int_type) {
+void RegisterLine::CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type) {
const uint32_t vregA = inst->VRegA_12x();
const uint32_t vregB = inst->VRegB_12x();
- if (VerifyRegisterTypeWide(vregA, long_lo_type, long_hi_type) &&
- VerifyRegisterType(vregB, int_type)) {
- SetRegisterTypeWide(vregA, long_lo_type, long_hi_type);
+ if (VerifyRegisterTypeWide(verifier, vregA, long_lo_type, long_hi_type) &&
+ VerifyRegisterType(verifier, vregB, int_type)) {
+ SetRegisterTypeWide(verifier, vregA, long_lo_type, long_hi_type);
}
}
-void RegisterLine::CheckLiteralOp(const Instruction* inst,
- RegType& dst_type, RegType& src_type,
+void RegisterLine::CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type, const RegType& src_type,
bool check_boolean_op, bool is_lit16) {
const uint32_t vregA = is_lit16 ? inst->VRegA_22s() : inst->VRegA_22b();
const uint32_t vregB = is_lit16 ? inst->VRegB_22s() : inst->VRegB_22b();
- if (VerifyRegisterType(vregB, src_type)) {
+ if (VerifyRegisterType(verifier, vregB, src_type)) {
if (check_boolean_op) {
DCHECK(dst_type.IsInteger());
/* check vB with the call, then check the constant manually */
const uint32_t val = is_lit16 ? inst->VRegC_22s() : inst->VRegC_22b();
- if (GetRegisterType(vregB).IsBooleanTypes() && (val == 0 || val == 1)) {
- SetRegisterType(vregA, verifier_->GetRegTypeCache()->Boolean());
+ if (GetRegisterType(verifier, vregB).IsBooleanTypes() && (val == 0 || val == 1)) {
+ SetRegisterType(verifier, vregA, verifier->GetRegTypeCache()->Boolean());
return;
}
}
- SetRegisterType(vregA, dst_type);
+ SetRegisterType(verifier, vregA, dst_type);
}
}
-void RegisterLine::PushMonitor(uint32_t reg_idx, int32_t insn_idx) {
- RegType& reg_type = GetRegisterType(reg_idx);
+void RegisterLine::PushMonitor(MethodVerifier* verifier, uint32_t reg_idx, int32_t insn_idx) {
+ const RegType& reg_type = GetRegisterType(verifier, reg_idx);
if (!reg_type.IsReferenceTypes()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object (" << reg_type << ")";
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter on non-object ("
+ << reg_type << ")";
} else if (monitors_.size() >= 32) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter stack overflow: " << monitors_.size();
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-enter stack overflow: "
+ << monitors_.size();
} else {
SetRegToLockDepth(reg_idx, monitors_.size());
monitors_.push_back(insn_idx);
}
}
-void RegisterLine::PopMonitor(uint32_t reg_idx) {
- RegType& reg_type = GetRegisterType(reg_idx);
+void RegisterLine::PopMonitor(MethodVerifier* verifier, uint32_t reg_idx) {
+ const RegType& reg_type = GetRegisterType(verifier, reg_idx);
if (!reg_type.IsReferenceTypes()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit on non-object (" << reg_type << ")";
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit on non-object (" << reg_type << ")";
} else if (monitors_.empty()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit stack underflow";
+ verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "monitor-exit stack underflow";
} else {
monitors_.pop_back();
if (!IsSetLockDepth(reg_idx, monitors_.size())) {
// Bug 3215458: Locks and unlocks are on objects, if that object is a literal then before
// format "036" the constant collector may create unlocks on the same object but referenced
// via different registers.
- ((verifier_->DexFileVersion() >= 36) ? verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
- : verifier_->LogVerifyInfo())
+ ((verifier->DexFileVersion() >= 36) ? verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT)
+ : verifier->LogVerifyInfo())
<< "monitor-exit not unlocking the top of the monitor stack";
} else {
// Record the register was unlocked
@@ -446,41 +337,34 @@
}
}
-bool RegisterLine::VerifyMonitorStackEmpty() const {
- if (MonitorStackDepth() != 0) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected empty monitor stack";
- return false;
- } else {
- return true;
- }
-}
-
-bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) {
+bool RegisterLine::MergeRegisters(MethodVerifier* verifier, const RegisterLine* incoming_line) {
bool changed = false;
DCHECK(incoming_line != nullptr);
for (size_t idx = 0; idx < num_regs_; idx++) {
if (line_[idx] != incoming_line->line_[idx]) {
- RegType& incoming_reg_type = incoming_line->GetRegisterType(idx);
- RegType& cur_type = GetRegisterType(idx);
- RegType& new_type = cur_type.Merge(incoming_reg_type, verifier_->GetRegTypeCache());
+ const RegType& incoming_reg_type = incoming_line->GetRegisterType(verifier, idx);
+ const RegType& cur_type = GetRegisterType(verifier, idx);
+ const RegType& new_type = cur_type.Merge(incoming_reg_type, verifier->GetRegTypeCache());
changed = changed || !cur_type.Equals(new_type);
line_[idx] = new_type.GetId();
}
}
- if (monitors_.size() != incoming_line->monitors_.size()) {
- LOG(WARNING) << "mismatched stack depths (depth=" << MonitorStackDepth()
- << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")";
- } else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) {
- for (uint32_t idx = 0; idx < num_regs_; idx++) {
- size_t depths = reg_to_lock_depths_.count(idx);
- size_t incoming_depths = incoming_line->reg_to_lock_depths_.count(idx);
- if (depths != incoming_depths) {
- if (depths == 0 || incoming_depths == 0) {
- reg_to_lock_depths_.erase(idx);
- } else {
- LOG(WARNING) << "mismatched stack depths for register v" << idx
- << ": " << depths << " != " << incoming_depths;
- break;
+ if (monitors_.size() > 0 || incoming_line->monitors_.size() > 0) {
+ if (monitors_.size() != incoming_line->monitors_.size()) {
+ LOG(WARNING) << "mismatched stack depths (depth=" << MonitorStackDepth()
+ << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")";
+ } else if (reg_to_lock_depths_ != incoming_line->reg_to_lock_depths_) {
+ for (uint32_t idx = 0; idx < num_regs_; idx++) {
+ size_t depths = reg_to_lock_depths_.count(idx);
+ size_t incoming_depths = incoming_line->reg_to_lock_depths_.count(idx);
+ if (depths != incoming_depths) {
+ if (depths == 0 || incoming_depths == 0) {
+ reg_to_lock_depths_.erase(idx);
+ } else {
+ LOG(WARNING) << "mismatched stack depths for register v" << idx
+ << ": " << depths << " != " << incoming_depths;
+ break;
+ }
}
}
}
@@ -488,12 +372,13 @@
return changed;
}
-void RegisterLine::WriteReferenceBitMap(std::vector<uint8_t>& data, size_t max_bytes) {
+void RegisterLine::WriteReferenceBitMap(MethodVerifier* verifier,
+ std::vector<uint8_t>* data, size_t max_bytes) {
for (size_t i = 0; i < num_regs_; i += 8) {
uint8_t val = 0;
for (size_t j = 0; j < 8 && (i + j) < num_regs_; j++) {
// Note: we write 1 for a Reference but not for Null
- if (GetRegisterType(i + j).IsNonZeroReferenceTypes()) {
+ if (GetRegisterType(verifier, i + j).IsNonZeroReferenceTypes()) {
val |= 1 << j;
}
}
@@ -502,16 +387,9 @@
continue;
}
DCHECK_LT(i / 8, max_bytes) << "val=" << static_cast<uint32_t>(val);
- data.push_back(val);
+ data->push_back(val);
}
}
-std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- RegisterLine& rhs_non_const = const_cast<RegisterLine&>(rhs);
- os << rhs_non_const.Dump();
- return os;
-}
-
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 06b7cca..c7fd369 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -57,50 +57,54 @@
}
// Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst".
- void CopyRegister1(uint32_t vdst, uint32_t vsrc, TypeCategory cat)
+ void CopyRegister1(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc, TypeCategory cat)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Implement category-2 "move" instructions. Copy a 64-bit value from "vsrc" to "vdst". This
// copies both halves of the register.
- void CopyRegister2(uint32_t vdst, uint32_t vsrc)
+ void CopyRegister2(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Implement "move-result". Copy the category-1 value from the result register to another
// register, and reset the result register.
- void CopyResultRegister1(uint32_t vdst, bool is_reference)
+ void CopyResultRegister1(MethodVerifier* verifier, uint32_t vdst, bool is_reference)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Implement "move-result-wide". Copy the category-2 value from the result register to another
// register, and reset the result register.
- void CopyResultRegister2(uint32_t vdst)
+ void CopyResultRegister2(MethodVerifier* verifier, uint32_t vdst)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Set the invisible result register to unknown
- void SetResultTypeToUnknown() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetResultTypeToUnknown(MethodVerifier* verifier) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Set the type of register N, verifying that the register is valid. If "newType" is the "Lo"
// part of a 64-bit value, register N+1 will be set to "newType+1".
// The register index was validated during the static pass, so we don't need to check it here.
- bool SetRegisterType(uint32_t vdst, RegType& new_type)
+ ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, uint32_t vdst,
+ const RegType& new_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool SetRegisterTypeWide(uint32_t vdst, RegType& new_type1, RegType& new_type2)
+ bool SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst, const RegType& new_type1,
+ const RegType& new_type2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/* Set the type of the "result" register. */
- void SetResultRegisterType(RegType& new_type)
+ void SetResultRegisterType(MethodVerifier* verifier, const RegType& new_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetResultRegisterTypeWide(RegType& new_type1, RegType& new_type2)
+ void SetResultRegisterTypeWide(const RegType& new_type1, const RegType& new_type2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get the type of register vsrc.
- RegType& GetRegisterType(uint32_t vsrc) const;
+ const RegType& GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const;
- bool VerifyRegisterType(uint32_t vsrc, RegType& check_type)
+ ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
+ const RegType& check_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool VerifyRegisterTypeWide(uint32_t vsrc, RegType& check_type1, RegType& check_type2)
+ bool VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc, const RegType& check_type1,
+ const RegType& check_type2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void CopyFromLine(const RegisterLine* src) {
@@ -110,7 +114,7 @@
reg_to_lock_depths_ = src->reg_to_lock_depths_;
}
- std::string Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ std::string Dump(MethodVerifier* verifier) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void FillWithGarbage() {
memset(&line_, 0xf1, num_regs_ * sizeof(uint16_t));
@@ -126,7 +130,7 @@
* to prevent them from being used (otherwise, MarkRefsAsInitialized would mark the old ones and
* the new ones at the same time).
*/
- void MarkUninitRefsAsInvalid(RegType& uninit_type)
+ void MarkUninitRefsAsInvalid(MethodVerifier* verifier, const RegType& uninit_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -134,15 +138,15 @@
* reference type. This is called when an appropriate constructor is invoked -- all copies of
* the reference must be marked as initialized.
*/
- void MarkRefsAsInitialized(RegType& uninit_type)
+ void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Update all registers to be Conflict except vsrc.
*/
- void MarkAllRegistersAsConflicts();
- void MarkAllRegistersAsConflictsExcept(uint32_t vsrc);
- void MarkAllRegistersAsConflictsExceptWide(uint32_t vsrc);
+ void MarkAllRegistersAsConflicts(MethodVerifier* verifier);
+ void MarkAllRegistersAsConflictsExcept(MethodVerifier* verifier, uint32_t vsrc);
+ void MarkAllRegistersAsConflictsExceptWide(MethodVerifier* verifier, uint32_t vsrc);
/*
* Check constraints on constructor return. Specifically, make sure that the "this" argument got
@@ -151,7 +155,7 @@
* of the list in slot 0. If we see a register with an uninitialized slot 0 reference, we know it
* somehow didn't get initialized.
*/
- bool CheckConstructorReturn() const;
+ bool CheckConstructorReturn(MethodVerifier* verifier) const;
// Compare two register lines. Returns 0 if they match.
// Using this for a sort is unwise, since the value can change based on machine endianness.
@@ -173,30 +177,31 @@
* The argument count is in vA, and the first argument is in vC, for both "simple" and "range"
* versions. We just need to make sure vA is >= 1 and then return vC.
*/
- RegType& GetInvocationThis(const Instruction* inst, bool is_range)
+ const RegType& GetInvocationThis(MethodVerifier* verifier, const Instruction* inst,
+ bool is_range)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Verify types for a simple two-register instruction (e.g. "neg-int").
* "dst_type" is stored into vA, and "src_type" is verified against vB.
*/
- void CheckUnaryOp(const Instruction* inst, RegType& dst_type,
- RegType& src_type)
+ void CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst, const RegType& dst_type,
+ const RegType& src_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckUnaryOpWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type1, RegType& src_type2)
+ void CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1, const RegType& src_type2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckUnaryOpToWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type)
+ void CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckUnaryOpFromWide(const Instruction* inst,
- RegType& dst_type,
- RegType& src_type1, RegType& src_type2)
+ void CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type,
+ const RegType& src_type1, const RegType& src_type2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -204,41 +209,41 @@
* "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
* against vB/vC.
*/
- void CheckBinaryOp(const Instruction* inst,
- RegType& dst_type, RegType& src_type1, RegType& src_type2,
+ void CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type, const RegType& src_type1, const RegType& src_type2,
bool check_boolean_op)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckBinaryOpWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type1_1, RegType& src_type1_2,
- RegType& src_type2_1, RegType& src_type2_2)
+ void CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckBinaryOpWideShift(const Instruction* inst,
- RegType& long_lo_type, RegType& long_hi_type,
- RegType& int_type)
+ void CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Verify types for a binary "2addr" operation. "src_type1"/"src_type2"
* are verified against vA/vB, then "dst_type" is stored into vA.
*/
- void CheckBinaryOp2addr(const Instruction* inst,
- RegType& dst_type,
- RegType& src_type1, RegType& src_type2,
+ void CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type,
+ const RegType& src_type1, const RegType& src_type2,
bool check_boolean_op)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckBinaryOp2addrWide(const Instruction* inst,
- RegType& dst_type1, RegType& dst_type2,
- RegType& src_type1_1, RegType& src_type1_2,
- RegType& src_type2_1, RegType& src_type2_2)
+ void CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void CheckBinaryOp2addrWideShift(const Instruction* inst,
- RegType& long_lo_type, RegType& long_hi_type,
- RegType& int_type)
+ void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -247,16 +252,18 @@
*
* If "check_boolean_op" is set, we use the constant value in vC.
*/
- void CheckLiteralOp(const Instruction* inst,
- RegType& dst_type, RegType& src_type,
+ void CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst,
+ const RegType& dst_type, const RegType& src_type,
bool check_boolean_op, bool is_lit16)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Verify/push monitor onto the monitor stack, locking the value in reg_idx at location insn_idx.
- void PushMonitor(uint32_t reg_idx, int32_t insn_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PushMonitor(MethodVerifier* verifier, uint32_t reg_idx, int32_t insn_idx)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Verify/pop monitor from monitor stack ensuring that we believe the monitor is locked
- void PopMonitor(uint32_t reg_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PopMonitor(MethodVerifier* verifier, uint32_t reg_idx)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Stack of currently held monitors and where they were locked
size_t MonitorStackDepth() const {
@@ -265,23 +272,23 @@
// We expect no monitors to be held at certain points, such a method returns. Verify the stack
// is empty, failing and returning false if not.
- bool VerifyMonitorStackEmpty() const;
+ bool VerifyMonitorStackEmpty(MethodVerifier* verifier) const;
- bool MergeRegisters(const RegisterLine* incoming_line)
+ bool MergeRegisters(MethodVerifier* verifier, const RegisterLine* incoming_line)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- size_t GetMaxNonZeroReferenceReg(size_t max_ref_reg) {
+ size_t GetMaxNonZeroReferenceReg(MethodVerifier* verifier, size_t max_ref_reg) {
size_t i = static_cast<int>(max_ref_reg) < 0 ? 0 : max_ref_reg;
for (; i < num_regs_; i++) {
- if (GetRegisterType(i).IsNonZeroReferenceTypes()) {
+ if (GetRegisterType(verifier, i).IsNonZeroReferenceTypes()) {
max_ref_reg = i;
}
}
return max_ref_reg;
}
- // Write a bit at each register location that holds a reference
- void WriteReferenceBitMap(std::vector<uint8_t>& data, size_t max_bytes);
+ // Write a bit at each register location that holds a reference.
+ void WriteReferenceBitMap(MethodVerifier* verifier, std::vector<uint8_t>* data, size_t max_bytes);
size_t GetMonitorEnterCount() {
return monitors_.size();
@@ -337,19 +344,17 @@
}
RegisterLine(size_t num_regs, MethodVerifier* verifier)
- : verifier_(verifier), num_regs_(num_regs) {
+ : num_regs_(num_regs) {
memset(&line_, 0, num_regs_ * sizeof(uint16_t));
- SetResultTypeToUnknown();
+ SetResultTypeToUnknown(verifier);
}
// Storage for the result register's type, valid after an invocation
uint16_t result_[2];
- // Back link to the verifier
- MethodVerifier* verifier_;
-
// Length of reg_types_
const uint32_t num_regs_;
+
// A stack of monitor enter locations
std::vector<uint32_t, TrackingAllocator<uint32_t, kAllocatorTagVerifier>> monitors_;
// A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor
@@ -360,7 +365,6 @@
// An array of RegType Ids associated with each dex register.
uint16_t line_[0];
};
-std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs);
} // namespace verifier
} // namespace art
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 6b67dfa..4a3c3ec 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -21,6 +21,7 @@
#include "base/logging.h"
#include "mirror/class.h"
#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
#include "thread-inl.h"
namespace art {
@@ -28,7 +29,7 @@
jclass WellKnownClasses::com_android_dex_Dex;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
-jclass WellKnownClasses::dalvik_system_DexPathList$Element;
+jclass WellKnownClasses::dalvik_system_DexPathList__Element;
jclass WellKnownClasses::dalvik_system_PathClassLoader;
jclass WellKnownClasses::java_lang_BootClassLoader;
jclass WellKnownClasses::java_lang_ClassLoader;
@@ -47,7 +48,7 @@
jclass WellKnownClasses::java_lang_String;
jclass WellKnownClasses::java_lang_System;
jclass WellKnownClasses::java_lang_Thread;
-jclass WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler;
+jclass WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler;
jclass WellKnownClasses::java_lang_ThreadGroup;
jclass WellKnownClasses::java_lang_Throwable;
jclass WellKnownClasses::java_nio_DirectByteBuffer;
@@ -77,7 +78,7 @@
jmethodID WellKnownClasses::java_lang_System_runFinalization = NULL;
jmethodID WellKnownClasses::java_lang_Thread_init;
jmethodID WellKnownClasses::java_lang_Thread_run;
-jmethodID WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler_uncaughtException;
+jmethodID WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler_uncaughtException;
jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -86,7 +87,7 @@
jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList;
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
-jfieldID WellKnownClasses::dalvik_system_DexPathList$Element_dexFile;
+jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
jfieldID WellKnownClasses::java_lang_Thread_lock;
@@ -122,18 +123,32 @@
return reinterpret_cast<jclass>(env->NewGlobalRef(c.get()));
}
-static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) {
- jfieldID fid = is_static ? env->GetStaticFieldID(c, name, signature) : env->GetFieldID(c, name, signature);
+static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static,
+ const char* name, const char* signature) {
+ jfieldID fid = (is_static ?
+ env->GetStaticFieldID(c, name, signature) :
+ env->GetFieldID(c, name, signature));
if (fid == NULL) {
- LOG(FATAL) << "Couldn't find field \"" << name << "\" with signature \"" << signature << "\"";
+ ScopedObjectAccess soa(env);
+ std::ostringstream os;
+ WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "Couldn't find field \"" << name << "\" with signature \"" << signature << "\": "
+ << os.str();
}
return fid;
}
-jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static, const char* name, const char* signature) {
- jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) : env->GetMethodID(c, name, signature);
+jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
+ const char* name, const char* signature) {
+ jmethodID mid = (is_static ?
+ env->GetStaticMethodID(c, name, signature) :
+ env->GetMethodID(c, name, signature));
if (mid == NULL) {
- LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\"";
+ ScopedObjectAccess soa(env);
+ std::ostringstream os;
+ WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\": "
+ << os.str();
}
return mid;
}
@@ -148,7 +163,7 @@
com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
- dalvik_system_DexPathList$Element = CacheClass(env, "dalvik/system/DexPathList$Element");
+ dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
@@ -167,7 +182,8 @@
java_lang_String = CacheClass(env, "java/lang/String");
java_lang_System = CacheClass(env, "java/lang/System");
java_lang_Thread = CacheClass(env, "java/lang/Thread");
- java_lang_Thread$UncaughtExceptionHandler = CacheClass(env, "java/lang/Thread$UncaughtExceptionHandler");
+ java_lang_Thread__UncaughtExceptionHandler = CacheClass(env,
+ "java/lang/Thread$UncaughtExceptionHandler");
java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
@@ -192,7 +208,7 @@
java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/ArtMethod;[Ljava/lang/Object;)Ljava/lang/Object;");
java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
- java_lang_Thread$UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread$UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+ java_lang_Thread__UncaughtExceptionHandler_uncaughtException = CacheMethod(env, java_lang_Thread__UncaughtExceptionHandler, false, "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "removeThread", "(Ljava/lang/Thread;)V");
java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
@@ -201,7 +217,7 @@
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "J");
dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
- dalvik_system_DexPathList$Element_dexFile = CacheField(env, dalvik_system_DexPathList$Element, false, "dexFile", "Ldalvik/system/DexFile;");
+ dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 3780733..790d7f7 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -42,7 +42,7 @@
static jclass com_android_dex_Dex;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
- static jclass dalvik_system_DexPathList$Element;
+ static jclass dalvik_system_DexPathList__Element;
static jclass dalvik_system_PathClassLoader;
static jclass java_lang_BootClassLoader;
static jclass java_lang_ClassLoader;
@@ -62,7 +62,7 @@
static jclass java_lang_System;
static jclass java_lang_Thread;
static jclass java_lang_ThreadGroup;
- static jclass java_lang_Thread$UncaughtExceptionHandler;
+ static jclass java_lang_Thread__UncaughtExceptionHandler;
static jclass java_lang_Throwable;
static jclass java_util_Collections;
static jclass java_nio_DirectByteBuffer;
@@ -91,7 +91,7 @@
static jmethodID java_lang_System_runFinalization;
static jmethodID java_lang_Thread_init;
static jmethodID java_lang_Thread_run;
- static jmethodID java_lang_Thread$UncaughtExceptionHandler_uncaughtException;
+ static jmethodID java_lang_Thread__UncaughtExceptionHandler_uncaughtException;
static jmethodID java_lang_ThreadGroup_removeThread;
static jmethodID java_nio_DirectByteBuffer_init;
static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
@@ -99,7 +99,7 @@
static jfieldID dalvik_system_DexFile_cookie;
static jfieldID dalvik_system_DexPathList_dexElements;
- static jfieldID dalvik_system_DexPathList$Element_dexFile;
+ static jfieldID dalvik_system_DexPathList__Element_dexFile;
static jfieldID dalvik_system_PathClassLoader_pathList;
static jfieldID java_lang_reflect_AbstractMethod_artMethod;
static jfieldID java_lang_reflect_Field_artField;
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index c02f310..63bfc44 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -123,7 +123,7 @@
// Resist the urge to delete the space. <: is a bigraph sequence.
std::unique_ptr< ::ZipEntry> zip_entry(new ::ZipEntry);
- const int32_t error = FindEntry(handle_, name, zip_entry.get());
+ const int32_t error = FindEntry(handle_, ZipEntryName(name), zip_entry.get());
if (error) {
*error_msg = std::string(ErrorCodeString(error));
return nullptr;
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index c655226..74bfb7e 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -26,15 +26,18 @@
#include <stdio.h>
#include <stdlib.h>
+#include "sigchain.h"
+
#if defined(__APPLE__)
#define _NSIG NSIG
+#define sighandler_t sig_t
#endif
namespace art {
class SignalAction {
public:
- SignalAction() : claimed_(false) {
+ SignalAction() : claimed_(false), uses_old_style_(false) {
}
// Claim the signal and keep the action specified.
@@ -60,17 +63,29 @@
}
// Change the recorded action to that specified.
- void SetAction(const struct sigaction& action) {
+ // If oldstyle is true then this action is from an older style signal()
+ // call as opposed to sigaction(). In this case the sa_handler is
+ // used when invoking the user's handler.
+ void SetAction(const struct sigaction& action, bool oldstyle) {
action_ = action;
+ uses_old_style_ = oldstyle;
+ }
+
+ bool OldStyle() const {
+ return uses_old_style_;
}
private:
struct sigaction action_; // Action to be performed.
bool claimed_; // Whether signal is claimed or not.
+ bool uses_old_style_; // Action is created using signal(). Use sa_handler.
};
// User's signal handlers
static SignalAction user_sigactions[_NSIG];
+static bool initialized;
+static void* linked_sigaction_sym;
+static void* linked_sigprocmask_sym;
static void log(const char* format, ...) {
char buf[256];
@@ -92,6 +107,7 @@
}
}
+
// Claim a signal chain for a particular signal.
void ClaimSignalChain(int signal, struct sigaction* oldaction) {
CheckSignalValid(signal);
@@ -115,7 +131,7 @@
}
const struct sigaction& action = user_sigactions[sig].GetAction();
- if ((action.sa_flags & SA_SIGINFO) == 0) {
+ if (user_sigactions[sig].OldStyle()) {
if (action.sa_handler != NULL) {
action.sa_handler(sig);
} else {
@@ -145,7 +161,7 @@
*old_action = user_sigactions[signal].GetAction();
}
if (new_action != NULL) {
- user_sigactions[signal].SetAction(*new_action);
+ user_sigactions[signal].SetAction(*new_action, false);
}
return 0;
}
@@ -153,14 +169,17 @@
// Will only get here if the signal chain has not been claimed. We want
// to pass the sigaction on to the kernel via the real sigaction in libc.
- void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
if (linked_sigaction_sym == nullptr) {
- linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
- if (linked_sigaction_sym == nullptr ||
- linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
- log("Unable to find next sigaction in signal chain");
- abort();
- }
+ // Perform lazy initialization.
+ // This will only occur outside of a signal context since we have
+ // not been initialized and therefore cannot be within the ART
+ // runtime.
+ InitializeSignalChain();
+ }
+
+ if (linked_sigaction_sym == nullptr) {
+ log("Unable to find next sigaction in signal chain");
+ abort();
}
typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
@@ -168,6 +187,45 @@
return linked_sigaction(signal, new_action, old_action);
}
+sighandler_t signal(int signal, sighandler_t handler) {
+ struct sigaction sa;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = handler;
+ sa.sa_flags = SA_RESTART;
+ sighandler_t oldhandler;
+
+ // If this signal has been claimed as a signal chain, record the user's
+ // action but don't pass it on to the kernel.
+ // Note that we check that the signal number is in range here. An out of range signal
+ // number should behave exactly as the libc sigaction.
+ if (signal > 0 && signal < _NSIG && user_sigactions[signal].IsClaimed()) {
+ oldhandler = reinterpret_cast<sighandler_t>(user_sigactions[signal].GetAction().sa_handler);
+ user_sigactions[signal].SetAction(sa, true);
+ return oldhandler;
+ }
+
+ // Will only get here if the signal chain has not been claimed. We want
+ // to pass the sigaction on to the kernel via the real sigaction in libc.
+
+ if (linked_sigaction_sym == nullptr) {
+ // Perform lazy initialization.
+ InitializeSignalChain();
+ }
+
+ if (linked_sigaction_sym == nullptr) {
+ log("Unable to find next sigaction in signal chain");
+ abort();
+ }
+
+ typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*);
+ SigAction linked_sigaction = reinterpret_cast<SigAction>(linked_sigaction_sym);
+ if (linked_sigaction(signal, &sa, &sa) == -1) {
+ return SIG_ERR;
+ }
+
+ return reinterpret_cast<sighandler_t>(sa.sa_handler);
+}
+
int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
const sigset_t* new_set_ptr = bionic_new_set;
sigset_t tmpset;
@@ -186,14 +244,14 @@
new_set_ptr = &tmpset;
}
- void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
if (linked_sigprocmask_sym == nullptr) {
- linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
- if (linked_sigprocmask_sym == nullptr ||
- linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
- log("Unable to find next sigprocmask in signal chain");
- abort();
- }
+ // Perform lazy initialization.
+ InitializeSignalChain();
+ }
+
+ if (linked_sigprocmask_sym == nullptr) {
+ log("Unable to find next sigprocmask in signal chain");
+ abort();
}
typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*);
@@ -201,5 +259,36 @@
return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
}
} // extern "C"
+
+void InitializeSignalChain() {
+ // Warning.
+ // Don't call this from within a signal context as it makes calls to
+ // dlsym. Calling into the dynamic linker will result in locks being
+ // taken and if it so happens that a signal occurs while one of these
+ // locks is already taken, dlsym will block trying to reenter a
+ // mutex and we will never get out of it.
+ if (initialized) {
+ // Don't initialize twice.
+ return;
+ }
+ linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
+ if (linked_sigaction_sym == nullptr) {
+ linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
+ if (linked_sigaction_sym == nullptr ||
+ linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
+ linked_sigaction_sym = nullptr;
+ }
+ }
+
+ linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
+ if (linked_sigprocmask_sym == nullptr) {
+ linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
+ if (linked_sigprocmask_sym == nullptr ||
+ linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
+ linked_sigprocmask_sym = nullptr;
+ }
+ }
+ initialized = true;
+}
} // namespace art
diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h
index a4ce81c..5bc4026 100644
--- a/sigchainlib/sigchain.h
+++ b/sigchainlib/sigchain.h
@@ -21,6 +21,8 @@
namespace art {
+void InitializeSignalChain();
+
void ClaimSignalChain(int signal, struct sigaction* oldaction);
void UnclaimSignalChain(int signal);
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index e69de29..49d9cc0 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -0,0 +1,29 @@
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+Super.<init>
+Subclass.<init>
+Super.<init>
+RUNNING super object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, super class, super nonstatic
+Super.<init>
+RUNNING super object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED super object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, super class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, super class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, super nonstatic
+Super.nonstaticMethod
+PASSED sub object, sub class, super nonstatic
+Super.<init>
+Subclass.<init>
+RUNNING sub object, sub class, sub nonstatic
+Subclass.nonstaticMethod
+PASSED sub object, sub class, sub nonstatic
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index f5a1d65..6fc4484 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -353,3 +353,198 @@
extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
PthreadHelper(&testShallowGetStackClass2);
}
+
+class JniCallNonvirtualVoidMethodTest {
+ public:
+ explicit JniCallNonvirtualVoidMethodTest(JNIEnv* env)
+ : env_(env),
+ check_jni_ri_(true),
+ check_jni_android_(true),
+ super_(GetClass("JniCallNonvirtualTest")),
+ sub_(GetClass("JniCallNonvirtualTestSubclass")),
+ super_constructor_(GetMethodID(super_, true, "<init>")),
+ super_static_(GetMethodID(super_, false, "staticMethod")),
+ super_nonstatic_(GetMethodID(super_, true, "nonstaticMethod")),
+ sub_constructor_(GetMethodID(sub_, true, "<init>")),
+ sub_static_(GetMethodID(sub_, false, "staticMethod")),
+ sub_nonstatic_(GetMethodID(sub_, true, "nonstaticMethod")),
+ super_field_(GetFieldID(super_, "nonstaticMethodSuperCalled")),
+ sub_field_(GetFieldID(super_, "nonstaticMethodSubCalled")) {}
+
+ void Test() {
+ TestStaticCallNonvirtualMethod();
+ TestNewObject();
+ TestnonstaticCallNonvirtualMethod();
+ }
+
+ JNIEnv* const env_;
+
+ bool const check_jni_ri_;
+ bool const check_jni_android_;
+
+ jclass const super_;
+ jclass const sub_;
+
+ jmethodID const super_constructor_;
+ jmethodID const super_static_;
+ jmethodID const super_nonstatic_;
+ jmethodID const sub_constructor_;
+ jmethodID const sub_static_;
+ jmethodID const sub_nonstatic_;
+
+ jfieldID const super_field_;
+ jfieldID const sub_field_;
+
+ private:
+ jclass GetClass(const char* class_name) {
+ jclass c = env_->FindClass(class_name);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(!env_->ExceptionCheck());
+ assert(c != nullptr);
+ return c;
+ }
+
+ jmethodID GetMethodID(jclass c, bool nonstatic, const char* method_name) {
+ jmethodID m = ((nonstatic) ?
+ env_->GetMethodID(c, method_name, "()V") :
+ env_->GetStaticMethodID(c, method_name, "()V"));
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(m != nullptr);
+ return m;
+ }
+
+ jobject CallConstructor(jclass c, jmethodID m) {
+ jobject o = env_->NewObject(c, m);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(o != nullptr);
+ return o;
+ }
+
+ void CallMethod(jobject o, jclass c, jmethodID m, bool nonstatic, const char* test_case) {
+ printf("RUNNING %s\n", test_case);
+ env_->CallNonvirtualVoidMethod(o, c, m);
+ bool exception_check = env_->ExceptionCheck();
+ if (c == nullptr || !nonstatic) {
+ if (!exception_check) {
+ printf("FAILED %s due to missing exception\n", test_case);
+ env_->FatalError("Expected NullPointerException with null jclass");
+ }
+ env_->ExceptionClear();
+ } else if (exception_check) {
+ printf("FAILED %s due to pending exception\n", test_case);
+ env_->ExceptionDescribe();
+ env_->FatalError(test_case);
+ }
+ printf("PASSED %s\n", test_case);
+ }
+
+ jfieldID GetFieldID(jclass c, const char* field_name) {
+ jfieldID m = env_->GetFieldID(c, field_name, "Z");
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ assert(m != nullptr);
+ return m;
+ }
+
+ jboolean GetBooleanField(jobject o, jfieldID f) {
+ jboolean b = env_->GetBooleanField(o, f);
+ if (env_->ExceptionCheck()) {
+ env_->ExceptionDescribe();
+ env_->FatalError(__FUNCTION__);
+ }
+ return b;
+ }
+
+ void TestStaticCallNonvirtualMethod() {
+ if (!check_jni_ri_&& !check_jni_android_) {
+ CallMethod(nullptr, nullptr, super_static_, false, "null object, null class, super static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, super_, super_static_, false, "null object, super class, super static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, sub_, super_static_, false, "null object, sub class, super static");
+ }
+
+ if (!check_jni_ri_ && !check_jni_android_) {
+ CallMethod(nullptr, nullptr, sub_static_, false, "null object, null class, sub static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, sub_, sub_static_, false, "null object, super class, sub static");
+ }
+ if (!check_jni_android_) {
+ CallMethod(nullptr, super_, sub_static_, false, "null object, super class, sub static");
+ }
+ }
+
+ void TestNewObject() {
+ jobject super_super = CallConstructor(super_, super_constructor_);
+ jobject super_sub = CallConstructor(super_, sub_constructor_);
+ jobject sub_super = CallConstructor(sub_, super_constructor_);
+ jobject sub_sub = CallConstructor(sub_, sub_constructor_);
+
+ assert(env_->IsInstanceOf(super_super, super_));
+ assert(!env_->IsInstanceOf(super_super, sub_));
+
+ // Note that even though we called (and ran) the subclass
+ // constructor, we are not the subclass.
+ assert(env_->IsInstanceOf(super_sub, super_));
+ assert(!env_->IsInstanceOf(super_sub, sub_));
+
+ // Note that even though we called the superclass constructor, we
+ // are still the subclass.
+ assert(env_->IsInstanceOf(sub_super, super_));
+ assert(env_->IsInstanceOf(sub_super, sub_));
+
+ assert(env_->IsInstanceOf(sub_sub, super_));
+ assert(env_->IsInstanceOf(sub_sub, sub_));
+ }
+
+ void TestnonstaticCallNonvirtualMethod(bool super_object, bool super_class, bool super_method, const char* test_case) {
+ if (check_jni_android_) {
+ if (super_object && !super_method) {
+ return; // We don't allow a call with sub class method on the super class instance.
+ }
+ if (super_class && !super_method) {
+ return; // We don't allow a call with the sub class method with the super class argument.
+ }
+ }
+ jobject o = ((super_object) ?
+ CallConstructor(super_, super_constructor_) :
+ CallConstructor(sub_, sub_constructor_));
+ jclass c = (super_class) ? super_ : sub_;
+ jmethodID m = (super_method) ? super_nonstatic_ : sub_nonstatic_;
+ CallMethod(o, c, m, true, test_case);
+ jboolean super_field = GetBooleanField(o, super_field_);
+ jboolean sub_field = GetBooleanField(o, sub_field_);
+ assert(super_field == super_method);
+ assert(sub_field != super_method);
+ }
+
+ void TestnonstaticCallNonvirtualMethod() {
+ TestnonstaticCallNonvirtualMethod(true, true, true, "super object, super class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, false, true, "super object, sub class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, false, false, "super object, sub class, sub nonstatic");
+ TestnonstaticCallNonvirtualMethod(true, true, false, "super object, super class, sub nonstatic");
+
+ TestnonstaticCallNonvirtualMethod(false, true, true, "sub object, super class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, false, true, "sub object, sub class, super nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, false, false, "sub object, sub class, sub nonstatic");
+ TestnonstaticCallNonvirtualMethod(false, true, false, "sub object, super class, sub nonstatic");
+ }
+};
+
+extern "C" void JNICALL Java_Main_testCallNonvirtual(JNIEnv* env, jclass) {
+ JniCallNonvirtualVoidMethodTest(env).Test();
+}
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 5884bc0..8e92010 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -32,6 +32,7 @@
testIsAssignableFromOnPrimitiveTypes();
testShallowGetCallingClassLoader();
testShallowGetStackClass2();
+ testCallNonvirtual();
}
private static native void testFindClassOnAttachedNativeThread();
@@ -94,7 +95,7 @@
// Test sign-extension for values < 32b
- native static byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
+ static native byte byteMethod(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7,
byte b8, byte b9, byte b10);
private static void testByteMethod() {
@@ -109,7 +110,7 @@
}
}
- native static short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
+ private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);
private static void testShortMethod() {
@@ -126,7 +127,7 @@
// Test zero-extension for values < 32b
- native static boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
+ private static native boolean booleanMethod(boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, boolean b6, boolean b7,
boolean b8, boolean b9, boolean b10);
private static void testBooleanMethod() {
@@ -139,7 +140,7 @@
}
}
- native static char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
+ private static native char charMethod(char c1, char c2, char c3, char c4, char c5, char c6, char c7,
char c8, char c9, char c10);
private static void testCharMethod() {
@@ -168,17 +169,55 @@
}
}
- native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
+ private static native boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
- static void testShallowGetCallingClassLoader() {
+ private static void testShallowGetCallingClassLoader() {
nativeTestShallowGetCallingClassLoader();
}
- native static void nativeTestShallowGetCallingClassLoader();
+ private native static void nativeTestShallowGetCallingClassLoader();
- static void testShallowGetStackClass2() {
+ private static void testShallowGetStackClass2() {
nativeTestShallowGetStackClass2();
}
- native static void nativeTestShallowGetStackClass2();
+ private static native void nativeTestShallowGetStackClass2();
+
+ private static native void testCallNonvirtual();
+}
+
+class JniCallNonvirtualTest {
+ public boolean nonstaticMethodSuperCalled = false;
+ public boolean nonstaticMethodSubCalled = false;
+
+ private static native void testCallNonvirtual();
+
+ public JniCallNonvirtualTest() {
+ System.out.println("Super.<init>");
+ }
+
+ public static void staticMethod() {
+ System.out.println("Super.staticMethod");
+ }
+
+ public void nonstaticMethod() {
+ System.out.println("Super.nonstaticMethod");
+ nonstaticMethodSuperCalled = true;
+ }
+}
+
+class JniCallNonvirtualTestSubclass extends JniCallNonvirtualTest {
+
+ public JniCallNonvirtualTestSubclass() {
+ System.out.println("Subclass.<init>");
+ }
+
+ public static void staticMethod() {
+ System.out.println("Subclass.staticMethod");
+ }
+
+ public void nonstaticMethod() {
+ System.out.println("Subclass.nonstaticMethod");
+ nonstaticMethodSubCalled = true;
+ }
}
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 7929554..e914bd9 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -14,138 +14,57 @@
* limitations under the License.
*/
-#include <stdio.h>
-#include <memory>
-
-#include "class_linker.h"
-#include "dex_file-inl.h"
-#include "gc_map.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
+#include "check_reference_map_visitor.h"
#include "jni.h"
-#include "verifier/method_verifier.h"
namespace art {
-#define IS_IN_REF_BITMAP(ref_bitmap, reg) \
- (((reg) < m->GetCodeItem()->registers_size_) && \
- ((*((ref_bitmap) + (reg)/8) >> ((reg) % 8) ) & 0x01))
+#define CHECK_REGS_CONTAIN_REFS(native_pc_offset, ...) do { \
+ int t[] = {__VA_ARGS__}; \
+ int t_size = sizeof(t) / sizeof(*t); \
+ CheckReferences(t, t_size, m->NativePcOffset(m->ToNativePc(native_pc_offset))); \
+} while (false);
-#define CHECK_REGS_CONTAIN_REFS(...) \
- do { \
- int t[] = {__VA_ARGS__}; \
- int t_size = sizeof(t) / sizeof(*t); \
- for (int i = 0; i < t_size; ++i) \
- CHECK(IS_IN_REF_BITMAP(ref_bitmap, t[i])) \
- << "Error: Reg @ " << i << "-th argument is not in GC map"; \
- } while (false)
-
-struct ReferenceMap2Visitor : public StackVisitor {
- explicit ReferenceMap2Visitor(Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, NULL) {
- }
+struct ReferenceMap2Visitor : public CheckReferenceMapVisitor {
+ explicit ReferenceMap2Visitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : CheckReferenceMapVisitor(thread) {}
bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (CheckReferenceMapVisitor::VisitFrame()) {
+ return true;
+ }
mirror::ArtMethod* m = GetMethod();
- if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) {
- return true;
- }
- LOG(INFO) << "At " << PrettyMethod(m, false);
-
- NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
-
- if (m->IsCalleeSaveMethod()) {
- LOG(WARNING) << "no PC for " << PrettyMethod(m);
- return true;
- }
-
- const uint8_t* ref_bitmap = NULL;
std::string m_name(m->GetName());
// Given the method name and the number of times the method has been called,
// we know the Dex registers with live reference values. Assert that what we
// find is what is expected.
if (m_name.compare("f") == 0) {
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x03U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8); // v8: this
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x06U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 1); // v8: this, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x08U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x0cU)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x0eU)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x10U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x13U)));
- CHECK(ref_bitmap);
+ CHECK_REGS_CONTAIN_REFS(0x03U, 8); // v8: this
+ CHECK_REGS_CONTAIN_REFS(0x06U, 8, 1); // v8: this, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x08U, 8, 3, 1); // v8: this, v3: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x0cU, 8, 3, 1); // v8: this, v3: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x0eU, 8, 3, 1); // v8: this, v3: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x10U, 8, 3, 1); // v8: this, v3: y, v1: x
// v2 is added because of the instruction at DexPC 0024. Object merges with 0 is Object. See:
// 0024: move-object v3, v2
// 0025: goto 0013
// Detaled dex instructions for ReferenceMap.java are at the end of this function.
// CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x
// We eliminate the non-live registers at a return, so only v3 is live:
- CHECK_REGS_CONTAIN_REFS(3); // v3: y
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x18U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1aU)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1dU)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1fU)));
- CHECK(ref_bitmap);
+ CHECK_REGS_CONTAIN_REFS(0x13U); // v3: y
+ CHECK_REGS_CONTAIN_REFS(0x18U, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
+ CHECK_REGS_CONTAIN_REFS(0x1aU, 8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex
+ CHECK_REGS_CONTAIN_REFS(0x1dU, 8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex
// v5 is removed from the root set because there is a "merge" operation.
// See 0015: if-nez v2, 001f.
- CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x21U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x27U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x29U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x2cU)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x2fU)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 4, 3, 2, 1); // v8: this, v4: ex, v3: y, v2: y, v1: x
-
- ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x32U)));
- CHECK(ref_bitmap);
- CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1, 0); // v8: this, v3: y, v2: y, v1: x, v0: ex
+ CHECK_REGS_CONTAIN_REFS(0x1fU, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
+ CHECK_REGS_CONTAIN_REFS(0x21U, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex
+ CHECK_REGS_CONTAIN_REFS(0x27U, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x29U, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x2cU, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x2fU, 8, 4, 3, 2, 1); // v8: this, v4: ex, v3: y, v2: y, v1: x
+ CHECK_REGS_CONTAIN_REFS(0x32U, 8, 3, 2, 1, 0); // v8: this, v3: y, v2: y, v1: x, v0: ex
}
return true;
diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index f09fc26..a6d9b66 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -87,12 +87,12 @@
// Prevent the compiler being a smart-alec and optimizing out the assignment
// to nullptr.
-char *p = nullptr;
+char *go_away_compiler = nullptr;
extern "C" JNIEXPORT jint JNICALL Java_Main_testSignal(JNIEnv*, jclass) {
#if defined(__arm__) || defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
// On supported architectures we cause a real SEGV.
- *p = 'a';
+ *go_away_compiler = 'a';
#else
// On other architectures we simulate SEGV.
kill(getpid(), SIGSEGV);
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 30a0d59..c40de7e 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -14,54 +14,29 @@
* limitations under the License.
*/
-#include <stdio.h>
-#include <memory>
-
-#include "class_linker.h"
-#include "gc_map.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object_array-inl.h"
-#include "mirror/object-inl.h"
+#include "check_reference_map_visitor.h"
#include "jni.h"
-#include "scoped_thread_state_change.h"
namespace art {
-#define REG(reg_bitmap, reg) \
- (((reg) < m->GetCodeItem()->registers_size_) && \
- ((*((reg_bitmap) + (reg)/8) >> ((reg) % 8) ) & 0x01))
-
-#define CHECK_REGS(...) if (!IsShadowFrame()) { \
- int t[] = {__VA_ARGS__}; \
- int t_size = sizeof(t) / sizeof(*t); \
- for (int i = 0; i < t_size; ++i) \
- CHECK(REG(reg_bitmap, t[i])) << "Error: Reg " << i << " is not in RegisterMap"; \
- }
+#define CHECK_REGS(...) do { \
+ int t[] = {__VA_ARGS__}; \
+ int t_size = sizeof(t) / sizeof(*t); \
+ CheckReferences(t, t_size, GetNativePcOffset()); \
+} while (false);
static int gJava_StackWalk_refmap_calls = 0;
-struct TestReferenceMapVisitor : public StackVisitor {
- explicit TestReferenceMapVisitor(Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, NULL) {
- }
+class TestReferenceMapVisitor : public CheckReferenceMapVisitor {
+ public:
+ explicit TestReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : CheckReferenceMapVisitor(thread) {}
bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* m = GetMethod();
- CHECK(m != NULL);
- LOG(INFO) << "At " << PrettyMethod(m, false);
-
- if (m->IsCalleeSaveMethod() || m->IsNative()) {
- LOG(WARNING) << "no PC for " << PrettyMethod(m);
- CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex);
+ if (CheckReferenceMapVisitor::VisitFrame()) {
return true;
}
- const uint8_t* reg_bitmap = NULL;
- if (!IsShadowFrame()) {
- NativePcOffsetToReferenceMap map(m->GetNativeGcMap());
- reg_bitmap = map.FindBitMap(GetNativePcOffset());
- }
+ mirror::ArtMethod* m = GetMethod();
StringPiece m_name(m->GetName());
// Given the method name and the number of times the method has been called,
diff --git a/test/012-math/expected.txt b/test/012-math/expected.txt
index af9708e..75a559e 100644
--- a/test/012-math/expected.txt
+++ b/test/012-math/expected.txt
@@ -30,3 +30,11 @@
f:21.0
f:3.0
f:3.0
+0
+0
+0
+0
+0
+0
+0
+0
diff --git a/test/012-math/src/Main.java b/test/012-math/src/Main.java
index a4a8c71..07b7540 100644
--- a/test/012-math/src/Main.java
+++ b/test/012-math/src/Main.java
@@ -99,7 +99,27 @@
f %= g;
System.out.println("f:" +f);
}
+ public static void math_012_numerator(int a, int b, int d, int e, int f) {
+ int c = 0;
+ c /= b;
+ System.out.println(c);
+ c %= b;
+ System.out.println(c);
+ c = a / b;
+ System.out.println(c);
+ c = a % b;
+ System.out.println(c);
+ c = c / d;
+ System.out.println(c);
+ c = c / e;
+ System.out.println(c);
+ c = c / f;
+ System.out.println(c);
+ c = c % f;
+ System.out.println(c);
+ }
public static void main(String args[]) {
math_012();
+ math_012_numerator(0, 3, -1, 4, 5);
}
}
diff --git a/test/021-string2/src/junit/framework/Assert.java b/test/021-string2/src/junit/framework/Assert.java
index 364e646..3dcc23d 100644
--- a/test/021-string2/src/junit/framework/Assert.java
+++ b/test/021-string2/src/junit/framework/Assert.java
@@ -5,287 +5,292 @@
*/
public class Assert {
- /**
- * Protect constructor since it is a static only class
- */
- protected Assert() {
- }
+ /**
+ * Protect constructor since it is a static only class
+ */
+ protected Assert() {
+ }
- /**
- * Asserts that a condition is true. If it isn't it throws
- * an AssertionFailedError with the given message.
- */
- static public void assertTrue(String message, boolean condition) {
- if (!condition)
- fail(message);
- }
- /**
- * Asserts that a condition is true. If it isn't it throws
- * an AssertionFailedError.
- */
- static public void assertTrue(boolean condition) {
- assertTrue(null, condition);
- }
- /**
- * Asserts that a condition is false. If it isn't it throws
- * an AssertionFailedError with the given message.
- */
- static public void assertFalse(String message, boolean condition) {
- assertTrue(message, !condition);
- }
- /**
- * Asserts that a condition is false. If it isn't it throws
- * an AssertionFailedError.
- */
- static public void assertFalse(boolean condition) {
- assertFalse(null, condition);
- }
- /**
- * Fails a test with the given message.
- */
- static public void fail(String message) {
- throw new AssertionFailedError(message);
- }
- /**
- * Fails a test with no message.
- */
- static public void fail() {
- fail(null);
- }
- /**
- * Asserts that two objects are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, Object expected, Object actual) {
- if (expected == null && actual == null)
- return;
- if (expected != null && expected.equals(actual))
- return;
- failNotEquals(message, expected, actual);
- }
- /**
- * Asserts that two objects are equal. If they are not
- * an AssertionFailedError is thrown.
- */
- static public void assertEquals(Object expected, Object actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two Strings are equal.
- */
- static public void assertEquals(String message, String expected, String actual) {
- if (expected == null && actual == null)
- return;
- if (expected != null && expected.equals(actual))
- return;
- throw new ComparisonFailure(message, expected, actual);
- }
- /**
- * Asserts that two Strings are equal.
- */
- static public void assertEquals(String expected, String actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two doubles are equal concerning a delta. If they are not
- * an AssertionFailedError is thrown with the given message. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(String message, double expected, double actual, double delta) {
- // handle infinity specially since subtracting to infinite values gives NaN and the
- // the following test fails
- if (Double.isInfinite(expected)) {
- if (!(expected == actual))
- failNotEquals(message, new Double(expected), new Double(actual));
- } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
- failNotEquals(message, new Double(expected), new Double(actual));
- }
- /**
- * Asserts that two doubles are equal concerning a delta. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(double expected, double actual, double delta) {
- assertEquals(null, expected, actual, delta);
- }
- /**
- * Asserts that two floats are equal concerning a delta. If they are not
- * an AssertionFailedError is thrown with the given message. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(String message, float expected, float actual, float delta) {
- // handle infinity specially since subtracting to infinite values gives NaN and the
- // the following test fails
- if (Float.isInfinite(expected)) {
- if (!(expected == actual))
- failNotEquals(message, new Float(expected), new Float(actual));
- } else if (!(Math.abs(expected-actual) <= delta))
- failNotEquals(message, new Float(expected), new Float(actual));
- }
- /**
- * Asserts that two floats are equal concerning a delta. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(float expected, float actual, float delta) {
- assertEquals(null, expected, actual, delta);
- }
- /**
- * Asserts that two longs are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, long expected, long actual) {
- assertEquals(message, new Long(expected), new Long(actual));
- }
- /**
- * Asserts that two longs are equal.
- */
- static public void assertEquals(long expected, long actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two booleans are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, boolean expected, boolean actual) {
- assertEquals(message, new Boolean(expected), new Boolean(actual));
- }
- /**
- * Asserts that two booleans are equal.
- */
- static public void assertEquals(boolean expected, boolean actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two bytes are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, byte expected, byte actual) {
- assertEquals(message, new Byte(expected), new Byte(actual));
- }
- /**
- * Asserts that two bytes are equal.
- */
- static public void assertEquals(byte expected, byte actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two chars are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, char expected, char actual) {
- assertEquals(message, new Character(expected), new Character(actual));
- }
- /**
- * Asserts that two chars are equal.
- */
- static public void assertEquals(char expected, char actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two shorts are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, short expected, short actual) {
- assertEquals(message, new Short(expected), new Short(actual));
- }
- /**
- * Asserts that two shorts are equal.
- */
- static public void assertEquals(short expected, short actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two ints are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, int expected, int actual) {
- assertEquals(message, new Integer(expected), new Integer(actual));
- }
- /**
- * Asserts that two ints are equal.
- */
- static public void assertEquals(int expected, int actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that an object isn't null.
- */
- static public void assertNotNull(Object object) {
- assertNotNull(null, object);
- }
- /**
- * Asserts that an object isn't null. If it is
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertNotNull(String message, Object object) {
- assertTrue(message, object != null);
- }
- /**
- * Asserts that an object is null.
- */
- static public void assertNull(Object object) {
- assertNull(null, object);
- }
- /**
- * Asserts that an object is null. If it is not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertNull(String message, Object object) {
- assertTrue(message, object == null);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertSame(String message, Object expected, Object actual) {
- if (expected == actual)
- return;
- failNotSame(message, expected, actual);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * the same an AssertionFailedError is thrown.
- */
- static public void assertSame(Object expected, Object actual) {
- assertSame(null, expected, actual);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertNotSame(String message, Object expected, Object actual) {
- if (expected == actual)
- failSame(message);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * the same an AssertionFailedError is thrown.
- */
- static public void assertNotSame(Object expected, Object actual) {
- assertNotSame(null, expected, actual);
- }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertTrue(String message, boolean condition) {
+ if (!condition)
+ fail(message);
+ }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertTrue(boolean condition) {
+ assertTrue(null, condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertFalse(String message, boolean condition) {
+ assertTrue(message, !condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertFalse(boolean condition) {
+ assertFalse(null, condition);
+ }
+ /**
+ * Fails a test with the given message.
+ */
+ static public void fail(String message) {
+ if (message == null) {
+ throw new AssertionFailedError();
+ }
+ throw new AssertionFailedError(message);
+ }
+ /**
+ * Fails a test with no message.
+ */
+ static public void fail() {
+ fail(null);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ failNotEquals(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown.
+ */
+ static public void assertEquals(Object expected, Object actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String message, String expected, String actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ String cleanMessage= message == null ? "" : message;
+ throw new ComparisonFailure(cleanMessage, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String expected, String actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, double expected, double actual, double delta) {
+ if (Double.compare(expected, actual) == 0)
+ return;
+ if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Double(expected), new Double(actual));
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two floats are equal concerning a positive delta. If they
+ * are not an AssertionFailedError is thrown with the given message. If the
+ * expected value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, float expected, float actual, float delta) {
+ if (Float.compare(expected, actual) == 0)
+ return;
+ if (!(Math.abs(expected - actual) <= delta))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(float expected, float actual, float delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two longs are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, long expected, long actual) {
+ assertEquals(message, new Long(expected), new Long(actual));
+ }
+ /**
+ * Asserts that two longs are equal.
+ */
+ static public void assertEquals(long expected, long actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two booleans are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+ }
+ /**
+ * Asserts that two booleans are equal.
+ */
+ static public void assertEquals(boolean expected, boolean actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two bytes are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, byte expected, byte actual) {
+ assertEquals(message, new Byte(expected), new Byte(actual));
+ }
+ /**
+ * Asserts that two bytes are equal.
+ */
+ static public void assertEquals(byte expected, byte actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two chars are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, char expected, char actual) {
+ assertEquals(message, new Character(expected), new Character(actual));
+ }
+ /**
+ * Asserts that two chars are equal.
+ */
+ static public void assertEquals(char expected, char actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two shorts are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, short expected, short actual) {
+ assertEquals(message, new Short(expected), new Short(actual));
+ }
+ /**
+ * Asserts that two shorts are equal.
+ */
+ static public void assertEquals(short expected, short actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two ints are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, int expected, int actual) {
+ assertEquals(message, new Integer(expected), new Integer(actual));
+ }
+ /**
+ * Asserts that two ints are equal.
+ */
+ static public void assertEquals(int expected, int actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that an object isn't null.
+ */
+ static public void assertNotNull(Object object) {
+ assertNotNull(null, object);
+ }
+ /**
+ * Asserts that an object isn't null. If it is
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+ /**
+ * Asserts that an object is null. If it isn't an {@link AssertionError} is
+ * thrown.
+ * Message contains: Expected: <null> but was: object
+ *
+ * @param object
+ * Object to check or <code>null</code>
+ */
+ static public void assertNull(Object object) {
+ String message = "Expected: <null> but was: " + String.valueOf(object);
+ assertNull(message, object);
+ }
+ /**
+ * Asserts that an object is null. If it is not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNull(String message, Object object) {
+ assertTrue(message, object == null);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ return;
+ failNotSame(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertSame(Object expected, Object actual) {
+ assertSame(null, expected, actual);
+ }
+ /**
+ * Asserts that two objects do not refer to the same object. If they do
+ * refer to the same object an AssertionFailedError is thrown with the
+ * given message.
+ */
+ static public void assertNotSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ failSame(message);
+ }
+ /**
+ * Asserts that two objects do not refer to the same object. If they do
+ * refer to the same object an AssertionFailedError is thrown.
+ */
+ static public void assertNotSame(Object expected, Object actual) {
+ assertNotSame(null, expected, actual);
+ }
- static private void failSame(String message) {
- String formatted= "";
- if (message != null)
- formatted= message+" ";
- fail(formatted+"expected not same");
- }
+ static public void failSame(String message) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected not same");
+ }
- static private void failNotSame(String message, Object expected, Object actual) {
- String formatted= "";
- if (message != null)
- formatted= message+" ";
- fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
- }
+ static public void failNotSame(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+ }
- static private void failNotEquals(String message, Object expected, Object actual) {
- fail(format(message, expected, actual));
- }
+ static public void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
- static String format(String message, Object expected, Object actual) {
- String formatted= "";
- if (message != null)
- formatted= message+" ";
- return formatted+"expected:<"+expected+"> but was:<"+actual+">";
- }
+ public static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null && message.length() > 0)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
}
diff --git a/test/021-string2/src/junit/framework/AssertionFailedError.java b/test/021-string2/src/junit/framework/AssertionFailedError.java
index e9cb3a3..0d7802c 100644
--- a/test/021-string2/src/junit/framework/AssertionFailedError.java
+++ b/test/021-string2/src/junit/framework/AssertionFailedError.java
@@ -3,11 +3,18 @@
/**
* Thrown when an assertion failed.
*/
-public class AssertionFailedError extends Error {
+public class AssertionFailedError extends AssertionError {
- public AssertionFailedError () {
- }
- public AssertionFailedError (String message) {
- super (message);
- }
-}
+ private static final long serialVersionUID= 1L;
+
+ public AssertionFailedError() {
+ }
+
+ public AssertionFailedError(String message) {
+ super(defaultString(message));
+ }
+
+ private static String defaultString(String message) {
+ return message == null ? "" : message;
+ }
+}
\ No newline at end of file
diff --git a/test/021-string2/src/junit/framework/ComparisonCompactor.java b/test/021-string2/src/junit/framework/ComparisonCompactor.java
new file mode 100644
index 0000000..e540f03
--- /dev/null
+++ b/test/021-string2/src/junit/framework/ComparisonCompactor.java
@@ -0,0 +1,87 @@
+package junit.framework;
+
+// android-changed add @hide
+/**
+ * @hide not needed for public API
+ */
+public class ComparisonCompactor {
+
+ private static final String ELLIPSIS= "...";
+ private static final String DELTA_END= "]";
+ private static final String DELTA_START= "[";
+
+ private int fContextLength;
+ private String fExpected;
+ private String fActual;
+ private int fPrefix;
+ private int fSuffix;
+
+ public ComparisonCompactor(int contextLength, String expected, String actual) {
+ fContextLength= contextLength;
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ public String compact(String message) {
+ if (fExpected == null || fActual == null || areStringsEqual()) {
+ // android-changed use local method instead of Assert.format, since
+ // the later is not part of Android API till API 16
+ return format(message, fExpected, fActual);
+ }
+ findCommonPrefix();
+ findCommonSuffix();
+ String expected= compactString(fExpected);
+ String actual= compactString(fActual);
+ // android-changed use local format method
+ return format(message, expected, actual);
+ }
+
+ private String compactString(String source) {
+ String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+ if (fPrefix > 0)
+ result= computeCommonPrefix() + result;
+ if (fSuffix > 0)
+ result= result + computeCommonSuffix();
+ return result;
+ }
+
+ private void findCommonPrefix() {
+ fPrefix= 0;
+ int end= Math.min(fExpected.length(), fActual.length());
+ for (; fPrefix < end; fPrefix++) {
+ if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
+ break;
+ }
+ }
+
+ private void findCommonSuffix() {
+ int expectedSuffix= fExpected.length() - 1;
+ int actualSuffix= fActual.length() - 1;
+ for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+ if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
+ break;
+ }
+ fSuffix= fExpected.length() - expectedSuffix;
+ }
+
+ private String computeCommonPrefix() {
+ return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+ }
+
+ private String computeCommonSuffix() {
+ int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+ return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+ }
+
+ private boolean areStringsEqual() {
+ return fExpected.equals(fActual);
+ }
+
+ // android-changed copy of Assert.format for reasons described above
+ private static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null && message.length() > 0)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
+}
diff --git a/test/021-string2/src/junit/framework/ComparisonFailure.java b/test/021-string2/src/junit/framework/ComparisonFailure.java
index ccd476b..5077993 100644
--- a/test/021-string2/src/junit/framework/ComparisonFailure.java
+++ b/test/021-string2/src/junit/framework/ComparisonFailure.java
@@ -2,67 +2,51 @@
/**
* Thrown when an assert equals for Strings failed.
- *
+ *
* Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
*/
public class ComparisonFailure extends AssertionFailedError {
- private String fExpected;
- private String fActual;
+ private static final int MAX_CONTEXT_LENGTH= 20;
+ private static final long serialVersionUID= 1L;
+
+ private String fExpected;
+ private String fActual;
- /**
- * Constructs a comparison failure.
- * @param message the identifying message or null
- * @param expected the expected string value
- * @param actual the actual string value
- */
- public ComparisonFailure (String message, String expected, String actual) {
- super (message);
- fExpected= expected;
- fActual= actual;
- }
-
- /**
- * Returns "..." in place of common prefix and "..." in
- * place of common suffix between expected and actual.
- *
- * @see java.lang.Throwable#getMessage()
- */
- public String getMessage() {
- if (fExpected == null || fActual == null)
- return Assert.format(super.getMessage(), fExpected, fActual);
-
- int end= Math.min(fExpected.length(), fActual.length());
-
- int i= 0;
- for (; i < end; i++) {
- if (fExpected.charAt(i) != fActual.charAt(i))
- break;
- }
- int j= fExpected.length()-1;
- int k= fActual.length()-1;
- for (; k >= i && j >= i; k--,j--) {
- if (fExpected.charAt(j) != fActual.charAt(k))
- break;
- }
- String actual, expected;
-
- // equal strings
- if (j < i && k < i) {
- expected= fExpected;
- actual= fActual;
- } else {
- expected= fExpected.substring(i, j+1);
- actual= fActual.substring(i, k+1);
- if (i <= end && i > 0) {
- expected= "..."+expected;
- actual= "..."+actual;
- }
-
- if (j < fExpected.length()-1)
- expected= expected+"...";
- if (k < fActual.length()-1)
- actual= actual+"...";
- }
- return Assert.format(super.getMessage(), expected, actual);
- }
-}
+ /**
+ * Constructs a comparison failure.
+ * @param message the identifying message or null
+ * @param expected the expected string value
+ * @param actual the actual string value
+ */
+ public ComparisonFailure (String message, String expected, String actual) {
+ super (message);
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ /**
+ * Returns "..." in place of common prefix and "..." in
+ * place of common suffix between expected and actual.
+ *
+ * @see Throwable#getMessage()
+ */
+ @Override
+ public String getMessage() {
+ return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+ }
+
+ /**
+ * Gets the actual string value
+ * @return the actual string value
+ */
+ public String getActual() {
+ return fActual;
+ }
+ /**
+ * Gets the expected string value
+ * @return the expected string value
+ */
+ public String getExpected() {
+ return fExpected;
+ }
+}
\ No newline at end of file
diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt
index a2a74fc..36fa5f8 100644
--- a/test/036-finalizer/expected.txt
+++ b/test/036-finalizer/expected.txt
@@ -11,3 +11,4 @@
sleep
reborn: [FinalizerTest message=nothing, finalized=false]
wimp: null
+Finalized 1024 / 1024
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 328425f..390472d 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -120,6 +120,39 @@
System.out.println("reborn: " + FinalizerTest.mReborn);
System.out.println("wimp: " + wimpString(wimp));
+ // Test runFinalization with multiple objects.
+ runFinalizationTest();
+ }
+
+ static class FinalizeCounter {
+ private static Object finalizeLock = new Object();
+ private static volatile int finalizeCount = 0;
+ private int index;
+ static int getCount() {
+ return finalizeCount;
+ }
+ FinalizeCounter(int index) {
+ this.index = index;
+ }
+ protected void finalize() {
+ synchronized(finalizeLock) {
+ ++finalizeCount;
+ }
+ }
+ }
+
+ private static void runFinalizationTest() {
+ int count = 1024;
+ Object[] objs = new Object[count];
+ for (int i = 0; i < count; ++i) {
+ objs[i] = new FinalizeCounter(i);
+ }
+ for (int i = 0; i < count; ++i) {
+ objs[i] = null;
+ }
+ System.gc();
+ System.runFinalization();
+ System.out.println("Finalized " + FinalizeCounter.getCount() + " / " + count);
}
public static class FinalizerTest {
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index ecb3599..fa053fb 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -123,3 +123,17 @@
fields are .equals
methods are unique
methods are .equals
+type1 is a ParameterizedType
+type2 is a ParameterizedType
+type3 is a ParameterizedType
+type1(java.util.Set<java.lang.String>) equals type2(java.util.Set<java.lang.String>)
+type1(java.util.Set<java.lang.String>) equals type3(java.util.Set<java.lang.String>)
+type1(java.util.Set<java.lang.String>) hashCode equals type2(java.util.Set<java.lang.String>) hashCode
+type1(java.util.Set<java.lang.String>) hashCode equals type3(java.util.Set<java.lang.String>) hashCode
+type1 is a GenericArrayType
+type2 is a GenericArrayType
+type3 is a GenericArrayType
+type1(T[]) equals type2(T[])
+type1(T[]) equals type3(T[])
+type1(T[]) hashCode equals type2(T[]) hashCode
+type1(T[]) hashCode equals type3(T[]) hashCode
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 3e6d700..3fe3881 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -18,8 +18,10 @@
import java.io.IOException;
import java.util.Collections;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Reflection test.
@@ -579,16 +581,155 @@
}
}
+ public static void checkParametrizedTypeEqualsAndHashCode() {
+ Method method1;
+ Method method2;
+ Method method3;
+ try {
+ method1 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
+ method2 = ParametrizedTypeTest.class.getDeclaredMethod("aMethod", Set.class);
+ method3 = ParametrizedTypeTest.class.getDeclaredMethod("aMethodIdentical", Set.class);
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(nsme);
+ }
+
+ List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
+ List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
+ List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
+
+ Type type1 = types1.get(0);
+ Type type2 = types2.get(0);
+ Type type3 = types3.get(0);
+
+ if (type1 instanceof ParameterizedType) {
+ System.out.println("type1 is a ParameterizedType");
+ }
+ if (type2 instanceof ParameterizedType) {
+ System.out.println("type2 is a ParameterizedType");
+ }
+ if (type3 instanceof ParameterizedType) {
+ System.out.println("type3 is a ParameterizedType");
+ }
+
+ if (type1.equals(type2)) {
+ System.out.println("type1("+type1+") equals type2("+type2+")");
+ } else {
+ System.out.println("type1("+type1+") does not equal type2("+type2+")");
+ }
+
+ if (type1.equals(type3)) {
+ System.out.println("type1("+type1+") equals type3("+type3+")");
+ } else {
+ System.out.println("type1("+type1+") does not equal type3("+type3+")");
+ }
+ if (type1.hashCode() == type2.hashCode()) {
+ System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
+ } else {
+ System.out.println(
+ "type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
+ }
+
+ if (type1.hashCode() == type3.hashCode()) {
+ System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
+ } else {
+ System.out.println(
+ "type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
+ }
+ }
+
+ public static void checkGenericArrayTypeEqualsAndHashCode() {
+ Method method1;
+ Method method2;
+ Method method3;
+ try {
+ method1 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
+ method2 = GenericArrayTypeTest.class.getDeclaredMethod("aMethod", Object[].class);
+ method3 = GenericArrayTypeTest.class.getDeclaredMethod("aMethodIdentical", Object[].class);
+ } catch (NoSuchMethodException nsme) {
+ throw new RuntimeException(nsme);
+ }
+
+ List<Type> types1 = Arrays.asList(method1.getGenericParameterTypes());
+ List<Type> types2 = Arrays.asList(method2.getGenericParameterTypes());
+ List<Type> types3 = Arrays.asList(method3.getGenericParameterTypes());
+
+ Type type1 = types1.get(0);
+ Type type2 = types2.get(0);
+ Type type3 = types3.get(0);
+
+ if (type1 instanceof GenericArrayType) {
+ System.out.println("type1 is a GenericArrayType");
+ }
+ if (type2 instanceof GenericArrayType) {
+ System.out.println("type2 is a GenericArrayType");
+ }
+ if (type3 instanceof GenericArrayType) {
+ System.out.println("type3 is a GenericArrayType");
+ }
+
+ if (type1.equals(type2)) {
+ System.out.println("type1("+type1+") equals type2("+type2+")");
+ } else {
+ System.out.println("type1("+type1+") does not equal type2("+type2+")");
+ }
+
+ if (type1.equals(type3)) {
+ System.out.println("type1("+type1+") equals type3("+type3+")");
+ } else {
+ System.out.println("type1("+type1+") does not equal type3("+type3+")");
+ }
+ if (type1.hashCode() == type2.hashCode()) {
+ System.out.println("type1("+type1+") hashCode equals type2("+type2+") hashCode");
+ } else {
+ System.out.println(
+ "type1("+type1+") hashCode does not equal type2("+type2+") hashCode");
+ }
+
+ if (type1.hashCode() == type3.hashCode()) {
+ System.out.println("type1("+type1+") hashCode equals type3("+type3+") hashCode");
+ } else {
+ System.out.println(
+ "type1("+type1+") hashCode does not equal type3("+type3+") hashCode");
+ }
+ }
+
+ private static void checkGetDeclaredConstructor() {
+ try {
+ Method.class.getDeclaredConstructor().setAccessible(true);
+ System.out.print("Didn't get an exception from method getDeclaredConstructor");
+ } catch (NoSuchMethodException e) {
+ } catch (Exception e) {
+ System.out.print(e);
+ }
+ try {
+ Field.class.getDeclaredConstructor().setAccessible(true);
+ System.out.print("Didn't get an exception from field getDeclaredConstructor");
+ } catch (NoSuchMethodException e) {
+ } catch (Exception e) {
+ System.out.print(e);
+ }
+ try {
+ Class.class.getDeclaredConstructor().setAccessible(true);
+ System.out.print("Didn't get an exception from class getDeclaredConstructor()");
+ } catch (SecurityException e) {
+ } catch (Exception e) {
+ System.out.print(e);
+ }
+ }
+
public static void main(String[] args) throws Exception {
Main test = new Main();
test.run();
+ checkGetDeclaredConstructor();
checkAccess();
checkType();
checkClinitForFields();
checkClinitForMethods();
checkGeneric();
checkUnique();
+ checkParametrizedTypeEqualsAndHashCode();
+ checkGenericArrayTypeEqualsAndHashCode();
}
}
@@ -696,3 +837,13 @@
throw new UnsupportedOperationException();
}
}
+
+class ParametrizedTypeTest {
+ public void aMethod(Set<String> names) {}
+ public void aMethodIdentical(Set<String> names) {}
+}
+
+class GenericArrayTypeTest<T> {
+ public void aMethod(T[] names) {}
+ public void aMethodIdentical(T[] names) {}
+}
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index 035690f..c93f8bb 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -31,6 +31,7 @@
static class InstanceMemEater {
static boolean sawOome;
+ static InstanceMemEater hook;
InstanceMemEater next;
double d1, d2, d3, d4, d5, d6, d7, d8; // Bloat this object so we fill the heap faster.
@@ -45,6 +46,7 @@
}
static void confuseCompilerOptimization(InstanceMemEater instance) {
+ hook = instance;
}
}
@@ -61,6 +63,7 @@
lastMemEater = lastMemEater.next;
} while (lastMemEater != null);
memEater.confuseCompilerOptimization(memEater);
+ InstanceMemEater.hook = null;
return InstanceMemEater.sawOome;
}
diff --git a/test/082-inline-execute/src/junit/framework/Assert.java b/test/082-inline-execute/src/junit/framework/Assert.java
index 364e646..3dcc23d 100644
--- a/test/082-inline-execute/src/junit/framework/Assert.java
+++ b/test/082-inline-execute/src/junit/framework/Assert.java
@@ -5,287 +5,292 @@
*/
public class Assert {
- /**
- * Protect constructor since it is a static only class
- */
- protected Assert() {
- }
+ /**
+ * Protect constructor since it is a static only class
+ */
+ protected Assert() {
+ }
- /**
- * Asserts that a condition is true. If it isn't it throws
- * an AssertionFailedError with the given message.
- */
- static public void assertTrue(String message, boolean condition) {
- if (!condition)
- fail(message);
- }
- /**
- * Asserts that a condition is true. If it isn't it throws
- * an AssertionFailedError.
- */
- static public void assertTrue(boolean condition) {
- assertTrue(null, condition);
- }
- /**
- * Asserts that a condition is false. If it isn't it throws
- * an AssertionFailedError with the given message.
- */
- static public void assertFalse(String message, boolean condition) {
- assertTrue(message, !condition);
- }
- /**
- * Asserts that a condition is false. If it isn't it throws
- * an AssertionFailedError.
- */
- static public void assertFalse(boolean condition) {
- assertFalse(null, condition);
- }
- /**
- * Fails a test with the given message.
- */
- static public void fail(String message) {
- throw new AssertionFailedError(message);
- }
- /**
- * Fails a test with no message.
- */
- static public void fail() {
- fail(null);
- }
- /**
- * Asserts that two objects are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, Object expected, Object actual) {
- if (expected == null && actual == null)
- return;
- if (expected != null && expected.equals(actual))
- return;
- failNotEquals(message, expected, actual);
- }
- /**
- * Asserts that two objects are equal. If they are not
- * an AssertionFailedError is thrown.
- */
- static public void assertEquals(Object expected, Object actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two Strings are equal.
- */
- static public void assertEquals(String message, String expected, String actual) {
- if (expected == null && actual == null)
- return;
- if (expected != null && expected.equals(actual))
- return;
- throw new ComparisonFailure(message, expected, actual);
- }
- /**
- * Asserts that two Strings are equal.
- */
- static public void assertEquals(String expected, String actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two doubles are equal concerning a delta. If they are not
- * an AssertionFailedError is thrown with the given message. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(String message, double expected, double actual, double delta) {
- // handle infinity specially since subtracting to infinite values gives NaN and the
- // the following test fails
- if (Double.isInfinite(expected)) {
- if (!(expected == actual))
- failNotEquals(message, new Double(expected), new Double(actual));
- } else if (!(Math.abs(expected-actual) <= delta)) // Because comparison with NaN always returns false
- failNotEquals(message, new Double(expected), new Double(actual));
- }
- /**
- * Asserts that two doubles are equal concerning a delta. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(double expected, double actual, double delta) {
- assertEquals(null, expected, actual, delta);
- }
- /**
- * Asserts that two floats are equal concerning a delta. If they are not
- * an AssertionFailedError is thrown with the given message. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(String message, float expected, float actual, float delta) {
- // handle infinity specially since subtracting to infinite values gives NaN and the
- // the following test fails
- if (Float.isInfinite(expected)) {
- if (!(expected == actual))
- failNotEquals(message, new Float(expected), new Float(actual));
- } else if (!(Math.abs(expected-actual) <= delta))
- failNotEquals(message, new Float(expected), new Float(actual));
- }
- /**
- * Asserts that two floats are equal concerning a delta. If the expected
- * value is infinity then the delta value is ignored.
- */
- static public void assertEquals(float expected, float actual, float delta) {
- assertEquals(null, expected, actual, delta);
- }
- /**
- * Asserts that two longs are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, long expected, long actual) {
- assertEquals(message, new Long(expected), new Long(actual));
- }
- /**
- * Asserts that two longs are equal.
- */
- static public void assertEquals(long expected, long actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two booleans are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, boolean expected, boolean actual) {
- assertEquals(message, new Boolean(expected), new Boolean(actual));
- }
- /**
- * Asserts that two booleans are equal.
- */
- static public void assertEquals(boolean expected, boolean actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two bytes are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, byte expected, byte actual) {
- assertEquals(message, new Byte(expected), new Byte(actual));
- }
- /**
- * Asserts that two bytes are equal.
- */
- static public void assertEquals(byte expected, byte actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two chars are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, char expected, char actual) {
- assertEquals(message, new Character(expected), new Character(actual));
- }
- /**
- * Asserts that two chars are equal.
- */
- static public void assertEquals(char expected, char actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two shorts are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, short expected, short actual) {
- assertEquals(message, new Short(expected), new Short(actual));
- }
- /**
- * Asserts that two shorts are equal.
- */
- static public void assertEquals(short expected, short actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that two ints are equal. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertEquals(String message, int expected, int actual) {
- assertEquals(message, new Integer(expected), new Integer(actual));
- }
- /**
- * Asserts that two ints are equal.
- */
- static public void assertEquals(int expected, int actual) {
- assertEquals(null, expected, actual);
- }
- /**
- * Asserts that an object isn't null.
- */
- static public void assertNotNull(Object object) {
- assertNotNull(null, object);
- }
- /**
- * Asserts that an object isn't null. If it is
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertNotNull(String message, Object object) {
- assertTrue(message, object != null);
- }
- /**
- * Asserts that an object is null.
- */
- static public void assertNull(Object object) {
- assertNull(null, object);
- }
- /**
- * Asserts that an object is null. If it is not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertNull(String message, Object object) {
- assertTrue(message, object == null);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertSame(String message, Object expected, Object actual) {
- if (expected == actual)
- return;
- failNotSame(message, expected, actual);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * the same an AssertionFailedError is thrown.
- */
- static public void assertSame(Object expected, Object actual) {
- assertSame(null, expected, actual);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * an AssertionFailedError is thrown with the given message.
- */
- static public void assertNotSame(String message, Object expected, Object actual) {
- if (expected == actual)
- failSame(message);
- }
- /**
- * Asserts that two objects refer to the same object. If they are not
- * the same an AssertionFailedError is thrown.
- */
- static public void assertNotSame(Object expected, Object actual) {
- assertNotSame(null, expected, actual);
- }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertTrue(String message, boolean condition) {
+ if (!condition)
+ fail(message);
+ }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertTrue(boolean condition) {
+ assertTrue(null, condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertFalse(String message, boolean condition) {
+ assertTrue(message, !condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertFalse(boolean condition) {
+ assertFalse(null, condition);
+ }
+ /**
+ * Fails a test with the given message.
+ */
+ static public void fail(String message) {
+ if (message == null) {
+ throw new AssertionFailedError();
+ }
+ throw new AssertionFailedError(message);
+ }
+ /**
+ * Fails a test with no message.
+ */
+ static public void fail() {
+ fail(null);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ failNotEquals(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown.
+ */
+ static public void assertEquals(Object expected, Object actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String message, String expected, String actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ String cleanMessage= message == null ? "" : message;
+ throw new ComparisonFailure(cleanMessage, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String expected, String actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, double expected, double actual, double delta) {
+ if (Double.compare(expected, actual) == 0)
+ return;
+ if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Double(expected), new Double(actual));
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two floats are equal concerning a positive delta. If they
+ * are not an AssertionFailedError is thrown with the given message. If the
+ * expected value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, float expected, float actual, float delta) {
+ if (Float.compare(expected, actual) == 0)
+ return;
+ if (!(Math.abs(expected - actual) <= delta))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(float expected, float actual, float delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two longs are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, long expected, long actual) {
+ assertEquals(message, new Long(expected), new Long(actual));
+ }
+ /**
+ * Asserts that two longs are equal.
+ */
+ static public void assertEquals(long expected, long actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two booleans are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+ }
+ /**
+ * Asserts that two booleans are equal.
+ */
+ static public void assertEquals(boolean expected, boolean actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two bytes are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, byte expected, byte actual) {
+ assertEquals(message, new Byte(expected), new Byte(actual));
+ }
+ /**
+ * Asserts that two bytes are equal.
+ */
+ static public void assertEquals(byte expected, byte actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two chars are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, char expected, char actual) {
+ assertEquals(message, new Character(expected), new Character(actual));
+ }
+ /**
+ * Asserts that two chars are equal.
+ */
+ static public void assertEquals(char expected, char actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two shorts are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, short expected, short actual) {
+ assertEquals(message, new Short(expected), new Short(actual));
+ }
+ /**
+ * Asserts that two shorts are equal.
+ */
+ static public void assertEquals(short expected, short actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two ints are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, int expected, int actual) {
+ assertEquals(message, new Integer(expected), new Integer(actual));
+ }
+ /**
+ * Asserts that two ints are equal.
+ */
+ static public void assertEquals(int expected, int actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that an object isn't null.
+ */
+ static public void assertNotNull(Object object) {
+ assertNotNull(null, object);
+ }
+ /**
+ * Asserts that an object isn't null. If it is
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+ /**
+ * Asserts that an object is null. If it isn't an {@link AssertionError} is
+ * thrown.
+ * Message contains: Expected: <null> but was: object
+ *
+ * @param object
+ * Object to check or <code>null</code>
+ */
+ static public void assertNull(Object object) {
+ String message = "Expected: <null> but was: " + String.valueOf(object);
+ assertNull(message, object);
+ }
+ /**
+ * Asserts that an object is null. If it is not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNull(String message, Object object) {
+ assertTrue(message, object == null);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ return;
+ failNotSame(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertSame(Object expected, Object actual) {
+ assertSame(null, expected, actual);
+ }
+ /**
+ * Asserts that two objects do not refer to the same object. If they do
+ * refer to the same object an AssertionFailedError is thrown with the
+ * given message.
+ */
+ static public void assertNotSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ failSame(message);
+ }
+ /**
+ * Asserts that two objects do not refer to the same object. If they do
+ * refer to the same object an AssertionFailedError is thrown.
+ */
+ static public void assertNotSame(Object expected, Object actual) {
+ assertNotSame(null, expected, actual);
+ }
- static private void failSame(String message) {
- String formatted= "";
- if (message != null)
- formatted= message+" ";
- fail(formatted+"expected not same");
- }
+ static public void failSame(String message) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected not same");
+ }
- static private void failNotSame(String message, Object expected, Object actual) {
- String formatted= "";
- if (message != null)
- formatted= message+" ";
- fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
- }
+ static public void failNotSame(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+ }
- static private void failNotEquals(String message, Object expected, Object actual) {
- fail(format(message, expected, actual));
- }
+ static public void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
- static String format(String message, Object expected, Object actual) {
- String formatted= "";
- if (message != null)
- formatted= message+" ";
- return formatted+"expected:<"+expected+"> but was:<"+actual+">";
- }
+ public static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null && message.length() > 0)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
}
diff --git a/test/082-inline-execute/src/junit/framework/AssertionFailedError.java b/test/082-inline-execute/src/junit/framework/AssertionFailedError.java
index e9cb3a3..0d7802c 100644
--- a/test/082-inline-execute/src/junit/framework/AssertionFailedError.java
+++ b/test/082-inline-execute/src/junit/framework/AssertionFailedError.java
@@ -3,11 +3,18 @@
/**
* Thrown when an assertion failed.
*/
-public class AssertionFailedError extends Error {
+public class AssertionFailedError extends AssertionError {
- public AssertionFailedError () {
- }
- public AssertionFailedError (String message) {
- super (message);
- }
-}
+ private static final long serialVersionUID= 1L;
+
+ public AssertionFailedError() {
+ }
+
+ public AssertionFailedError(String message) {
+ super(defaultString(message));
+ }
+
+ private static String defaultString(String message) {
+ return message == null ? "" : message;
+ }
+}
\ No newline at end of file
diff --git a/test/082-inline-execute/src/junit/framework/ComparisonCompactor.java b/test/082-inline-execute/src/junit/framework/ComparisonCompactor.java
new file mode 100644
index 0000000..e540f03
--- /dev/null
+++ b/test/082-inline-execute/src/junit/framework/ComparisonCompactor.java
@@ -0,0 +1,87 @@
+package junit.framework;
+
+// android-changed add @hide
+/**
+ * @hide not needed for public API
+ */
+public class ComparisonCompactor {
+
+ private static final String ELLIPSIS= "...";
+ private static final String DELTA_END= "]";
+ private static final String DELTA_START= "[";
+
+ private int fContextLength;
+ private String fExpected;
+ private String fActual;
+ private int fPrefix;
+ private int fSuffix;
+
+ public ComparisonCompactor(int contextLength, String expected, String actual) {
+ fContextLength= contextLength;
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ public String compact(String message) {
+ if (fExpected == null || fActual == null || areStringsEqual()) {
+ // android-changed use local method instead of Assert.format, since
+ // the later is not part of Android API till API 16
+ return format(message, fExpected, fActual);
+ }
+ findCommonPrefix();
+ findCommonSuffix();
+ String expected= compactString(fExpected);
+ String actual= compactString(fActual);
+ // android-changed use local format method
+ return format(message, expected, actual);
+ }
+
+ private String compactString(String source) {
+ String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+ if (fPrefix > 0)
+ result= computeCommonPrefix() + result;
+ if (fSuffix > 0)
+ result= result + computeCommonSuffix();
+ return result;
+ }
+
+ private void findCommonPrefix() {
+ fPrefix= 0;
+ int end= Math.min(fExpected.length(), fActual.length());
+ for (; fPrefix < end; fPrefix++) {
+ if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
+ break;
+ }
+ }
+
+ private void findCommonSuffix() {
+ int expectedSuffix= fExpected.length() - 1;
+ int actualSuffix= fActual.length() - 1;
+ for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+ if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
+ break;
+ }
+ fSuffix= fExpected.length() - expectedSuffix;
+ }
+
+ private String computeCommonPrefix() {
+ return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+ }
+
+ private String computeCommonSuffix() {
+ int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+ return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+ }
+
+ private boolean areStringsEqual() {
+ return fExpected.equals(fActual);
+ }
+
+ // android-changed copy of Assert.format for reasons described above
+ private static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null && message.length() > 0)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
+}
diff --git a/test/082-inline-execute/src/junit/framework/ComparisonFailure.java b/test/082-inline-execute/src/junit/framework/ComparisonFailure.java
index ccd476b..5077993 100644
--- a/test/082-inline-execute/src/junit/framework/ComparisonFailure.java
+++ b/test/082-inline-execute/src/junit/framework/ComparisonFailure.java
@@ -2,67 +2,51 @@
/**
* Thrown when an assert equals for Strings failed.
- *
+ *
* Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
*/
public class ComparisonFailure extends AssertionFailedError {
- private String fExpected;
- private String fActual;
+ private static final int MAX_CONTEXT_LENGTH= 20;
+ private static final long serialVersionUID= 1L;
+
+ private String fExpected;
+ private String fActual;
- /**
- * Constructs a comparison failure.
- * @param message the identifying message or null
- * @param expected the expected string value
- * @param actual the actual string value
- */
- public ComparisonFailure (String message, String expected, String actual) {
- super (message);
- fExpected= expected;
- fActual= actual;
- }
-
- /**
- * Returns "..." in place of common prefix and "..." in
- * place of common suffix between expected and actual.
- *
- * @see java.lang.Throwable#getMessage()
- */
- public String getMessage() {
- if (fExpected == null || fActual == null)
- return Assert.format(super.getMessage(), fExpected, fActual);
-
- int end= Math.min(fExpected.length(), fActual.length());
-
- int i= 0;
- for (; i < end; i++) {
- if (fExpected.charAt(i) != fActual.charAt(i))
- break;
- }
- int j= fExpected.length()-1;
- int k= fActual.length()-1;
- for (; k >= i && j >= i; k--,j--) {
- if (fExpected.charAt(j) != fActual.charAt(k))
- break;
- }
- String actual, expected;
-
- // equal strings
- if (j < i && k < i) {
- expected= fExpected;
- actual= fActual;
- } else {
- expected= fExpected.substring(i, j+1);
- actual= fActual.substring(i, k+1);
- if (i <= end && i > 0) {
- expected= "..."+expected;
- actual= "..."+actual;
- }
-
- if (j < fExpected.length()-1)
- expected= expected+"...";
- if (k < fActual.length()-1)
- actual= actual+"...";
- }
- return Assert.format(super.getMessage(), expected, actual);
- }
-}
+ /**
+ * Constructs a comparison failure.
+ * @param message the identifying message or null
+ * @param expected the expected string value
+ * @param actual the actual string value
+ */
+ public ComparisonFailure (String message, String expected, String actual) {
+ super (message);
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ /**
+ * Returns "..." in place of common prefix and "..." in
+ * place of common suffix between expected and actual.
+ *
+ * @see Throwable#getMessage()
+ */
+ @Override
+ public String getMessage() {
+ return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+ }
+
+ /**
+ * Gets the actual string value
+ * @return the actual string value
+ */
+ public String getActual() {
+ return fActual;
+ }
+ /**
+ * Gets the expected string value
+ * @return the expected string value
+ */
+ public String getExpected() {
+ return fExpected;
+ }
+}
\ No newline at end of file
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 911abdf..92a4ec2 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -21,21 +21,30 @@
RUN="${RUN/push-and-run-prebuilt-test-jar/push-and-run-test-jar}"
if [ $(basename $RUN) == 'host-run-test-jar' ]; then
- BPATH="--runtime-option -Xbootclasspath:$ANDROID_HOST_OUT/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/javalib.jar"
- # Remove prebuild from the flags, this test is for testing not having oat files.
- flags="${flags/--prebuild/}"
+ framework="${ANDROID_HOST_OUT}/framework"
+ bpath_suffix="-hostdex"
+ # Remove prebuild from the flags, this test is for testing not having oat files.
+ flags="${flags/--prebuild/}"
else
- BPATH="--runtime-option -Xbootclasspath:/system/framework/core-libart.jar"
+ framework="/system/framework"
+ bpath_suffix=""
fi
+bpath="${framework}/core-libart${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/core-junit${bpath_suffix}.jar"
+bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
+bpath_arg="--runtime-option -Xbootclasspath:${bpath}"
+
# Make sure we can run without an oat file,
echo "Run -Xnoimage-dex2oat"
-${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
# Make sure we can run with the oat file.
echo "Run -Ximage-dex2oat"
-${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat
+${RUN} ${flags} ${bpath_arg} --runtime-option -Ximage-dex2oat
# Make sure we can run with the default settings.
echo "Run default"
-${RUN} ${flags} ${BPATH}
+${RUN} ${flags} ${bpath_arg}
diff --git a/test/121-simple-suspend-check/expected.txt b/test/121-simple-suspend-check/expected.txt
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/test/121-simple-suspend-check/expected.txt
@@ -0,0 +1 @@
+PASS
diff --git a/test/121-simple-suspend-check/info.txt b/test/121-simple-suspend-check/info.txt
new file mode 100644
index 0000000..61611f9
--- /dev/null
+++ b/test/121-simple-suspend-check/info.txt
@@ -0,0 +1 @@
+Simple test to ensure the compiler emits suspend checks on loops.
diff --git a/test/121-simple-suspend-check/src/Main.java b/test/121-simple-suspend-check/src/Main.java
new file mode 100644
index 0000000..80daf37
--- /dev/null
+++ b/test/121-simple-suspend-check/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String args[]) {
+ SpinThread thread = new SpinThread();
+ thread.setDaemon(true);
+ thread.start();
+ Runtime.getRuntime().gc();
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException ie) {/*ignore */}
+ Runtime.getRuntime().gc();
+ System.out.println("PASS");
+ }
+}
+
+class SpinThread extends Thread {
+ public void run() {
+ while (true) {}
+ }
+}
diff --git a/test/122-npe/expected.txt b/test/122-npe/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/122-npe/expected.txt
diff --git a/test/122-npe/info.txt b/test/122-npe/info.txt
new file mode 100644
index 0000000..eef46d8
--- /dev/null
+++ b/test/122-npe/info.txt
@@ -0,0 +1 @@
+Test that our NPE checks and stack traces work.
diff --git a/test/122-npe/src/Main.java b/test/122-npe/src/Main.java
new file mode 100644
index 0000000..2fdcb9c
--- /dev/null
+++ b/test/122-npe/src/Main.java
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ */
+
+/**
+ * Test that null pointer exceptions are thrown by the VM.
+ */
+public class Main {
+ private int f;
+ public static void main(String[] args) {
+ methodOne();
+ }
+
+ static void methodOne() {
+ methodTwo();
+ }
+
+ private int callSpecial() {
+ return f;
+ }
+
+ final int callFinal() {
+ return f;
+ }
+
+ static void methodTwo() {
+ NullPointerException npe = null;
+
+ int thisLine = 41;
+
+ new Object().getClass(); // Ensure compiled.
+ try {
+ ((Object) null).getClass();
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 4);
+
+ new Main().callSpecial(); // Ensure compiled.
+ try {
+ ((Main) null).callSpecial(); // Test invokespecial.
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 8);
+
+ new Main().callFinal(); // Ensure compiled.
+ try {
+ ((Main) null).callFinal(); // Test invokevirtual on final.
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 8);
+
+ try {
+ ((Value) null).objectField.toString();
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).intField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useFloat(((Value) null).floatField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useLong(((Value) null).longField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useDouble(((Value) null).doubleField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).objectField = "Fisk";
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).intField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).floatField = 42.0F;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).longField = 42L;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).doubleField = 42.0d;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).byteField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ if (((Value) null).booleanField) { }
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).charField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Value) null).shortField);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).byteField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).booleanField = true;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).charField = '\u0042';
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Value) null).shortField = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Object[]) null)[0].toString();
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((int[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useFloat(((float[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useLong(((long[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useDouble(((double[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((Object[]) null)[0] = "Fisk";
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((int[]) null)[0] = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((float[]) null)[0] = 42.0F;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((long[]) null)[0] = 42L;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((double[]) null)[0] = 42.0d;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((byte[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ if (((boolean[]) null)[0]) { }
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((char[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((short[]) null)[0]);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((byte[]) null)[0] = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((boolean[]) null)[0] = true;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((char[]) null)[0] = '\u0042';
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ ((short[]) null)[0] = 42;
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((Object[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((int[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((float[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((long[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((double[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((byte[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((boolean[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((char[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ useInt(((short[]) null).length);
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 7);
+
+ try {
+ Interface i = null;
+ i.methodInterface(); // Test null on invokeinterface.
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 8);
+
+ try {
+ Object o = null;
+ o.toString(); // Test null on invokevirtual.
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 8);
+
+ npe = null;
+ try {
+ String s = null;
+ try {
+ throw new AssertionError();
+ } finally {
+ // Cause an implicit NPE.
+ s.getClass();
+ }
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 13);
+
+ npe = null;
+ try {
+ String s = null;
+ try {
+ throw new AssertionError();
+ } catch (AssertionError ex) {
+ }
+ s.getClass();
+ } catch (NullPointerException e) {
+ npe = e;
+ }
+ check(npe, thisLine += 14);
+ }
+
+ static void check(NullPointerException npe, int firstLine) {
+ final boolean debug = false;
+ if (debug) {
+ System.out.print("Got to line ");
+ System.out.print(firstLine);
+ System.out.println();
+ }
+ StackTraceElement[] trace = npe.getStackTrace();
+ checkElement(trace[0], "Main", "methodTwo", "Main.java", firstLine);
+ checkElement(trace[1], "Main", "methodOne", "Main.java", 27);
+ checkElement(trace[2], "Main", "main", "Main.java", 23);
+ }
+
+ static void checkElement(StackTraceElement element,
+ String declaringClass, String methodName,
+ String fileName, int lineNumber) {
+ assertEquals(declaringClass, element.getClassName());
+ assertEquals(methodName, element.getMethodName());
+ assertEquals(fileName, element.getFileName());
+ assertEquals(lineNumber, element.getLineNumber());
+ }
+
+ static void assertEquals(Object expected, Object actual) {
+ if (!expected.equals(actual)) {
+ String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+ throw new AssertionError(msg);
+ }
+ }
+
+ static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+
+ interface Interface {
+ void methodInterface();
+ }
+
+ static void useInt(int i) {
+ }
+
+ static void useFloat(float f) {
+ }
+
+ static void useDouble(double d) {
+ }
+
+ static void useLong(long l) {
+ }
+
+ static class Value {
+ Object objectField;
+ int intField;
+ float floatField; long longField;
+ double doubleField;
+ byte byteField;
+ boolean booleanField;
+ char charField;
+ short shortField;
+ }
+}
diff --git a/test/123-compiler-regressions-mt/expected.txt b/test/123-compiler-regressions-mt/expected.txt
new file mode 100644
index 0000000..a11e5bf
--- /dev/null
+++ b/test/123-compiler-regressions-mt/expected.txt
@@ -0,0 +1,2 @@
+b17689750TestVolatile passed.
+b17689750TestMonitor passed.
diff --git a/test/123-compiler-regressions-mt/info.txt b/test/123-compiler-regressions-mt/info.txt
new file mode 100644
index 0000000..cac7e75
--- /dev/null
+++ b/test/123-compiler-regressions-mt/info.txt
@@ -0,0 +1,6 @@
+This is a test for bad optimizations affecting multi-threaded program
+behavior.
+
+This test covers fixed AOT/JIT bugs to prevent regressions.
+
+17689750 GVN assigns the same value names across MONITOR_ENTER and volatile reads.
diff --git a/test/123-compiler-regressions-mt/src/Main.java b/test/123-compiler-regressions-mt/src/Main.java
new file mode 100644
index 0000000..11fa021
--- /dev/null
+++ b/test/123-compiler-regressions-mt/src/Main.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 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.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Test for Jit regressions.
+ */
+public class Main {
+ public static void main(String args[]) throws Exception {
+ b17689750TestVolatile();
+ b17689750TestMonitor();
+ }
+
+ static void b17689750TestVolatile() {
+ final B17689750TestVolatile test = new B17689750TestVolatile();
+ new Thread() {
+ public void run() {
+ test.thread1();
+ }
+ }.start();
+ try {
+ test.thread2();
+ } catch (NullPointerException expected) {
+ System.out.println("b17689750TestVolatile passed.");
+ }
+ }
+
+ static void b17689750TestMonitor() {
+ final B17689750TestMonitor test = new B17689750TestMonitor();
+ new Thread() {
+ public void run() {
+ test.thread1();
+ }
+ }.start();
+ try {
+ test.thread2();
+ } catch (NullPointerException expected) {
+ System.out.println("b17689750TestMonitor passed.");
+ }
+ }
+}
+
+class B17689750TestVolatile {
+ private volatile int state = 0;
+ private int[] values = { 42 };
+
+ void thread1() {
+ while (state != 1) { } // Busy loop.
+ values = null;
+ state = 2;
+ }
+
+ void thread2() {
+ int[] vs1 = values;
+ state = 1;
+ while (state != 2) { } // Busy loop.
+ int[] vs2 = values;
+ int v1 = vs1[0];
+ int v2 = vs2[0];
+ System.out.println("b17689750TestVolatile failed: " + v1 + ", " + v2);
+ }
+}
+
+class B17689750TestMonitor {
+ private int state = 0;
+ private Object lock = new Object();
+ private int[] values = { 42 };
+
+ void thread1() {
+ int s;
+ do {
+ synchronized (lock) {
+ s = state;
+ }
+ } while (s != 1); // Busy loop.
+
+ synchronized (lock) {
+ values = null;
+ state = 2;
+ }
+ }
+
+ void thread2() {
+ int[] vs1;
+ synchronized (lock) {
+ vs1 = values;
+ state = 1;
+ }
+
+ int s;
+ do {
+ synchronized (lock) {
+ s = state;
+ }
+ } while (s != 2); // Busy loop.
+
+ int[] vs2 = values;
+ int v1 = vs1[0];
+ int v2 = vs2[0];
+ System.out.println("b17689750TestMonitor failed: " + v1 + ", " + v2);
+ }
+}
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index 2c6d1c2..07c407b 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -97,6 +97,11 @@
if (exception == null) {
throw new Error("Missing NullPointerException");
}
+
+ result = $opt$InvokeVirtualMethod();
+ if (result != 42) {
+ throw new Error("Unexpected result: " + result);
+ }
}
public static void invokePrivate() {
@@ -205,5 +210,13 @@
m.o = new Main();
}
+ public static int $opt$InvokeVirtualMethod() {
+ return new Main().virtualMethod();
+ }
+
+ public int virtualMethod() {
+ return 42;
+ }
+
Object o;
}
diff --git a/test/407-arrays/src/Main.java b/test/407-arrays/src/Main.java
index 5d27e6d..b5e95b0 100644
--- a/test/407-arrays/src/Main.java
+++ b/test/407-arrays/src/Main.java
@@ -57,7 +57,7 @@
int[] ints, Object[] objects, long[] longs, int index) {
bools[0] = true;
assertEquals(true, bools[0]);
- bools[1] = true;
+ bools[index] = true;
assertEquals(true, bools[index]);
bytes[0] = -4;
diff --git a/test/408-move-bug/expected.txt b/test/408-move-bug/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/408-move-bug/expected.txt
diff --git a/test/408-move-bug/info.txt b/test/408-move-bug/info.txt
new file mode 100644
index 0000000..27a3dbc
--- /dev/null
+++ b/test/408-move-bug/info.txt
@@ -0,0 +1,2 @@
+Regression test for the register allocator in the optimizing
+compiler. Input moves where being overridden by sibling moves.
diff --git a/test/408-move-bug/src/Main.java b/test/408-move-bug/src/Main.java
new file mode 100644
index 0000000..420298b
--- /dev/null
+++ b/test/408-move-bug/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ public static void main(String[] args) {
+ crash();
+ npe();
+ }
+
+ static void crash() {
+ boolean b = baz();
+ // Create many objects to starve registers.
+ Main foo1 = create();
+ Main foo2 = create();
+ Main foo3 = create();
+ Main foo4 = create();
+ foo1.otherField = null;
+ // On X86, we would force b to be in a byte register, which
+ // would generate moves. This code exposed a bug in the
+ // register allocator, where an input move was not just before
+ // the instruction itself, and its destination was overridden
+ // by another value.
+ foo1.field = b;
+ foo2.field = b;
+ foo3.field = b;
+ foo4.field = b;
+ foo1.lastField = b;
+ }
+
+ // Similar to `crash` but generated an NPE.
+ static void npe() {
+ boolean b = baz();
+ Main foo1 = create();
+ Main foo2 = create();
+ Main foo3 = create();
+ Main foo4 = create();
+ foo1.field = b;
+ foo2.field = b;
+ foo3.field = b;
+ foo4.field = b;
+ foo1.lastField = b;
+ }
+
+ static Main create() {
+ return new Main();
+ }
+
+ static boolean baz() {
+ return false;
+ }
+
+ boolean field;
+ Object otherField;
+ boolean lastField;
+}
diff --git a/test/409-materialized-condition/expected.txt b/test/409-materialized-condition/expected.txt
new file mode 100644
index 0000000..a0796cd
--- /dev/null
+++ b/test/409-materialized-condition/expected.txt
@@ -0,0 +1,5 @@
+foo1
+In do nothing.
+In if.
+foo2
+In do nothing.
diff --git a/test/409-materialized-condition/info.txt b/test/409-materialized-condition/info.txt
new file mode 100644
index 0000000..898560d
--- /dev/null
+++ b/test/409-materialized-condition/info.txt
@@ -0,0 +1 @@
+Test that materialized conditions are evaluated correctly.
diff --git a/test/409-materialized-condition/src/Main.java b/test/409-materialized-condition/src/Main.java
new file mode 100644
index 0000000..0c179a9
--- /dev/null
+++ b/test/409-materialized-condition/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ public static void doNothing(boolean b) {
+ System.out.println("In do nothing.");
+ }
+
+ public static void inIf() {
+ System.out.println("In if.");
+ }
+
+ public static int bar() {
+ return 42;
+ }
+
+ public static int foo1() {
+ int b = bar();
+ doNothing(b == 42);
+ // This second `b == 42` will be GVN'ed away.
+ if (b == 42) {
+ inIf();
+ return b;
+ }
+ return 0;
+ }
+
+ public static int foo2() {
+ int b = bar();
+ doNothing(b == 41);
+ // This second `b == 41` will be GVN'ed away.
+ if (b == 41) {
+ inIf();
+ return 0;
+ }
+ return b;
+ }
+
+ public static void main(String[] args) {
+ System.out.println("foo1");
+ int res = foo1();
+ if (res != 42) {
+ throw new Error("Unexpected return value for foo1: " + res + ", expected 42.");
+ }
+
+ System.out.println("foo2");
+ res = foo2();
+ if (res != 42) {
+ throw new Error("Unexpected return value for foo2: " + res + ", expected 42.");
+ }
+ }
+}
diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build
new file mode 100644
index 0000000..eacf730
--- /dev/null
+++ b/test/702-LargeBranchOffset/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of source files.
+cpp -P src/Main.java.in src/Main.java
+
+mkdir classes
+${JAVAC} -d classes src/*.java
+
+${DX} --debug --dex --output=classes.dex classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/702-LargeBranchOffset/expected.txt b/test/702-LargeBranchOffset/expected.txt
new file mode 100644
index 0000000..130678f
--- /dev/null
+++ b/test/702-LargeBranchOffset/expected.txt
@@ -0,0 +1,5 @@
+0
+0
+2
+1
+512
diff --git a/test/702-LargeBranchOffset/info.txt b/test/702-LargeBranchOffset/info.txt
new file mode 100644
index 0000000..747263e
--- /dev/null
+++ b/test/702-LargeBranchOffset/info.txt
@@ -0,0 +1 @@
+Simple test to check if large branch offset works correctly.
diff --git a/test/702-LargeBranchOffset/src/Main.java.in b/test/702-LargeBranchOffset/src/Main.java.in
new file mode 100644
index 0000000..270d766
--- /dev/null
+++ b/test/702-LargeBranchOffset/src/Main.java.in
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#define DO_2_TIMES(x) x x
+#define DO_4_TIMES(x) DO_2_TIMES(DO_2_TIMES(x))
+#define DO_16_TIMES(x) DO_4_TIMES(DO_4_TIMES(x))
+#define DO_256_TIMES(x) DO_16_TIMES(DO_16_TIMES(x))
+#define DO_512_TIMES(x) DO_256_TIMES(DO_2_TIMES(x))
+
+
+public class Main {
+ public static void main(String[] args) {
+ Main m = new Main();
+ System.out.println(m.foo(-1, -1));
+ System.out.println(m.foo(-1, +1));
+ System.out.println(m.foo(+1, -1));
+ System.out.println(m.foo(+1, +1));
+ System.out.println(m.value);
+ }
+
+ public int foo(int a, int b) {
+ if ( a >= 0 ) {
+ if ( b < 0 ) {
+ DO_512_TIMES( synchronized(lock) { value++; } )
+ return 2;
+ }
+ return 1;
+ }
+ return 0;
+ }
+
+ Object lock = new Object();
+ int value = 0;
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 4ff6c65..302db38 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -21,140 +21,8 @@
TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*)
TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS))
-# List all the test names for host and target and compiler variants.
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): undefined, -trace, -gcverify or -gcstress
-# $(3): -relocate, -norelocate, -no-prebuild, or undefined.
-define all-run-test-names
- test-art-host-run-test$(2)-default$(3)-$(1)32 \
- test-art-host-run-test$(2)-optimizing$(3)-$(1)32 \
- test-art-host-run-test$(2)-interpreter$(3)-$(1)32 \
- test-art-host-run-test$(2)-default$(3)-$(1)64 \
- test-art-host-run-test$(2)-optimizing$(3)-$(1)64 \
- test-art-host-run-test$(2)-interpreter$(3)-$(1)64 \
- test-art-target-run-test$(2)-default$(3)-$(1)32 \
- test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
- test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
- test-art-target-run-test$(2)-default$(3)-$(1)64 \
- test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
- test-art-target-run-test$(2)-interpreter$(3)-$(1)64
-endef # all-run-test-names
-
-# Subset of the above for target only.
-define all-run-test-target-names
- test-art-target-run-test$(2)-default$(3)-$(1)32 \
- test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
- test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
- test-art-target-run-test$(2)-default$(3)-$(1)64 \
- test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
- test-art-target-run-test$(2)-interpreter$(3)-$(1)64
-endef # all-run-test-target-names
-
-# Tests that are timing sensitive and flaky on heavily loaded systems.
-TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
- 053-wait-some \
- 055-enum-performance
-
- # disable timing sensitive tests on "dist" builds.
-ifdef dist_goal
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-relocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-norelocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
-endif
-
-# Tests that are broken in --trace mode.
-TEST_ART_BROKEN_TRACE_RUN_TESTS :=
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
-
-# Tests that need more than 2MB of RAM or are running into other corner cases in GC stress related
-# to OOMEs.
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
-
-# 115-native-bridge setup is complicated. Need to implement it correctly for the target.
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-relocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-norelocate)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,,-no-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-trace,-no-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcverify,-no-prebuild)
-ART_TEST_KNOWN_BROKEN += $(call all-run-test-target-names,115-native-bridge,-gcstress,-no-prebuild)
-
-# NB 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
-# On host this is patched around by changing a run flag but we cannot do this on the target due to
-# a different run-script.
-TEST_ART_TARGET_BROKEN_PREBUILD_RUN_TESTS := \
- 116-nodex2oat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-trace,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcverify,-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TARGET_PREBUILD_RUN_TESTS), $(call all-run-test-target-names,$(test),-gcstress,-prebuild))
-
-# NB 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without --prebuild --relocate
-TEST_ART_BROKEN_RELOCATE_TESTS := \
- 117-nopatchoat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_RELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
-
-TEST_ART_BROKEN_NORELOCATE_TESTS := \
- 117-nopatchoat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NORELOCATE_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
-
-TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
- 117-nopatchoat
-
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
+########################################################################
+# The art-run-tests module, used to build all run-tests into an image.
# The path where build only targets will be output, e.g.
# out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA
@@ -189,132 +57,227 @@
include $(BUILD_PHONY_PACKAGE)
# Clear temp vars.
-all-run-test-names :=
art_run_tests_dir :=
define-build-art-run-test :=
TEST_ART_RUN_TEST_BUILD_RULES :=
-TEST_ART_TIMING_SENSITIVE_RUN_TESTS :=
-TEST_ART_BROKEN_TRACE_RUN_TESTS :=
-TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
########################################################################
+# General rules to build and run a run-test.
-ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+# Test rule names or of the form:
+# test-art-{1: host or target}-run-test-{2: prebuild no-prebuild no-dex2oat}-
+# {3: interpreter default optimizing}-{4: relocate no-relocate relocate-no-patchoat}-
+# {5: trace or no-trace}-{6: gcstress gcverify cms}-{7: forcecopy checkjni jni}-
+# {8: no-image or image}-{9: test name}{10: 32 or 64}
+TARGET_TYPES := host target
+PREBUILD_TYPES := prebuild
+ifeq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true)
+ PREBUILD_TYPES += no-prebuild
+endif
+ifeq ($(ART_TEST_RUN_TEST_NO_DEX2OAT),true)
+ PREBUILD_TYPES += no-dex2oat
+endif
+COMPILER_TYPES :=
+ifeq ($(ART_TEST_DEFAULT_COMPILER),true)
+ COMPILER_TYPES += default
+endif
+ifeq ($(ART_TEST_INTERPRETER),true)
+ COMPILER_TYPES += interpreter
+endif
+ifeq ($(ART_TEST_OPTIMIZING),true)
+ COMPILER_TYPES += optimizing
+endif
+RELOCATE_TYPES := relocate
+ifeq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
+ RELOCATE_TYPES += no-relocate
+endif
+ifeq ($(ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT),true)
+ RELOCATE_TYPES := relocate-no-patchoat
+endif
+TRACE_TYPES := no-trace
+ifeq ($(ART_TEST_TRACE),true)
+ TRACE_TYPES += trace
+endif
+GC_TYPES := cms
+ifeq ($(ART_TEST_GC_STRESS),true)
+ GC_TYPES += gcstress
+endif
+ifeq ($(ART_TEST_GC_VERIFY),true)
+ GC_TYPES += gcverify
+endif
+JNI_TYPES := checkjni
+ifeq ($(ART_TEST_JNI_FORCECOPY),true)
+ JNI_TYPES += forcecopy
+endif
+IMAGE_TYPES := image
+ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true)
+ IMAGE_TYPES += no-image
+endif
+ADDRESS_SIZES_TARGET := $(ART_PHONY_TEST_TARGET_SUFFIX) $(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+ADDRESS_SIZES_HOST := $(ART_PHONY_TEST_HOST_SUFFIX) $(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+ALL_ADDRESS_SIZES := 64 32
+
+# List all run test names with number arguments agreeing with the comment above.
+define all-run-test-names
+ $(foreach target, $(1), \
+ $(foreach prebuild, $(2), \
+ $(foreach compiler, $(3), \
+ $(foreach relocate, $(4), \
+ $(foreach trace, $(5), \
+ $(foreach gc, $(6), \
+ $(foreach jni, $(7), \
+ $(foreach image, $(8), \
+ $(foreach test, $(9), \
+ $(foreach address_size, $(10), \
+ test-art-$(target)-run-test-$(prebuild)-$(compiler)-$(relocate)-$(trace)-$(gc)-$(jni)-$(image)-$(test)$(address_size) \
+ ))))))))))
+endef # all-run-test-names
+
+# To generate a full list or tests:
+# $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \
+# $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+# $(TEST_ART_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+# Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE
+define name-to-var
+$(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
+endef # name-to-var
+
+# Tests that are timing sensitive and flaky on heavily loaded systems.
+TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
+ 053-wait-some \
+ 055-enum-performance
+
+ # disable timing sensitive tests on "dist" builds.
+ifdef dist_goal
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_TIMING_SENSITIVE_RUN_TESTS :=
+
+TEST_ART_BROKEN_RUN_TESTS := \
+ 004-ThreadStress
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(TEST_ART_BROKEN_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_RUN_TESTS :=
+
+# Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild.
+TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \
+ 116-nodex2oat
+
+ifneq (,$(filter prebuild,$(PREBUILD_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),prebuild, \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(TEST_ART_BROKEN_PREBUILD_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_PREBUILD_RUN_TESTS :=
+
+TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
+ 117-nopatchoat
+
+ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),no-prebuild, \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_NO_PREBUILD_TESTS :=
+
+# Note 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without
+# --prebuild --relocate
+TEST_ART_BROKEN_NO_RELOCATE_TESTS := \
+ 117-nopatchoat
+
+ifneq (,$(filter no-relocate,$(RELOCATE_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES), no-relocate,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(TEST_ART_BROKEN_NO_RELOCATE_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_NO_RELOCATE_TESTS :=
+
+# Tests that are broken with GC stress.
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
+ 004-SignalTest
+
+ifneq (,$(filter gcstress,$(GC_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),gcstress,$(JNI_TYPES), \
+ $(IMAGE_TYPES), $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
+
+# 115-native-bridge setup is complicated. Need to implement it correctly for the target.
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(PREBUILD_TYPES),$(COMPILER_TYPES), \
+ $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),115-native-bridge, \
+ $(ALL_ADDRESS_SIZES))
+
+# All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
+# Therefore we shouldn't run them in situations where we actually don't have these since they
+# explicitly test for them. These all also assume we have an image.
+TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
+ 116-nodex2oat \
+ 117-nopatchoat \
+ 118-noimage-dex2oat \
+ 119-noimage-patchoat
+
+ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),no-dex2oat, \
+ $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+ $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+
+ifneq (,$(filter no-image,$(IMAGE_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),no-image, \
+ $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+ifneq (,$(filter relocate-no-patchoat,$(RELOCATE_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(PREBUILD_TYPES), \
+ $(COMPILER_TYPES), relocate-no-patchoat,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),$(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_FALLBACK_RUN_TESTS :=
+
+# Clear variables ahead of appending to them when defining tests.
+$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach prebuild, $(PREBUILD_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach compiler, $(COMPILER_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach relocate, $(RELOCATE_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach trace, $(TRACE_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach gc, $(GC_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach jni, $(JNI_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach image, $(IMAGE_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach test, $(TEST_ART_RUN_TESTS), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach address_size, $(ALL_ADDRESS_SIZES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=)))
# We need dex2oat and dalvikvm on the target as well as the core image.
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
@@ -347,659 +310,265 @@
$(2ND_HOST_CORE_IMG_OUT)
endif
-# For a given test create all the combinations of host/target, compiler and suffix such as:
-# test-art-host-run-test-optimizing-003-omnibus-opcodes32
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): host or target
-# $(3): default, optimizing or interpreter
-# $(4): 32 or 64
-# $(5): run tests with tracing or GC verification enabled or not: trace, gcverify or undefined
-# $(6): relocate, norelocate, no-prebuild or undefined.
+# Create a rule to build and run a tests following the form:
+# test-art-{1: host or target}-run-test-{2: prebuild no-prebuild no-dex2oat}-
+# {3: interpreter default optimizing}-{4: relocate no-relocate relocate-no-patchoat}-
+# {5: trace or no-trace}-{6: gcstress gcverify cms}-{7: forcecopy checkjni jni}-
+# {8: no-image image}-{9: test name}{10: 32 or 64}
define define-test-art-run-test
run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS))
- run_test_rule_name :=
- uc_host_or_target :=
prereq_rule :=
- skip_test := false
- uc_reloc_type :=
+ test_groups :=
+ uc_host_or_target :=
ifeq ($(ART_TEST_RUN_TEST_ALWAYS_CLEAN),true)
run_test_options += --always-clean
endif
- ifeq ($(2),host)
+ ifeq ($(1),host)
uc_host_or_target := HOST
+ test_groups := ART_RUN_TEST_HOST_RULES
run_test_options += --host
prereq_rule := $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES)
else
- ifeq ($(2),target)
+ ifeq ($(1),target)
uc_host_or_target := TARGET
+ test_groups := ART_RUN_TEST_TARGET_RULES
prereq_rule := test-art-target-sync
else
- $$(error found $(2) expected host or target)
+ $$(error found $(1) expected $(TARGET_TYPES))
endif
endif
- ifeq ($(6),relocate)
- uc_reloc_type := RELOCATE
- run_test_options += --relocate --no-prebuild
- ifneq ($(ART_TEST_RUN_TEST_RELOCATE),true)
- skip_test := true
- endif
+ ifeq ($(2),prebuild)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PREBUILD_RULES
+ run_test_options += --prebuild
else
- ifeq ($(6),no-prebuild)
- uc_reloc_type := NO_PREBUILD
- run_test_options += --no-relocate --no-prebuild
- ifneq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true)
- skip_test := true
- endif
+ ifeq ($(2),no-prebuild)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_PREBUILD_RULES
+ run_test_options += --no-prebuild
else
- ifeq ($(6),norelocate)
- uc_reloc_type := NORELOCATE
- run_test_options += --no-relocate --prebuild
- ifneq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
- skip_test := true
- endif
+ ifeq ($(2),no-dex2oat)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_DEX2OAT_RULES
+ run_test_options += --no-prebuild --no-dex2oat
else
- uc_reloc_type := PREBUILD
- run_test_options += --relocate --prebuild
- ifneq ($(ART_TEST_RUN_TEST_PREBUILD),true)
- skip_test := true
- endif
+ $$(error found $(2) expected $(PREBUILD_TYPES))
endif
endif
endif
- uc_compiler :=
ifeq ($(3),optimizing)
- uc_compiler := OPTIMIZING
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_OPTIMIZING_RULES
run_test_options += -Xcompiler-option --compiler-backend=Optimizing
- ifneq ($$(ART_TEST_OPTIMIZING),true)
- skip_test := true
- endif
else
ifeq ($(3),interpreter)
- uc_compiler := INTERPRETER
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_RULES
run_test_options += --interpreter
else
ifeq ($(3),default)
- uc_compiler := DEFAULT
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEFAULT_RULES
else
- $$(error found $(3) expected optimizing, interpreter or default)
+ $$(error found $(3) expected $(COMPILER_TYPES))
endif
endif
endif
- ifeq ($(4),64)
- run_test_options += --64
+ ifeq ($(4),relocate)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_RULES
+ run_test_options += --relocate
else
- ifneq ($(4),32)
- $$(error found $(4) expected 32 or 64)
+ ifeq ($(4),no-relocate)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_RELOCATE_RULES
+ run_test_options += --no-relocate
+ else
+ ifeq ($(4),relocate-no-patchoat)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_NO_PATCHOAT_RULES
+ run_test_options += --relocate --no-patchoat
+ else
+ $$(error found $(4) expected $(RELOCATE_TYPES))
+ endif
endif
endif
ifeq ($(5),trace)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_TRACE_RULES
run_test_options += --trace
- run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(6)-$(1)$(4)
- ifneq ($$(ART_TEST_TRACE),true)
- skip_test := true
- endif
else
- ifeq ($(5),gcverify)
- run_test_options += --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify \
- --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc
- run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(6)-$(1)$(4)
- ifneq ($$(ART_TEST_GC_VERIFY),true)
- skip_test := true
- endif
+ ifeq ($(5),no-trace)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_TRACE_RULES
else
- ifeq ($(5),gcstress)
- run_test_options += --runtime-option -Xgc:SS --runtime-option -Xms2m \
- --runtime-option -Xmx2m --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify
- run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(6)-$(1)$(4)
- ifneq ($$(ART_TEST_GC_STRESS),true)
- skip_test := true
- endif
+ $$(error found $(5) expected $(TRACE_TYPES))
+ endif
+ endif
+ ifeq ($(6),gcverify)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCVERIFY_RULES
+ run_test_options += --gcverify
+ else
+ ifeq ($(6),gcstress)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCSTRESS_RULES
+ run_test_options += --gcstress
+ else
+ ifeq ($(6),cms)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CMS_RULES
else
- ifneq (,$(5))
- $$(error found $(5) expected undefined or gcverify, gcstress or trace)
- endif
- run_test_rule_name := test-art-$(2)-run-test-$(3)-$(6)-$(1)$(4)
+ $$(error found $(6) expected $(GC_TYPES))
endif
endif
endif
- ifeq ($$(skip_test),false)
- run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
+ ifeq ($(7),forcecopy)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_FORCECOPY_RULES
+ run_test_options += --runtime-option -Xjniopts:forcecopy
+ ifneq ($$(ART_TEST_JNI_FORCECOPY),true)
+ skip_test := true
+ endif
+ else
+ ifeq ($(7),checkjni)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CHECKJNI_RULES
+ run_test_options += --runtime-option -Xcheck:jni
+ else
+ ifeq ($(7),jni)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_JNI_RULES
+ else
+ $$(error found $(7) expected $(JNI_TYPES))
+ endif
+ endif
+ endif
+ ifeq ($(8),no-image)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES
+ run_test_options += --no-image
+ else
+ ifeq ($(8),image)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES
+ else
+ $$(error found $(8) expected $(IMAGE_TYPES))
+ endif
+ endif
+ # $(9) is the test name
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_$(call name-to-var,$(9))_RULES
+ ifeq ($(10),64)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_64_RULES
+ run_test_options += --64
+ else
+ ifeq ($(10),32)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_32_RULES
+ else
+ $$(error found $(10) expected $(ALL_ADDRESS_SIZES))
+ endif
+ endif
+ run_test_rule_name := test-art-$(1)-run-test-$(2)-$(3)-$(4)-$(5)-$(6)-$(7)-$(8)-$(9)$(10)
+ run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
$$(run_test_options)
$$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
.PHONY: $$(run_test_rule_name)
$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $$(prereq_rule)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
- art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(1) \
+ art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(9) \
&& $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
$$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \
rm -r $(ART_HOST_TEST_DIR)) || true
- else
- .PHONY: $$(run_test_rule_name)
-$$(run_test_rule_name):
- endif
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)$(4)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$(1)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)$(4)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL$(4)_RULES += $$(run_test_rule_name)
+ $$(foreach test_group,$$(test_groups), $$(eval $$(value test_group) += $$(run_test_rule_name)))
# Clear locally defined variables.
- skip_test :=
+ uc_host_or_target :=
+ test_groups :=
run_test_options :=
run_test_rule_name :=
- uc_host_or_target :=
prereq_rule :=
- uc_reloc_type :=
- uc_compiler :=
endef # define-test-art-run-test
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach test, $(TEST_ART_RUN_TESTS), \
+ $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), \
+ $(foreach prebuild, $(PREBUILD_TYPES), \
+ $(foreach compiler, $(COMPILER_TYPES), \
+ $(foreach relocate, $(RELOCATE_TYPES), \
+ $(foreach trace, $(TRACE_TYPES), \
+ $(foreach gc, $(GC_TYPES), \
+ $(foreach jni, $(JNI_TYPES), \
+ $(foreach image, $(IMAGE_TYPES), \
+ $(eval $(call define-test-art-run-test,$(target),$(prebuild),$(compiler),$(relocate),$(trace),$(gc),$(jni),$(image),$(test),$(address_size))) \
+ ))))))))))
+define-test-art-run-test :=
+
# Define a phony rule whose purpose is to test its prerequisites.
-# $(1): rule name, e.g. test-art-host-run-test32
+# $(1): host or target
# $(2): list of prerequisites
-define define-test-art-run-test-group-rule
+define define-test-art-run-test-group
.PHONY: $(1)
$(1): $(2)
$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-endef # define-test-art-run-test-group-rule
-
-# Create rules for a group of run tests.
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): host or target
-# $(3): relocate, norelocate or no-prebuild, or prebuild.
-define define-test-art-run-test-group-type
- group_uc_host_or_target :=
- ifeq ($(2),host)
- group_uc_host_or_target := HOST
- else
- ifeq ($(2),target)
- group_uc_host_or_target := TARGET
- else
- $$(error found $(2) expected host or target)
- endif
- endif
-
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
- do_second := false
- ifeq ($(2),host)
- ifneq ($$(HOST_PREFER_32_BIT),true)
- do_second := true
- endif
- else
- ifdef TARGET_2ND_ARCH
- do_second := true
- endif
- endif
- ifeq (true,$$(do_second))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
- endif
-endef # define-test-art-run-test-group-type
-
-# Create rules for a group of run tests.
-# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): host or target
-define define-test-art-run-test-group
- group_uc_host_or_target :=
- ifeq ($(2),host)
- group_uc_host_or_target := HOST
- else
- ifeq ($(2),target)
- group_uc_host_or_target := TARGET
- else
- $$(error found $(2) expected host or target)
- endif
- endif
- do_second := false
- ifeq ($(2),host)
- ifneq ($$(HOST_PREFER_32_BIT),true)
- do_second := true
- endif
- else
- ifdef TARGET_2ND_ARCH
- do_second := true
- endif
- endif
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
- ifeq ($$(do_second),true)
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
- endif
- $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),prebuild))
- $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),norelocate))
- $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),relocate))
- $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),no-prebuild))
- $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \
- $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES)))
- $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-interpreter-$(1), \
- $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES)))
- $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-optimizing-$(1), \
- $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES)))
- $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1), \
- $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES)))
- $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX), \
- $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES)))
- ifeq ($$(do_second),true)
- $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX), \
- $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES)))
- endif
-
- # Clear locally defined variables.
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
- ifeq ($$(do_second),true)
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
- endif
- group_uc_host_or_target :=
- do_second :=
endef # define-test-art-run-test-group
-$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),target)))
-$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),host)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate, \
- $(ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate, \
- $(ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test, \
- $(ART_TEST_TARGET_RUN_TEST_ALL_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default, \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter, \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing, \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild, \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate, \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate, \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate, \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate, \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate, \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate, \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-ifdef TARGET_2ND_ARCH
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
- $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
-endif
+$(foreach target, $(TARGET_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test,$(ART_RUN_TEST_$(call name-to-var,$(target))_RULES))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach prebuild, $(PREBUILD_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(prebuild),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach compiler, $(COMPILER_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(compiler),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach relocate, $(RELOCATE_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(relocate),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach trace, $(TRACE_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(trace),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach gc, $(GC_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(gc),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach jni, $(JNI_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(jni),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach image, $(IMAGE_TYPES), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(image),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach test, $(TEST_ART_RUN_TESTS), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(test),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES)))))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), $(eval \
+ $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(address_size),$(ART_RUN_TEST_$(address_size)_RULES)))))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate, \
- $(ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate, \
- $(ART_TEST_HOST_RUN_TEST_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test, \
- $(ART_TEST_HOST_RUN_TEST_ALL_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default, \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter, \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing, \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild, \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate, \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate, \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate, \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate, \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate, \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate, \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-ifneq ($(HOST_PREFER_32_BIT),true)
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
- $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
- $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
-endif
-
-# include libarttest build rules.
-include $(LOCAL_PATH)/Android.libarttest.mk
-
-# Include libnativebridgetest build rules.
-include art/test/Android.libnativebridgetest.mk
-
-define-test-art-run-test :=
-define-test-art-run-test-group-rule :=
+# Clear variables now we're finished with them.
+$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach prebuild, $(PREBUILD_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach compiler, $(COMPILER_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach relocate, $(RELOCATE_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach trace, $(TRACE_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach gc, $(GC_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach jni, $(JNI_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach image, $(IMAGE_TYPES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach test, $(TEST_ART_RUN_TESTS), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES :=)))
+$(foreach target, $(TARGET_TYPES), \
+ $(foreach address_size, $(ALL_ADDRESS_SIZES), \
+ $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=)))
define-test-art-run-test-group :=
-TEST_ART_RUN_TESTS :=
-ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+TARGET_TYPES :=
+PREBUILD_TYPES :=
+COMPILER_TYPES :=
+RELOCATE_TYPES :=
+TRACE_TYPES :=
+GC_TYPES :=
+JNI_TYPES :=
+IMAGE_TYPES :=
+ADDRESS_SIZES_TARGET :=
+ADDRESS_SIZES_HOST :=
+ALL_ADDRESS_SIZES :=
+
+include $(LOCAL_PATH)/Android.libarttest.mk
+include art/test/Android.libnativebridgetest.mk
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 7253a2b..c020478 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Run the code in test.jar using the host-mode virtual machine. The jar should
# contain a top-level class named Main to run.
@@ -23,6 +23,12 @@
FLAGS=""
COMPILER_FLAGS=""
BUILD_BOOT_OPT=""
+PATCHOAT=""
+DEX2OAT=""
+FALSE_BIN="/bin/false"
+HAVE_IMAGE="y"
+TIME_OUT="y"
+TIME_OUT_VALUE=5m
exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
main="Main"
@@ -33,6 +39,12 @@
elif [ "x$1" = "x--prebuild" ]; then
PREBUILD="y"
shift
+ elif [ "x$1" = "x--no-dex2oat" ]; then
+ DEX2OAT="-Xcompiler:${FALSE_BIN}"
+ shift
+ elif [ "x$1" = "x--no-patchoat" ]; then
+ PATCHOAT="-Xpatchoat:${FALSE_BIN}"
+ shift
elif [ "x$1" = "x--lib" ]; then
shift
if [ "x$1" = "x" ]; then
@@ -44,6 +56,9 @@
LIB=${LIB/%so/dylib}
fi
shift
+ elif [ "x$1" = "x--no-image" ]; then
+ HAVE_IMAGE="n"
+ shift
elif [ "x$1" = "x--boot" ]; then
shift
option="$1"
@@ -52,10 +67,12 @@
shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"
+ TIME_OUT="n"
shift
elif [ "x$1" = "x--gdb" ]; then
GDB="y"
DEV_MODE="y"
+ TIME_OUT="n"
shift
elif [ "x$1" = "x--invoke-with" ]; then
shift
@@ -157,6 +174,11 @@
COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
fi
+if [ "$HAVE_IMAGE" = "n" ]; then
+ # Set image to a place were there isn't one.
+ BOOT_OPT="-Ximage:/system/non-existant/core.art"
+fi
+
if [ "$RELOCATE" = "y" ]; then
FLAGS="${FLAGS} -Xrelocate"
COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --include-patch-information"
@@ -180,7 +202,11 @@
fi
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar $main"
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $PATCHOAT $DEX2OAT $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar $main"
+if [ "$TIME_OUT" = "y" ]; then
+ # Add timeout command if time out is desired.
+ cmdline="timeout $TIME_OUT_VALUE $cmdline"
+fi
if [ "$DEV_MODE" = "y" ]; then
if [ "$PREBUILD" = "y" ]; then
echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
@@ -192,4 +218,20 @@
fi
cd $ANDROID_BUILD_TOP
-$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@"
+
+$mkdir_cmd || exit 1
+$prebuild_cmd || exit 2
+
+if [ "$GDB" = "y" ]; then
+ # When running under gdb, we cannot do piping and grepping...
+ LD_PRELOAD=libsigchain.so $cmdline "$@"
+else
+ # If we are execing /bin/false we might not be on the same ISA as libsigchain.so
+ # ld.so will helpfully warn us of this. Unfortunately this messes up our error
+ # checking so we will just filter out the error with a grep.
+ LD_PRELOAD=libsigchain.so $cmdline "$@" 2>&1 | grep -v -E "^ERROR: ld\.so: object '.+\.so' from LD_PRELOAD cannot be preloaded.*: ignored\.$"
+ # Add extra detail if time out is enabled.
+ if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "y" ]; then
+ echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
+ fi
+fi
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
index ad23edf..91b8a0f 100755
--- a/test/etc/push-and-run-prebuilt-test-jar
+++ b/test/etc/push-and-run-prebuilt-test-jar
@@ -26,6 +26,10 @@
TARGET_SUFFIX="32"
GDB_TARGET_SUFFIX=""
COMPILE_FLAGS=""
+FALSE_BIN="/system/bin/false"
+PATCHOAT=""
+DEX2OAT=""
+HAVE_IMAGE="y"
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -55,12 +59,21 @@
BOOT_OPT="$1"
BUILD_BOOT_OPT="--boot-image=${1#-Ximage:}"
shift
+ elif [ "x$1" = "x--no-dex2oat" ]; then
+ DEX2OAT="-Xcompiler:${FALSE_BIN}"
+ shift
+ elif [ "x$1" = "x--no-patchoat" ]; then
+ PATCHOAT="-Xpatchoat:${FALSE_BIN}"
+ shift
elif [ "x$1" = "x--relocate" ]; then
RELOCATE="y"
shift
elif [ "x$1" = "x--no-relocate" ]; then
RELOCATE="n"
shift
+ elif [ "x$1" = "x--no-image" ]; then
+ HAVE_IMAGE="n"
+ shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"
shift
@@ -136,6 +149,10 @@
msg "------------------------------"
+if [ "$HAVE_IMAGE" = "n" ]; then
+ BOOT_OPT="-Ximage:/system/non-existant/core.art"
+fi
+
ARCH=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
if [ x"$ARCH" = "x" ]; then
echo "Unable to determine architecture"
@@ -194,7 +211,7 @@
cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
$INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
- $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $PATCHOAT $DEX2OAT $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
echo "$cmdline" > $cmdfile
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index 06075c2..e398b5d 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -22,6 +22,10 @@
FLAGS=""
TARGET_SUFFIX="32"
GDB_TARGET_SUFFIX=""
+FALSE_BIN="/system/bin/false"
+PATCHOAT=""
+DEX2OAT=""
+HAVE_IMAGE="y"
while true; do
if [ "x$1" = "x--quiet" ]; then
@@ -40,6 +44,15 @@
option="$1"
FLAGS="${FLAGS} -Xcompiler-option $option"
shift
+ elif [ "x$1" = "x--no-image" ]; then
+ HAVE_IMAGE="n"
+ shift
+ elif [ "x$1" = "x--no-dex2oat" ]; then
+ DEX2OAT="-Xcompiler:${FALSE_BIN}"
+ shift
+ elif [ "x$1" = "x--no-patchoat" ]; then
+ PATCHOAT="-Xpatchoat:${FALSE_BIN}"
+ shift
elif [ "x$1" = "x--runtime-option" ]; then
shift
option="$1"
@@ -129,6 +142,10 @@
msg "------------------------------"
+if [ "$HAVE_IMAGE" = "n" ]; then
+ BOOT_OPT="-Ximage:/system/non-existant/core.art"
+fi
+
if [ "$QUIET" = "n" ]; then
adb shell rm -r $DEX_LOCATION
adb shell mkdir -p $DEX_LOCATION
@@ -172,7 +189,7 @@
fi
cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
- $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $PATCHOAT $DEX2OAT $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
if [ "$DEV_MODE" = "y" ]; then
echo $cmdline "$@"
fi
diff --git a/test/run-all-tests b/test/run-all-tests
index 284cca0..318a0de 100755
--- a/test/run-all-tests
+++ b/test/run-all-tests
@@ -80,6 +80,12 @@
elif [ "x$1" = "x--64" ]; then
run_args="${run_args} --64"
shift
+ elif [ "x$1" = "x--gcstress" ]; then
+ run_args="${run_args} --gcstress"
+ shift
+ elif [ "x$1" = "x--gcverify" ]; then
+ run_args="${run_args} --gcverify"
+ shift
elif [ "x$1" = "x--trace" ]; then
run_args="${run_args} --trace"
shift
@@ -95,6 +101,12 @@
elif [ "x$1" = "x--prebuild" ]; then
run_args="${run_args} --prebuild"
shift;
+ elif [ "x$1" = "x--no-dex2oat" ]; then
+ run_args="${run_args} --no-dex2oat"
+ shift;
+ elif [ "x$1" = "x--no-patchoat" ]; then
+ run_args="${run_args} --no-patchoat"
+ shift;
elif [ "x$1" = "x--always-clean" ]; then
run_args="${run_args} --always-clean"
elif expr "x$1" : "x--" >/dev/null 2>&1; then
@@ -116,7 +128,8 @@
"further documentation:"
echo " --debug --dev --host --interpreter --jvm --no-optimize"
echo " --no-verify -O --update --valgrind --zygote --64 --relocate"
- echo " --prebuild --always-clean"
+ echo " --prebuild --always-clean --gcstress --gcverify --trace"
+ echo " --no-patchoat --no-dex2oat"
echo " Specific Runtime Options:"
echo " --seq Run tests one-by-one, avoiding failures caused by busy CPU"
) 1>&2
diff --git a/test/run-test b/test/run-test
index 5d3cbac..b140fbf 100755
--- a/test/run-test
+++ b/test/run-test
@@ -77,7 +77,13 @@
build_only="no"
suffix64=""
trace="false"
+basic_verify="false"
+gc_verify="false"
+gc_stress="false"
always_clean="no"
+have_dex2oat="yes"
+have_patchoat="yes"
+have_image="yes"
while true; do
if [ "x$1" = "x--host" ]; then
@@ -96,6 +102,15 @@
lib="libdvm.so"
runtime="dalvik"
shift
+ elif [ "x$1" = "x--no-dex2oat" ]; then
+ have_dex2oat="no"
+ shift
+ elif [ "x$1" = "x--no-patchoat" ]; then
+ have_patchoat="no"
+ shift
+ elif [ "x$1" = "x--no-image" ]; then
+ have_image="no"
+ shift
elif [ "x$1" = "x--relocate" ]; then
relocate="yes"
shift
@@ -108,6 +123,14 @@
elif [ "x$1" = "x--no-prebuild" ]; then
prebuild_mode="no"
shift;
+ elif [ "x$1" = "x--gcverify" ]; then
+ basic_verify="true"
+ gc_verify="true"
+ shift
+ elif [ "x$1" = "x--gcstress" ]; then
+ basic_verify="true"
+ gc_stress="true"
+ shift
elif [ "x$1" = "x--image" ]; then
shift
image="$1"
@@ -202,9 +225,19 @@
# Cannot us a simple "cd", as the path might not be created yet.
# Cannot use readlink -m, as it does not exist on Mac.
# Fallback to nuclear option:
+noncanonical_tmp_dir=$tmp_dir
tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"`"
mkdir -p $tmp_dir
+if [ "$basic_verify" = "true" ]; then
+ run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify"
+fi
+if [ "$gc_verify" = "true" ]; then
+ run_args="${run_args} --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc"
+fi
+if [ "$gc_stress" = "true" ]; then
+ run_args="${run_args} --runtime-option -Xgc:SS --runtime-option -Xms2m --runtime-option -Xmx2m"
+fi
if [ "$trace" = "true" ]; then
run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin --runtime-option -Xmethod-trace-file-size:2000000"
fi
@@ -240,6 +273,14 @@
fi
fi
+if [ "$have_patchoat" = "no" ]; then
+ run_args="${run_args} --no-patchoat"
+fi
+
+if [ "$have_dex2oat" = "no" ]; then
+ run_args="${run_args} --no-dex2oat"
+fi
+
if [ ! "$runtime" = "jvm" ]; then
run_args="${run_args} --lib $lib"
fi
@@ -275,6 +316,30 @@
fi
fi
+if [ "$have_image" = "no" ]; then
+ if [ "$runtime" != "art" ]; then
+ echo "--no-image is only supported on the art runtime"
+ exit 1
+ fi
+ if [ "$target_mode" = "no" ]; then
+ framework="${ANDROID_HOST_OUT}/framework"
+ bpath_suffix="-hostdex"
+ else
+ framework="/system/framework"
+ bpath_suffix=""
+ fi
+ # TODO If the target was compiled WITH_DEXPREOPT=true then these tests will
+ # fail since these jar files will be stripped.
+ bpath="${framework}/core-libart${bpath_suffix}.jar"
+ bpath="${bpath}:${framework}/conscrypt${bpath_suffix}.jar"
+ bpath="${bpath}:${framework}/okhttp${bpath_suffix}.jar"
+ bpath="${bpath}:${framework}/core-junit${bpath_suffix}.jar"
+ bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
+ # Pass down the bootclasspath
+ run_args="${run_args} --runtime-option -Xbootclasspath:${bpath}"
+ run_args="${run_args} --no-image"
+fi
+
if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
echo "--dev and --update are mutually exclusive" 1>&2
usage="yes"
@@ -326,6 +391,8 @@
echo " --zygote Spawn the process from the Zygote." \
"If used, then the"
echo " other runtime options are ignored."
+ echo " --no-dex2oat Run as though dex2oat was failing."
+ echo " --no-patchoat Run as though patchoat was failing."
echo " --prebuild Run dex2oat on the files before starting test. (default)"
echo " --no-prebuild Do not run dex2oat on the files before starting"
echo " the test."
@@ -341,6 +408,8 @@
"files."
echo " --64 Run the test in 64-bit mode"
echo " --trace Run with method tracing"
+ echo " --gcstress Run with gc stress testing"
+ echo " --gcverify Run with gc verification"
echo " --always-clean Delete the test files even if the test fails."
) 1>&2
exit 1
@@ -452,6 +521,8 @@
"./${run}" $run_args "$@" >"$output" 2>&1
else
cp "$build_output" "$output"
+ echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`"
+ echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}"
echo "build exit status: $build_exit" >>"$output"
fi
./$check_cmd "$expected" "$output"
diff --git a/tools/Android.mk b/tools/Android.mk
index d3be17f..9a96f7a 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -27,3 +27,13 @@
@echo "Copy: $(PRIVATE_MODULE) ($@)"
$(copy-file-to-new-target)
$(hide) chmod 755 $@
+
+# Copy the art shell script to the target's bin directory
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := art
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
diff --git a/tools/art b/tools/art
old mode 100755
new mode 100644
index 85517d3..0103071
--- a/tools/art
+++ b/tools/art
@@ -14,8 +14,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-lib=-XXlib:libart.so
+function follow_links() {
+ if [ z"$BASH_SOURCE" != z ]; then
+ file="$BASH_SOURCE"
+ else
+ file="$0"
+ fi
+ while [ -h "$file" ]; do
+ # On Mac OS, readlink -f doesn't work.
+ file="$(readlink "$file")"
+ done
+ echo "$file"
+}
+
+function find_libdir() {
+ if [ "$(readlink "$ANDROID_ROOT/bin/$DALVIKVM")" = "dalvikvm64" ]; then
+ echo "lib64"
+ else
+ echo "lib"
+ fi
+}
+
invoke_with=
+DALVIKVM=dalvikvm
+LIBART=libart.so
while true; do
if [ "$1" = "--invoke-with" ]; then
@@ -23,7 +45,13 @@
invoke_with="$1"
shift
elif [ "$1" = "-d" ]; then
- lib="-XXlib:libartd.so"
+ LIBART="libartd.so"
+ shift
+ elif [ "$1" = "--32" ]; then
+ DALVIKVM=dalvikvm32
+ shift
+ elif [ "$1" = "--64" ]; then
+ DALVIKVM=dalvikvm64
shift
elif expr "$1" : "--" >/dev/null 2>&1; then
echo "unknown option: $1" 1>&2
@@ -33,38 +61,20 @@
fi
done
-function follow_links() {
- file="$1"
- while [ -h "$file" ]; do
- # On Mac OS, readlink -f doesn't work.
- file="$(readlink "$file")"
- done
- echo "$file"
-}
-
-PROG_NAME="$(follow_links "$BASH_SOURCE")"
+PROG_NAME="$(follow_links)"
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_BUILD_TOP="$(cd "${PROG_DIR}/../../../../" ; pwd -P)/"
-ANDROID_HOST_OUT=$PROG_DIR/..
+ANDROID_ROOT=$PROG_DIR/..
ANDROID_DATA=$PWD/android-data$$
-DALVIKVM_EXECUTABLE=$ANDROID_HOST_OUT/bin/dalvikvm
+LIBDIR=$(find_libdir)
+LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBDIR
-function find_libdir() {
- if [ "$(readlink "$DALVIKVM_EXECUTABLE")" = "dalvikvm64" ]; then
- echo "lib64"
- else
- echo "lib"
- fi
-}
-
-LD_LIBRARY_PATH=$ANDROID_HOST_OUT/"$(find_libdir)"
-
-mkdir -p $ANDROID_DATA/dalvik-cache/{x86,x86_64}
+mkdir -p $ANDROID_DATA/dalvik-cache/{arm,arm64,x86,x86_64}
ANDROID_DATA=$ANDROID_DATA \
- ANDROID_ROOT=$ANDROID_HOST_OUT \
+ ANDROID_ROOT=$ANDROID_ROOT \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
- $invoke_with $DALVIKVM_EXECUTABLE $lib \
- -Ximage:$ANDROID_HOST_OUT/framework/core.art \
+ $invoke_with $ANDROID_ROOT/bin/$DALVIKVM $lib \
+ -XXlib:$LIBART \
+ -Ximage:$ANDROID_ROOT/framework/core.art \
"$@"
EXIT_STATUS=$?
rm -rf $ANDROID_DATA
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
new file mode 100755
index 0000000..b66191f
--- /dev/null
+++ b/tools/symbolize.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+# 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.
+#
+#
+# Symbolize oat files from the dalvik cache of a device.
+#
+# By default, pulls everything from the dalvik cache. A simple yes/no/quit prompt for each file can
+# be requested by giving "--interactive" as a parameter.
+
+INTERACTIVE="no"
+if [ "x$1" = "x--interactive" ] ; then
+ INTERACTIVE="yes"
+fi
+
+# Pull the file from the device and symbolize it.
+function one() {
+ echo $1 $2
+ if [ "x$INTERACTIVE" = "xyes" ] ; then
+ echo -n "What to do? [Y/n/q] "
+ read -e input
+ if [ "x$input" = "xn" ] ; then
+ return
+ fi
+ if [ "x$input" = "xq" ] ; then
+ exit 0
+ fi
+ fi
+ adb pull /data/dalvik-cache/$1/$2 /tmp || exit 1
+ mkdir -p $OUT/symbols/data/dalvik-cache/$1
+ oatdump --symbolize=/tmp/$2 --output=$OUT/symbols/data/dalvik-cache/$1/$2
+}
+
+# adb shell ls seems to output in DOS format (CRLF), which messes up scripting
+function adbls() {
+ adb shell ls $@ | sed 's/\r$//'
+}
+
+# Check for all ISA directories on device.
+function all() {
+ DIRS=$(adbls /data/dalvik-cache/)
+ for DIR in $DIRS ; do
+ case $DIR in
+ arm|arm64|mips|x86|x86_64)
+ FILES=$(adbls /data/dalvik-cache/$DIR/*.oat /data/dalvik-cache/$DIR/*.dex)
+ for FILE in $FILES ; do
+ # Cannot use basename as the file doesn't exist.
+ NAME=$(echo $FILE | sed -e 's/.*\///')
+ one $DIR $NAME
+ done
+ ;;
+ esac
+ done
+}
+
+all