Merge branch 'master' of persistent-https://googleplex-android.git.corp.google.com/platform/art into merge-goog-master-to-aosp-master
diff --git a/Android.mk b/Android.mk
index 46a7c1e..3112ab0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,6 +85,7 @@
include $(art_path)/runtime/Android.mk
include $(art_path)/compiler/Android.mk
include $(art_path)/dex2oat/Android.mk
+include $(art_path)/disassembler/Android.mk
include $(art_path)/oatdump/Android.mk
include $(art_path)/dalvikvm/Android.mk
include $(art_path)/jdwpspy/Android.mk
@@ -269,7 +270,7 @@
$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY)
@mkdir -p $$(dir $$@)
- $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --android-root=$(PRODUCT_OUT)/system
+ $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system
endif
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..faed58a
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/build/Android.common.mk b/build/Android.common.mk
index ac1be1e..a2c2e1a 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -82,7 +82,8 @@
endif
LLVM_ROOT_PATH := external/llvm
-include $(LLVM_ROOT_PATH)/llvm.mk
+# Don't fail a dalvik minimal host build.
+-include $(LLVM_ROOT_PATH)/llvm.mk
# Clang build.
# ART_TARGET_CLANG := true
@@ -104,6 +105,7 @@
ART_C_INCLUDES := \
external/gtest/include \
external/valgrind/main/include \
+ external/valgrind/main \
external/zlib \
frameworks/compile/mclinker/include
@@ -147,11 +149,8 @@
IMG_TARGET_BASE_ADDRESS := 0x60000000
endif
-ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS)
-
-ifeq ($(TARGET_ARCH),x86)
-ART_TARGET_CFLAGS += -msse2
-endif
+ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS)
+ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default
ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(IMG_TARGET_BASE_ADDRESS)
ifeq ($(TARGET_CPU_SMP),true)
@@ -160,6 +159,17 @@
ART_TARGET_CFLAGS += -DANDROID_SMP=0
endif
+# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on
+# the TARGET_CPU_VARIANT
+ifeq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),)
+$(error Required DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is not set)
+endif
+ART_TARGET_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
+
+ifeq ($(TARGET_ARCH),x86)
+ART_TARGET_CFLAGS += -msse2
+endif
+
# Enable thread-safety for GCC 4.6 on the target but not for GCC 4.7 where this feature was removed.
ifneq ($(filter 4.6 4.6.%, $(TARGET_GCC_VERSION)),)
ART_TARGET_CFLAGS += -Wthread-safety
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4c658a2..655c7dd 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -17,6 +17,7 @@
LOCAL_PATH := art
TEST_COMMON_SRC_FILES := \
+ compiler/dex/arena_allocator_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
compiler/image_test.cc \
@@ -27,6 +28,7 @@
compiler/utils/arm/managed_register_arm_test.cc \
compiler/utils/x86/managed_register_x86_test.cc \
runtime/barrier_test.cc \
+ runtime/base/bit_vector_test.cc \
runtime/base/histogram_test.cc \
runtime/base/mutex_test.cc \
runtime/base/timing_logger_test.cc \
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index ea7b0b0..f964346 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -26,7 +26,7 @@
# By default, do not run rerun dex2oat if the tool changes.
# Comment out the | to force dex2oat to rerun on after all changes.
-DEX2OAT_DEPENDENCY := |
+DEX2OAT_DEPENDENCY := #|
DEX2OAT_DEPENDENCY += $(DEX2OAT)
DEX2OAT_DEPENDENCY += $(LIBART_COMPILER)
@@ -57,15 +57,23 @@
HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/core.art
TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/core.art
+TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
+
$(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY)
@echo "host dex2oat: $@ ($?)"
@mkdir -p $(dir $@)
- $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT)
+ $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
+ --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) \
+ --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) \
+ --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT)
$(TARGET_CORE_IMG_OUT): $(TARGET_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY)
@echo "target dex2oat: $@ ($?)"
@mkdir -p $(dir $@)
- $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
+ $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
+ --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) \
+ --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \
+ --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
$(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT)
@@ -81,6 +89,19 @@
include $(BUILD_PHONY_PACKAGE)
endif
+# If we aren't building the host toolchain, skip building the target core.art.
+ifeq ($(WITH_HOST_DALVIK),true)
+ifeq ($(ART_BUILD_TARGET),true)
+include $(CLEAR_VARS)
+LOCAL_MODULE := core.art
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
+LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk
+LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT)
+include $(BUILD_PHONY_PACKAGE)
+endif
+endif
+
########################################################################
# The full system boot classpath
TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS))
@@ -97,7 +118,10 @@
@echo "target dex2oat: $@ ($?)"
@mkdir -p $(dir $@)
@mkdir -p $(dir $(TARGET_BOOT_OAT_UNSTRIPPED_OUT))
- $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
+ $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) \
+ --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) \
+ --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \
+ --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
$(TARGET_BOOT_OAT_UNSTRIPPED_OUT): $(TARGET_BOOT_IMG_OUT)
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 66ff461..fc2f02b 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -74,7 +74,6 @@
llvm/md_builder.cc \
llvm/runtime_support_builder.cc \
llvm/runtime_support_builder_arm.cc \
- llvm/runtime_support_builder_thumb2.cc \
llvm/runtime_support_builder_x86.cc \
trampolines/trampoline_compiler.cc \
utils/arm/assembler_arm.cc \
diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc
index 36393e7..95e44b3 100644
--- a/compiler/dex/arena_allocator.cc
+++ b/compiler/dex/arena_allocator.cc
@@ -19,12 +19,15 @@
#include "arena_allocator.h"
#include "base/logging.h"
#include "base/mutex.h"
+#include "thread-inl.h"
+#include <memcheck/memcheck.h>
namespace art {
// Memmap is a bit slower than malloc according to my measurements.
static constexpr bool kUseMemMap = false;
static constexpr bool kUseMemSet = true && kUseMemMap;
+static constexpr size_t kValgrindRedZoneBytes = 8;
static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = {
"Misc ",
@@ -47,7 +50,9 @@
map_(nullptr),
next_(nullptr) {
if (kUseMemMap) {
- map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE);
+ std::string error_msg;
+ map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE, &error_msg);
+ CHECK(map_ != nullptr) << error_msg;
memory_ = map_->Begin();
size_ = map_->Size();
} else {
@@ -107,6 +112,9 @@
void ArenaPool::FreeArena(Arena* arena) {
Thread* self = Thread::Current();
+ if (UNLIKELY(RUNNING_ON_VALGRIND)) {
+ VALGRIND_MAKE_MEM_UNDEFINED(arena->memory_, arena->bytes_allocated_);
+ }
{
MutexLock lock(self, lock_);
arena->next_ = free_arenas_;
@@ -128,7 +136,8 @@
end_(nullptr),
ptr_(nullptr),
arena_head_(nullptr),
- num_allocations_(0) {
+ num_allocations_(0),
+ running_on_valgrind_(RUNNING_ON_VALGRIND) {
memset(&alloc_stats_[0], 0, sizeof(alloc_stats_));
}
@@ -140,6 +149,29 @@
}
}
+void* ArenaAllocator::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
+ size_t rounded_bytes = (bytes + 3 + kValgrindRedZoneBytes) & ~3;
+ if (UNLIKELY(ptr_ + rounded_bytes > end_)) {
+ // Obtain a new block.
+ ObtainNewArenaForAllocation(rounded_bytes);
+ if (UNLIKELY(ptr_ == nullptr)) {
+ return nullptr;
+ }
+ }
+ if (kCountAllocations) {
+ alloc_stats_[kind] += rounded_bytes;
+ ++num_allocations_;
+ }
+ uint8_t* ret = ptr_;
+ ptr_ += rounded_bytes;
+ // Check that the memory is already zeroed out.
+ for (uint8_t* ptr = ret; ptr < ptr_; ++ptr) {
+ CHECK_EQ(*ptr, 0U);
+ }
+ VALGRIND_MAKE_MEM_NOACCESS(ret + bytes, rounded_bytes - bytes);
+ return ret;
+}
+
ArenaAllocator::~ArenaAllocator() {
// Reclaim all the arenas by giving them back to the thread pool.
UpdateBytesAllocated();
diff --git a/compiler/dex/arena_allocator.h b/compiler/dex/arena_allocator.h
index dda52a2..d11d67c 100644
--- a/compiler/dex/arena_allocator.h
+++ b/compiler/dex/arena_allocator.h
@@ -103,6 +103,9 @@
// Returns zeroed memory.
void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE {
+ if (UNLIKELY(running_on_valgrind_)) {
+ return AllocValgrind(bytes, kind);
+ }
bytes = (bytes + 3) & ~3;
if (UNLIKELY(ptr_ + bytes > end_)) {
// Obtain a new block.
@@ -120,6 +123,7 @@
return ret;
}
+ void* AllocValgrind(size_t bytes, ArenaAllocKind kind);
void ObtainNewArenaForAllocation(size_t allocation_size);
size_t BytesAllocated() const;
void DumpMemStats(std::ostream& os) const;
@@ -132,10 +136,9 @@
uint8_t* end_;
uint8_t* ptr_;
Arena* arena_head_;
-
- // Statistics.
size_t num_allocations_;
- size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds.
+ size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds.
+ bool running_on_valgrind_;
DISALLOW_COPY_AND_ASSIGN(ArenaAllocator);
}; // ArenaAllocator
diff --git a/compiler/dex/arena_allocator_test.cc b/compiler/dex/arena_allocator_test.cc
new file mode 100644
index 0000000..63dc615
--- /dev/null
+++ b/compiler/dex/arena_allocator_test.cc
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#include "arena_allocator.h"
+#include "arena_bit_vector.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(ArenaAllocator, Test) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ ArenaBitVector bv(&arena, 10, true);
+ bv.SetBit(5);
+ EXPECT_EQ(1U, bv.GetStorageSize());
+ bv.SetBit(35);
+ EXPECT_EQ(2U, bv.GetStorageSize());
+}
+
+} // namespace art
diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc
index 3fa9295..b567ae8 100644
--- a/compiler/dex/arena_bit_vector.cc
+++ b/compiler/dex/arena_bit_vector.cc
@@ -19,125 +19,29 @@
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 };
+class ArenaBitVectorAllocator : public Allocator {
+ public:
+ explicit ArenaBitVectorAllocator(ArenaAllocator* arena) : arena_(arena) {}
+ ~ArenaBitVectorAllocator() {}
+
+ virtual void* Alloc(size_t size) {
+ return arena_->Alloc(size, ArenaAllocator::kAllocGrowableBitMap);
+ }
+
+ virtual void Free(void*) {} // Nop.
+
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->Alloc(sizeof(ArenaBitVectorAllocator), ArenaAllocator::kAllocGrowableBitMap);
+ }
+ static void operator delete(void* p) {} // Nop.
+
+ private:
+ ArenaAllocator* arena_;
+ DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator);
+};
ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits,
bool expandable, OatBitMapKind kind)
- : arena_(arena),
- expandable_(expandable),
- kind_(kind),
- storage_size_((start_bits + 31) >> 5),
- storage_(static_cast<uint32_t*>(arena_->Alloc(storage_size_ * sizeof(uint32_t),
- ArenaAllocator::kAllocGrowableBitMap))) {
- DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units.
-}
-
-/*
- * Determine whether or not the specified bit is set.
- */
-bool ArenaBitVector::IsBitSet(unsigned int num) {
- DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
-
- unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f];
- return (val != 0);
-}
-
-// Mark all bits bit as "clear".
-void ArenaBitVector::ClearAllBits() {
- memset(storage_, 0, storage_size_ * sizeof(uint32_t));
-}
-
-// 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 ArenaBitVector::SetBit(unsigned int num) {
- if (num >= storage_size_ * sizeof(uint32_t) * 8) {
- DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
-
- /* Round up to word boundaries for "num+1" bits */
- unsigned int new_size = (num + 1 + 31) >> 5;
- DCHECK_GT(new_size, storage_size_);
- uint32_t *new_storage =
- static_cast<uint32_t*>(arena_->Alloc(new_size * sizeof(uint32_t),
- ArenaAllocator::kAllocGrowableBitMap));
- memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t));
- // Zero out the new storage words.
- memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t));
- // 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 ArenaBitVector::ClearBit(unsigned int num) {
- DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
- storage_[num >> 5] &= ~check_masks[num & 0x1f];
-}
-
-// Copy a whole vector to the other. Sizes must match.
-void ArenaBitVector::Copy(ArenaBitVector* src) {
- DCHECK_EQ(storage_size_, src->GetStorageSize());
- memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_);
-}
-
-// Intersect with another bit vector. Sizes and expandability must be the same.
-void ArenaBitVector::Intersect(const ArenaBitVector* src) {
- DCHECK_EQ(storage_size_, src->GetStorageSize());
- DCHECK_EQ(expandable_, src->IsExpandable());
- for (unsigned int idx = 0; idx < storage_size_; idx++) {
- storage_[idx] &= src->GetRawStorageWord(idx);
- }
-}
-
-/*
- * Union with another bit vector. Sizes and expandability must be the same.
- */
-void ArenaBitVector::Union(const ArenaBitVector* src) {
- DCHECK_EQ(storage_size_, src->GetStorageSize());
- DCHECK_EQ(expandable_, src->IsExpandable());
- for (unsigned int idx = 0; idx < storage_size_; idx++) {
- storage_[idx] |= src->GetRawStorageWord(idx);
- }
-}
-
-// Count the number of bits that are set.
-int ArenaBitVector::NumSetBits() {
- unsigned int count = 0;
-
- for (unsigned int word = 0; word < storage_size_; word++) {
- count += __builtin_popcount(storage_[word]);
- }
- return count;
-}
-
-/*
- * 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 ArenaBitVector::SetInitialBits(unsigned int num_bits) {
- DCHECK_LE(((num_bits + 31) >> 5), storage_size_);
- unsigned int idx;
- for (idx = 0; idx < (num_bits >> 5); idx++) {
- storage_[idx] = -1;
- }
- unsigned int rem_num_bits = num_bits & 0x1f;
- if (rem_num_bits) {
- storage_[idx] = (1 << rem_num_bits) - 1;
- }
-}
+ : BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) {}
} // namespace art
diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h
index 8bcd628..4b2193a 100644
--- a/compiler/dex/arena_bit_vector.h
+++ b/compiler/dex/arena_bit_vector.h
@@ -17,107 +17,28 @@
#ifndef ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_
#define ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_
-#include <stdint.h>
-#include <stddef.h>
-#include "compiler_enums.h"
#include "arena_allocator.h"
+#include "base/bit_vector.h"
+#include "compiler_enums.h"
namespace art {
/*
- * Expanding bitmap, used for tracking resources. Bits are numbered starting
- * from zero. All operations on a BitVector are unsynchronized.
+ * A BitVector implementation that uses Arena allocation.
*/
-class ArenaBitVector {
+class ArenaBitVector : public BitVector {
public:
- class Iterator {
- public:
- explicit Iterator(ArenaBitVector* bit_vector)
- : p_bits_(bit_vector),
- bit_storage_(bit_vector->GetRawStorage()),
- bit_index_(0),
- bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {}
-
- // Return the position of the next set bit. -1 means end-of-element reached.
- int Next() {
- // Did anything obviously change since we started?
- DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8);
- DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage());
-
- if (bit_index_ >= bit_size_) return -1;
-
- uint32_t word_index = bit_index_ / 32;
- uint32_t word = bit_storage_[word_index];
- // Mask out any bits in the first word we've already considered.
- word >>= bit_index_ & 0x1f;
- if (word == 0) {
- bit_index_ &= ~0x1f;
- do {
- word_index++;
- if ((word_index * 32) >= bit_size_) {
- bit_index_ = bit_size_;
- return -1;
- }
- word = bit_storage_[word_index];
- bit_index_ += 32;
- } while (word == 0);
- }
- bit_index_ += CTZ(word) + 1;
- return bit_index_ - 1;
- }
-
- static void* operator new(size_t size, ArenaAllocator* arena) {
- return arena->Alloc(sizeof(ArenaBitVector::Iterator),
- ArenaAllocator::kAllocGrowableBitMap);
- };
- static void operator delete(void* p) {} // Nop.
-
- private:
- ArenaBitVector* const p_bits_;
- uint32_t* const bit_storage_;
- uint32_t bit_index_; // Current index (size in bits).
- const uint32_t bit_size_; // Size of vector in bits.
- };
-
- ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, bool expandable,
+ ArenaBitVector(ArenaAllocator* 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), ArenaAllocator::kAllocGrowableBitMap);
- }
- static void operator delete(void* p) {} // Nop.
-
- void SetBit(unsigned int num);
- void ClearBit(unsigned int num);
- void MarkAllBits(bool set);
- void DebugBitVector(char* msg, int length);
- bool IsBitSet(unsigned int num);
- void ClearAllBits();
- void SetInitialBits(unsigned int num_bits);
- void Copy(ArenaBitVector* src);
- void Intersect(const ArenaBitVector* src2);
- void Union(const ArenaBitVector* src);
- // Are we equal to another bit vector? Note: expandability attributes must also match.
- bool Equal(const ArenaBitVector* src) {
- return (storage_size_ == src->GetStorageSize()) &&
- (expandable_ == src->IsExpandable()) &&
- (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0);
- }
- int NumSetBits();
-
- 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_; }
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap);
+ }
+ static void operator delete(void* p) {} // Nop.
private:
- ArenaAllocator* const arena_;
- const bool expandable_; // expand bitmap if we run out?
- const OatBitMapKind kind_; // for memory use tuning.
- uint32_t storage_size_; // current size, in 32-bit words.
- uint32_t* storage_;
+ const OatBitMapKind kind_; // for memory use tuning. TODO: currently unused.
};
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 97a682f..56facfd 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -44,6 +44,8 @@
kRet0,
kRet1,
kInvokeTgt,
+ kHiddenArg,
+ kHiddenFpArg,
kCount
};
@@ -55,6 +57,7 @@
};
enum BBType {
+ kNullBlock,
kEntryBlock,
kDalvikByteCode,
kExitBlock,
@@ -180,6 +183,8 @@
kOpBic,
kOpCmn,
kOpTst,
+ kOpRev,
+ kOpRevsh,
kOpBkpt,
kOpBlx,
kOpPush,
@@ -412,6 +417,27 @@
std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind);
+// LIR fixup kinds for Arm
+enum FixupKind {
+ kFixupNone,
+ kFixupLabel, // For labels we just adjust the offset.
+ kFixupLoad, // Mostly for imediates.
+ kFixupVLoad, // FP load which *may* be pc-relative.
+ kFixupCBxZ, // Cbz, Cbnz.
+ kFixupPushPop, // Not really pc relative, but changes size based on args.
+ kFixupCondBranch, // Conditional branch
+ kFixupT1Branch, // Thumb1 Unconditional branch
+ kFixupT2Branch, // Thumb2 Unconditional branch
+ kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair).
+ kFixupBl1, // Bl1 (start of Bl1/Bl2 pair).
+ kFixupAdr, // Adr.
+ kFixupMovImmLST, // kThumb2MovImm16LST.
+ kFixupMovImmHST, // kThumb2MovImm16HST.
+ kFixupAlign4, // Align to 4-byte boundary.
+};
+
+std::ostream& operator<<(std::ostream& os, const FixupKind& kind);
+
} // namespace art
#endif // ART_COMPILER_DEX_COMPILER_ENUMS_H_
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 6607562..fd46975 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -29,6 +29,7 @@
#include "llvm/intrinsic_helper.h"
#include "llvm/ir_builder.h"
#include "safe_map.h"
+#include "base/timing_logger.h"
namespace art {
@@ -68,7 +69,14 @@
compiler_flip_match(false),
arena(pool),
mir_graph(NULL),
- cg(NULL) {}
+ cg(NULL),
+ timings("QuickCompiler", true, false) {
+ }
+
+ void StartTimingSplit(const char* label);
+ void NewTimingSplit(const char* label);
+ void EndTiming();
+
/*
* Fields needed/generated by common frontend and generally used throughout
* the compiler.
@@ -89,15 +97,18 @@
CompilerBackend compiler_backend;
InstructionSet instruction_set;
+ const InstructionSetFeatures& GetInstructionSetFeatures() {
+ return compiler_driver->GetInstructionSetFeatures();
+ }
// TODO: much of this info available elsewhere. Go to the original source?
- int num_dalvik_registers; // method->registers_size.
+ uint16_t num_dalvik_registers; // method->registers_size.
const uint16_t* insns;
- int num_ins;
- int num_outs;
- int num_regs; // Unlike num_dalvik_registers, does not include ins.
+ uint16_t num_ins;
+ uint16_t num_outs;
+ uint16_t num_regs; // Unlike num_dalvik_registers, does not include ins.
// TODO: may want to move this to MIRGraph.
- int num_compiler_temps;
+ uint16_t num_compiler_temps;
// If non-empty, apply optimizer/debug flags only to matching methods.
std::string compiler_method_match;
@@ -109,6 +120,7 @@
UniquePtr<MIRGraph> mir_graph; // MIR container.
UniquePtr<Backend> cg; // Target-specific codegen.
+ base::TimingLogger timings;
};
} // namespace art
diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h
index 06cc505..74f36dd 100644
--- a/compiler/dex/dataflow_iterator-inl.h
+++ b/compiler/dex/dataflow_iterator-inl.h
@@ -21,42 +21,63 @@
namespace art {
-inline BasicBlock* DataflowIterator::NextBody(bool had_change) {
- changed_ |= had_change;
+// Single forward pass over the nodes.
+inline BasicBlock* DataflowIterator::ForwardSingleNext() {
BasicBlock* res = NULL;
- if (reverse_) {
- if (is_iterative_ && changed_ && (idx_ < 0)) {
- idx_ = start_idx_;
- changed_ = false;
- }
- if (idx_ >= 0) {
- int bb_id = block_id_list_->Get(idx_--);
- res = mir_graph_->GetBasicBlock(bb_id);
- }
- } else {
- if (is_iterative_ && changed_ && (idx_ >= end_idx_)) {
- idx_ = start_idx_;
- changed_ = false;
- }
- if (idx_ < end_idx_) {
- int bb_id = block_id_list_->Get(idx_++);
- res = mir_graph_->GetBasicBlock(bb_id);
- }
+ if (idx_ < end_idx_) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_++);
+ res = mir_graph_->GetBasicBlock(bb_id);
}
return res;
}
-// AllNodes uses the existing GrowableArray iterator, so use different NextBody().
-inline BasicBlock* AllNodesIterator::NextBody(bool had_change) {
+// Repeat full forward passes over all nodes until no change occurs during a complete pass.
+inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) {
changed_ |= had_change;
BasicBlock* res = NULL;
+ if ((idx_ >= end_idx_) && changed_) {
+ idx_ = start_idx_;
+ changed_ = false;
+ }
+ if (idx_ < end_idx_) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_++);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ return res;
+}
+
+// Single reverse pass over the nodes.
+inline BasicBlock* DataflowIterator::ReverseSingleNext() {
+ BasicBlock* res = NULL;
+ if (idx_ >= 0) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_--);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ return res;
+}
+
+// Repeat full backwards passes over all nodes until no change occurs during a complete pass.
+inline BasicBlock* DataflowIterator::ReverseRepeatNext(bool had_change) {
+ changed_ |= had_change;
+ BasicBlock* res = NULL;
+ if ((idx_ < 0) && changed_) {
+ idx_ = start_idx_;
+ changed_ = false;
+ }
+ if (idx_ >= 0) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_--);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ return res;
+}
+
+// AllNodes uses the existing GrowableArray iterator, and should be considered unordered.
+inline BasicBlock* AllNodesIterator::Next() {
+ BasicBlock* res = NULL;
bool keep_looking = true;
while (keep_looking) {
res = all_nodes_iterator_->Next();
- if (is_iterative_ && changed_ && (res == NULL)) {
- all_nodes_iterator_->Reset();
- changed_ = false;
- } else if ((res == NULL) || (!res->hidden)) {
+ if ((res == NULL) || (!res->hidden)) {
keep_looking = false;
}
}
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index da44ffd..26e3665 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -27,124 +27,130 @@
* interesting orders. Note that for efficiency, the visit orders have been pre-computed.
* The order itself will not change during the iteration. However, for some uses,
* auxiliary data associated with the basic blocks may be changed during the iteration,
- * necessitating another pass over the list.
- *
- * To support this usage, we have is_iterative_. If false, the iteration is a one-shot
- * pass through the pre-computed list using Next(). If true, the caller must tell the
- * iterator whether a change has been made that necessitates another pass. Use
- * Next(had_change) for this. The general idea is that the iterative_ use case means
- * that the iterator will keep repeating the full basic block list until a complete pass
- * is made through it with no changes. Note that calling Next(true) does not affect
- * the iteration order or short-curcuit the current pass - it simply tells the iterator
- * that once it has finished walking through the block list it should reset and do another
- * full pass through the list.
+ * necessitating another pass over the list. If this behavior is required, use the
+ * "Repeating" variant. For the repeating variant, the caller must tell the iterator
+ * whether a change has been made that necessitates another pass. Note that calling Next(true)
+ * does not affect the iteration order or short-circuit the current pass - it simply tells
+ * the iterator that once it has finished walking through the block list it should reset and
+ * do another full pass through the list.
*/
class DataflowIterator {
public:
virtual ~DataflowIterator() {}
- // Return the next BasicBlock* to visit.
- BasicBlock* Next() {
- DCHECK(!is_iterative_);
- return NextBody(false);
- }
-
- /*
- * Return the next BasicBlock* to visit, and tell the iterator whether any change
- * has occurred that requires another full pass over the block list.
- */
- BasicBlock* Next(bool had_change) {
- DCHECK(is_iterative_);
- return NextBody(had_change);
- }
-
protected:
- DataflowIterator(MIRGraph* mir_graph, bool is_iterative, int start_idx, int end_idx,
- bool reverse)
+ DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx)
: mir_graph_(mir_graph),
- is_iterative_(is_iterative),
start_idx_(start_idx),
end_idx_(end_idx),
- reverse_(reverse),
block_id_list_(NULL),
idx_(0),
changed_(false) {}
- virtual BasicBlock* NextBody(bool had_change) ALWAYS_INLINE;
+ virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE;
+ virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE;
+ virtual BasicBlock* ForwardRepeatNext(bool had_change) ALWAYS_INLINE;
+ virtual BasicBlock* ReverseRepeatNext(bool had_change) ALWAYS_INLINE;
MIRGraph* const mir_graph_;
- const bool is_iterative_;
- const int start_idx_;
- const int end_idx_;
- const bool reverse_;
- GrowableArray<int>* block_id_list_;
- int idx_;
+ const int32_t start_idx_;
+ const int32_t end_idx_;
+ GrowableArray<BasicBlockId>* block_id_list_;
+ int32_t idx_;
bool changed_;
}; // DataflowIterator
- class ReachableNodesIterator : public DataflowIterator {
- public:
- ReachableNodesIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
- idx_ = start_idx_;
- block_id_list_ = mir_graph->GetDfsOrder();
- }
- };
-
class PreOrderDfsIterator : public DataflowIterator {
public:
- PreOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit PreOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsOrder();
}
+
+ BasicBlock* Next() {
+ return ForwardSingleNext();
+ }
};
- class PostOrderDfsIterator : public DataflowIterator {
+ class RepeatingPreOrderDfsIterator : public DataflowIterator {
public:
- PostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsOrder();
+ }
+
+ BasicBlock* Next(bool had_change) {
+ return ForwardRepeatNext(had_change);
+ }
+ };
+
+ class RepeatingPostOrderDfsIterator : public DataflowIterator {
+ public:
+ explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsPostOrder();
}
+
+ BasicBlock* Next(bool had_change) {
+ return ForwardRepeatNext(had_change);
+ }
};
class ReversePostOrderDfsIterator : public DataflowIterator {
public:
- ReversePostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative,
- mir_graph->GetNumReachableBlocks() -1, 0, true) {
+ explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsPostOrder();
}
+
+ BasicBlock* Next() {
+ return ReverseSingleNext();
+ }
+ };
+
+ class RepeatingReversePostOrderDfsIterator : public DataflowIterator {
+ public:
+ explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsPostOrder();
+ }
+
+ BasicBlock* Next(bool had_change) {
+ return ReverseRepeatNext(had_change);
+ }
};
class PostOrderDOMIterator : public DataflowIterator {
public:
- PostOrderDOMIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit PostOrderDOMIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDomPostOrder();
}
+
+ BasicBlock* Next() {
+ return ForwardSingleNext();
+ }
};
class AllNodesIterator : public DataflowIterator {
public:
- AllNodesIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0, 0, false) {
- all_nodes_iterator_ =
- new (mir_graph->GetArena()) GrowableArray<BasicBlock*>::Iterator(mir_graph->GetBlockList());
+ explicit AllNodesIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, 0) {
+ all_nodes_iterator_ = new
+ (mir_graph->GetArena()) GrowableArray<BasicBlock*>::Iterator(mir_graph->GetBlockList());
}
void Reset() {
all_nodes_iterator_->Reset();
}
- BasicBlock* NextBody(bool had_change) ALWAYS_INLINE;
+ BasicBlock* Next() ALWAYS_INLINE;
private:
GrowableArray<BasicBlock*>::Iterator* all_nodes_iterator_;
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 63d8aa0..abafbc5 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -24,6 +24,7 @@
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
+#include "thread-inl.h"
namespace art {
namespace optimizer {
@@ -216,8 +217,8 @@
uint32_t field_idx = inst->VRegC_22c();
int field_offset;
bool is_volatile;
- bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
- is_volatile, is_put);
+ bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put,
+ &field_offset, &is_volatile);
if (fast_path && !is_volatile && IsUint(16, field_offset)) {
VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
<< " to " << Instruction::Name(new_opcode)
@@ -246,11 +247,13 @@
int vtable_idx;
uintptr_t direct_code;
uintptr_t direct_method;
- bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type,
- target_method, vtable_idx,
- direct_code, direct_method,
- false);
// TODO: support devirtualization.
+ const bool kEnableDevirtualization = false;
+ bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc,
+ false, kEnableDevirtualization,
+ &invoke_type,
+ &target_method, &vtable_idx,
+ &direct_code, &direct_method);
if (fast_path && original_invoke_type == invoke_type) {
if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index fefcab9..2f8521f 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -24,6 +24,7 @@
#include "runtime.h"
#include "backend.h"
#include "base/logging.h"
+#include "base/timing_logger.h"
#if defined(ART_USE_PORTABLE_COMPILER)
#include "dex/portable/mir_to_gbc.h"
@@ -104,8 +105,30 @@
// (1 << kDebugVerifyBitcode) |
// (1 << kDebugShowSummaryMemoryUsage) |
// (1 << kDebugShowFilterStats) |
+ // (1 << kDebugTimings) |
0;
+// TODO: Add a cumulative version of logging, and combine with dex2oat --dump-timing
+void CompilationUnit::StartTimingSplit(const char* label) {
+ if (enable_debug & (1 << kDebugTimings)) {
+ timings.StartSplit(label);
+ }
+}
+
+void CompilationUnit::NewTimingSplit(const char* label) {
+ if (enable_debug & (1 << kDebugTimings)) {
+ timings.NewSplit(label);
+ }
+}
+
+void CompilationUnit::EndTiming() {
+ if (enable_debug & (1 << kDebugTimings)) {
+ timings.EndSplit();
+ LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file);
+ LOG(INFO) << Dumpable<base::TimingLogger>(timings);
+ }
+}
+
static CompiledMethod* CompileMethod(CompilerDriver& compiler,
const CompilerBackend compiler_backend,
const DexFile::CodeItem* code_item,
@@ -117,6 +140,11 @@
#endif
) {
VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
+ if (code_item->insns_size_in_code_units_ >= 0x10000) {
+ LOG(INFO) << "Method size exceeds compiler limits: " << code_item->insns_size_in_code_units_
+ << " in " << PrettyMethod(method_idx, dex_file);
+ return NULL;
+ }
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
CompilationUnit cu(&compiler.GetArenaPool());
@@ -151,7 +179,7 @@
*/
if (compiler_backend == kPortable) {
- // Fused long branches not currently usseful in bitcode.
+ // Fused long branches not currently useful in bitcode.
cu.disable_opt |= (1 << kBranchFusing);
}
@@ -170,6 +198,7 @@
(1 << kPromoteCompilerTemps));
}
+ cu.StartTimingSplit("BuildMIRGraph");
cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
/* Gathering opcode stats? */
@@ -187,22 +216,28 @@
}
#endif
+ cu.NewTimingSplit("MIROpt:CodeLayout");
+
/* Do a code layout pass */
cu.mir_graph->CodeLayout();
/* Perform SSA transformation for the whole method */
+ cu.NewTimingSplit("MIROpt:SSATransform");
cu.mir_graph->SSATransformation();
/* Do constant propagation */
+ cu.NewTimingSplit("MIROpt:ConstantProp");
cu.mir_graph->PropagateConstants();
/* Count uses */
cu.mir_graph->MethodUseCount();
/* Perform null check elimination */
+ cu.NewTimingSplit("MIROpt:NullCheckElimination");
cu.mir_graph->NullCheckElimination();
/* Combine basic blocks where possible */
+ cu.NewTimingSplit("MIROpt:BBOpt");
cu.mir_graph->BasicBlockCombine();
/* Do some basic block optimizations */
@@ -245,6 +280,7 @@
cu.cg->Materialize();
+ cu.NewTimingSplit("Cleanup");
result = cu.cg->GetCompiledMethod();
if (result) {
@@ -265,6 +301,7 @@
<< " " << PrettyMethod(method_idx, dex_file);
}
+ cu.EndTiming();
return result;
}
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
index 6c33d10..43f6855 100644
--- a/compiler/dex/frontend.h
+++ b/compiler/dex/frontend.h
@@ -78,6 +78,7 @@
kDebugVerifyBitcode,
kDebugShowSummaryMemoryUsage,
kDebugShowFilterStats,
+ kDebugTimings
};
class LLVMInfo {
diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h
index 8e2abfb..639120a 100644
--- a/compiler/dex/growable_array.h
+++ b/compiler/dex/growable_array.h
@@ -131,6 +131,11 @@
elem_list_[index]++;
}
+ /*
+ * Remove an existing element from list. If there are more than one copy
+ * of the element, only the first one encountered will be deleted.
+ */
+ // TODO: consider renaming this.
void Delete(T element) {
bool found = false;
for (size_t i = 0; i < num_used_ - 1; i++) {
@@ -150,6 +155,11 @@
size_t Size() const { return num_used_; }
+ void SetSize(size_t new_size) {
+ Resize(new_size);
+ num_used_ = new_size;
+ }
+
T* GetRawStorage() const { return elem_list_; }
static void* operator new(size_t size, ArenaAllocator* arena) {
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index d7a4136..89af06e 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -864,7 +864,7 @@
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) {
- ending_bb = ending_bb->fall_through;
+ ending_bb = GetBasicBlock(ending_bb->fall_through);
ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
}
}
@@ -876,13 +876,14 @@
*/
int loop_scale_factor = 1;
// Simple for and while loops
- if ((ending_bb->taken != NULL) && (ending_bb->fall_through == NULL)) {
- if ((ending_bb->taken->taken == bb) || (ending_bb->taken->fall_through == bb)) {
+ if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->fall_through == NullBasicBlockId)) {
+ if ((GetBasicBlock(ending_bb->taken)->taken == bb->id) ||
+ (GetBasicBlock(ending_bb->taken)->fall_through == bb->id)) {
loop_scale_factor = 25;
}
}
// Simple do-while loop
- if ((ending_bb->taken != NULL) && (ending_bb->taken == bb)) {
+ if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->taken == bb->id)) {
loop_scale_factor = 25;
}
@@ -922,7 +923,7 @@
if (tbb == ending_bb) {
done = true;
} else {
- tbb = tbb->fall_through;
+ tbb = GetBasicBlock(tbb->fall_through);
}
}
if (has_math && computational_block && (loop_scale_factor > 1)) {
@@ -1032,6 +1033,14 @@
*/
if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
skip_compilation = true;
+ // If we're got a huge number of basic blocks, don't bother with further analysis.
+ if (static_cast<size_t>(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) {
+ return true;
+ }
+ } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() &&
+ /* If it's large and contains no branches, it's likely to be machine generated initialization */
+ (GetBranchCount() == 0)) {
+ return true;
} else if (compiler_filter == Runtime::kSpeed) {
// If not huge, compile.
return false;
@@ -1061,7 +1070,7 @@
memset(&stats, 0, sizeof(stats));
ClearAllVisitedFlags();
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
AnalyzeBlock(bb, &stats);
}
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index 3a73717..11e19dc 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -1221,10 +1221,10 @@
uint32_t current_offset = static_cast<uint32_t>(current_offset_);
bool fast_path =
cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset,
- type, target_method,
- vtable_idx,
- direct_code, direct_method,
- false) &&
+ 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)));
@@ -1243,7 +1243,8 @@
if (mir->ssa_rep == NULL) {
continue;
}
- uint32_t weight = std::min(16U, static_cast<uint32_t>(bb->nesting_depth));
+ // Each level of nesting adds *16 to count, up to 3 levels deep.
+ uint32_t weight = std::min(3U, static_cast<uint32_t>(bb->nesting_depth) * 4);
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);
@@ -1287,7 +1288,7 @@
if (cu_->disable_opt & (1 << kPromoteRegs)) {
return;
}
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CountUses(bb);
}
@@ -1295,23 +1296,23 @@
/* Verify if all the successor is connected with all the claimed predecessors */
bool MIRGraph::VerifyPredInfo(BasicBlock* bb) {
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
while (true) {
- BasicBlock *pred_bb = iter.Next();
+ BasicBlock *pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) break;
bool found = false;
- if (pred_bb->taken == bb) {
+ if (pred_bb->taken == bb->id) {
found = true;
- } else if (pred_bb->fall_through == bb) {
+ } else if (pred_bb->fall_through == bb->id) {
found = true;
- } else if (pred_bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(pred_bb->successor_block_list.blocks);
+ } 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;
- BasicBlock *succ_bb = successor_block_info->block;
- if (succ_bb == bb) {
+ BasicBlockId succ_bb = successor_block_info->block;
+ if (succ_bb == bb->id) {
found = true;
break;
}
@@ -1331,7 +1332,7 @@
void MIRGraph::VerifyDataflow() {
/* Verify if all blocks are connected as claimed */
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
VerifyPredInfo(bb);
}
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index a12bf39..cf758fc 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -96,9 +96,9 @@
try_block_addr_(NULL),
entry_block_(NULL),
exit_block_(NULL),
- cur_block_(NULL),
num_blocks_(0),
current_code_item_(NULL),
+ dex_pc_to_block_map_(arena, 0, kGrowableArrayMisc),
current_method_(kInvalidEntry),
current_offset_(kInvalidEntry),
def_count_(0),
@@ -108,7 +108,9 @@
attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke.
checkstats_(NULL),
special_case_(kNoHandler),
- arena_(arena) {
+ arena_(arena),
+ backward_branches_(0),
+ forward_branches_(0) {
try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */);
}
@@ -128,11 +130,14 @@
/* Split an existing block from the specified code offset into two */
-BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset,
+BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset,
BasicBlock* orig_block, BasicBlock** immed_pred_block_p) {
+ DCHECK_GT(code_offset, orig_block->start_offset);
MIR* insn = orig_block->first_mir_insn;
+ MIR* prev = NULL;
while (insn) {
if (insn->offset == code_offset) break;
+ prev = insn;
insn = insn->next;
}
if (insn == NULL) {
@@ -150,43 +155,46 @@
orig_block->terminated_by_return = false;
/* Add it to the quick lookup cache */
- block_map_.Put(bottom_block->start_offset, bottom_block);
+ dex_pc_to_block_map_.Put(bottom_block->start_offset, bottom_block->id);
/* Handle the taken path */
bottom_block->taken = orig_block->taken;
- if (bottom_block->taken) {
- orig_block->taken = NULL;
- bottom_block->taken->predecessors->Delete(orig_block);
- bottom_block->taken->predecessors->Insert(bottom_block);
+ 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);
}
/* Handle the fallthrough path */
bottom_block->fall_through = orig_block->fall_through;
- orig_block->fall_through = bottom_block;
- bottom_block->predecessors->Insert(orig_block);
- if (bottom_block->fall_through) {
- bottom_block->fall_through->predecessors->Delete(orig_block);
- bottom_block->fall_through->predecessors->Insert(bottom_block);
+ orig_block->fall_through = bottom_block->id;
+ bottom_block->predecessors->Insert(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);
}
/* Handle the successor list */
- if (orig_block->successor_block_list.block_list_type != kNotUsed) {
- bottom_block->successor_block_list = orig_block->successor_block_list;
- orig_block->successor_block_list.block_list_type = kNotUsed;
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_block_list.blocks);
+ 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;
+ orig_block->successor_block_list_type = kNotUsed;
+ orig_block->successor_blocks = NULL;
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_blocks);
while (true) {
SuccessorBlockInfo *successor_block_info = iterator.Next();
if (successor_block_info == NULL) break;
- BasicBlock *bb = successor_block_info->block;
- bb->predecessors->Delete(orig_block);
- bb->predecessors->Insert(bottom_block);
+ BasicBlock *bb = GetBasicBlock(successor_block_info->block);
+ bb->predecessors->Delete(orig_block->id);
+ bb->predecessors->Insert(bottom_block->id);
}
}
- orig_block->last_mir_insn = insn->prev;
+ orig_block->last_mir_insn = prev;
+ prev->next = NULL;
- insn->prev->next = NULL;
- insn->prev = NULL;
/*
* Update the immediate predecessor block pointer so that outgoing edges
* can be applied to the proper block.
@@ -195,6 +203,23 @@
DCHECK_EQ(*immed_pred_block_p, orig_block);
*immed_pred_block_p = bottom_block;
}
+
+ // Associate dex instructions in the bottom block with the new container.
+ MIR* p = bottom_block->first_mir_insn;
+ while (p != NULL) {
+ int opcode = p->dalvikInsn.opcode;
+ /*
+ * Some messiness here to ensure that we only enter real opcodes and only the
+ * first half of a potentially throwing instruction that has been split into
+ * CHECK and work portions. The 2nd half of a split operation will have a non-null
+ * throw_insn pointer that refers to the 1st half.
+ */
+ if ((opcode == kMirOpCheck) || (!IsPseudoMirOp(opcode) && (p->meta.throw_insn == NULL))) {
+ dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+ }
+ p = (p == bottom_block->last_mir_insn) ? NULL : p->next;
+ }
+
return bottom_block;
}
@@ -206,45 +231,43 @@
* (by the caller)
* Utilizes a map for fast lookup of the typical cases.
*/
-BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool create,
+BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool split, bool create,
BasicBlock** immed_pred_block_p) {
- BasicBlock* bb;
- unsigned int i;
- SafeMap<unsigned int, BasicBlock*>::iterator it;
-
- it = block_map_.find(code_offset);
- if (it != block_map_.end()) {
- return it->second;
- } else if (!create) {
+ if (code_offset >= cu_->code_item->insns_size_in_code_units_) {
return NULL;
}
- if (split) {
- for (i = 0; i < block_list_.Size(); i++) {
- bb = block_list_.Get(i);
- if (bb->block_type != kDalvikByteCode) continue;
- /* Check if a branch jumps into the middle of an existing block */
- if ((code_offset > bb->start_offset) && (bb->last_mir_insn != NULL) &&
- (code_offset <= bb->last_mir_insn->offset)) {
- BasicBlock *new_bb = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?
- immed_pred_block_p : NULL);
- return new_bb;
- }
- }
+ int block_id = dex_pc_to_block_map_.Get(code_offset);
+ BasicBlock* bb = (block_id == 0) ? NULL : block_list_.Get(block_id);
+
+ if ((bb != NULL) && (bb->start_offset == code_offset)) {
+ // Does this containing block start with the desired instruction?
+ return bb;
}
- /* Create a new one */
+ // No direct hit.
+ if (!create) {
+ return NULL;
+ }
+
+ if (bb != NULL) {
+ // The target exists somewhere in an existing block.
+ return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : NULL);
+ }
+
+ // Create a new block.
bb = NewMemBB(kDalvikByteCode, num_blocks_++);
block_list_.Insert(bb);
bb->start_offset = code_offset;
- block_map_.Put(bb->start_offset, bb);
+ dex_pc_to_block_map_.Put(bb->start_offset, bb->id);
return bb;
}
+
/* Identify code range in try blocks and set up the empty catch blocks */
void MIRGraph::ProcessTryCatchBlocks() {
int tries_size = current_code_item_->tries_size_;
- int offset;
+ DexOffset offset;
if (tries_size == 0) {
return;
@@ -253,8 +276,8 @@
for (int i = 0; i < tries_size; i++) {
const DexFile::TryItem* pTry =
DexFile::GetTryItems(*current_code_item_, i);
- int start_offset = pTry->start_addr_;
- int end_offset = start_offset + pTry->insn_count_;
+ DexOffset start_offset = pTry->start_addr_;
+ DexOffset end_offset = start_offset + pTry->insn_count_;
for (offset = start_offset; offset < end_offset; offset++) {
try_block_addr_->SetBit(offset);
}
@@ -275,10 +298,10 @@
}
/* Process instructions with the kBranch flag */
-BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
- int flags, const uint16_t* code_ptr,
+BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
+ int width, int flags, const uint16_t* code_ptr,
const uint16_t* code_end) {
- int target = cur_offset;
+ DexOffset target = cur_offset;
switch (insn->dalvikInsn.opcode) {
case Instruction::GOTO:
case Instruction::GOTO_16:
@@ -306,10 +329,11 @@
default:
LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set";
}
+ CountBranch(target);
BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true,
/* immed_pred_block_p */ &cur_block);
- cur_block->taken = taken_block;
- taken_block->predecessors->Insert(cur_block);
+ cur_block->taken = taken_block->id;
+ taken_block->predecessors->Insert(cur_block->id);
/* Always terminate the current block for conditional branches */
if (flags & Instruction::kContinue) {
@@ -331,8 +355,8 @@
true,
/* immed_pred_block_p */
&cur_block);
- cur_block->fall_through = fallthrough_block;
- fallthrough_block->predecessors->Insert(cur_block);
+ cur_block->fall_through = fallthrough_block->id;
+ fallthrough_block->predecessors->Insert(cur_block->id);
} else if (code_ptr < code_end) {
FindBlock(cur_offset + width, /* split */ false, /* create */ true,
/* immed_pred_block_p */ NULL);
@@ -341,7 +365,7 @@
}
/* Process instructions with the kSwitch flag */
-void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags) {
const uint16_t* switch_data =
reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB);
@@ -385,14 +409,13 @@
first_key = 0; // To make the compiler happy
}
- if (cur_block->successor_block_list.block_list_type != kNotUsed) {
+ if (cur_block->successor_block_list_type != kNotUsed) {
LOG(FATAL) << "Successor block list already in use: "
- << static_cast<int>(cur_block->successor_block_list.block_list_type);
+ << static_cast<int>(cur_block->successor_block_list_type);
}
- cur_block->successor_block_list.block_list_type =
- (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
- kPackedSwitch : kSparseSwitch;
- cur_block->successor_block_list.blocks =
+ 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);
for (i = 0; i < size; i++) {
@@ -401,24 +424,24 @@
SuccessorBlockInfo *successor_block_info =
static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
ArenaAllocator::kAllocSuccessor));
- successor_block_info->block = case_block;
+ successor_block_info->block = case_block->id;
successor_block_info->key =
(insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
first_key + i : keyTable[i];
- cur_block->successor_block_list.blocks->Insert(successor_block_info);
- case_block->predecessors->Insert(cur_block);
+ cur_block->successor_blocks->Insert(successor_block_info);
+ case_block->predecessors->Insert(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;
- fallthrough_block->predecessors->Insert(cur_block);
+ cur_block->fall_through = fallthrough_block->id;
+ fallthrough_block->predecessors->Insert(cur_block->id);
}
/* Process instructions with the kThrow flag */
-BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
- int flags, ArenaBitVector* try_block_addr,
+BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
+ int width, int flags, ArenaBitVector* try_block_addr,
const uint16_t* code_ptr, const uint16_t* code_end) {
bool in_try_block = try_block_addr->IsBitSet(cur_offset);
@@ -426,14 +449,14 @@
if (in_try_block) {
CatchHandlerIterator iterator(*current_code_item_, cur_offset);
- if (cur_block->successor_block_list.block_list_type != kNotUsed) {
+ if (cur_block->successor_block_list_type != kNotUsed) {
LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
LOG(FATAL) << "Successor block list already in use: "
- << static_cast<int>(cur_block->successor_block_list.block_list_type);
+ << static_cast<int>(cur_block->successor_block_list_type);
}
- cur_block->successor_block_list.block_list_type = kCatch;
- cur_block->successor_block_list.blocks =
+ cur_block->successor_block_list_type = kCatch;
+ cur_block->successor_blocks =
new (arena_) GrowableArray<SuccessorBlockInfo*>(arena_, 2, kGrowableArraySuccessorBlocks);
for (; iterator.HasNext(); iterator.Next()) {
@@ -445,17 +468,17 @@
}
SuccessorBlockInfo *successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
(arena_->Alloc(sizeof(SuccessorBlockInfo), ArenaAllocator::kAllocSuccessor));
- successor_block_info->block = catch_block;
+ successor_block_info->block = catch_block->id;
successor_block_info->key = iterator.GetHandlerTypeIndex();
- cur_block->successor_block_list.blocks->Insert(successor_block_info);
- catch_block->predecessors->Insert(cur_block);
+ cur_block->successor_blocks->Insert(successor_block_info);
+ catch_block->predecessors->Insert(cur_block->id);
}
} else {
BasicBlock *eh_block = NewMemBB(kExceptionHandling, num_blocks_++);
- cur_block->taken = eh_block;
+ cur_block->taken = eh_block->id;
block_list_.Insert(eh_block);
eh_block->start_offset = cur_offset;
- eh_block->predecessors->Insert(cur_block);
+ eh_block->predecessors->Insert(cur_block->id);
}
if (insn->dalvikInsn.opcode == Instruction::THROW) {
@@ -484,12 +507,15 @@
* pseudo exception edge MIR. Note also that this new block is
* not automatically terminated after the work portion, and may
* contain following instructions.
+ *
+ * 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);
new_block->start_offset = insn->offset;
- cur_block->fall_through = new_block;
- new_block->predecessors->Insert(cur_block);
+ cur_block->fall_through = new_block->id;
+ new_block->predecessors->Insert(cur_block->id);
MIR* new_insn = static_cast<MIR*>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR));
*new_insn = *insn;
insn->dalvikInsn.opcode =
@@ -517,7 +543,10 @@
current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_;
// 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_);
+
// 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_);
try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_);
@@ -527,9 +556,14 @@
DCHECK(entry_block_ == NULL);
DCHECK(exit_block_ == NULL);
DCHECK_EQ(num_blocks_, 0);
+ // Use id 0 to represent a null block.
+ BasicBlock* null_block = NewMemBB(kNullBlock, num_blocks_++);
+ DCHECK_EQ(null_block->id, NullBasicBlockId);
+ null_block->hidden = true;
+ block_list_.Insert(null_block);
entry_block_ = NewMemBB(kEntryBlock, num_blocks_++);
- exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
block_list_.Insert(entry_block_);
+ exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
block_list_.Insert(exit_block_);
// TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
cu_->dex_file = &dex_file;
@@ -554,15 +588,12 @@
/* Current block to record parsed instructions */
BasicBlock *cur_block = NewMemBB(kDalvikByteCode, num_blocks_++);
- DCHECK_EQ(current_offset_, 0);
+ DCHECK_EQ(current_offset_, 0U);
cur_block->start_offset = current_offset_;
block_list_.Insert(cur_block);
- /* Add first block to the fast lookup cache */
-// FIXME: block map needs association with offset/method pair rather than just offset
- block_map_.Put(cur_block->start_offset, cur_block);
-// FIXME: this needs to insert at the insert point rather than entry block.
- entry_block_->fall_through = cur_block;
- cur_block->predecessors->Insert(entry_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);
/* Identify code range in try blocks and set up the empty catch blocks */
ProcessTryCatchBlocks();
@@ -586,7 +617,6 @@
opcode_count_[static_cast<int>(opcode)]++;
}
-
/* Possible simple method? */
if (live_pattern) {
live_pattern = false;
@@ -628,8 +658,8 @@
// It is a simple nop - treat normally.
AppendMIR(cur_block, insn);
} else {
- DCHECK(cur_block->fall_through == NULL);
- DCHECK(cur_block->taken == NULL);
+ DCHECK(cur_block->fall_through == NullBasicBlockId);
+ DCHECK(cur_block->taken == NullBasicBlockId);
// Unreachable instruction, mark for no continuation.
flags &= ~Instruction::kContinue;
}
@@ -637,6 +667,9 @@
AppendMIR(cur_block, insn);
}
+ // Associate the starting dex_pc for this opcode with its containing basic block.
+ dex_pc_to_block_map_.Put(insn->offset, cur_block->id);
+
code_ptr += width;
if (flags & Instruction::kBranch) {
@@ -644,8 +677,8 @@
width, flags, code_ptr, code_end);
} else if (flags & Instruction::kReturn) {
cur_block->terminated_by_return = true;
- cur_block->fall_through = exit_block_;
- exit_block_->predecessors->Insert(cur_block);
+ cur_block->fall_through = exit_block_->id;
+ exit_block_->predecessors->Insert(cur_block->id);
/*
* Terminate the current block if there are instructions
* afterwards.
@@ -674,13 +707,13 @@
* instruction is not an unconditional branch, connect them through
* the fall-through link.
*/
- DCHECK(cur_block->fall_through == NULL ||
- cur_block->fall_through == next_block ||
- cur_block->fall_through == exit_block_);
+ DCHECK(cur_block->fall_through == NullBasicBlockId ||
+ GetBasicBlock(cur_block->fall_through) == next_block ||
+ GetBasicBlock(cur_block->fall_through) == exit_block_);
- if ((cur_block->fall_through == NULL) && (flags & Instruction::kContinue)) {
- cur_block->fall_through = next_block;
- next_block->predecessors->Insert(cur_block);
+ if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) {
+ cur_block->fall_through = next_block->id;
+ next_block->predecessors->Insert(cur_block->id);
}
cur_block = next_block;
}
@@ -712,7 +745,7 @@
std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
ReplaceSpecialChars(fname);
fname = StringPrintf("%s%s%x.dot", dir_prefix, fname.c_str(),
- GetEntryBlock()->fall_through->start_offset);
+ GetBasicBlock(GetEntryBlock()->fall_through)->start_offset);
file = fopen(fname.c_str(), "w");
if (file == NULL) {
return;
@@ -759,31 +792,30 @@
char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN];
- if (bb->taken) {
+ if (bb->taken != NullBasicBlockId) {
GetBlockName(bb, block_name1);
- GetBlockName(bb->taken, block_name2);
+ GetBlockName(GetBasicBlock(bb->taken), block_name2);
fprintf(file, " %s:s -> %s:n [style=dotted]\n",
block_name1, block_name2);
}
- if (bb->fall_through) {
+ if (bb->fall_through != NullBasicBlockId) {
GetBlockName(bb, block_name1);
- GetBlockName(bb->fall_through, block_name2);
+ GetBlockName(GetBasicBlock(bb->fall_through), block_name2);
fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2);
}
- if (bb->successor_block_list.block_list_type != kNotUsed) {
+ if (bb->successor_block_list_type != kNotUsed) {
fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n",
bb->start_offset, bb->id,
- (bb->successor_block_list.block_list_type == kCatch) ?
- "Mrecord" : "record");
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ (bb->successor_block_list_type == kCatch) ? "Mrecord" : "record");
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
SuccessorBlockInfo *successor_block_info = iterator.Next();
int succ_id = 0;
while (true) {
if (successor_block_info == NULL) break;
- BasicBlock *dest_block = successor_block_info->block;
+ BasicBlock *dest_block = GetBasicBlock(successor_block_info->block);
SuccessorBlockInfo *next_successor_block_info = iterator.Next();
fprintf(file, " {<f%d> %04x: %04x\\l}%s\\\n",
@@ -800,16 +832,16 @@
fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n",
block_name1, bb->start_offset, bb->id);
- if (bb->successor_block_list.block_list_type == kPackedSwitch ||
- bb->successor_block_list.block_list_type == kSparseSwitch) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks);
+ if (bb->successor_block_list_type == kPackedSwitch ||
+ bb->successor_block_list_type == kSparseSwitch) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
succ_id = 0;
while (true) {
SuccessorBlockInfo *successor_block_info = iter.Next();
if (successor_block_info == NULL) break;
- BasicBlock *dest_block = successor_block_info->block;
+ BasicBlock* dest_block = GetBasicBlock(successor_block_info->block);
GetBlockName(dest_block, block_name2);
fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset,
@@ -825,7 +857,7 @@
fprintf(file, " cfg%s [label=\"%s\", shape=none];\n",
block_name1, block_name1);
if (bb->i_dom) {
- GetBlockName(bb->i_dom, block_name2);
+ GetBlockName(GetBasicBlock(bb->i_dom), block_name2);
fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1);
}
}
@@ -839,10 +871,9 @@
if (bb->first_mir_insn == NULL) {
DCHECK(bb->last_mir_insn == NULL);
bb->last_mir_insn = bb->first_mir_insn = mir;
- mir->prev = mir->next = NULL;
+ mir->next = NULL;
} else {
bb->last_mir_insn->next = mir;
- mir->prev = bb->last_mir_insn;
mir->next = NULL;
bb->last_mir_insn = mir;
}
@@ -853,25 +884,19 @@
if (bb->first_mir_insn == NULL) {
DCHECK(bb->last_mir_insn == NULL);
bb->last_mir_insn = bb->first_mir_insn = mir;
- mir->prev = mir->next = NULL;
+ mir->next = NULL;
} else {
- bb->first_mir_insn->prev = mir;
mir->next = bb->first_mir_insn;
- mir->prev = NULL;
bb->first_mir_insn = mir;
}
}
/* Insert a MIR instruction after the specified MIR */
void MIRGraph::InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir) {
- new_mir->prev = current_mir;
new_mir->next = current_mir->next;
current_mir->next = new_mir;
- if (new_mir->next) {
- /* Is not the last MIR in the block */
- new_mir->next->prev = new_mir;
- } else {
+ if (bb->last_mir_insn == current_mir) {
/* Is the last MIR in the block */
bb->last_mir_insn = new_mir;
}
@@ -901,8 +926,9 @@
opcode = insn.opcode;
} else if (opcode == kMirOpNop) {
str.append("[");
- insn.opcode = mir->meta.original_opcode;
- opcode = mir->meta.original_opcode;
+ // Recover original opcode.
+ insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode();
+ opcode = insn.opcode;
nop = true;
}
@@ -915,7 +941,7 @@
}
if (opcode == kMirOpPhi) {
- int* incoming = reinterpret_cast<int*>(insn.vB);
+ 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()));
@@ -1065,7 +1091,7 @@
}
const char* MIRGraph::GetShortyFromTargetIdx(int target_idx) {
- // FIXME: use current code unit for inline support.
+ // TODO: for inlining support, use current code unit.
const DexFile::MethodId& method_id = cu_->dex_file->GetMethodId(target_idx);
return cu_->dex_file->GetShorty(method_id.proto_idx_);
}
@@ -1095,13 +1121,13 @@
bb->start_offset,
bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset,
bb->last_mir_insn ? "" : " empty");
- if (bb->taken) {
- LOG(INFO) << " Taken branch: block " << bb->taken->id
- << "(0x" << std::hex << bb->taken->start_offset << ")";
+ if (bb->taken != NullBasicBlockId) {
+ LOG(INFO) << " Taken branch: block " << bb->taken
+ << "(0x" << std::hex << GetBasicBlock(bb->taken)->start_offset << ")";
}
- if (bb->fall_through) {
- LOG(INFO) << " Fallthrough : block " << bb->fall_through->id
- << " (0x" << std::hex << bb->fall_through->start_offset << ")";
+ if (bb->fall_through != NullBasicBlockId) {
+ LOG(INFO) << " Fallthrough : block " << bb->fall_through
+ << " (0x" << std::hex << GetBasicBlock(bb->fall_through)->start_offset << ")";
}
}
}
@@ -1121,7 +1147,6 @@
info->result.location = kLocInvalid;
} else {
info->result = GetRawDest(move_result_mir);
- move_result_mir->meta.original_opcode = move_result_mir->dalvikInsn.opcode;
move_result_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
}
info->num_arg_words = mir->ssa_rep->num_uses;
@@ -1145,10 +1170,10 @@
bb->block_type = block_type;
bb->id = block_id;
// TUNING: better estimate of the exit block predecessors?
- bb->predecessors = new (arena_) GrowableArray<BasicBlock*>(arena_,
+ bb->predecessors = new (arena_) GrowableArray<BasicBlockId>(arena_,
(block_type == kExitBlock) ? 2048 : 2,
kGrowableArrayPredecessors);
- bb->successor_block_list.block_list_type = kNotUsed;
+ bb->successor_block_list_type = kNotUsed;
block_id_map_.Put(block_id, block_id);
return bb;
}
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 6f8bd85..a69dde0 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -149,7 +149,7 @@
#define DF_C_IS_REG (DF_UC)
#define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER)
#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C)
-
+#define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)
enum OatMethodAttributes {
kIsLeaf, // Method is leaf.
kHasLoop, // Method contains simple loop.
@@ -183,6 +183,9 @@
#define BLOCK_NAME_LEN 80
+typedef uint16_t BasicBlockId;
+static const BasicBlockId NullBasicBlockId = 0;
+
/*
* In general, vreg/sreg describe Dalvik registers that originated with dx. However,
* it is useful to have compiler-generated temporary registers and have them treated
@@ -190,15 +193,15 @@
* name of compiler-introduced temporaries.
*/
struct CompilerTemp {
- int s_reg;
+ int32_t s_reg;
};
// When debug option enabled, records effectiveness of null and range check elimination.
struct Checkstats {
- int null_checks;
- int null_checks_eliminated;
- int range_checks;
- int range_checks_eliminated;
+ int32_t null_checks;
+ int32_t null_checks_eliminated;
+ int32_t range_checks;
+ int32_t range_checks_eliminated;
};
// Dataflow attributes of a basic block.
@@ -207,7 +210,7 @@
ArenaBitVector* def_v;
ArenaBitVector* live_in_v;
ArenaBitVector* phi_v;
- int* vreg_to_ssa_map;
+ int32_t* vreg_to_ssa_map;
ArenaBitVector* ending_null_check_v;
};
@@ -220,11 +223,11 @@
* we may want to revisit in the future.
*/
struct SSARepresentation {
- int num_uses;
- int* uses;
+ int16_t num_uses;
+ int16_t num_defs;
+ int32_t* uses;
bool* fp_use;
- int num_defs;
- int* defs;
+ int32_t* defs;
bool* fp_def;
};
@@ -233,51 +236,53 @@
* wrapper around a Dalvik byte code.
*/
struct MIR {
+ /*
+ * TODO: remove embedded DecodedInstruction to save space, keeping only opcode. Recover
+ * additional fields on as-needed basis. Question: how to support MIR Pseudo-ops; probably
+ * need to carry aux data pointer.
+ */
DecodedInstruction dalvikInsn;
- uint32_t width; // NOTE: only need 16 bits for width.
- unsigned int offset;
- int m_unit_index; // From which method was this MIR included
- MIR* prev;
+ uint16_t width; // Note: width can include switch table or fill array data.
+ NarrowDexOffset offset; // Offset of the instruction in code units.
+ uint16_t optimization_flags;
+ int16_t m_unit_index; // From which method was this MIR included
MIR* next;
SSARepresentation* ssa_rep;
- int optimization_flags;
union {
+ // Incoming edges for phi node.
+ BasicBlockId* phi_incoming;
// Establish link between two halves of throwing instructions.
MIR* throw_insn;
- // Saved opcode for NOP'd MIRs
- Instruction::Code original_opcode;
} meta;
};
struct SuccessorBlockInfo;
struct BasicBlock {
- int id;
- int dfs_id;
- bool visited;
- bool hidden;
- bool catch_entry;
- bool explicit_throw;
- bool conditional_branch;
- bool terminated_by_return; // Block ends with a Dalvik return opcode.
- bool dominates_return; // Is a member of return extended basic block.
- uint16_t start_offset;
+ BasicBlockId id;
+ BasicBlockId dfs_id;
+ NarrowDexOffset start_offset; // Offset in code units.
+ BasicBlockId fall_through;
+ BasicBlockId taken;
+ BasicBlockId i_dom; // Immediate dominator.
uint16_t nesting_depth;
- BBType block_type;
+ BBType block_type:4;
+ BlockListType successor_block_list_type:4;
+ bool visited:1;
+ bool hidden:1;
+ bool catch_entry:1;
+ bool explicit_throw:1;
+ bool conditional_branch:1;
+ bool terminated_by_return:1; // Block ends with a Dalvik return opcode.
+ bool dominates_return:1; // Is a member of return extended basic block.
MIR* first_mir_insn;
MIR* last_mir_insn;
- BasicBlock* fall_through;
- BasicBlock* taken;
- BasicBlock* i_dom; // Immediate dominator.
BasicBlockDataFlow* data_flow_info;
- GrowableArray<BasicBlock*>* predecessors;
ArenaBitVector* dominators;
ArenaBitVector* i_dominated; // Set nodes being immediately dominated.
ArenaBitVector* dom_frontier; // Dominance frontier.
- struct { // For one-to-many successors like.
- BlockListType block_list_type; // switch and exception handling.
- GrowableArray<SuccessorBlockInfo*>* blocks;
- } successor_block_list;
+ GrowableArray<BasicBlockId>* predecessors;
+ GrowableArray<SuccessorBlockInfo*>* successor_blocks;
};
/*
@@ -285,9 +290,8 @@
* "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For swtich
* blocks, key is the case value.
*/
-// TODO: make class with placement new.
struct SuccessorBlockInfo {
- BasicBlock* block;
+ BasicBlockId block;
int key;
};
@@ -296,6 +300,15 @@
* the type of an SSA name (and, can also be used by code generators to record where the
* value is located (i.e. - physical register, frame, spill, etc.). For each SSA name (SReg)
* there is a RegLocation.
+ * A note on SSA names:
+ * o SSA names for Dalvik vRegs v0..vN will be assigned 0..N. These represent the "vN_0"
+ * names. Negative SSA names represent special values not present in the Dalvik byte code.
+ * For example, SSA name -1 represents an invalid SSA name, and SSA name -2 represents the
+ * the Method pointer. SSA names < -2 are reserved for future use.
+ * o The vN_0 names for non-argument Dalvik should in practice never be used (as they would
+ * represent the read of an undefined local variable). The first definition of the
+ * underlying Dalvik vReg will result in a vN_1 name.
+ *
* FIXME: The orig_sreg field was added as a workaround for llvm bitcode generation. With
* the latest restructuring, we should be able to remove it and rely on s_reg_low throughout.
*/
@@ -311,9 +324,9 @@
unsigned home:1; // Does this represent the home location?
uint8_t low_reg; // First physical register.
uint8_t high_reg; // 2nd physical register (if wide).
- int32_t s_reg_low; // SSA name for low Dalvik word.
- int32_t orig_sreg; // TODO: remove after Bitcode gen complete
- // and consolodate usage w/ s_reg_low.
+ int16_t s_reg_low; // SSA name for low Dalvik word.
+ int16_t orig_sreg; // TODO: remove after Bitcode gen complete
+ // and consolidate usage w/ s_reg_low.
};
/*
@@ -334,7 +347,7 @@
RegLocation target; // Target of following move_result.
bool skip_this;
bool is_range;
- int offset; // Dalvik offset.
+ DexOffset offset; // Offset in code units.
};
@@ -361,7 +374,7 @@
uint32_t method_idx, jobject class_loader, const DexFile& dex_file);
/* Find existing block */
- BasicBlock* FindBlock(unsigned int code_offset) {
+ BasicBlock* FindBlock(DexOffset code_offset) {
return FindBlock(code_offset, false, false, NULL);
}
@@ -394,7 +407,7 @@
}
BasicBlock* GetBasicBlock(int block_id) const {
- return block_list_.Get(block_id);
+ return (block_id == NullBasicBlockId) ? NULL : block_list_.Get(block_id);
}
size_t GetBasicBlockListCount() const {
@@ -405,15 +418,15 @@
return &block_list_;
}
- GrowableArray<int>* GetDfsOrder() {
+ GrowableArray<BasicBlockId>* GetDfsOrder() {
return dfs_order_;
}
- GrowableArray<int>* GetDfsPostOrder() {
+ GrowableArray<BasicBlockId>* GetDfsPostOrder() {
return dfs_post_order_;
}
- GrowableArray<int>* GetDomPostOrder() {
+ GrowableArray<BasicBlockId>* GetDomPostOrder() {
return dom_post_order_traversal_;
}
@@ -477,6 +490,12 @@
}
void SetNumSSARegs(int new_num) {
+ /*
+ * TODO: It's theoretically possible to exceed 32767, though any cases which did
+ * would be filtered out with current settings. When orig_sreg field is removed
+ * from RegLocation, expand s_reg_low to handle all possible cases and remove DCHECK().
+ */
+ DCHECK_EQ(new_num, static_cast<int16_t>(new_num));
num_ssa_regs_ = new_num;
}
@@ -561,14 +580,35 @@
return special_case_;
}
- bool IsBackedge(BasicBlock* branch_bb, BasicBlock* target_bb) {
- return ((target_bb != NULL) && (target_bb->start_offset <= branch_bb->start_offset));
+ bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
+ return ((target_bb_id != NullBasicBlockId) &&
+ (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset));
}
bool IsBackwardsBranch(BasicBlock* branch_bb) {
return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through);
}
+ void CountBranch(DexOffset target_offset) {
+ if (target_offset <= current_offset_) {
+ backward_branches_++;
+ } else {
+ forward_branches_++;
+ }
+ }
+
+ int GetBranchCount() {
+ return backward_branches_ + forward_branches_;
+ }
+
+ bool IsPseudoMirOp(Instruction::Code opcode) {
+ return static_cast<int>(opcode) >= static_cast<int>(kMirOpFirst);
+ }
+
+ bool IsPseudoMirOp(int opcode) {
+ return opcode >= static_cast<int>(kMirOpFirst);
+ }
+
void BasicBlockCombine();
void CodeLayout();
void DumpCheckStats();
@@ -580,11 +620,34 @@
void SSATransformation();
void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb);
void NullCheckElimination();
+ /*
+ * Type inference handling helpers. Because Dalvik's bytecode is not fully typed,
+ * we have to do some work to figure out the sreg type. For some operations it is
+ * clear based on the opcode (i.e. ADD_FLOAT v0, v1, v2), but for others (MOVE), we
+ * may never know the "real" type.
+ *
+ * We perform the type inference operation by using an iterative walk over
+ * the graph, propagating types "defined" by typed opcodes to uses and defs in
+ * non-typed opcodes (such as MOVE). The Setxx(index) helpers are used to set defined
+ * types on typed opcodes (such as ADD_INT). The Setxx(index, is_xx) form is used to
+ * propagate types through non-typed opcodes such as PHI and MOVE. The is_xx flag
+ * tells whether our guess of the type is based on a previously typed definition.
+ * If so, the defined type takes precedence. Note that it's possible to have the same sreg
+ * show multiple defined types because dx treats constants as untyped bit patterns.
+ * The return value of the Setxx() helpers says whether or not the Setxx() action changed
+ * the current guess, and is used to know when to terminate the iterative walk.
+ */
bool SetFp(int index, bool is_fp);
+ bool SetFp(int index);
bool SetCore(int index, bool is_core);
+ bool SetCore(int index);
bool SetRef(int index, bool is_ref);
+ bool SetRef(int index);
bool SetWide(int index, bool is_wide);
+ bool SetWide(int index);
bool SetHigh(int index, bool is_high);
+ bool SetHigh(int index);
+
void AppendMIR(BasicBlock* bb, MIR* mir);
void PrependMIR(BasicBlock* bb, MIR* mir);
void InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir);
@@ -597,6 +660,9 @@
void DumpMIRGraph();
CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range);
BasicBlock* NewMemBB(BBType block_type, int block_id);
+ MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir);
+ BasicBlock* NextDominatedBlock(BasicBlock* bb);
+ bool LayoutBlocks(BasicBlock* bb);
/*
* IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
@@ -625,15 +691,16 @@
bool InvokeUsesMethodStar(MIR* mir);
int ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction);
bool ContentIsInsn(const uint16_t* code_ptr);
- BasicBlock* SplitBlock(unsigned int code_offset, BasicBlock* orig_block,
+ BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block,
BasicBlock** immed_pred_block_p);
- BasicBlock* FindBlock(unsigned int code_offset, bool split, bool create,
+ BasicBlock* FindBlock(DexOffset code_offset, bool split, bool create,
BasicBlock** immed_pred_block_p);
void ProcessTryCatchBlocks();
- BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags, const uint16_t* code_ptr, const uint16_t* code_end);
- void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, int flags);
- BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
+ int flags);
+ BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr,
const uint16_t* code_end);
int AddNewSReg(int v_reg);
@@ -689,9 +756,9 @@
GrowableArray<uint32_t> use_counts_; // Weighted by nesting depth
GrowableArray<uint32_t> raw_use_counts_; // Not weighted
unsigned int num_reachable_blocks_;
- GrowableArray<int>* dfs_order_;
- GrowableArray<int>* dfs_post_order_;
- GrowableArray<int>* dom_post_order_traversal_;
+ GrowableArray<BasicBlockId>* dfs_order_;
+ GrowableArray<BasicBlockId>* dfs_post_order_;
+ GrowableArray<BasicBlockId>* dom_post_order_traversal_;
int* i_dom_list_;
ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks.
ArenaBitVector* temp_block_v_;
@@ -702,24 +769,25 @@
ArenaBitVector* try_block_addr_;
BasicBlock* entry_block_;
BasicBlock* exit_block_;
- BasicBlock* cur_block_;
int num_blocks_;
const DexFile::CodeItem* current_code_item_;
- SafeMap<unsigned int, BasicBlock*> block_map_; // FindBlock lookup cache.
+ GrowableArray<uint16_t> dex_pc_to_block_map_; // FindBlock lookup cache.
std::vector<DexCompilationUnit*> m_units_; // List of methods included in this graph
typedef std::pair<int, int> MIRLocation; // Insert point, (m_unit_ index, offset)
std::vector<MIRLocation> method_stack_; // Include stack
int current_method_;
- int current_offset_;
+ DexOffset current_offset_; // Offset in code units
int def_count_; // Used to estimate size of ssa name storage.
int* opcode_count_; // Dex opcode coverage stats.
int num_ssa_regs_; // Number of names following SSA transformation.
- std::vector<BasicBlock*> extended_basic_blocks_; // Heads of block "traces".
+ std::vector<BasicBlockId> extended_basic_blocks_; // Heads of block "traces".
int method_sreg_;
unsigned int attributes_;
Checkstats* checkstats_;
SpecialCaseHandler special_case_;
ArenaAllocator* arena_;
+ int backward_branches_;
+ int forward_branches_;
};
} // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index b7611f8..f5913a5 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -96,19 +96,19 @@
is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false);
constant_values_ = static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumSSARegs(),
ArenaAllocator::kAllocDFInfo));
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
DoConstantPropogation(bb);
}
}
/* Advance to next strictly dominated MIR node in an extended basic block */
-static MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir) {
+MIR* MIRGraph::AdvanceMIR(BasicBlock** p_bb, MIR* mir) {
BasicBlock* bb = *p_bb;
if (mir != NULL) {
mir = mir->next;
if (mir == NULL) {
- bb = bb->fall_through;
+ bb = GetBasicBlock(bb->fall_through);
if ((bb == NULL) || Predecessors(bb) != 1) {
mir = NULL;
} else {
@@ -147,19 +147,21 @@
return mir;
}
-static BasicBlock* NextDominatedBlock(BasicBlock* bb) {
+BasicBlock* MIRGraph::NextDominatedBlock(BasicBlock* bb) {
if (bb->block_type == kDead) {
return NULL;
}
DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode)
|| (bb->block_type == kExitBlock));
- if (((bb->taken != NULL) && (bb->fall_through == NULL)) &&
- ((bb->taken->block_type == kDalvikByteCode) || (bb->taken->block_type == kExitBlock))) {
+ BasicBlock* bb_taken = GetBasicBlock(bb->taken);
+ BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through);
+ if (((bb_taken != NULL) && (bb_fall_through == NULL)) &&
+ ((bb_taken->block_type == kDalvikByteCode) || (bb_taken->block_type == kExitBlock))) {
// Follow simple unconditional branches.
- bb = bb->taken;
+ bb = bb_taken;
} else {
// Follow simple fallthrough
- bb = (bb->taken != NULL) ? NULL : bb->fall_through;
+ bb = (bb_taken != NULL) ? NULL : bb_fall_through;
}
if (bb == NULL || (Predecessors(bb) != 1)) {
return NULL;
@@ -311,11 +313,13 @@
case Instruction::IF_GTZ:
case Instruction::IF_LEZ:
// If we've got a backwards branch to return, no need to suspend check.
- if ((IsBackedge(bb, bb->taken) && bb->taken->dominates_return) ||
- (IsBackedge(bb, bb->fall_through) && bb->fall_through->dominates_return)) {
+ if ((IsBackedge(bb, bb->taken) && GetBasicBlock(bb->taken)->dominates_return) ||
+ (IsBackedge(bb, bb->fall_through) &&
+ GetBasicBlock(bb->fall_through)->dominates_return)) {
mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
if (cu_->verbose) {
- LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex << mir->offset;
+ LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex
+ << mir->offset;
}
}
break;
@@ -328,15 +332,15 @@
if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) &&
((mir->dalvikInsn.opcode == Instruction::IF_EQZ) ||
(mir->dalvikInsn.opcode == Instruction::IF_NEZ))) {
- BasicBlock* ft = bb->fall_through;
+ BasicBlock* ft = GetBasicBlock(bb->fall_through);
DCHECK(ft != NULL);
- BasicBlock* ft_ft = ft->fall_through;
- BasicBlock* ft_tk = ft->taken;
+ BasicBlock* ft_ft = GetBasicBlock(ft->fall_through);
+ BasicBlock* ft_tk = GetBasicBlock(ft->taken);
- BasicBlock* tk = bb->taken;
+ BasicBlock* tk = GetBasicBlock(bb->taken);
DCHECK(tk != NULL);
- BasicBlock* tk_ft = tk->fall_through;
- BasicBlock* tk_tk = tk->taken;
+ BasicBlock* tk_ft = GetBasicBlock(tk->fall_through);
+ BasicBlock* tk_tk = GetBasicBlock(tk->taken);
/*
* In the select pattern, the taken edge goes to a block that unconditionally
@@ -434,7 +438,7 @@
int dead_def = if_false->ssa_rep->defs[0];
int live_def = if_true->ssa_rep->defs[0];
mir->ssa_rep->defs[0] = live_def;
- int* incoming = reinterpret_cast<int*>(phi->dalvikInsn.vB);
+ BasicBlockId* incoming = phi->meta.phi_incoming;
for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
if (phi->ssa_rep->uses[i] == live_def) {
incoming[i] = bb->id;
@@ -449,7 +453,7 @@
}
}
phi->ssa_rep->num_uses--;
- bb->taken = NULL;
+ bb->taken = NullBasicBlockId;
tk->block_type = kDead;
for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) {
tmir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
@@ -500,7 +504,7 @@
}
/* Try to make common case the fallthrough path */
-static bool LayoutBlocks(struct BasicBlock* bb) {
+bool MIRGraph::LayoutBlocks(BasicBlock* bb) {
// TODO: For now, just looking for direct throws. Consider generalizing for profile feedback
if (!bb->explicit_throw) {
return false;
@@ -511,13 +515,13 @@
if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) {
break;
}
- BasicBlock* prev = walker->predecessors->Get(0);
+ BasicBlock* prev = GetBasicBlock(walker->predecessors->Get(0));
if (prev->conditional_branch) {
- if (prev->fall_through == walker) {
+ if (GetBasicBlock(prev->fall_through) == walker) {
// Already done - return
break;
}
- DCHECK_EQ(walker, prev->taken);
+ DCHECK_EQ(walker, GetBasicBlock(prev->taken));
// Got one. Flip it and exit
Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode;
switch (opcode) {
@@ -536,7 +540,7 @@
default: LOG(FATAL) << "Unexpected opcode " << opcode;
}
prev->last_mir_insn->dalvikInsn.opcode = opcode;
- BasicBlock* t_bb = prev->taken;
+ BasicBlockId t_bb = prev->taken;
prev->taken = prev->fall_through;
prev->fall_through = t_bb;
break;
@@ -556,8 +560,9 @@
|| (bb->block_type == kExceptionHandling)
|| (bb->block_type == kExitBlock)
|| (bb->block_type == kDead)
- || ((bb->taken == NULL) || (bb->taken->block_type != kExceptionHandling))
- || (bb->successor_block_list.block_list_type != kNotUsed)
+ || (bb->taken == NullBasicBlockId)
+ || (GetBasicBlock(bb->taken)->block_type != kExceptionHandling)
+ || (bb->successor_block_list_type != kNotUsed)
|| (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) {
break;
}
@@ -578,19 +583,18 @@
break;
}
// OK - got one. Combine
- BasicBlock* bb_next = bb->fall_through;
+ BasicBlock* bb_next = GetBasicBlock(bb->fall_through);
DCHECK(!bb_next->catch_entry);
DCHECK_EQ(Predecessors(bb_next), 1U);
- MIR* t_mir = bb->last_mir_insn->prev;
// Overwrite the kOpCheck insn with the paired opcode
DCHECK_EQ(bb_next->first_mir_insn, throw_insn);
*bb->last_mir_insn = *throw_insn;
- bb->last_mir_insn->prev = t_mir;
// Use the successor info from the next block
- bb->successor_block_list = bb_next->successor_block_list;
+ bb->successor_block_list_type = bb_next->successor_block_list_type;
+ bb->successor_blocks = bb_next->successor_blocks;
// Use the ending block linkage from the next block
bb->fall_through = bb_next->fall_through;
- bb->taken->block_type = kDead; // Kill the unused exception block
+ GetBasicBlock(bb->taken)->block_type = kDead; // Kill the unused exception block
bb->taken = bb_next->taken;
// Include the rest of the instructions
bb->last_mir_insn = bb_next->last_mir_insn;
@@ -625,49 +629,57 @@
*/
if ((bb->block_type == kEntryBlock) | bb->catch_entry) {
temp_ssa_register_v_->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++) {
+ temp_ssa_register_v_->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;
- temp_ssa_register_v_->SetBit(this_reg);
+ temp_ssa_register_v_->ClearBit(this_reg);
}
} else if (bb->predecessors->Size() == 1) {
- BasicBlock* pred_bb = bb->predecessors->Get(0);
+ BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v);
if (pred_bb->block_type == kDalvikByteCode) {
// Check to see if predecessor had an explicit null-check.
MIR* last_insn = pred_bb->last_mir_insn;
Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
if (last_opcode == Instruction::IF_EQZ) {
- if (pred_bb->fall_through == bb) {
+ if (pred_bb->fall_through == bb->id) {
// The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that
// it can't be null.
- temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]);
+ temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]);
}
} else if (last_opcode == Instruction::IF_NEZ) {
- if (pred_bb->taken == bb) {
+ if (pred_bb->taken == bb->id) {
// The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be
// null.
- temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]);
+ temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]);
}
}
}
} else {
- // Starting state is intersection of all incoming arcs
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
- BasicBlock* pred_bb = iter.Next();
+ // Starting state is union of all incoming arcs
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
DCHECK(pred_bb != NULL);
temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v);
while (true) {
- pred_bb = iter.Next();
+ pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) break;
if ((pred_bb->data_flow_info == NULL) ||
(pred_bb->data_flow_info->ending_null_check_v == NULL)) {
continue;
}
- temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v);
+ temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v);
}
}
+ // At this point, temp_ssa_register_v_ shows which sregs have an object definition with
+ // no intervening uses.
+
// Walk through the instruction in the block, updating as necessary
for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
if (mir->ssa_rep == NULL) {
@@ -675,11 +687,49 @@
}
int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
- // Mark target of NEW* as non-null
- if (df_attributes & DF_NON_NULL_DST) {
+ // Might need a null check?
+ if (df_attributes & DF_HAS_NULL_CHKS) {
+ int src_idx;
+ if (df_attributes & DF_NULL_CHK_1) {
+ src_idx = 1;
+ } else if (df_attributes & DF_NULL_CHK_2) {
+ src_idx = 2;
+ } else {
+ src_idx = 0;
+ }
+ int src_sreg = mir->ssa_rep->uses[src_idx];
+ if (!temp_ssa_register_v_->IsBitSet(src_sreg)) {
+ // Eliminate the null check.
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ // Do the null check.
+ mir->optimization_flags &= ~MIR_IGNORE_NULL_CHECK;
+ // Mark s_reg as null-checked
+ temp_ssa_register_v_->ClearBit(src_sreg);
+ }
+ }
+
+ if ((df_attributes & DF_A_WIDE) ||
+ (df_attributes & (DF_REF_A | DF_SETS_CONST | DF_NULL_TRANSFER)) == 0) {
+ continue;
+ }
+
+ /*
+ * First, mark all object definitions as requiring null check.
+ * Note: we can't tell if a CONST definition might be used as an object, so treat
+ * them all as object definitions.
+ */
+ if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) ||
+ (df_attributes & DF_SETS_CONST)) {
temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]);
}
+ // Now, remove mark from all object definitions we know are non-null.
+ if (df_attributes & DF_NON_NULL_DST) {
+ // Mark target of NEW* as non-null
+ temp_ssa_register_v_->ClearBit(mir->ssa_rep->defs[0]);
+ }
+
// Mark non-null returns from invoke-style NEW*
if (df_attributes & DF_NON_NULL_RET) {
MIR* next_mir = mir->next;
@@ -687,13 +737,13 @@
if (next_mir &&
next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
// Mark as null checked
- temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]);
+ temp_ssa_register_v_->ClearBit(next_mir->ssa_rep->defs[0]);
} else {
if (next_mir) {
LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
- } else if (bb->fall_through) {
+ } else if (bb->fall_through != NullBasicBlockId) {
// Look in next basic block
- struct BasicBlock* next_bb = bb->fall_through;
+ struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through);
for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL;
tmir =tmir->next) {
if (static_cast<int>(tmir->dalvikInsn.opcode) >= static_cast<int>(kMirOpFirst)) {
@@ -702,7 +752,7 @@
// First non-pseudo should be MOVE_RESULT_OBJECT
if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
// Mark as null checked
- temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]);
+ temp_ssa_register_v_->ClearBit(tmir->ssa_rep->defs[0]);
} else {
LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode;
}
@@ -715,40 +765,22 @@
/*
* Propagate nullcheck state on register copies (including
* Phi pseudo copies. For the latter, nullcheck state is
- * the "and" of all the Phi's operands.
+ * the "or" of all the Phi's operands.
*/
if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) {
int tgt_sreg = mir->ssa_rep->defs[0];
int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 :
mir->ssa_rep->num_uses;
- bool null_checked = true;
+ bool needs_null_check = false;
for (int i = 0; i < operands; i++) {
- null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]);
+ needs_null_check |= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]);
}
- if (null_checked) {
+ if (needs_null_check) {
temp_ssa_register_v_->SetBit(tgt_sreg);
+ } else {
+ temp_ssa_register_v_->ClearBit(tgt_sreg);
}
}
-
- // Already nullchecked?
- if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
- int src_idx;
- if (df_attributes & DF_NULL_CHK_1) {
- src_idx = 1;
- } else if (df_attributes & DF_NULL_CHK_2) {
- src_idx = 2;
- } else {
- src_idx = 0;
- }
- int src_sreg = mir->ssa_rep->uses[src_idx];
- if (temp_ssa_register_v_->IsBitSet(src_sreg)) {
- // Eliminate the null check
- mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
- } else {
- // Mark s_reg as null-checked
- temp_ssa_register_v_->SetBit(src_sreg);
- }
- }
}
// Did anything change?
@@ -762,11 +794,11 @@
void MIRGraph::NullCheckElimination() {
if (!(cu_->disable_opt & (1 << kNullCheckElimination))) {
DCHECK(temp_ssa_register_v_ != NULL);
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
NullCheckEliminationInit(bb);
}
- PreOrderDfsIterator iter2(this, true /* iterative */);
+ RepeatingPreOrderDfsIterator iter2(this);
bool change = false;
for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) {
change = EliminateNullChecks(bb);
@@ -778,7 +810,7 @@
}
void MIRGraph::BasicBlockCombine() {
- PreOrderDfsIterator iter(this, false /* not iterative */);
+ PreOrderDfsIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CombineBlocks(bb);
}
@@ -791,7 +823,7 @@
if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) {
VerifyDataflow();
}
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
LayoutBlocks(bb);
}
@@ -804,7 +836,7 @@
Checkstats* stats =
static_cast<Checkstats*>(arena_->Alloc(sizeof(Checkstats), ArenaAllocator::kAllocDFInfo));
checkstats_ = stats;
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CountChecks(bb);
}
@@ -834,7 +866,7 @@
}
// Must be head of extended basic block.
BasicBlock* start_bb = bb;
- extended_basic_blocks_.push_back(bb);
+ extended_basic_blocks_.push_back(bb->id);
bool terminated_by_return = false;
// Visit blocks strictly dominated by this head.
while (bb != NULL) {
@@ -858,13 +890,13 @@
if (!(cu_->disable_opt & (1 << kBBOpt))) {
DCHECK_EQ(cu_->num_compiler_temps, 0);
ClearAllVisitedFlags();
- PreOrderDfsIterator iter2(this, false /* not iterative */);
+ PreOrderDfsIterator iter2(this);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
BuildExtendedBBList(bb);
}
// Perform extended basic block optimizations.
for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) {
- BasicBlockOpt(extended_basic_blocks_[i]);
+ BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i]));
}
}
if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
index 90cec75..07bd2aa 100644
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -30,10 +30,10 @@
#include "dex/compiler_internals.h"
#include "dex/dataflow_iterator-inl.h"
#include "dex/frontend.h"
-#include "mir_to_gbc.h"
-
#include "llvm/llvm_compilation_unit.h"
#include "llvm/utils_llvm.h"
+#include "mir_to_gbc.h"
+#include "thread-inl.h"
const char* kLabelFormat = "%c0x%x_%d";
const char kInvalidBlock = 0xff;
@@ -132,7 +132,7 @@
::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
::llvm::SwitchInst* sw =
- irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id),
+ irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
payload->case_count);
for (uint16_t i = 0; i < payload->case_count; ++i) {
@@ -143,8 +143,8 @@
::llvm::MDNode* switch_node =
::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
sw->setMetadata("SwitchTable", switch_node);
- bb->taken = NULL;
- bb->fall_through = NULL;
+ bb->taken = NullBasicBlockId;
+ bb->fall_through = NullBasicBlockId;
}
void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
@@ -159,7 +159,7 @@
::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
::llvm::SwitchInst* sw =
- irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id),
+ irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
payload->case_count);
for (size_t i = 0; i < payload->case_count; ++i) {
@@ -170,8 +170,8 @@
::llvm::MDNode* switch_node =
::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
sw->setMetadata("SwitchTable", switch_node);
- bb->taken = NULL;
- bb->fall_through = NULL;
+ bb->taken = NullBasicBlockId;
+ bb->fall_through = NullBasicBlockId;
}
void MirConverter::ConvertSget(int32_t field_index,
@@ -311,22 +311,22 @@
void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir,
ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2) {
- if (bb->taken->start_offset <= mir->offset) {
+ if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
EmitSuspendCheck();
}
::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
cond_value->setName(StringPrintf("t%d", temp_name_++));
- irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id),
- GetLLVMBlock(bb->fall_through->id));
+ irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
+ GetLLVMBlock(bb->fall_through));
// Don't redo the fallthrough branch in the BB driver
- bb->fall_through = NULL;
+ bb->fall_through = NullBasicBlockId;
}
void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb,
MIR* mir, ConditionCode cc, RegLocation rl_src1) {
- if (bb->taken->start_offset <= mir->offset) {
+ if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
EmitSuspendCheck();
}
::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
@@ -337,10 +337,10 @@
src2 = irb_->getInt32(0);
}
::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
- irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id),
- GetLLVMBlock(bb->fall_through->id));
+ irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
+ GetLLVMBlock(bb->fall_through));
// Don't redo the fallthrough branch in the BB driver
- bb->fall_through = NULL;
+ bb->fall_through = NullBasicBlockId;
}
::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long,
@@ -941,10 +941,10 @@
case Instruction::GOTO:
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
- if (bb->taken->start_offset <= bb->start_offset) {
+ if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= bb->start_offset) {
EmitSuspendCheck();
}
- irb_->CreateBr(GetLLVMBlock(bb->taken->id));
+ irb_->CreateBr(GetLLVMBlock(bb->taken));
}
break;
@@ -1190,11 +1190,11 @@
* If it might rethrow, force termination
* of the following block.
*/
- if (bb->fall_through == NULL) {
+ if (bb->fall_through == NullBasicBlockId) {
irb_->CreateUnreachable();
} else {
- bb->fall_through->fall_through = NULL;
- bb->fall_through->taken = NULL;
+ mir_graph_->GetBasicBlock(bb->fall_through)->fall_through = NullBasicBlockId;
+ mir_graph_->GetBasicBlock(bb->fall_through)->taken = NullBasicBlockId;
}
break;
@@ -1552,7 +1552,7 @@
if (rl_dest.high_word) {
continue; // No Phi node - handled via low word
}
- int* incoming = reinterpret_cast<int*>(mir->dalvikInsn.vB);
+ BasicBlockId* incoming = mir->meta.phi_incoming;
::llvm::Type* phi_type =
LlvmTypeFromLocRec(rl_dest);
::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses);
@@ -1597,8 +1597,8 @@
break;
}
case kMirOpNop:
- if ((mir == bb->last_mir_insn) && (bb->taken == NULL) &&
- (bb->fall_through == NULL)) {
+ if ((mir == bb->last_mir_insn) && (bb->taken == NullBasicBlockId) &&
+ (bb->fall_through == NullBasicBlockId)) {
irb_->CreateUnreachable();
}
break;
@@ -1718,25 +1718,23 @@
SSARepresentation* ssa_rep = work_half->ssa_rep;
work_half->ssa_rep = mir->ssa_rep;
mir->ssa_rep = ssa_rep;
- work_half->meta.original_opcode = work_half->dalvikInsn.opcode;
work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
- if (bb->successor_block_list.block_list_type == kCatch) {
+ if (bb->successor_block_list_type == kCatch) {
::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
art::llvm::IntrinsicHelper::CatchTargets);
::llvm::Value* switch_key =
irb_->CreateCall(intr, irb_->getInt32(mir->offset));
- GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks);
+ 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_block_list.blocks->Size());
+ irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks->Size());
while (true) {
SuccessorBlockInfo *successor_block_info = iter.Next();
if (successor_block_info == NULL) break;
::llvm::BasicBlock *target =
- GetLLVMBlock(successor_block_info->block->id);
+ GetLLVMBlock(successor_block_info->block);
int type_index = successor_block_info->key;
sw->addCase(irb_->getInt32(type_index), target);
}
@@ -1761,9 +1759,9 @@
}
if (bb->block_type == kEntryBlock) {
- entry_target_bb_ = GetLLVMBlock(bb->fall_through->id);
- } else if ((bb->fall_through != NULL) && !bb->terminated_by_return) {
- irb_->CreateBr(GetLLVMBlock(bb->fall_through->id));
+ entry_target_bb_ = GetLLVMBlock(bb->fall_through);
+ } else if ((bb->fall_through != NullBasicBlockId) && !bb->terminated_by_return) {
+ irb_->CreateBr(GetLLVMBlock(bb->fall_through));
}
return false;
@@ -1877,7 +1875,7 @@
CreateFunction();
// Create an LLVM basic block for each MIR block in dfs preorder
- PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
+ PreOrderDfsIterator iter(mir_graph_);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CreateLLVMBasicBlock(bb);
}
@@ -1909,7 +1907,7 @@
}
}
- PreOrderDfsIterator iter2(mir_graph_, false /* not iterative */);
+ PreOrderDfsIterator iter2(mir_graph_);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
BlockBitcodeConversion(bb);
}
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 2f54190..ffaaf84 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -296,6 +296,8 @@
kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0].
kThumbPop, // pop [1011110] r[8..8] rl[7..0].
kThumbPush, // push [1011010] r[8..8] rl[7..0].
+ kThumbRev, // rev [1011101000] rm[5..3] rd[2..0]
+ kThumbRevsh, // revsh [1011101011] rm[5..3] rd[2..0]
kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0].
kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0].
kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0].
@@ -378,6 +380,8 @@
kThumb2CmnRR, // cmn [111010110001] rn[19..16] [0000] [1111] [0000] rm[3..0].
kThumb2EorRRR, // eor [111010101000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
kThumb2MulRRR, // mul [111110110000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2SdivRRR, // sdiv [111110111001] rn[19..16] [1111] rd[11..8] [1111] rm[3..0].
+ kThumb2UdivRRR, // udiv [111110111011] rn[19..16] [1111] rd[11..8] [1111] rm[3..0].
kThumb2MnvRR, // mvn [11101010011011110] rd[11-8] [0000] rm[3..0].
kThumb2RsubRRI8, // rsub [111100011100] rn[19..16] [0000] rd[11..8] imm8[7..0].
kThumb2NegRR, // actually rsub rd, rn, #0.
@@ -399,6 +403,8 @@
kThumb2AdcRRI8, // adc [111100010101] rn[19..16] [0] imm3 rd[11..8] imm8.
kThumb2SubRRI8, // sub [111100011011] rn[19..16] [0] imm3 rd[11..8] imm8.
kThumb2SbcRRI8, // sbc [111100010111] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2RevRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1000 rm[3..0]
+ kThumb2RevshRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1011 rm[3..0]
kThumb2It, // it [10111111] firstcond[7-4] mask[3-0].
kThumb2Fmstat, // fmstat [11101110111100011111101000010000].
kThumb2Vcmpd, // vcmp [111011101] D [11011] rd[15-12] [1011] E [1] M [0] rm[3-0].
@@ -462,7 +468,7 @@
// Instruction assembly field_loc kind.
enum ArmEncodingKind {
- kFmtUnused,
+ kFmtUnused, // Unused field and marks end of formats.
kFmtBitBlt, // Bit string using end/start.
kFmtDfp, // Double FP reg.
kFmtSfp, // Single FP reg.
@@ -477,6 +483,7 @@
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.
@@ -492,6 +499,7 @@
const char* name;
const char* fmt;
int size; // Note: size is in bytes.
+ FixupKind fixup;
};
} // namespace art
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 0649c9f..3d0f263 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -37,9 +37,9 @@
* fmt: for pretty-printing
*/
#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
- k3, k3s, k3e, flags, name, fmt, size) \
+ k3, k3s, k3e, flags, name, fmt, size, fixup) \
{skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
- {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+ {k3, k3s, k3e}}, opcode, flags, name, fmt, size, fixup}
/* Instruction dump string format keys: !pf, where "!" is the start
* of the key, "p" is which numeric operand to use and "f" is the
@@ -79,916 +79,966 @@
const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = {
ENCODING_MAP(kArm16BitData, 0x0000,
kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
- kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2),
+ kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2, kFixupNone),
ENCODING_MAP(kThumbAdcRR, 0x4140,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
- "adcs", "!0C, !1C", 2),
+ "adcs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRI3, 0x1c00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "adds", "!0C, !1C, #!2d", 2),
+ "adds", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbAddRI8, 0x3000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
- "adds", "!0C, !0C, #!1d", 2),
+ "adds", "!0C, !0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRR, 0x1800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
- "adds", "!0C, !1C, !2C", 2),
+ "adds", "!0C, !1C, !2C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRLH, 0x4440,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
- "add", "!0C, !1C", 2),
+ "add", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRHL, 0x4480,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
- "add", "!0C, !1C", 2),
+ "add", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRHH, 0x44c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
- "add", "!0C, !1C", 2),
+ "add", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddPcRel, 0xa000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH | NEEDS_FIXUP,
- "add", "!0C, pc, #!1E", 2),
+ "add", "!0C, pc, #!1E", 2, kFixupLoad),
ENCODING_MAP(kThumbAddSpRel, 0xa800,
- kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
- "add", "!0C, sp, #!2E", 2),
+ "add", "!0C, sp, #!2E", 2, kFixupNone),
ENCODING_MAP(kThumbAddSpI7, 0xb000,
kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
- "add", "sp, #!0d*4", 2),
+ "add", "sp, #!0d*4", 2, kFixupNone),
ENCODING_MAP(kThumbAndRR, 0x4000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "ands", "!0C, !1C", 2),
+ "ands", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAsrRRI5, 0x1000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "asrs", "!0C, !1C, #!2d", 2),
+ "asrs", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbAsrRR, 0x4100,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "asrs", "!0C, !1C", 2),
+ "asrs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbBCond, 0xd000,
kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES |
- NEEDS_FIXUP, "b!1c", "!0t", 2),
+ NEEDS_FIXUP, "b!1c", "!0t", 2, kFixupCondBranch),
ENCODING_MAP(kThumbBUncond, 0xe000,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP,
- "b", "!0t", 2),
+ "b", "!0t", 2, kFixupT1Branch),
ENCODING_MAP(kThumbBicRR, 0x4380,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "bics", "!0C, !1C", 2),
+ "bics", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbBkpt, 0xbe00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
- "bkpt", "!0d", 2),
+ "bkpt", "!0d", 2, kFixupNone),
ENCODING_MAP(kThumbBlx1, 0xf000,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
- NEEDS_FIXUP, "blx_1", "!0u", 2),
+ NEEDS_FIXUP, "blx_1", "!0u", 2, kFixupBlx1),
ENCODING_MAP(kThumbBlx2, 0xe800,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
- NEEDS_FIXUP, "blx_2", "!0v", 2),
+ NEEDS_FIXUP, "blx_2", "!0v", 2, kFixupLabel),
ENCODING_MAP(kThumbBl1, 0xf000,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
- kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
- "bl_1", "!0u", 2),
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+ "bl_1", "!0u", 2, kFixupBl1),
ENCODING_MAP(kThumbBl2, 0xf800,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
- kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
- "bl_2", "!0v", 2),
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+ "bl_2", "!0v", 2, kFixupLabel),
ENCODING_MAP(kThumbBlxR, 0x4780,
kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
- "blx", "!0C", 2),
+ "blx", "!0C", 2, kFixupNone),
ENCODING_MAP(kThumbBx, 0x4700,
kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
- "bx", "!0C", 2),
+ "bx", "!0C", 2, kFixupNone),
ENCODING_MAP(kThumbCmnRR, 0x42c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmn", "!0C, !1C", 2),
+ "cmn", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpRI8, 0x2800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
- "cmp", "!0C, #!1d", 2),
+ "cmp", "!0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbCmpRR, 0x4280,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpLH, 0x4540,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpHL, 0x4580,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpHH, 0x45c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbEorRR, 0x4040,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "eors", "!0C, !1C", 2),
+ "eors", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbLdmia, 0xc800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
- "ldmia", "!0C!!, <!1R>", 2),
+ "ldmia", "!0C!!, <!1R>", 2, kFixupNone),
ENCODING_MAP(kThumbLdrRRI5, 0x6800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldr", "!0C, [!1C, #!2E]", 2),
+ "ldr", "!0C, [!1C, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrRRR, 0x5800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldr", "!0C, [!1C, !2C]", 2),
+ "ldr", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrPcRel, 0x4800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
- | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2),
+ | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2, kFixupLoad),
ENCODING_MAP(kThumbLdrSpRel, 0x9800,
- kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
- | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2),
+ | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrbRRI5, 0x7800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrb", "!0C, [!1C, #2d]", 2),
+ "ldrb", "!0C, [!1C, #2d]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrbRRR, 0x5c00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrb", "!0C, [!1C, !2C]", 2),
+ "ldrb", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrhRRI5, 0x8800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrh", "!0C, [!1C, #!2F]", 2),
+ "ldrh", "!0C, [!1C, #!2F]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrhRRR, 0x5a00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrh", "!0C, [!1C, !2C]", 2),
+ "ldrh", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrsbRRR, 0x5600,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsb", "!0C, [!1C, !2C]", 2),
+ "ldrsb", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrshRRR, 0x5e00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsh", "!0C, [!1C, !2C]", 2),
+ "ldrsh", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLslRRI5, 0x0000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "lsls", "!0C, !1C, #!2d", 2),
+ "lsls", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbLslRR, 0x4080,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "lsls", "!0C, !1C", 2),
+ "lsls", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbLsrRRI5, 0x0800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "lsrs", "!0C, !1C, #!2d", 2),
+ "lsrs", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbLsrRR, 0x40c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "lsrs", "!0C, !1C", 2),
+ "lsrs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovImm, 0x2000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
- "movs", "!0C, #!1d", 2),
+ "movs", "!0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR, 0x1c00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "movs", "!0C, !1C", 2),
+ "movs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR_H2H, 0x46c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 2),
+ "mov", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR_H2L, 0x4640,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 2),
+ "mov", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR_L2H, 0x4680,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 2),
+ "mov", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMul, 0x4340,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "muls", "!0C, !1C", 2),
+ "muls", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMvn, 0x43c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "mvns", "!0C, !1C", 2),
+ "mvns", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbNeg, 0x4240,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "negs", "!0C, !1C", 2),
+ "negs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbOrr, 0x4300,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "orrs", "!0C, !1C", 2),
+ "orrs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbPop, 0xbc00,
kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
- | IS_LOAD, "pop", "<!0R>", 2),
+ | IS_LOAD, "pop", "<!0R>", 2, kFixupNone),
ENCODING_MAP(kThumbPush, 0xb400,
kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
- | IS_STORE, "push", "<!0R>", 2),
+ | IS_STORE, "push", "<!0R>", 2, kFixupNone),
+ ENCODING_MAP(kThumbRev, 0xba00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1,
+ "rev", "!0C, !1C", 2, kFixupNone),
+ ENCODING_MAP(kThumbRevsh, 0xbac0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1,
+ "rev", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbRorRR, 0x41c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "rors", "!0C, !1C", 2),
+ "rors", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbSbc, 0x4180,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
- "sbcs", "!0C, !1C", 2),
+ "sbcs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbStmia, 0xc000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
- "stmia", "!0C!!, <!1R>", 2),
+ "stmia", "!0C!!, <!1R>", 2, kFixupNone),
ENCODING_MAP(kThumbStrRRI5, 0x6000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "str", "!0C, [!1C, #!2E]", 2),
+ "str", "!0C, [!1C, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbStrRRR, 0x5000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
- "str", "!0C, [!1C, !2C]", 2),
+ "str", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbStrSpRel, 0x9000,
- kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
- | IS_STORE, "str", "!0C, [sp, #!2E]", 2),
+ | IS_STORE, "str", "!0C, [sp, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbStrbRRI5, 0x7000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strb", "!0C, [!1C, #!2d]", 2),
+ "strb", "!0C, [!1C, #!2d]", 2, kFixupNone),
ENCODING_MAP(kThumbStrbRRR, 0x5400,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
- "strb", "!0C, [!1C, !2C]", 2),
+ "strb", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbStrhRRI5, 0x8000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strh", "!0C, [!1C, #!2F]", 2),
+ "strh", "!0C, [!1C, #!2F]", 2, kFixupNone),
ENCODING_MAP(kThumbStrhRRR, 0x5200,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
- "strh", "!0C, [!1C, !2C]", 2),
+ "strh", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbSubRRI3, 0x1e00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "subs", "!0C, !1C, #!2d", 2),
+ "subs", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbSubRI8, 0x3800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
- "subs", "!0C, #!1d", 2),
+ "subs", "!0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbSubRRR, 0x1a00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
- "subs", "!0C, !1C, !2C", 2),
+ "subs", "!0C, !1C, !2C", 2, kFixupNone),
ENCODING_MAP(kThumbSubSpI7, 0xb080,
kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
- "sub", "sp, #!0d*4", 2),
+ "sub", "sp, #!0d*4", 2, kFixupNone),
ENCODING_MAP(kThumbSwi, 0xdf00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
- "swi", "!0d", 2),
+ "swi", "!0d", 2, kFixupNone),
ENCODING_MAP(kThumbTst, 0x4200,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
- "tst", "!0C, !1C", 2),
+ "tst", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumb2Vldrs, 0xed900a00,
kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
- REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4),
+ 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 |
- REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4),
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4, kFixupVLoad),
ENCODING_MAP(kThumb2Vmuls, 0xee200a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12,
- "vmuls", "!0s, !1s, !2s", 4),
+ "vmuls", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmuld, 0xee200b00,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vmuld", "!0S, !1S, !2S", 4),
+ "vmuld", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vstrs, 0xed800a00,
kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "vstr", "!0s, [!1C, #!2E]", 4),
+ "vstr", "!0s, [!1C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Vstrd, 0xed800b00,
kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "vstr", "!0S, [!1C, #!2E]", 4),
+ "vstr", "!0S, [!1C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsubs, 0xee300a40,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vsub", "!0s, !1s, !2s", 4),
+ "vsub", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsubd, 0xee300b40,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vsub", "!0S, !1S, !2S", 4),
+ "vsub", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vadds, 0xee300a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vadd", "!0s, !1s, !2s", 4),
+ "vadd", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vaddd, 0xee300b00,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vadd", "!0S, !1S, !2S", 4),
+ "vadd", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vdivs, 0xee800a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vdivs", "!0s, !1s, !2s", 4),
+ "vdivs", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vdivd, 0xee800b00,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vdivd", "!0S, !1S, !2S", 4),
+ "vdivd", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtIF, 0xeeb80ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f32", "!0s, !1s", 4),
+ "vcvt.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtID, 0xeeb80bc0,
kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f64", "!0S, !1s", 4),
+ "vcvt.f64", "!0S, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtFI, 0xeebd0ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.s32.f32 ", "!0s, !1s", 4),
+ "vcvt.s32.f32 ", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtDI, 0xeebd0bc0,
kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.s32.f64 ", "!0s, !1S", 4),
+ "vcvt.s32.f64 ", "!0s, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtFd, 0xeeb70ac0,
kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f64.f32 ", "!0S, !1s", 4),
+ "vcvt.f64.f32 ", "!0S, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtDF, 0xeeb70bc0,
kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f32.f64 ", "!0s, !1S", 4),
+ "vcvt.f32.f64 ", "!0s, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsqrts, 0xeeb10ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vsqrt.f32 ", "!0s, !1s", 4),
+ "vsqrt.f32 ", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsqrtd, 0xeeb10bc0,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vsqrt.f64 ", "!0S, !1S", 4),
+ "vsqrt.f64 ", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */
kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "mov", "!0C, #!1m", 4),
+ "mov", "!0C, #!1m", 4, kFixupNone),
ENCODING_MAP(kThumb2MovImm16, 0xf2400000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "mov", "!0C, #!1M", 4),
+ "mov", "!0C, #!1M", 4, kFixupNone),
ENCODING_MAP(kThumb2StrRRI12, 0xf8c00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "str", "!0C, [!1C, #!2d]", 4),
+ "str", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrRRI12, 0xf8d00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldr", "!0C, [!1C, #!2d]", 4),
+ "ldr", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrRRI8Predec, 0xf8400c00,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "str", "!0C, [!1C, #-!2d]", 4),
+ "str", "!0C, [!1C, #-!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrRRI8Predec, 0xf8500c00,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldr", "!0C, [!1C, #-!2d]", 4),
+ "ldr", "!0C, [!1C, #-!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Cbnz, 0xb900, /* Note: does not affect flags */
kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
- NEEDS_FIXUP, "cbnz", "!0C,!1t", 2),
+ NEEDS_FIXUP, "cbnz", "!0C,!1t", 2, kFixupCBxZ),
ENCODING_MAP(kThumb2Cbz, 0xb100, /* Note: does not affect flags */
kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
- NEEDS_FIXUP, "cbz", "!0C,!1t", 2),
+ NEEDS_FIXUP, "cbz", "!0C,!1t", 2, kFixupCBxZ),
ENCODING_MAP(kThumb2AddRRI12, 0xf2000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
- "add", "!0C,!1C,#!2d", 4),
+ "add", "!0C,!1C,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2MovRR, 0xea4f0000, /* no setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 4),
+ "mov", "!0C, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovs, 0xeeb00a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f32 ", " !0s, !1s", 4),
+ "vmov.f32 ", " !0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovd, 0xeeb00b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f64 ", " !0S, !1S", 4),
+ "vmov.f64 ", " !0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
- "ldmia", "!0C!!, <!1R>", 4),
+ "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2Stmia, 0xe8800000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
- "stmia", "!0C!!, <!1R>", 4),
+ "stmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2AddRRR, 0xeb100000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "adds", "!0C, !1C, !2C!3H", 4),
+ "adds", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2SubRRR, 0xebb00000, /* setflags enconding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "subs", "!0C, !1C, !2C!3H", 4),
+ "subs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2SbcRRR, 0xeb700000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES,
- "sbcs", "!0C, !1C, !2C!3H", 4),
+ "sbcs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2CmpRR, 0xebb00f00,
kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 4),
+ "cmp", "!0C, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2SubRRI12, 0xf2a00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
- "sub", "!0C,!1C,#!2d", 4),
+ "sub", "!0C,!1C,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2MvnImm12, 0xf06f0000, /* no setflags encoding */
kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "mvn", "!0C, #!1n", 4),
+ "mvn", "!0C, #!1n", 4, kFixupNone),
ENCODING_MAP(kThumb2Sel, 0xfaa0f080,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES,
- "sel", "!0C, !1C, !2C", 4),
+ "sel", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2Ubfx, 0xf3c00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
- "ubfx", "!0C, !1C, #!2d, #!3d", 4),
+ "ubfx", "!0C, !1C, #!2d, #!3d", 4, kFixupNone),
ENCODING_MAP(kThumb2Sbfx, 0xf3400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
- "sbfx", "!0C, !1C, #!2d, #!3d", 4),
+ "sbfx", "!0C, !1C, #!2d, #!3d", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrRRR, 0xf8500000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrhRRR, 0xf8300000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrshRRR, 0xf9300000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrbRRR, 0xf8100000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrsbRRR, 0xf9100000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrRRR, 0xf8400000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
- "str", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "str", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrhRRR, 0xf8200000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
- "strh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "strh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrbRRR, 0xf8000000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
- "strb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "strb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrhRRI12, 0xf8b00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrh", "!0C, [!1C, #!2d]", 4),
+ "ldrh", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrshRRI12, 0xf9b00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrsh", "!0C, [!1C, #!2d]", 4),
+ "ldrsh", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrbRRI12, 0xf8900000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrb", "!0C, [!1C, #!2d]", 4),
+ "ldrb", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrsbRRI12, 0xf9900000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrsb", "!0C, [!1C, #!2d]", 4),
+ "ldrsb", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrhRRI12, 0xf8a00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strh", "!0C, [!1C, #!2d]", 4),
+ "strh", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrbRRI12, 0xf8800000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strb", "!0C, [!1C, #!2d]", 4),
+ "strb", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Pop, 0xe8bd0000,
kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
- | IS_LOAD | NEEDS_FIXUP, "pop", "<!0R>", 4),
+ | IS_LOAD | NEEDS_FIXUP, "pop", "<!0R>", 4, kFixupPushPop),
ENCODING_MAP(kThumb2Push, 0xe92d0000,
kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
- | IS_STORE | NEEDS_FIXUP, "push", "<!0R>", 4),
+ | IS_STORE | NEEDS_FIXUP, "push", "<!0R>", 4, kFixupPushPop),
ENCODING_MAP(kThumb2CmpRI12, 0xf1b00f00,
kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | SETS_CCODES,
- "cmp", "!0C, #!1m", 4),
+ "cmp", "!0C, #!1m", 4, kFixupNone),
ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "adcs", "!0C, !1C, !2C!3H", 4),
+ "adcs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2AndRRR, 0xea000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "and", "!0C, !1C, !2C!3H", 4),
+ "and", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2BicRRR, 0xea200000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "bic", "!0C, !1C, !2C!3H", 4),
+ "bic", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2CmnRR, 0xeb000000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "cmn", "!0C, !1C, shift !2d", 4),
+ "cmn", "!0C, !1C, shift !2d", 4, kFixupNone),
ENCODING_MAP(kThumb2EorRRR, 0xea800000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "eor", "!0C, !1C, !2C!3H", 4),
+ "eor", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2MulRRR, 0xfb00f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "mul", "!0C, !1C, !2C", 4),
+ "mul", "!0C, !1C, !2C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2SdivRRR, 0xfb90f0f0,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "sdiv", "!0C, !1C, !2C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2UdivRRR, 0xfbb0f0f0,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "udiv", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2MnvRR, 0xea6f0000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "mvn", "!0C, !1C, shift !2d", 4),
+ "mvn", "!0C, !1C, shift !2d", 4, kFixupNone),
ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "rsb", "!0C,!1C,#!2m", 4),
+ "rsb", "!0C,!1C,#!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "neg", "!0C,!1C", 4),
+ "neg", "!0C,!1C", 4, kFixupNone),
ENCODING_MAP(kThumb2OrrRRR, 0xea400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "orr", "!0C, !1C, !2C!3H", 4),
+ "orr", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2TstRR, 0xea100f00,
kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
- "tst", "!0C, !1C, shift !2d", 4),
+ "tst", "!0C, !1C, shift !2d", 4, kFixupNone),
ENCODING_MAP(kThumb2LslRRR, 0xfa00f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "lsl", "!0C, !1C, !2C", 4),
+ "lsl", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2LsrRRR, 0xfa20f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "lsr", "!0C, !1C, !2C", 4),
+ "lsr", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2AsrRRR, 0xfa40f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "asr", "!0C, !1C, !2C", 4),
+ "asr", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2RorRRR, 0xfa60f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "ror", "!0C, !1C, !2C", 4),
+ "ror", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2LslRRI5, 0xea4f0000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "lsl", "!0C, !1C, #!2d", 4),
+ "lsl", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2LsrRRI5, 0xea4f0010,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "lsr", "!0C, !1C, #!2d", 4),
+ "lsr", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2AsrRRI5, 0xea4f0020,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "asr", "!0C, !1C, #!2d", 4),
+ "asr", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2RorRRI5, 0xea4f0030,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "ror", "!0C, !1C, #!2d", 4),
+ "ror", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2BicRRI8, 0xf0200000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "bic", "!0C, !1C, #!2m", 4),
+ "bic", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2AndRRI8, 0xf0000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "and", "!0C, !1C, #!2m", 4),
+ "and", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "orr", "!0C, !1C, #!2m", 4),
+ "orr", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2EorRRI8, 0xf0800000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "eor", "!0C, !1C, #!2m", 4),
+ "eor", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2AddRRI8, 0xf1100000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "adds", "!0C, !1C, #!2m", 4),
+ "adds", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
- "adcs", "!0C, !1C, #!2m", 4),
+ "adcs", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "subs", "!0C, !1C, #!2m", 4),
+ "subs", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
- "sbcs", "!0C, !1C, #!2m", 4),
+ "sbcs", "!0C, !1C, #!2m", 4, kFixupNone),
+ ENCODING_MAP(kThumb2RevRR, 0xfa90f080,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12, // Binary, but rm is stored twice.
+ "rev", "!0C, !1C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2RevshRR, 0xfa90f0b0,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12, // Binary, but rm is stored twice.
+ "revsh", "!0C, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2It, 0xbf00,
kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
- "it:!1b", "!0c", 2),
+ "it:!1b", "!0c", 2, kFixupNone),
ENCODING_MAP(kThumb2Fmstat, 0xeef1fa10,
kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
- "fmstat", "", 4),
+ "fmstat", "", 4, kFixupNone),
ENCODING_MAP(kThumb2Vcmpd, 0xeeb40b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
- "vcmp.f64", "!0S, !1S", 4),
+ "vcmp.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vcmps, 0xeeb40a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
- "vcmp.f32", "!0s, !1s", 4),
+ "vcmp.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrPcRel12, 0xf8df0000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
- "ldr", "!0C, [r15pc, #!1d]", 4),
+ "ldr", "!0C, [r15pc, #!1d]", 4, kFixupLoad),
ENCODING_MAP(kThumb2BCond, 0xf0008000,
kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | IS_BRANCH | USES_CCODES | NEEDS_FIXUP,
- "b!1c", "!0t", 4),
+ "b!1c", "!0t", 4, kFixupCondBranch),
ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f64", "!0S, !1S", 4),
+ "vmov.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f32", "!0s, !1s", 4),
+ "vmov.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmrs, 0xee100a10,
kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "fmrs", "!0C, !1s", 4),
+ "fmrs", "!0C, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmsr, 0xee000a10,
kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "fmsr", "!0s, !1C", 4),
+ "fmsr", "!0s, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmrrd, 0xec500b10,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
- "fmrrd", "!0C, !1C, !2S", 4),
+ "fmrrd", "!0C, !1C, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmdrr, 0xec400b10,
kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "fmdrr", "!0S, !1C, !2C", 4),
+ "fmdrr", "!0S, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2Vabsd, 0xeeb00bc0,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vabs.f64", "!0S, !1S", 4),
+ "vabs.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vabss, 0xeeb00ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vabs.f32", "!0s, !1s", 4),
+ "vabs.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vnegd, 0xeeb10b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vneg.f64", "!0S, !1S", 4),
+ "vneg.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vnegs, 0xeeb10a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vneg.f32", "!0s, !1s", 4),
+ "vneg.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovs_IMM8, 0xeeb00a00,
kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "vmov.f32", "!0s, #0x!1h", 4),
+ "vmov.f32", "!0s, #0x!1h", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovd_IMM8, 0xeeb00b00,
kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "vmov.f64", "!0S, #0x!1h", 4),
+ "vmov.f64", "!0S, #0x!1h", 4, kFixupNone),
ENCODING_MAP(kThumb2Mla, 0xfb000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 15, 12,
IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
- "mla", "!0C, !1C, !2C, !3C", 4),
+ "mla", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2Umull, 0xfba00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 3, 0,
IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
- "umull", "!0C, !1C, !2C, !3C", 4),
+ "umull", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldrex, 0xe8500f00,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrex", "!0C, [!1C, #!2E]", 4),
+ "ldrex", "!0C, [!1C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Strex, 0xe8400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE,
- "strex", "!0C,!1C, [!2C, #!2E]", 4),
+ "strex", "!0C,!1C, [!2C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f,
kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND,
- "clrex", "", 4),
+ "clrex", "", 4, kFixupNone),
ENCODING_MAP(kThumb2Bfi, 0xf3600000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1,
kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
- "bfi", "!0C,!1C,#!2d,#!3d", 4),
+ "bfi", "!0C,!1C,#!2d,#!3d", 4, kFixupNone),
ENCODING_MAP(kThumb2Bfc, 0xf36f0000,
kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
- "bfc", "!0C,#!1d,#!2d", 4),
+ "bfc", "!0C,#!1d,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50,
kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP,
- "dmb", "#!0B", 4),
+ "dmb", "#!0B", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrPcReln12, 0xf85f0000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
- "ldr", "!0C, [r15pc, -#!1d]", 4),
+ "ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Stm, 0xe9000000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
- "stm", "!0C, <!1R>", 4),
+ "stm", "!0C, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumbUndefined, 0xde00,
kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND,
- "undefined", "", 2),
+ "undefined", "", 2, kFixupNone),
// NOTE: vpop, vpush hard-encoded for s16+ reg list
ENCODING_MAP(kThumb2VPopCS, 0xecbd8a00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_FPCS_LIST0
- | IS_LOAD, "vpop", "<!0P>", 4),
+ | IS_LOAD, "vpop", "<!0P>", 4, kFixupNone),
ENCODING_MAP(kThumb2VPushCS, 0xed2d8a00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_FPCS_LIST0
- | IS_STORE, "vpush", "<!0P>", 4),
+ | IS_STORE, "vpush", "<!0P>", 4, kFixupNone),
ENCODING_MAP(kThumb2Vldms, 0xec900a00,
kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE0 | REG_DEF_FPCS_LIST2
- | IS_LOAD, "vldms", "!0C, <!2Q>", 4),
+ | IS_LOAD, "vldms", "!0C, <!2Q>", 4, kFixupNone),
ENCODING_MAP(kThumb2Vstms, 0xec800a00,
kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE0 | REG_USE_FPCS_LIST2
- | IS_STORE, "vstms", "!0C, <!2Q>", 4),
+ | IS_STORE, "vstms", "!0C, <!2Q>", 4, kFixupNone),
ENCODING_MAP(kThumb2BUncond, 0xf0009000,
kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
- "b", "!0t", 4),
+ "b", "!0t", 4, kFixupT2Branch),
ENCODING_MAP(kThumb2MovImm16H, 0xf2c00000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0,
- "movt", "!0C, #!1M", 4),
+ "movt", "!0C, #!1M", 4, kFixupNone),
ENCODING_MAP(kThumb2AddPCR, 0x4487,
kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
- IS_UNARY_OP | REG_USE0 | IS_BRANCH,
- "add", "rPC, !0C", 2),
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP,
+ "add", "rPC, !0C", 2, kFixupLabel),
ENCODING_MAP(kThumb2Adr, 0xf20f0000,
kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
/* Note: doesn't affect flags */
IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP,
- "adr", "!0C,#!1d", 4),
+ "adr", "!0C,#!1d", 4, kFixupAdr),
ENCODING_MAP(kThumb2MovImm16LST, 0xf2400000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP,
- "mov", "!0C, #!1M", 4),
+ "mov", "!0C, #!1M", 4, kFixupMovImmLST),
ENCODING_MAP(kThumb2MovImm16HST, 0xf2c00000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
- "movt", "!0C, #!1M", 4),
+ "movt", "!0C, #!1M", 4, kFixupMovImmHST),
ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
- "ldmia", "!0C!!, <!1R>", 4),
+ "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2SubsRRI12, 0xf1b00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "subs", "!0C,!1C,#!2d", 4),
+ "subs", "!0C,!1C,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2OrrRRRs, 0xea500000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "orrs", "!0C, !1C, !2C!3H", 4),
+ "orrs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2Push1, 0xf84d0d04,
kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE0
- | IS_STORE, "push1", "!0C", 4),
+ | IS_STORE, "push1", "!0C", 4, kFixupNone),
ENCODING_MAP(kThumb2Pop1, 0xf85d0b04,
kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF0
- | IS_LOAD, "pop1", "!0C", 4),
+ | IS_LOAD, "pop1", "!0C", 4, kFixupNone),
ENCODING_MAP(kThumb2RsubRRR, 0xebd00000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "rsbs", "!0C, !1C, !2C!3H", 4),
+ "rsbs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2Smull, 0xfb800000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 3, 0,
IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
- "smull", "!0C, !1C, !2C, !3C", 4),
+ "smull", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrdPcRel8, 0xe9df0000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0 | REG_DEF1 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
- "ldrd", "!0C, !1C, [pc, #!2E]", 4),
+ "ldrd", "!0C, !1C, [pc, #!2E]", 4, kFixupLoad),
ENCODING_MAP(kThumb2LdrdI8, 0xe9d00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 7, 0,
IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | IS_LOAD,
- "ldrd", "!0C, !1C, [!2C, #!3E]", 4),
+ "ldrd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrdI8, 0xe9c00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 7, 0,
IS_QUAD_OP | REG_USE0 | REG_USE1 | REG_USE2 | IS_STORE,
- "strd", "!0C, !1C, [!2C, #!3E]", 4),
+ "strd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone),
};
+// new_lir replaces orig_lir in the pcrel_fixup list.
+void ArmMir2Lir::ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) {
+ new_lir->u.a.pcrel_next = orig_lir->u.a.pcrel_next;
+ if (UNLIKELY(prev_lir == NULL)) {
+ first_fixup_ = new_lir;
+ } else {
+ prev_lir->u.a.pcrel_next = new_lir;
+ }
+ orig_lir->flags.fixup = kFixupNone;
+}
+
+// new_lir is inserted before orig_lir in the pcrel_fixup list.
+void ArmMir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) {
+ new_lir->u.a.pcrel_next = orig_lir;
+ if (UNLIKELY(prev_lir == NULL)) {
+ first_fixup_ = new_lir;
+ } else {
+ DCHECK(prev_lir->u.a.pcrel_next == orig_lir);
+ prev_lir->u.a.pcrel_next = new_lir;
+ }
+}
+
/*
* The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
* not ready. Since r5FP is not updated often, it is less likely to
@@ -997,398 +1047,641 @@
*/
#define PADDING_MOV_R5_R5 0x1C2D
-/*
- * Assemble the LIR into binary instruction format. Note that we may
- * discover that pc-relative displacements may not fit the selected
- * instruction.
- */
-AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr) {
- LIR* lir;
- AssemblerStatus res = kSuccess; // Assume success
-
- for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
- if (lir->opcode < 0) {
- /* 1 means padding is needed */
- if ((lir->opcode == kPseudoPseudoAlign4) && (lir->operands[0] == 1)) {
- code_buffer_.push_back(PADDING_MOV_R5_R5 & 0xFF);
- code_buffer_.push_back((PADDING_MOV_R5_R5 >> 8) & 0xFF);
- }
- continue;
+void ArmMir2Lir::EncodeLIR(LIR* lir) {
+ int opcode = lir->opcode;
+ if (IsPseudoLirOp(opcode)) {
+ if (UNLIKELY(opcode == kPseudoPseudoAlign4)) {
+ // Note: size for this opcode will be either 0 or 2 depending on final alignment.
+ lir->u.a.bytes[0] = (PADDING_MOV_R5_R5 & 0xff);
+ lir->u.a.bytes[1] = ((PADDING_MOV_R5_R5 >> 8) & 0xff);
+ lir->flags.size = (lir->offset & 0x2);
}
-
- if (lir->flags.is_nop) {
- continue;
- }
-
- /*
- * For PC-relative displacements we won't know if the
- * selected instruction will work until late (i.e. - now).
- * If something doesn't fit, we must replace the short-form
- * operation with a longer-form one. Note, though, that this
- * can change code we've already processed, so we'll need to
- * re-calculate offsets and restart. To limit the number of
- * restarts, the entire list will be scanned and patched.
- * Of course, the patching itself may cause new overflows so this
- * is an iterative process.
- */
- if (lir->flags.pcRelFixup) {
- if (lir->opcode == kThumbLdrPcRel ||
- lir->opcode == kThumb2LdrPcRel12 ||
- lir->opcode == kThumbAddPcRel ||
- lir->opcode == kThumb2LdrdPcRel8 ||
- ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) ||
- ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) {
- /*
- * PC-relative loads are mostly used to load immediates
- * that are too large to materialize directly in one shot.
- * However, if the load displacement exceeds the limit,
- * we revert to a multiple-instruction materialization sequence.
- */
- LIR *lir_target = lir->target;
- uintptr_t pc = (lir->offset + 4) & ~3;
- uintptr_t target = lir_target->offset;
- int delta = target - pc;
- if (delta & 0x3) {
- LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
- }
- // First, a sanity check for cases we shouldn't see now
- if (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) ||
- ((lir->opcode == kThumbLdrPcRel) && (delta > 1020))) {
- // Shouldn't happen in current codegen.
- LOG(FATAL) << "Unexpected pc-rel offset " << delta;
- }
- // Now, check for the difficult cases
- if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) ||
- ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) ||
- ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) ||
- ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) {
- /*
- * Note: because rARM_LR may be used to fix up out-of-range
- * vldrs/vldrd we include REG_DEF_LR in the resource
- * masks for these instructions.
- */
- int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) || (lir->opcode == kThumb2LdrPcRel12))
- ? lir->operands[0] : rARM_LR;
-
- // Add new Adr to generate the address.
- LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr,
- base_reg, 0, 0, 0, 0, lir->target);
- InsertLIRBefore(lir, new_adr);
-
- // Convert to normal load.
- if (lir->opcode == kThumb2LdrPcRel12) {
- lir->opcode = kThumb2LdrRRI12;
- } else if (lir->opcode == kThumb2LdrdPcRel8) {
- lir->opcode = kThumb2LdrdI8;
- }
- // Change the load to be relative to the new Adr base.
- if (lir->opcode == kThumb2LdrdI8) {
- lir->operands[3] = 0;
- lir->operands[2] = base_reg;
- } else {
- lir->operands[2] = 0;
- lir->operands[1] = base_reg;
- }
- SetupResourceMasks(lir);
- res = kRetryAll;
- } else {
- if ((lir->opcode == kThumb2Vldrs) ||
- (lir->opcode == kThumb2Vldrd) ||
- (lir->opcode == kThumb2LdrdPcRel8)) {
- lir->operands[2] = delta >> 2;
- } else {
- lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta :
- delta >> 2;
- }
- }
- } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) {
- LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- int delta = target - pc;
- if (delta > 126 || delta < 0) {
- /*
- * Convert to cmp rx,#0 / b[eq/ne] tgt pair
- * Make new branch instruction and insert after
- */
- LIR* new_inst =
- RawLIR(lir->dalvik_offset, kThumbBCond, 0,
- (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe,
- 0, 0, 0, lir->target);
- InsertLIRAfter(lir, new_inst);
- /* Convert the cb[n]z to a cmp rx, #0 ] */
- lir->opcode = kThumbCmpRI8;
- /* operand[0] is src1 in both cb[n]z & CmpRI8 */
- lir->operands[1] = 0;
- lir->target = 0;
- SetupResourceMasks(lir);
- res = kRetryAll;
- } else {
- lir->operands[1] = delta >> 1;
- }
- } else if (lir->opcode == kThumb2Push || lir->opcode == kThumb2Pop) {
- if (__builtin_popcount(lir->operands[0]) == 1) {
- /*
- * The standard push/pop multiple instruction
- * requires at least two registers in the list.
- * If we've got just one, switch to the single-reg
- * encoding.
- */
- lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 :
- kThumb2Pop1;
- int reg = 0;
- while (lir->operands[0]) {
- if (lir->operands[0] & 0x1) {
- break;
- } else {
- reg++;
- lir->operands[0] >>= 1;
- }
- }
- lir->operands[0] = reg;
- SetupResourceMasks(lir);
- res = kRetryAll;
- }
- } else if (lir->opcode == kThumbBCond || lir->opcode == kThumb2BCond) {
- LIR *target_lir = lir->target;
- int delta = 0;
- DCHECK(target_lir);
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- delta = target - pc;
- if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
- lir->opcode = kThumb2BCond;
- SetupResourceMasks(lir);
- res = kRetryAll;
- }
- lir->operands[0] = delta >> 1;
- } else if (lir->opcode == kThumb2BUncond) {
- LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- int delta = target - pc;
- lir->operands[0] = delta >> 1;
- if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
- lir->operands[0] == 0) { // Useless branch
- lir->flags.is_nop = true;
- res = kRetryAll;
- }
- } else if (lir->opcode == kThumbBUncond) {
- LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- int delta = target - pc;
- if (delta > 2046 || delta < -2048) {
- // Convert to Thumb2BCond w/ kArmCondAl
- lir->opcode = kThumb2BUncond;
- lir->operands[0] = 0;
- SetupResourceMasks(lir);
- res = kRetryAll;
- } else {
- lir->operands[0] = delta >> 1;
- if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
- lir->operands[0] == -1) { // Useless branch
- lir->flags.is_nop = true;
- res = kRetryAll;
- }
- }
- } else if (lir->opcode == kThumbBlx1) {
- DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2);
- /* cur_pc is Thumb */
- uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
- uintptr_t target = lir->operands[1];
-
- /* Match bit[1] in target with base */
- if (cur_pc & 0x2) {
- target |= 0x2;
- }
- int delta = target - cur_pc;
- DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
-
- lir->operands[0] = (delta >> 12) & 0x7ff;
- NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
- } else if (lir->opcode == kThumbBl1) {
- DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2);
- /* Both cur_pc and target are Thumb */
- uintptr_t cur_pc = start_addr + lir->offset + 4;
- uintptr_t target = lir->operands[1];
-
- int delta = target - cur_pc;
- DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
-
- lir->operands[0] = (delta >> 12) & 0x7ff;
- NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
- } else if (lir->opcode == kThumb2Adr) {
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[2]);
- LIR* target = lir->target;
- int target_disp = tab_rec ? tab_rec->offset
- : target->offset;
- int disp = target_disp - ((lir->offset + 4) & ~3);
- if (disp < 4096) {
- lir->operands[1] = disp;
- } else {
- // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0]
- // TUNING: if this case fires often, it can be improved. Not expected to be common.
- LIR *new_mov16L =
- RawLIR(lir->dalvik_offset, kThumb2MovImm16LST,
- lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
- reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
- InsertLIRBefore(lir, new_mov16L);
- LIR *new_mov16H =
- RawLIR(lir->dalvik_offset, kThumb2MovImm16HST,
- lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
- reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
- InsertLIRBefore(lir, new_mov16H);
- if (ARM_LOWREG(lir->operands[0])) {
- lir->opcode = kThumbAddRRLH;
- } else {
- lir->opcode = kThumbAddRRHH;
- }
- lir->operands[1] = rARM_PC;
- SetupResourceMasks(lir);
- res = kRetryAll;
- }
- } else if (lir->opcode == kThumb2MovImm16LST) {
- // operands[1] should hold disp, [2] has add, [3] has tab_rec
- LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
- // If tab_rec is null, this is a literal load. Use target
- LIR* target = lir->target;
- int target_disp = tab_rec ? tab_rec->offset : target->offset;
- lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff;
- } else if (lir->opcode == kThumb2MovImm16HST) {
- // operands[1] should hold disp, [2] has add, [3] has tab_rec
- LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
- // If tab_rec is null, this is a literal load. Use target
- LIR* target = lir->target;
- int target_disp = tab_rec ? tab_rec->offset : target->offset;
- lir->operands[1] =
- ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff;
- }
- }
- /*
- * If one of the pc-relative instructions expanded we'll have
- * to make another pass. Don't bother to fully assemble the
- * instruction.
- */
- if (res != kSuccess) {
- continue;
- }
+ } else if (LIKELY(!lir->flags.is_nop)) {
const ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
uint32_t bits = encoder->skeleton;
- int i;
- for (i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
uint32_t operand;
uint32_t value;
operand = lir->operands[i];
- switch (encoder->field_loc[i].kind) {
- case kFmtUnused:
- break;
- case kFmtFPImm:
- value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end;
- value |= (operand & 0x0F) << encoder->field_loc[i].start;
- bits |= value;
- break;
- case kFmtBrOffset:
- value = ((operand & 0x80000) >> 19) << 26;
- value |= ((operand & 0x40000) >> 18) << 11;
- value |= ((operand & 0x20000) >> 17) << 13;
- value |= ((operand & 0x1f800) >> 11) << 16;
- value |= (operand & 0x007ff);
- bits |= value;
- break;
- case kFmtShift5:
- value = ((operand & 0x1c) >> 2) << 12;
- value |= (operand & 0x03) << 6;
- bits |= value;
- break;
- case kFmtShift:
- value = ((operand & 0x70) >> 4) << 12;
- value |= (operand & 0x0f) << 4;
- bits |= value;
- break;
- case kFmtBWidth:
- value = operand - 1;
- bits |= value;
- break;
- case kFmtLsb:
- value = ((operand & 0x1c) >> 2) << 12;
- value |= (operand & 0x03) << 6;
- bits |= value;
- break;
- case kFmtImm6:
- value = ((operand & 0x20) >> 5) << 9;
- value |= (operand & 0x1f) << 3;
- bits |= value;
- break;
- case kFmtBitBlt:
- value = (operand << encoder->field_loc[i].start) &
- ((1 << (encoder->field_loc[i].end + 1)) - 1);
- bits |= value;
- break;
- case kFmtDfp: {
- DCHECK(ARM_DOUBLEREG(operand));
- DCHECK_EQ((operand & 0x1), 0U);
- int reg_name = (operand & ARM_FP_REG_MASK) >> 1;
- /* Snag the 1-bit slice and position it */
- value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end;
- /* Extract and position the 4-bit slice */
- value |= (reg_name & 0x0f) << encoder->field_loc[i].start;
- bits |= value;
- break;
- }
- case kFmtSfp:
- DCHECK(ARM_SINGLEREG(operand));
- /* Snag the 1-bit slice and position it */
- value = (operand & 0x1) << encoder->field_loc[i].end;
- /* Extract and position the 4-bit slice */
- value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start;
- bits |= value;
- break;
- case kFmtImm12:
- case kFmtModImm:
- value = ((operand & 0x800) >> 11) << 26;
- value |= ((operand & 0x700) >> 8) << 12;
- value |= operand & 0x0ff;
- bits |= value;
- break;
- case kFmtImm16:
- value = ((operand & 0x0800) >> 11) << 26;
- value |= ((operand & 0xf000) >> 12) << 16;
- value |= ((operand & 0x0700) >> 8) << 12;
- value |= operand & 0x0ff;
- bits |= value;
- break;
- case kFmtOff24: {
- uint32_t signbit = (operand >> 31) & 0x1;
- uint32_t i1 = (operand >> 22) & 0x1;
- uint32_t i2 = (operand >> 21) & 0x1;
- uint32_t imm10 = (operand >> 11) & 0x03ff;
- uint32_t imm11 = operand & 0x07ff;
- uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
- uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
- value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
- imm11;
- bits |= value;
+ ArmEncodingKind kind = encoder->field_loc[i].kind;
+ if (LIKELY(kind == kFmtBitBlt)) {
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ } else {
+ switch (encoder->field_loc[i].kind) {
+ case kFmtSkip:
+ break; // Nothing to do, but continue to next.
+ case kFmtUnused:
+ i = 4; // Done, break out of the enclosing loop.
+ break;
+ case kFmtFPImm:
+ value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end;
+ value |= (operand & 0x0F) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtBrOffset:
+ value = ((operand & 0x80000) >> 19) << 26;
+ value |= ((operand & 0x40000) >> 18) << 11;
+ value |= ((operand & 0x20000) >> 17) << 13;
+ value |= ((operand & 0x1f800) >> 11) << 16;
+ value |= (operand & 0x007ff);
+ bits |= value;
+ break;
+ case kFmtShift5:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtShift:
+ value = ((operand & 0x70) >> 4) << 12;
+ value |= (operand & 0x0f) << 4;
+ bits |= value;
+ break;
+ case kFmtBWidth:
+ value = operand - 1;
+ bits |= value;
+ break;
+ case kFmtLsb:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtImm6:
+ value = ((operand & 0x20) >> 5) << 9;
+ value |= (operand & 0x1f) << 3;
+ bits |= value;
+ break;
+ case kFmtDfp: {
+ DCHECK(ARM_DOUBLEREG(operand));
+ DCHECK_EQ((operand & 0x1), 0U);
+ uint32_t reg_name = (operand & ARM_FP_REG_MASK) >> 1;
+ /* Snag the 1-bit slice and position it */
+ value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= (reg_name & 0x0f) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
}
- break;
- default:
- LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind;
+ case kFmtSfp:
+ DCHECK(ARM_SINGLEREG(operand));
+ /* Snag the 1-bit slice and position it */
+ value = (operand & 0x1) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtImm12:
+ case kFmtModImm:
+ value = ((operand & 0x800) >> 11) << 26;
+ value |= ((operand & 0x700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtImm16:
+ value = ((operand & 0x0800) >> 11) << 26;
+ value |= ((operand & 0xf000) >> 12) << 16;
+ value |= ((operand & 0x0700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtOff24: {
+ uint32_t signbit = (operand >> 31) & 0x1;
+ uint32_t i1 = (operand >> 22) & 0x1;
+ uint32_t i2 = (operand >> 21) & 0x1;
+ uint32_t imm10 = (operand >> 11) & 0x03ff;
+ uint32_t imm11 = operand & 0x07ff;
+ uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
+ uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
+ value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
+ imm11;
+ bits |= value;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind;
+ }
}
}
if (encoder->size == 4) {
- code_buffer_.push_back((bits >> 16) & 0xff);
- code_buffer_.push_back((bits >> 24) & 0xff);
+ lir->u.a.bytes[0] = ((bits >> 16) & 0xff);
+ lir->u.a.bytes[1] = ((bits >> 24) & 0xff);
+ lir->u.a.bytes[2] = (bits & 0xff);
+ lir->u.a.bytes[3] = ((bits >> 8) & 0xff);
+ } else {
+ DCHECK_EQ(encoder->size, 2);
+ lir->u.a.bytes[0] = (bits & 0xff);
+ lir->u.a.bytes[1] = ((bits >> 8) & 0xff);
}
- code_buffer_.push_back(bits & 0xff);
- code_buffer_.push_back((bits >> 8) & 0xff);
+ lir->flags.size = encoder->size;
}
- return res;
+}
+
+// Assemble the LIR into binary instruction format.
+void ArmMir2Lir::AssembleLIR() {
+ LIR* lir;
+ LIR* prev_lir;
+ cu_->NewTimingSplit("Assemble");
+ int assembler_retries = 0;
+ CodeOffset starting_offset = EncodeRange(first_lir_insn_, last_lir_insn_, 0);
+ data_offset_ = (starting_offset + 0x3) & ~0x3;
+ int32_t offset_adjustment;
+ AssignDataOffsets();
+
+ /*
+ * Note: generation must be 1 on first pass (to distinguish from initialized state of 0 for non-visited nodes).
+ * Start at zero here, and bit will be flipped to 1 on entry to the loop.
+ */
+ int generation = 0;
+ while (true) {
+ offset_adjustment = 0;
+ AssemblerStatus res = kSuccess; // Assume success
+ generation ^= 1;
+ // Note: nodes requring possible fixup linked in ascending order.
+ 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.
+ */
+ lir->offset += offset_adjustment;
+ // During pass, allows us to tell whether a node has been updated with offset_adjustment yet.
+ lir->flags.generation = generation;
+ switch (static_cast<FixupKind>(lir->flags.fixup)) {
+ case kFixupLabel:
+ case kFixupNone:
+ break;
+ case kFixupVLoad:
+ if (lir->operands[1] != r15pc) {
+ break;
+ }
+ // NOTE: intentional fallthrough.
+ case kFixupLoad: {
+ /*
+ * PC-relative loads are mostly used to load immediates
+ * that are too large to materialize directly in one shot.
+ * However, if the load displacement exceeds the limit,
+ * we revert to a multiple-instruction materialization sequence.
+ */
+ LIR *lir_target = lir->target;
+ CodeOffset pc = (lir->offset + 4) & ~3;
+ CodeOffset target = lir_target->offset +
+ ((lir_target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ if (res != kSuccess) {
+ /*
+ * In this case, we're just estimating and will do it again for real. Ensure offset
+ * is legal.
+ */
+ delta &= ~0x3;
+ }
+ DCHECK_EQ((delta & 0x3), 0);
+ // First, a sanity check for cases we shouldn't see now
+ if (kIsDebugBuild && (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) ||
+ ((lir->opcode == kThumbLdrPcRel) && (delta > 1020)))) {
+ // Shouldn't happen in current codegen.
+ LOG(FATAL) << "Unexpected pc-rel offset " << delta;
+ }
+ // Now, check for the difficult cases
+ if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) ||
+ ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) {
+ /*
+ * Note: The reason vldrs/vldrd include rARM_LR in their use/def masks is that we
+ * sometimes have to use it to fix up out-of-range accesses. This is where that
+ * happens.
+ */
+ int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) ||
+ (lir->opcode == kThumb2LdrPcRel12)) ? lir->operands[0] : rARM_LR;
+
+ // Add new Adr to generate the address.
+ LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr,
+ base_reg, 0, 0, 0, 0, lir->target);
+ new_adr->offset = lir->offset;
+ new_adr->flags.fixup = kFixupAdr;
+ new_adr->flags.size = EncodingMap[kThumb2Adr].size;
+ InsertLIRBefore(lir, new_adr);
+ lir->offset += new_adr->flags.size;
+ offset_adjustment += new_adr->flags.size;
+
+ // lir no longer pcrel, unlink and link in new_adr.
+ ReplaceFixup(prev_lir, lir, new_adr);
+
+ // Convert to normal load.
+ offset_adjustment -= lir->flags.size;
+ if (lir->opcode == kThumb2LdrPcRel12) {
+ lir->opcode = kThumb2LdrRRI12;
+ } else if (lir->opcode == kThumb2LdrdPcRel8) {
+ lir->opcode = kThumb2LdrdI8;
+ }
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ offset_adjustment += lir->flags.size;
+ // Change the load to be relative to the new Adr base.
+ if (lir->opcode == kThumb2LdrdI8) {
+ lir->operands[3] = 0;
+ lir->operands[2] = base_reg;
+ } else {
+ lir->operands[2] = 0;
+ lir->operands[1] = base_reg;
+ }
+ // Must redo encoding here - won't ever revisit this node.
+ EncodeLIR(lir);
+ prev_lir = new_adr; // Continue scan with new_adr;
+ lir = new_adr->u.a.pcrel_next;
+ res = kRetryAll;
+ continue;
+ } else {
+ if ((lir->opcode == kThumb2Vldrs) ||
+ (lir->opcode == kThumb2Vldrd) ||
+ (lir->opcode == kThumb2LdrdPcRel8)) {
+ lir->operands[2] = delta >> 2;
+ } else {
+ lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta :
+ delta >> 2;
+ }
+ }
+ break;
+ }
+ case kFixupCBxZ: {
+ LIR *target_lir = lir->target;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ if (delta > 126 || delta < 0) {
+ /*
+ * Convert to cmp rx,#0 / b[eq/ne] tgt pair
+ * Make new branch instruction and insert after
+ */
+ LIR* new_inst =
+ RawLIR(lir->dalvik_offset, kThumbBCond, 0,
+ (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe,
+ 0, 0, 0, lir->target);
+ InsertLIRAfter(lir, new_inst);
+
+ /* Convert the cb[n]z to a cmp rx, #0 ] */
+ // Subtract the old size.
+ offset_adjustment -= lir->flags.size;
+ lir->opcode = kThumbCmpRI8;
+ /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+ lir->operands[1] = 0;
+ lir->target = 0;
+ EncodeLIR(lir); // NOTE: sets flags.size.
+ // Add back the new size.
+ DCHECK_EQ(lir->flags.size, static_cast<uint32_t>(EncodingMap[lir->opcode].size));
+ offset_adjustment += lir->flags.size;
+ // Set up the new following inst.
+ new_inst->offset = lir->offset + lir->flags.size;
+ new_inst->flags.fixup = kFixupCondBranch;
+ new_inst->flags.size = EncodingMap[new_inst->opcode].size;
+ offset_adjustment += new_inst->flags.size;
+
+ // lir no longer pcrel, unlink and link in new_inst.
+ ReplaceFixup(prev_lir, lir, new_inst);
+ prev_lir = new_inst; // Continue with the new instruction.
+ lir = new_inst->u.a.pcrel_next;
+ res = kRetryAll;
+ continue;
+ } else {
+ lir->operands[1] = delta >> 1;
+ }
+ break;
+ }
+ case kFixupPushPop: {
+ if (__builtin_popcount(lir->operands[0]) == 1) {
+ /*
+ * The standard push/pop multiple instruction
+ * requires at least two registers in the list.
+ * If we've got just one, switch to the single-reg
+ * encoding.
+ */
+ lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 :
+ kThumb2Pop1;
+ int reg = 0;
+ while (lir->operands[0]) {
+ if (lir->operands[0] & 0x1) {
+ break;
+ } else {
+ reg++;
+ lir->operands[0] >>= 1;
+ }
+ }
+ lir->operands[0] = reg;
+ // This won't change again, don't bother unlinking, just reset fixup kind
+ lir->flags.fixup = kFixupNone;
+ }
+ break;
+ }
+ case kFixupCondBranch: {
+ LIR *target_lir = lir->target;
+ int32_t delta = 0;
+ DCHECK(target_lir);
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ delta = target - pc;
+ if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+ offset_adjustment -= lir->flags.size;
+ lir->opcode = kThumb2BCond;
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ // Fixup kind remains the same.
+ offset_adjustment += lir->flags.size;
+ res = kRetryAll;
+ }
+ lir->operands[0] = delta >> 1;
+ break;
+ }
+ case kFixupT2Branch: {
+ LIR *target_lir = lir->target;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == 0) {
+ // Useless branch
+ offset_adjustment -= lir->flags.size;
+ lir->flags.is_nop = true;
+ // Don't unlink - just set to do-nothing.
+ lir->flags.fixup = kFixupNone;
+ res = kRetryAll;
+ }
+ break;
+ }
+ case kFixupT1Branch: {
+ LIR *target_lir = lir->target;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ if (delta > 2046 || delta < -2048) {
+ // Convert to Thumb2BCond w/ kArmCondAl
+ offset_adjustment -= lir->flags.size;
+ lir->opcode = kThumb2BUncond;
+ lir->operands[0] = 0;
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ lir->flags.fixup = kFixupT2Branch;
+ offset_adjustment += lir->flags.size;
+ res = kRetryAll;
+ } else {
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == -1) {
+ // Useless branch
+ offset_adjustment -= lir->flags.size;
+ lir->flags.is_nop = true;
+ // Don't unlink - just set to do-nothing.
+ lir->flags.fixup = kFixupNone;
+ res = kRetryAll;
+ }
+ }
+ break;
+ }
+ case kFixupBlx1: {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2);
+ /* cur_pc is Thumb */
+ CodeOffset cur_pc = (lir->offset + 4) & ~3;
+ CodeOffset target = lir->operands[1];
+
+ /* Match bit[1] in target with base */
+ if (cur_pc & 0x2) {
+ target |= 0x2;
+ }
+ int32_t delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ break;
+ }
+ case kFixupBl1: {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2);
+ /* Both cur_pc and target are Thumb */
+ CodeOffset cur_pc = lir->offset + 4;
+ CodeOffset target = lir->operands[1];
+
+ int32_t delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ break;
+ }
+ case kFixupAdr: {
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[2]));
+ LIR* target = lir->target;
+ int32_t target_disp = (tab_rec != NULL) ? tab_rec->offset + offset_adjustment
+ : target->offset + ((target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t disp = target_disp - ((lir->offset + 4) & ~3);
+ if (disp < 4096) {
+ lir->operands[1] = disp;
+ } else {
+ // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0]
+ // TUNING: if this case fires often, it can be improved. Not expected to be common.
+ LIR *new_mov16L =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, lir->operands[0], 0,
+ WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target);
+ new_mov16L->flags.size = EncodingMap[new_mov16L->opcode].size;
+ new_mov16L->flags.fixup = kFixupMovImmLST;
+ new_mov16L->offset = lir->offset;
+ // Link the new instruction, retaining lir.
+ InsertLIRBefore(lir, new_mov16L);
+ lir->offset += new_mov16L->flags.size;
+ offset_adjustment += new_mov16L->flags.size;
+ InsertFixupBefore(prev_lir, lir, new_mov16L);
+ prev_lir = new_mov16L; // Now we've got a new prev.
+ LIR *new_mov16H =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, lir->operands[0], 0,
+ WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target);
+ new_mov16H->flags.size = EncodingMap[new_mov16H->opcode].size;
+ new_mov16H->flags.fixup = kFixupMovImmHST;
+ new_mov16H->offset = lir->offset;
+ // Link the new instruction, retaining lir.
+ InsertLIRBefore(lir, new_mov16H);
+ lir->offset += new_mov16H->flags.size;
+ offset_adjustment += new_mov16H->flags.size;
+ InsertFixupBefore(prev_lir, lir, new_mov16H);
+ prev_lir = new_mov16H; // Now we've got a new prev.
+
+ offset_adjustment -= lir->flags.size;
+ if (ARM_LOWREG(lir->operands[0])) {
+ lir->opcode = kThumbAddRRLH;
+ } else {
+ lir->opcode = kThumbAddRRHH;
+ }
+ lir->operands[1] = rARM_PC;
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ offset_adjustment += lir->flags.size;
+ // Must stay in fixup list and have offset updated; will be used by LST/HSP pair.
+ lir->flags.fixup = kFixupNone;
+ res = kRetryAll;
+ }
+ break;
+ }
+ case kFixupMovImmLST: {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2]));
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int32_t target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff;
+ break;
+ }
+ case kFixupMovImmHST: {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2]));
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int32_t target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] =
+ ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff;
+ break;
+ }
+ case kFixupAlign4: {
+ int32_t required_size = lir->offset & 0x2;
+ if (lir->flags.size != required_size) {
+ offset_adjustment += required_size - lir->flags.size;
+ lir->flags.size = required_size;
+ res = kRetryAll;
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected case " << lir->flags.fixup;
+ }
+ /*
+ * If one of the pc-relative instructions expanded we'll have
+ * to make another pass. Don't bother to fully assemble the
+ * instruction.
+ */
+ if (res == kSuccess) {
+ EncodeLIR(lir);
+ if (assembler_retries == 0) {
+ // Go ahead and fix up the code buffer image.
+ for (int i = 0; i < lir->flags.size; i++) {
+ code_buffer_[lir->offset + i] = lir->u.a.bytes[i];
+ }
+ }
+ }
+ prev_lir = lir;
+ lir = lir->u.a.pcrel_next;
+ }
+
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ starting_offset += offset_adjustment;
+ data_offset_ = (starting_offset + 0x3) & ~0x3;
+ AssignDataOffsets();
+ }
+ }
+
+ // Rebuild the CodeBuffer if we had to retry; otherwise it should be good as-is.
+ if (assembler_retries != 0) {
+ code_buffer_.clear();
+ for (LIR* lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ if (lir->flags.is_nop) {
+ continue;
+ } else {
+ for (int i = 0; i < lir->flags.size; i++) {
+ code_buffer_.push_back(lir->u.a.bytes[i]);
+ }
+ }
+ }
+ }
+
+ data_offset_ = (code_buffer_.size() + 0x3) & ~0x3;
+
+ cu_->NewTimingSplit("LiteralData");
+ // Install literals
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ cu_->NewTimingSplit("PcMappingTable");
+ CreateMappingTables();
+
+ cu_->NewTimingSplit("GcMap");
+ CreateNativeGcMap();
}
int ArmMir2Lir::GetInsnSize(LIR* lir) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
return EncodingMap[lir->opcode].size;
}
+// Encode instruction bit pattern and assign offsets.
+uint32_t ArmMir2Lir::EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t offset) {
+ LIR* end_lir = tail_lir->next;
+
+ /*
+ * A significant percentage of methods can be assembled in a single pass. We'll
+ * go ahead and build the code image here, leaving holes for pc-relative fixup
+ * codes. If the code size changes during that pass, we'll have to throw away
+ * this work - but if not, we're ready to go.
+ */
+ code_buffer_.reserve(estimated_native_code_size_ + 256); // Add a little slop.
+ LIR* last_fixup = NULL;
+ for (LIR* lir = head_lir; lir != end_lir; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (!lir->flags.is_nop) {
+ if (lir->flags.fixup != kFixupNone) {
+ if (!IsPseudoLirOp(lir->opcode)) {
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ lir->flags.fixup = EncodingMap[lir->opcode].fixup;
+ } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) {
+ lir->flags.size = (offset & 0x2);
+ lir->flags.fixup = kFixupAlign4;
+ } else {
+ lir->flags.size = 0;
+ lir->flags.fixup = kFixupLabel;
+ }
+ // Link into the fixup chain.
+ lir->flags.use_def_invalid = true;
+ lir->u.a.pcrel_next = NULL;
+ if (first_fixup_ == NULL) {
+ first_fixup_ = lir;
+ } else {
+ last_fixup->u.a.pcrel_next = lir;
+ }
+ last_fixup = lir;
+ } else {
+ EncodeLIR(lir);
+ }
+ for (int i = 0; i < lir->flags.size; i++) {
+ code_buffer_.push_back(lir->u.a.bytes[i]);
+ }
+ offset += lir->flags.size;
+ }
+ }
+ return offset;
+}
+
+void ArmMir2Lir::AssignDataOffsets() {
+ /* Set up offsets for literals */
+ CodeOffset offset = data_offset_;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ total_size_ = AssignFillArrayDataOffset(offset);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 2dbe5f5..51aca85 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -92,7 +92,7 @@
}
/* Find the next MIR, which may be in a following basic block */
-// TODO: should this be a utility in mir_graph?
+// TODO: make this a utility in mir_graph.
MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) {
BasicBlock* bb = *p_bb;
MIR* orig_mir = mir;
@@ -103,7 +103,7 @@
if (mir != NULL) {
return mir;
} else {
- bb = bb->fall_through;
+ bb = mir_graph_->GetBasicBlock(bb->fall_through);
*p_bb = bb;
if (bb) {
mir = bb->first_mir_insn;
@@ -120,17 +120,18 @@
// TODO: move to common code
void ArmMir2Lir::GenPrintLabel(MIR* mir) {
/* Mark the beginning of a Dalvik instruction for line tracking */
- char* inst_str = cu_->verbose ?
- mir_graph_->GetDalvikDisassembly(mir) : NULL;
- MarkBoundary(mir->offset, inst_str);
+ if (cu_->verbose) {
+ char* inst_str = mir_graph_->GetDalvikDisassembly(mir);
+ MarkBoundary(mir->offset, inst_str);
+ }
}
MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir,
OpSize size, bool long_or_double, bool is_object) {
- int field_offset;
+ int32_t field_offset;
bool is_volatile;
uint32_t field_idx = mir->dalvikInsn.vC;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile);
if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
return NULL;
}
@@ -152,10 +153,10 @@
MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir,
OpSize size, bool long_or_double, bool is_object) {
- int field_offset;
+ int32_t field_offset;
bool is_volatile;
uint32_t field_idx = mir->dalvikInsn.vC;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile);
if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
return NULL;
}
@@ -319,9 +320,9 @@
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
- int size = table[1];
+ uint32_t size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
- ArenaAllocator::kAllocLIR));
+ ArenaAllocator::kAllocLIR));
switch_tables_.Insert(tab_rec);
// Get the switch value
@@ -337,7 +338,7 @@
r_key = tmp;
}
// Materialize a pointer to the switch table
- NewLIR3(kThumb2Adr, rBase, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR3(kThumb2Adr, rBase, 0, WrapPointer(tab_rec));
// Set up r_idx
int r_idx = AllocTemp();
LoadConstant(r_idx, size);
@@ -367,7 +368,7 @@
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
- int size = table[1];
+ uint32_t size = table[1];
tab_rec->targets =
static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), ArenaAllocator::kAllocLIR));
switch_tables_.Insert(tab_rec);
@@ -376,7 +377,7 @@
rl_src = LoadValue(rl_src, kCoreReg);
int table_base = AllocTemp();
// Materialize a pointer to the switch table
- NewLIR3(kThumb2Adr, table_base, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR3(kThumb2Adr, table_base, 0, WrapPointer(tab_rec));
int low_key = s4FromSwitchData(&table[2]);
int keyReg;
// Remove the bias, if necessary
@@ -432,95 +433,127 @@
LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData).Int32Value(),
rARM_LR);
// Materialize a pointer to the fill data image
- NewLIR3(kThumb2Adr, r1, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR3(kThumb2Adr, r1, 0, WrapPointer(tab_rec));
ClobberCalleeSave();
LIR* call_inst = OpReg(kOpBlx, rARM_LR);
MarkSafepointPC(call_inst);
}
/*
- * Handle simple case (thin lock) inline. If it's complicated, bail
- * out to the heavyweight lock/unlock routines. We'll use dedicated
- * registers here in order to be in the right position in case we
- * to bail to oat[Lock/Unlock]Object(self, object)
- *
- * r0 -> self pointer [arg0 for oat[Lock/Unlock]Object
- * r1 -> object [arg1 for oat[Lock/Unlock]Object
- * r2 -> intial contents of object->lock, later result of strex
- * r3 -> self->thread_id
- * r12 -> allow to be used by utilities as general temp
- *
- * The result of the strex is 0 if we acquire the lock.
- *
- * See comments in monitor.cc for the layout of the lock word.
- * Of particular interest to this code is the test for the
- * simple case - which we handle inline. For monitor enter, the
- * simple case is thin lock, held by no-one. For monitor exit,
- * the simple case is thin lock, held by the unlocking thread with
- * a recurse count of 0.
- *
- * A minor complication is that there is a field in the lock word
- * unrelated to locking: the hash state. This field must be ignored, but
- * preserved.
- *
+ * Handle unlocked -> thin locked transition inline or else call out to quick entrypoint. For more
+ * details see monitor.cc.
*/
void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
FlushAllRegs();
- DCHECK_EQ(LW_SHAPE_THIN, 0);
LoadValueDirectFixed(rl_src, r0); // Get obj
LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
- LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
- NewLIR3(kThumb2Ldrex, r1, r0,
- mirror::Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
- // Align owner
- OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
- // Is lock unheld on lock or held by us (==thread_id) on unlock?
- NewLIR4(kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
- NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
- OpRegImm(kOpCmp, r1, 0);
- OpIT(kCondEq, "");
- NewLIR4(kThumb2Strex, r1, r2, r0,
- mirror::Object::MonitorOffset().Int32Value() >> 2);
- OpRegImm(kOpCmp, r1, 0);
- OpIT(kCondNe, "T");
- // Go expensive route - artLockObjectFromCode(self, obj);
- LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR);
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, rARM_LR);
- MarkSafepointPC(call_inst);
- GenMemBarrier(kLoadLoad);
+ constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15.
+ if (kArchVariantHasGoodBranchPredictor) {
+ LIR* null_check_branch;
+ if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
+ null_check_branch = nullptr; // No null check.
+ } else {
+ // If the null-check fails its handled by the slow-path to reduce exception related meta-data.
+ null_check_branch = OpCmpImmBranch(kCondEq, r0, 0, NULL);
+ }
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, r1, 0, NULL);
+ NewLIR4(kThumb2Strex, r1, r2, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ LIR* lock_success_branch = OpCmpImmBranch(kCondEq, r1, 0, NULL);
+
+
+ LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+ not_unlocked_branch->target = slow_path_target;
+ if (null_check_branch != nullptr) {
+ null_check_branch->target = slow_path_target;
+ }
+ // TODO: move to a slow path.
+ // Go expensive route - artLockObjectFromCode(obj);
+ LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+
+ LIR* success_target = NewLIR0(kPseudoTargetLabel);
+ lock_success_branch->target = success_target;
+ GenMemBarrier(kLoadLoad);
+ } else {
+ // Explicit null-check as slow-path is entered using an IT.
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondEq, "");
+ NewLIR4(kThumb2Strex/*eq*/, r1, r2, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondNe, "T");
+ // Go expensive route - artLockObjectFromCode(self, obj);
+ LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kLoadLoad);
+ }
}
/*
- * For monitor unlock, we don't have to use ldrex/strex. Once
- * we've determined that the lock is thin and that we own it with
- * a zero recursion count, it's safe to punch it back to the
- * initial, unlock thin state with a store word.
+ * Handle thin locked -> unlocked transition inline or else call out to quick entrypoint. For more
+ * details see monitor.cc. Note the code below doesn't use ldrex/strex as the code holds the lock
+ * and can only give away ownership if its suspended.
*/
void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
- DCHECK_EQ(LW_SHAPE_THIN, 0);
FlushAllRegs();
LoadValueDirectFixed(rl_src, r0); // Get obj
LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
- LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock
+ LIR* null_check_branch;
LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
- // Is lock unheld on lock or held by us (==thread_id) on unlock?
- OpRegRegImm(kOpAnd, r3, r1,
- (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
- // Align owner
- OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
- NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
- OpRegReg(kOpSub, r1, r2);
- OpIT(kCondEq, "EE");
- StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
- // Go expensive route - UnlockObjectFromCode(obj);
- LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR);
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, rARM_LR);
- MarkSafepointPC(call_inst);
- GenMemBarrier(kStoreLoad);
+ constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15.
+ if (kArchVariantHasGoodBranchPredictor) {
+ if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
+ null_check_branch = nullptr; // No null check.
+ } else {
+ // If the null-check fails its handled by the slow-path to reduce exception related meta-data.
+ null_check_branch = OpCmpImmBranch(kCondEq, r0, 0, NULL);
+ }
+ LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1);
+ LoadConstantNoClobber(r3, 0);
+ LIR* slow_unlock_branch = OpCmpBranch(kCondNe, r1, r2, NULL);
+ StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
+ LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
+
+ LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+ slow_unlock_branch->target = slow_path_target;
+ if (null_check_branch != nullptr) {
+ null_check_branch->target = slow_path_target;
+ }
+ // TODO: move to a slow path.
+ // Go expensive route - artUnlockObjectFromCode(obj);
+ LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+
+ LIR* success_target = NewLIR0(kPseudoTargetLabel);
+ unlock_success_branch->target = success_target;
+ GenMemBarrier(kStoreLoad);
+ } else {
+ // Explicit null-check as slow-path is entered using an IT.
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ LoadConstantNoClobber(r3, 0);
+ // Is lock unheld on lock or held by us (==thread_id) on unlock?
+ OpRegReg(kOpCmp, r1, r2);
+ OpIT(kCondEq, "EE");
+ StoreWordDisp/*eq*/(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
+ // Go expensive route - UnlockObjectFromCode(obj);
+ LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kStoreLoad);
+ }
}
void ArmMir2Lir::GenMoveException(RegLocation rl_dest) {
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 291319f..15355be 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -51,7 +51,6 @@
int AllocTypedTempPair(bool fp_hint, int reg_class);
int S2d(int low_reg, int high_reg);
int TargetReg(SpecialTargetRegister reg);
- RegisterInfo* GetRegInfo(int reg);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -71,9 +70,13 @@
void CompilerInitializeRegAlloc();
// Required for target - miscellaneous.
- AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void AssembleLIR();
+ uint32_t EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t starting_offset);
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ void EncodeLIR(LIR* lir);
void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
- void SetupTargetResourceMasks(LIR* lir);
+ void SetupTargetResourceMasks(LIR* lir, uint64_t flags);
const char* GetTargetInstFmt(int opcode);
const char* GetTargetInstName(int opcode);
std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
@@ -85,12 +88,10 @@
// Required for target - Dalvik-level generators.
void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2);
- void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
- RegLocation rl_src, int scale);
void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale);
- void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src, int scale, bool card_mark);
void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_shift);
void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -106,6 +107,8 @@
bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
bool GenInlinedSqrt(CallInfo* info);
+ bool GenInlinedPeek(CallInfo* info, OpSize size);
+ bool GenInlinedPoke(CallInfo* info, OpSize size);
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -118,7 +121,7 @@
void GenDivZeroCheck(int reg_lo, int reg_hi);
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
- void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFillArrayData(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);
@@ -130,8 +133,8 @@
int first_bit, int second_bit);
void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
- void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
- void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
// Required for target - single operation generators.
@@ -188,6 +191,9 @@
MIR* SpecialIdentity(MIR* mir);
LIR* LoadFPConstantValue(int r_dest, int value);
bool BadOverlap(RegLocation rl_src, RegLocation rl_dest);
+ void ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir);
+ void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir);
+ void AssignDataOffsets();
};
} // namespace art
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 08d6778..480e021 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -176,7 +176,7 @@
void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
bool is_double) {
- LIR* target = &block_label_list_[bb->taken->id];
+ LIR* target = &block_label_list_[bb->taken];
RegLocation rl_src1;
RegLocation rl_src2;
if (is_double) {
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 6fbdd2f..42bf3d4 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -24,8 +24,7 @@
namespace art {
-LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1,
- int src2, LIR* target) {
+LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target) {
OpRegReg(kOpCmp, src1, src2);
return OpCondBranch(cond, target);
}
@@ -123,8 +122,8 @@
int32_t val_hi = High32Bits(val);
DCHECK_GE(ModifiedImmediate(val_lo), 0);
DCHECK_GE(ModifiedImmediate(val_hi), 0);
- LIR* taken = &block_label_list_[bb->taken->id];
- LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* taken = &block_label_list_[bb->taken];
+ LIR* not_taken = &block_label_list_[bb->fall_through];
rl_src1 = LoadValueWide(rl_src1, kCoreReg);
int32_t low_reg = rl_src1.low_reg;
int32_t high_reg = rl_src1.high_reg;
@@ -179,23 +178,6 @@
void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
RegLocation rl_result;
RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
- // Temporary debugging code
- int dest_sreg = mir->ssa_rep->defs[0];
- if ((dest_sreg < 0) || (dest_sreg >= mir_graph_->GetNumSSARegs())) {
- LOG(INFO) << "Bad target sreg: " << dest_sreg << ", in "
- << PrettyMethod(cu_->method_idx, *cu_->dex_file);
- LOG(INFO) << "at dex offset 0x" << std::hex << mir->offset;
- LOG(INFO) << "vreg = " << mir_graph_->SRegToVReg(dest_sreg);
- LOG(INFO) << "num uses = " << mir->ssa_rep->num_uses;
- if (mir->ssa_rep->num_uses == 1) {
- LOG(INFO) << "CONST case, vals = " << mir->dalvikInsn.vB << ", " << mir->dalvikInsn.vC;
- } else {
- LOG(INFO) << "MOVE case, operands = " << mir->ssa_rep->uses[1] << ", "
- << mir->ssa_rep->uses[2];
- }
- CHECK(false) << "Invalid target sreg on Select.";
- }
- // End temporary debugging code
RegLocation rl_dest = mir_graph_->GetDest(mir);
rl_src = LoadValue(rl_src, kCoreReg);
if (mir->ssa_rep->num_uses == 1) {
@@ -234,11 +216,17 @@
rl_false = LoadValue(rl_false, kCoreReg);
rl_result = EvalLoc(rl_dest, kCoreReg, true);
OpRegImm(kOpCmp, rl_src.low_reg, 0);
- OpIT(kCondEq, "E");
- LIR* l1 = OpRegCopy(rl_result.low_reg, rl_true.low_reg);
- l1->flags.is_nop = false; // Make sure this instruction isn't optimized away
- LIR* l2 = OpRegCopy(rl_result.low_reg, rl_false.low_reg);
- l2->flags.is_nop = false; // Make sure this instruction isn't optimized away
+ if (rl_result.low_reg == rl_true.low_reg) { // Is the "true" case already in place?
+ OpIT(kCondNe, "");
+ OpRegCopy(rl_result.low_reg, rl_false.low_reg);
+ } else if (rl_result.low_reg == rl_false.low_reg) { // False case in place?
+ OpIT(kCondEq, "");
+ OpRegCopy(rl_result.low_reg, rl_true.low_reg);
+ } else { // Normal - select between the two.
+ OpIT(kCondEq, "E");
+ OpRegCopy(rl_result.low_reg, rl_true.low_reg);
+ OpRegCopy(rl_result.low_reg, rl_false.low_reg);
+ }
GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
}
StoreValue(rl_dest, rl_result);
@@ -265,8 +253,8 @@
return;
}
}
- LIR* taken = &block_label_list_[bb->taken->id];
- LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* taken = &block_label_list_[bb->taken];
+ LIR* not_taken = &block_label_list_[bb->fall_through];
rl_src1 = LoadValueWide(rl_src1, kCoreReg);
rl_src2 = LoadValueWide(rl_src2, kCoreReg);
OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
@@ -313,7 +301,18 @@
LIR* branch;
int mod_imm;
ArmConditionCode arm_cond = ArmConditionEncoding(cond);
- if ((ARM_LOWREG(reg)) && (check_value == 0) &&
+ /*
+ * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit
+ * compare-and-branch if zero is ideal if it will reach. However, because null checks
+ * branch forward to a launch pad, they will frequently not reach - and thus have to
+ * be converted to a long form during assembly (which will trigger another assembly
+ * pass). Here we estimate the branch distance for checks, and if large directly
+ * generate the long form in an attempt to avoid an extra assembly pass.
+ * TODO: consider interspersing launchpads 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);
+ if (!skip && (ARM_LOWREG(reg)) && (check_value == 0) &&
((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) {
branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
reg, 0);
@@ -467,14 +466,39 @@
RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit,
bool is_div) {
- LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
- return rl_dest;
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+ // Put the literal in a temp.
+ int lit_temp = AllocTemp();
+ LoadConstant(lit_temp, lit);
+ // Use the generic case for div/rem with arg2 in a register.
+ // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
+ rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
+ FreeTemp(lit_temp);
+
+ return rl_result;
}
RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2,
bool is_div) {
- LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
- return rl_dest;
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (is_div) {
+ // Simple case, use sdiv instruction.
+ OpRegRegReg(kOpDiv, rl_result.low_reg, reg1, reg2);
+ } else {
+ // Remainder case, use the following code:
+ // temp = reg1 / reg2 - integer division
+ // temp = temp * reg2
+ // dest = reg1 - temp
+
+ int temp = AllocTemp();
+ OpRegRegReg(kOpDiv, temp, reg1, reg2);
+ OpRegReg(kOpMul, temp, reg2);
+ OpRegRegReg(kOpSub, rl_result.low_reg, reg1, temp);
+ FreeTemp(temp);
+ }
+
+ return rl_result;
}
bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
@@ -494,6 +518,50 @@
return true;
}
+bool ArmMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == kLong) {
+ // Fake unaligned LDRD by two unaligned LDR instructions on ARMv7 with SCTLR.A set to 0.
+ if (rl_address.low_reg != rl_result.low_reg) {
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, kWord, INVALID_SREG);
+ LoadBaseDisp(rl_address.low_reg, 4, rl_result.high_reg, kWord, INVALID_SREG);
+ } else {
+ LoadBaseDisp(rl_address.low_reg, 4, rl_result.high_reg, kWord, INVALID_SREG);
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, kWord, INVALID_SREG);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned load with LDR and LDRSH is allowed on ARMv7 with SCTLR.A set to 0.
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
+bool ArmMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_src_value = info->args[2]; // [size] value
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ if (size == kLong) {
+ // Fake unaligned STRD by two unaligned STR instructions on ARMv7 with SCTLR.A set to 0.
+ RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, kWord);
+ StoreBaseDisp(rl_address.low_reg, 4, rl_value.high_reg, kWord);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned store with STR and STRSH is allowed on ARMv7 with SCTLR.A set to 0.
+ RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
+ }
+ return true;
+}
+
void ArmMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
LOG(FATAL) << "Unexpected use of OpLea for Arm";
}
@@ -618,7 +686,7 @@
break;
}
LIR* dmb = NewLIR1(kThumb2Dmb, dmb_flavor);
- dmb->def_mask = ENCODE_ALL;
+ dmb->u.m.def_mask = ENCODE_ALL;
#endif
}
@@ -755,7 +823,7 @@
* Generate array load
*/
void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale) {
+ RegLocation rl_index, RegLocation rl_dest, int scale) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -845,13 +913,13 @@
*
*/
void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset;
bool constant_index = rl_index.is_const;
- if (rl_src.wide) {
+ int data_offset;
+ if (size == kLong || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -868,12 +936,14 @@
}
int reg_ptr;
+ bool allocated_reg_ptr_temp = false;
if (constant_index) {
reg_ptr = rl_array.low_reg;
- } else if (IsTemp(rl_array.low_reg)) {
+ } else if (IsTemp(rl_array.low_reg) && !card_mark) {
Clobber(rl_array.low_reg);
reg_ptr = rl_array.low_reg;
} else {
+ allocated_reg_ptr_temp = true;
reg_ptr = AllocTemp();
}
@@ -924,71 +994,15 @@
StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
scale, size);
}
- if (!constant_index) {
+ if (allocated_reg_ptr_temp) {
FreeTemp(reg_ptr);
}
-}
-
-/*
- * Generate array store
- *
- */
-void ArmMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
- int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
-
- FlushAllRegs(); // Use explicit registers
- LockCallTemps();
-
- int r_value = TargetReg(kArg0); // Register holding value
- int r_array_class = TargetReg(kArg1); // Register holding array's Class
- int r_array = TargetReg(kArg2); // Register holding array
- int r_index = TargetReg(kArg3); // Register holding index into array
-
- LoadValueDirectFixed(rl_array, r_array); // Grab array
- LoadValueDirectFixed(rl_src, r_value); // Grab value
- LoadValueDirectFixed(rl_index, r_index); // Grab index
-
- GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
-
- // Store of null?
- LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
-
- // Get the array's class.
- LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
- r_array_class, true);
- // Redo LoadValues in case they didn't survive the call.
- LoadValueDirectFixed(rl_array, r_array); // Reload array
- LoadValueDirectFixed(rl_index, r_index); // Reload index
- LoadValueDirectFixed(rl_src, r_value); // Reload value
- r_array_class = INVALID_REG;
-
- // Branch here if value to be stored == null
- LIR* target = NewLIR0(kPseudoTargetLabel);
- null_value_check->target = target;
-
- bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
- int reg_len = INVALID_REG;
- if (needs_range_check) {
- reg_len = TargetReg(kArg1);
- LoadWordDisp(r_array, len_offset, reg_len); // Get len
- }
- /* r_ptr -> array data */
- int r_ptr = AllocTemp();
- OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
- if (needs_range_check) {
- GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
- }
- StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
- FreeTemp(r_ptr);
- FreeTemp(r_index);
- if (!mir_graph_->IsConstantNullRef(rl_src)) {
- MarkGCCard(r_value, r_array);
+ if (card_mark) {
+ MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
+
void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
rl_src = LoadValueWide(rl_src, kCoreReg);
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 6cc3052..52aba9b 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -74,6 +74,8 @@
case kRet0: res = rARM_RET0; break;
case kRet1: res = rARM_RET1; break;
case kInvokeTgt: res = rARM_INVOKE_TGT; break;
+ case kHiddenArg: res = r12; break;
+ case kHiddenFpArg: res = INVALID_REG; break;
case kCount: res = rARM_COUNT; break;
}
return res;
@@ -118,78 +120,83 @@
return ENCODE_ARM_REG_PC;
}
-void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir) {
+// Thumb2 specific setup. TODO: inline?:
+void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) {
DCHECK_EQ(cu_->instruction_set, kThumb2);
+ DCHECK(!lir->flags.use_def_invalid);
- // Thumb2 specific setup
- uint64_t flags = ArmMir2Lir::EncodingMap[lir->opcode].flags;
int opcode = lir->opcode;
- if (flags & REG_DEF_SP) {
- lir->def_mask |= ENCODE_ARM_REG_SP;
- }
-
- if (flags & REG_USE_SP) {
- lir->use_mask |= ENCODE_ARM_REG_SP;
- }
-
- if (flags & REG_DEF_LIST0) {
- lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
- }
-
- if (flags & REG_DEF_LIST1) {
- lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
- }
-
- if (flags & REG_DEF_FPCS_LIST0) {
- lir->def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
- }
-
- if (flags & REG_DEF_FPCS_LIST2) {
- for (int i = 0; i < lir->operands[2]; i++) {
- SetupRegMask(&lir->def_mask, lir->operands[1] + i);
+ // These flags are somewhat uncommon - bypass if we can.
+ if ((flags & (REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0 | REG_DEF_LIST1 |
+ REG_DEF_FPCS_LIST0 | REG_DEF_FPCS_LIST2 | REG_USE_PC | IS_IT | REG_USE_LIST0 |
+ REG_USE_LIST1 | REG_USE_FPCS_LIST0 | REG_USE_FPCS_LIST2 | REG_DEF_LR)) != 0) {
+ if (flags & REG_DEF_SP) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_SP;
}
- }
- if (flags & REG_USE_PC) {
- lir->use_mask |= ENCODE_ARM_REG_PC;
- }
-
- /* Conservatively treat the IT block */
- if (flags & IS_IT) {
- lir->def_mask = ENCODE_ALL;
- }
-
- if (flags & REG_USE_LIST0) {
- lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
- }
-
- if (flags & REG_USE_LIST1) {
- lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
- }
-
- if (flags & REG_USE_FPCS_LIST0) {
- lir->use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
- }
-
- if (flags & REG_USE_FPCS_LIST2) {
- for (int i = 0; i < lir->operands[2]; i++) {
- SetupRegMask(&lir->use_mask, lir->operands[1] + i);
+ if (flags & REG_USE_SP) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_SP;
}
- }
- /* Fixup for kThumbPush/lr and kThumbPop/pc */
- if (opcode == kThumbPush || opcode == kThumbPop) {
- uint64_t r8Mask = GetRegMaskCommon(r8);
- if ((opcode == kThumbPush) && (lir->use_mask & r8Mask)) {
- lir->use_mask &= ~r8Mask;
- lir->use_mask |= ENCODE_ARM_REG_LR;
- } else if ((opcode == kThumbPop) && (lir->def_mask & r8Mask)) {
- lir->def_mask &= ~r8Mask;
- lir->def_mask |= ENCODE_ARM_REG_PC;
+
+ if (flags & REG_DEF_LIST0) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
}
- }
- if (flags & REG_DEF_LR) {
- lir->def_mask |= ENCODE_ARM_REG_LR;
+
+ if (flags & REG_DEF_LIST1) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & REG_DEF_FPCS_LIST0) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_DEF_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->u.m.def_mask, lir->operands[1] + i);
+ }
+ }
+
+ if (flags & REG_USE_PC) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_PC;
+ }
+
+ /* Conservatively treat the IT block */
+ if (flags & IS_IT) {
+ lir->u.m.def_mask = ENCODE_ALL;
+ }
+
+ if (flags & REG_USE_LIST0) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_LIST1) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
+
+ if (flags & REG_USE_FPCS_LIST0) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
+
+ if (flags & REG_USE_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[1] + i);
+ }
+ }
+ /* Fixup for kThumbPush/lr and kThumbPop/pc */
+ if (opcode == kThumbPush || opcode == kThumbPop) {
+ uint64_t r8Mask = GetRegMaskCommon(r8);
+ if ((opcode == kThumbPush) && (lir->u.m.use_mask & r8Mask)) {
+ lir->u.m.use_mask &= ~r8Mask;
+ lir->u.m.use_mask |= ENCODE_ARM_REG_LR;
+ } else if ((opcode == kThumbPop) && (lir->u.m.def_mask & r8Mask)) {
+ lir->u.m.def_mask &= ~r8Mask;
+ lir->u.m.def_mask |= ENCODE_ARM_REG_PC;
+ }
+ }
+ if (flags & REG_DEF_LR) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_LR;
+ }
}
}
@@ -277,8 +284,8 @@
return buf;
}
-static int ExpandImmediate(int value) {
- int mode = (value & 0xf00) >> 8;
+static int32_t ExpandImmediate(int value) {
+ int32_t mode = (value & 0xf00) >> 8;
uint32_t bits = value & 0xff;
switch (mode) {
case 0:
@@ -466,8 +473,8 @@
/* Memory bits */
if (arm_lir && (mask & ENCODE_DALVIK_REG)) {
- sprintf(buf + strlen(buf), "dr%d%s", arm_lir->alias_info & 0xffff,
- (arm_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(arm_lir->flags.alias_info),
+ DECODE_ALIAS_INFO_WIDE(arm_lir->flags.alias_info) ? "(+1)" : "");
}
if (mask & ENCODE_LITERAL) {
strcat(buf, "lit ");
@@ -691,11 +698,6 @@
return res;
}
-ArmMir2Lir::RegisterInfo* ArmMir2Lir::GetRegInfo(int reg) {
- return ARM_FPREG(reg) ? ®_pool_->FPRegs[reg & ARM_FP_REG_MASK]
- : ®_pool_->core_regs[reg];
-}
-
/* To be used when explicitly managing register use */
void ArmMir2Lir::LockCallTemps() {
LockTemp(r0);
@@ -718,14 +720,17 @@
}
uint64_t ArmMir2Lir::GetTargetInstFlags(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return ArmMir2Lir::EncodingMap[opcode].flags;
}
const char* ArmMir2Lir::GetTargetInstName(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return ArmMir2Lir::EncodingMap[opcode].name;
}
const char* ArmMir2Lir::GetTargetInstFmt(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return ArmMir2Lir::EncodingMap[opcode].fmt;
}
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index c63de69..d631cf7 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -22,14 +22,14 @@
/* This file contains codegen for the Thumb ISA. */
-static int EncodeImmSingle(int value) {
- int res;
- int bit_a = (value & 0x80000000) >> 31;
- int not_bit_b = (value & 0x40000000) >> 30;
- int bit_b = (value & 0x20000000) >> 29;
- int b_smear = (value & 0x3e000000) >> 25;
- int slice = (value & 0x01f80000) >> 19;
- int zeroes = (value & 0x0007ffff);
+static int32_t EncodeImmSingle(int32_t value) {
+ int32_t res;
+ int32_t bit_a = (value & 0x80000000) >> 31;
+ int32_t not_bit_b = (value & 0x40000000) >> 30;
+ int32_t bit_b = (value & 0x20000000) >> 29;
+ int32_t b_smear = (value & 0x3e000000) >> 25;
+ int32_t slice = (value & 0x01f80000) >> 19;
+ int32_t zeroes = (value & 0x0007ffff);
if (zeroes != 0)
return -1;
if (bit_b) {
@@ -47,15 +47,15 @@
* Determine whether value can be encoded as a Thumb2 floating point
* immediate. If not, return -1. If so return encoded 8-bit value.
*/
-static int EncodeImmDouble(int64_t value) {
- int res;
- int bit_a = (value & 0x8000000000000000ll) >> 63;
- int not_bit_b = (value & 0x4000000000000000ll) >> 62;
- int bit_b = (value & 0x2000000000000000ll) >> 61;
- int b_smear = (value & 0x3fc0000000000000ll) >> 54;
- int slice = (value & 0x003f000000000000ll) >> 48;
+static int32_t EncodeImmDouble(int64_t value) {
+ int32_t res;
+ int32_t bit_a = (value & 0x8000000000000000ll) >> 63;
+ int32_t not_bit_b = (value & 0x4000000000000000ll) >> 62;
+ int32_t bit_b = (value & 0x2000000000000000ll) >> 61;
+ int32_t b_smear = (value & 0x3fc0000000000000ll) >> 54;
+ int32_t slice = (value & 0x003f000000000000ll) >> 48;
uint64_t zeroes = (value & 0x0000ffffffffffffll);
- if (zeroes != 0)
+ if (zeroes != 0ull)
return -1;
if (bit_b) {
if ((not_bit_b != 0) || (b_smear != 0xff))
@@ -90,15 +90,14 @@
LIR* load_pc_rel = RawLIR(current_dalvik_offset_, kThumb2Vldrs,
r_dest, r15pc, 0, 0, 0, data_target);
SetMemRefType(load_pc_rel, true, kLiteral);
- load_pc_rel->alias_info = reinterpret_cast<uintptr_t>(data_target);
AppendLIR(load_pc_rel);
return load_pc_rel;
}
static int LeadingZeros(uint32_t val) {
uint32_t alt;
- int n;
- int count;
+ int32_t n;
+ int32_t count;
count = 16;
n = 32;
@@ -118,8 +117,8 @@
* immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
*/
int ArmMir2Lir::ModifiedImmediate(uint32_t value) {
- int z_leading;
- int z_trailing;
+ int32_t z_leading;
+ int32_t z_trailing;
uint32_t b0 = value & 0xff;
/* Note: case of value==0 must use 0:000:0:0000000 encoding */
@@ -315,6 +314,22 @@
case kOpSub:
opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR;
break;
+ case kOpRev:
+ DCHECK_EQ(shift, 0);
+ if (!thumb_form) {
+ // Binary, but rm is encoded twice.
+ return NewLIR3(kThumb2RevRR, r_dest_src1, r_src2, r_src2);
+ }
+ opcode = kThumbRev;
+ break;
+ case kOpRevsh:
+ DCHECK_EQ(shift, 0);
+ if (!thumb_form) {
+ // Binary, but rm is encoded twice.
+ return NewLIR3(kThumb2RevshRR, r_dest_src1, r_src2, r_src2);
+ }
+ opcode = kThumbRevsh;
+ break;
case kOp2Byte:
DCHECK_EQ(shift, 0);
return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 8);
@@ -328,7 +343,7 @@
LOG(FATAL) << "Bad opcode: " << op;
break;
}
- DCHECK_GE(static_cast<int>(opcode), 0);
+ DCHECK(!IsPseudoLirOp(opcode));
if (EncodingMap[opcode].flags & IS_BINARY_OP) {
return NewLIR2(opcode, r_dest_src1, r_src2);
} else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
@@ -380,6 +395,10 @@
DCHECK_EQ(shift, 0);
opcode = kThumb2MulRRR;
break;
+ case kOpDiv:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2SdivRRR;
+ break;
case kOpOr:
opcode = kThumb2OrrRRR;
break;
@@ -406,7 +425,7 @@
LOG(FATAL) << "Bad opcode: " << op;
break;
}
- DCHECK_GE(static_cast<int>(opcode), 0);
+ DCHECK(!IsPseudoLirOp(opcode));
if (EncodingMap[opcode].flags & IS_QUAD_OP) {
return NewLIR4(opcode, r_dest, r_src1, r_src2, shift);
} else {
@@ -422,12 +441,12 @@
LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) {
LIR* res;
bool neg = (value < 0);
- int abs_value = (neg) ? -value : value;
+ int32_t abs_value = (neg) ? -value : value;
ArmOpcode opcode = kThumbBkpt;
ArmOpcode alt_opcode = kThumbBkpt;
bool all_low_regs = (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1));
- int mod_imm = ModifiedImmediate(value);
- int mod_imm_neg = ModifiedImmediate(-value);
+ int32_t mod_imm = ModifiedImmediate(value);
+ int32_t mod_imm_neg = ModifiedImmediate(-value);
switch (op) {
case kOpLsl:
@@ -545,7 +564,7 @@
/* Handle Thumb-only variants here - otherwise punt to OpRegRegImm */
LIR* ArmMir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value) {
bool neg = (value < 0);
- int abs_value = (neg) ? -value : value;
+ int32_t abs_value = (neg) ? -value : value;
bool short_form = (((abs_value & 0xff) == abs_value) && ARM_LOWREG(r_dest_src1));
ArmOpcode opcode = kThumbBkpt;
switch (op) {
@@ -626,7 +645,6 @@
r_dest_lo, r_dest_hi, r15pc, 0, 0, data_target);
}
SetMemRefType(res, true, kLiteral);
- res->alias_info = reinterpret_cast<uintptr_t>(data_target);
AppendLIR(res);
}
return res;
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index a49fa7b..dfbc887 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -45,29 +45,54 @@
}
void Mir2Lir::MarkSafepointPC(LIR* inst) {
- inst->def_mask = ENCODE_ALL;
+ DCHECK(!inst->flags.use_def_invalid);
+ inst->u.m.def_mask = ENCODE_ALL;
LIR* safepoint_pc = NewLIR0(kPseudoSafepointPC);
- DCHECK_EQ(safepoint_pc->def_mask, ENCODE_ALL);
+ DCHECK_EQ(safepoint_pc->u.m.def_mask, ENCODE_ALL);
}
-bool Mir2Lir::FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put) {
+bool Mir2Lir::FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile) {
return cu_->compiler_driver->ComputeInstanceFieldInfo(
- field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, is_volatile, is_put);
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), is_put, field_offset, is_volatile);
+}
+
+/* Remove a LIR from the list. */
+void Mir2Lir::UnlinkLIR(LIR* lir) {
+ if (UNLIKELY(lir == first_lir_insn_)) {
+ first_lir_insn_ = lir->next;
+ if (lir->next != NULL) {
+ lir->next->prev = NULL;
+ } else {
+ DCHECK(lir->next == NULL);
+ DCHECK(lir == last_lir_insn_);
+ last_lir_insn_ = NULL;
+ }
+ } else if (lir == last_lir_insn_) {
+ last_lir_insn_ = lir->prev;
+ lir->prev->next = NULL;
+ } else if ((lir->prev != NULL) && (lir->next != NULL)) {
+ lir->prev->next = lir->next;
+ lir->next->prev = lir->prev;
+ }
}
/* Convert an instruction to a NOP */
void Mir2Lir::NopLIR(LIR* lir) {
lir->flags.is_nop = true;
+ if (!cu_->verbose) {
+ UnlinkLIR(lir);
+ }
}
void Mir2Lir::SetMemRefType(LIR* lir, bool is_load, int mem_type) {
uint64_t *mask_ptr;
uint64_t mask = ENCODE_MEM;
DCHECK(GetTargetInstFlags(lir->opcode) & (IS_LOAD | IS_STORE));
+ DCHECK(!lir->flags.use_def_invalid);
if (is_load) {
- mask_ptr = &lir->use_mask;
+ mask_ptr = &lir->u.m.use_mask;
} else {
- mask_ptr = &lir->def_mask;
+ mask_ptr = &lir->u.m.def_mask;
}
/* Clear out the memref flags */
*mask_ptr &= ~mask;
@@ -104,7 +129,7 @@
* Store the Dalvik register id in alias_info. Mark the MSB if it is a 64-bit
* access.
*/
- lir->alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit);
+ lir->flags.alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit);
}
/*
@@ -135,10 +160,12 @@
break;
case kPseudoDalvikByteCodeBoundary:
if (lir->operands[0] == 0) {
- lir->operands[0] = reinterpret_cast<uintptr_t>("No instruction string");
+ // NOTE: only used for debug listings.
+ lir->operands[0] = WrapPointer(ArenaStrdup("No instruction string"));
}
LOG(INFO) << "-------- dalvik offset: 0x" << std::hex
- << lir->dalvik_offset << " @ " << reinterpret_cast<char*>(lir->operands[0]);
+ << lir->dalvik_offset << " @ "
+ << reinterpret_cast<char*>(UnwrapPointer(lir->operands[0]));
break;
case kPseudoExitBlock:
LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest;
@@ -190,11 +217,11 @@
break;
}
- if (lir->use_mask && (!lir->flags.is_nop || dump_nop)) {
- DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->use_mask, "use"));
+ if (lir->u.m.use_mask && (!lir->flags.is_nop || dump_nop)) {
+ DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->u.m.use_mask, "use"));
}
- if (lir->def_mask && (!lir->flags.is_nop || dump_nop)) {
- DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->def_mask, "def"));
+ if (lir->u.m.def_mask && (!lir->flags.is_nop || dump_nop)) {
+ DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->u.m.def_mask, "def"));
}
}
@@ -225,12 +252,12 @@
}
/* Dump a mapping table */
-void Mir2Lir::DumpMappingTable(const char* table_name, const std::string& descriptor,
- const std::string& name, const std::string& signature,
+void Mir2Lir::DumpMappingTable(const char* table_name, const char* descriptor,
+ const char* name, const Signature& signature,
const std::vector<uint32_t>& v) {
if (v.size() > 0) {
std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name,
- descriptor.c_str(), name.c_str(), signature.c_str(), v.size()));
+ descriptor, name, signature.ToString().c_str(), v.size()));
std::replace(line.begin(), line.end(), ';', '_');
LOG(INFO) << line;
for (uint32_t i = 0; i < v.size(); i+=2) {
@@ -270,9 +297,9 @@
const DexFile::MethodId& method_id =
cu_->dex_file->GetMethodId(cu_->method_idx);
- std::string signature(cu_->dex_file->GetMethodSignature(method_id));
- std::string name(cu_->dex_file->GetMethodName(method_id));
- std::string descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
+ const Signature signature = cu_->dex_file->GetMethodSignature(method_id);
+ const char* name = cu_->dex_file->GetMethodName(method_id);
+ const char* descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
// Dump mapping tables
DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_);
@@ -325,6 +352,7 @@
new_value->operands[0] = value;
new_value->next = *constant_list_p;
*constant_list_p = new_value;
+ estimated_native_code_size_ += sizeof(value);
return new_value;
}
return NULL;
@@ -343,6 +371,17 @@
buf.push_back((data >> 24) & 0xff);
}
+// Push 8 bytes on 64-bit systems; 4 on 32-bit systems.
+static void PushPointer(std::vector<uint8_t>&buf, void const* pointer) {
+ uintptr_t data = reinterpret_cast<uintptr_t>(pointer);
+ if (sizeof(void*) == sizeof(uint64_t)) {
+ PushWord(buf, (data >> (sizeof(void*) * 4)) & 0xFFFFFFFF);
+ PushWord(buf, data & 0xFFFFFFFF);
+ } else {
+ PushWord(buf, data);
+ }
+}
+
static void AlignBuffer(std::vector<uint8_t>&buf, size_t offset) {
while (buf.size() < offset) {
buf.push_back(0);
@@ -369,9 +408,8 @@
static_cast<InvokeType>(data_lir->operands[1]),
code_buffer_.size());
const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
- // unique based on target to ensure code deduplication works
- uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
- PushWord(code_buffer_, unique_patch_value);
+ // unique value based on target to ensure code deduplication works
+ PushPointer(code_buffer_, &id);
data_lir = NEXT_LIR(data_lir);
}
data_lir = method_literal_list_;
@@ -385,9 +423,8 @@
static_cast<InvokeType>(data_lir->operands[1]),
code_buffer_.size());
const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
- // unique based on target to ensure code deduplication works
- uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
- PushWord(code_buffer_, unique_patch_value);
+ // unique value based on target to ensure code deduplication works
+ PushPointer(code_buffer_, &id);
data_lir = NEXT_LIR(data_lir);
}
}
@@ -408,6 +445,7 @@
int bx_offset = INVALID_OFFSET;
switch (cu_->instruction_set) {
case kThumb2:
+ DCHECK(tab_rec->anchor->flags.fixup != kFixupNone);
bx_offset = tab_rec->anchor->offset + 4;
break;
case kX86:
@@ -422,7 +460,7 @@
LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset;
}
if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
- const int* keys = reinterpret_cast<const int*>(&(tab_rec->table[2]));
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&(tab_rec->table[2]));
for (int elems = 0; elems < tab_rec->table[1]; elems++) {
int disp = tab_rec->targets[elems]->offset - bx_offset;
if (cu_->verbose) {
@@ -463,7 +501,7 @@
}
}
-static int AssignLiteralOffsetCommon(LIR* lir, int offset) {
+static int AssignLiteralOffsetCommon(LIR* lir, CodeOffset offset) {
for (; lir != NULL; lir = lir->next) {
lir->offset = offset;
offset += 4;
@@ -471,6 +509,17 @@
return offset;
}
+static int AssignLiteralPointerOffsetCommon(LIR* lir, CodeOffset offset) {
+ unsigned int element_size = sizeof(void*);
+ // Align to natural pointer size.
+ offset = (offset + (element_size - 1)) & ~(element_size - 1);
+ for (; lir != NULL; lir = lir->next) {
+ lir->offset = offset;
+ offset += element_size;
+ }
+ return offset;
+}
+
// Make sure we have a code address for every declared catch entry
bool Mir2Lir::VerifyCatchEntries() {
bool success = true;
@@ -580,8 +629,8 @@
table_index = (table_index + 1) % entries_;
}
in_use_[table_index] = true;
- SetNativeOffset(table_index, native_offset);
- DCHECK_EQ(native_offset, GetNativeOffset(table_index));
+ SetCodeOffset(table_index, native_offset);
+ DCHECK_EQ(native_offset, GetCodeOffset(table_index));
SetReferences(table_index, references);
}
@@ -590,7 +639,7 @@
return NativePcOffsetToReferenceMap::Hash(native_offset) % entries_;
}
- uint32_t GetNativeOffset(size_t table_index) {
+ uint32_t GetCodeOffset(size_t table_index) {
uint32_t native_offset = 0;
size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
for (size_t i = 0; i < native_offset_width_; i++) {
@@ -599,7 +648,7 @@
return native_offset;
}
- void SetNativeOffset(size_t table_index, uint32_t native_offset) {
+ void SetCodeOffset(size_t table_index, uint32_t native_offset) {
size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
for (size_t i = 0; i < native_offset_width_; i++) {
(*table_)[table_offset + i] = (native_offset >> (i * 8)) & 0xFF;
@@ -654,17 +703,17 @@
}
/* Determine the offset of each literal field */
-int Mir2Lir::AssignLiteralOffset(int offset) {
+int Mir2Lir::AssignLiteralOffset(CodeOffset offset) {
offset = AssignLiteralOffsetCommon(literal_list_, offset);
- offset = AssignLiteralOffsetCommon(code_literal_list_, offset);
- offset = AssignLiteralOffsetCommon(method_literal_list_, offset);
+ offset = AssignLiteralPointerOffsetCommon(code_literal_list_, offset);
+ offset = AssignLiteralPointerOffsetCommon(method_literal_list_, offset);
return offset;
}
-int Mir2Lir::AssignSwitchTablesOffset(int offset) {
+int Mir2Lir::AssignSwitchTablesOffset(CodeOffset offset) {
GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
while (true) {
- Mir2Lir::SwitchTable *tab_rec = iterator.Next();
+ Mir2Lir::SwitchTable* tab_rec = iterator.Next();
if (tab_rec == NULL) break;
tab_rec->offset = offset;
if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
@@ -678,7 +727,7 @@
return offset;
}
-int Mir2Lir::AssignFillArrayDataOffset(int offset) {
+int Mir2Lir::AssignFillArrayDataOffset(CodeOffset offset) {
GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
while (true) {
Mir2Lir::FillArrayData *tab_rec = iterator.Next();
@@ -691,122 +740,35 @@
return offset;
}
-// LIR offset assignment.
-int Mir2Lir::AssignInsnOffsets() {
- LIR* lir;
- int offset = 0;
-
- for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
- lir->offset = offset;
- if (lir->opcode >= 0) {
- if (!lir->flags.is_nop) {
- offset += lir->flags.size;
- }
- } else if (lir->opcode == kPseudoPseudoAlign4) {
- if (offset & 0x2) {
- offset += 2;
- lir->operands[0] = 1;
- } else {
- lir->operands[0] = 0;
- }
- }
- /* Pseudo opcodes don't consume space */
- }
-
- return offset;
-}
-
-/*
- * Walk the compilation unit and assign offsets to instructions
- * and literals and compute the total size of the compiled unit.
- */
-void Mir2Lir::AssignOffsets() {
- int offset = AssignInsnOffsets();
-
- /* Const values have to be word aligned */
- offset = (offset + 3) & ~3;
-
- /* Set up offsets for literals */
- data_offset_ = offset;
-
- offset = AssignLiteralOffset(offset);
-
- offset = AssignSwitchTablesOffset(offset);
-
- offset = AssignFillArrayDataOffset(offset);
-
- total_size_ = offset;
-}
-
-/*
- * Go over each instruction in the list and calculate the offset from the top
- * before sending them off to the assembler. If out-of-range branch distance is
- * seen rearrange the instructions a bit to correct it.
- */
-void Mir2Lir::AssembleLIR() {
- AssignOffsets();
- int assembler_retries = 0;
- /*
- * Assemble here. Note that we generate code with optimistic assumptions
- * and if found now to work, we'll have to redo the sequence and retry.
- */
-
- while (true) {
- AssemblerStatus res = AssembleInstructions(0);
- if (res == kSuccess) {
- break;
- } else {
- assembler_retries++;
- if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
- CodegenDump();
- LOG(FATAL) << "Assembler error - too many retries";
- }
- // Redo offsets and try again
- AssignOffsets();
- code_buffer_.clear();
- }
- }
-
- // Install literals
- InstallLiteralPools();
-
- // Install switch tables
- InstallSwitchTables();
-
- // Install fill array data
- InstallFillArrayData();
-
- // Create the mapping table and native offset to reference map.
- CreateMappingTables();
-
- CreateNativeGcMap();
-}
-
/*
* Insert a kPseudoCaseLabel at the beginning of the Dalvik
- * offset vaddr. This label will be used to fix up the case
- * branch table during the assembly phase. Be sure to set
- * all resource flags on this to prevent code motion across
- * target boundaries. KeyVal is just there for debugging.
+ * offset vaddr if pretty-printing, otherise use the standard block
+ * label. The selected label will be used to fix up the case
+ * branch table during the assembly phase. All resource flags
+ * are set to prevent code motion. KeyVal is just there for debugging.
*/
-LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) {
- SafeMap<unsigned int, LIR*>::iterator it;
- it = boundary_map_.find(vaddr);
- if (it == boundary_map_.end()) {
- LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
+LIR* Mir2Lir::InsertCaseLabel(DexOffset vaddr, int keyVal) {
+ LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id];
+ LIR* res = boundary_lir;
+ if (cu_->verbose) {
+ // Only pay the expense if we're pretty-printing.
+ LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
+ new_label->dalvik_offset = vaddr;
+ new_label->opcode = kPseudoCaseLabel;
+ new_label->operands[0] = keyVal;
+ new_label->flags.fixup = kFixupLabel;
+ DCHECK(!new_label->flags.use_def_invalid);
+ new_label->u.m.def_mask = ENCODE_ALL;
+ InsertLIRAfter(boundary_lir, new_label);
+ res = new_label;
}
- LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
- new_label->dalvik_offset = vaddr;
- new_label->opcode = kPseudoCaseLabel;
- new_label->operands[0] = keyVal;
- InsertLIRAfter(it->second, new_label);
- return new_label;
+ return res;
}
-void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec) {
+void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
const uint16_t* table = tab_rec->table;
- int base_vaddr = tab_rec->vaddr;
- const int *targets = reinterpret_cast<const int*>(&table[4]);
+ DexOffset base_vaddr = tab_rec->vaddr;
+ const int32_t *targets = reinterpret_cast<const int32_t*>(&table[4]);
int entries = table[1];
int low_key = s4FromSwitchData(&table[2]);
for (int i = 0; i < entries; i++) {
@@ -814,12 +776,12 @@
}
}
-void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec) {
+void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
const uint16_t* table = tab_rec->table;
- int base_vaddr = tab_rec->vaddr;
+ DexOffset base_vaddr = tab_rec->vaddr;
int entries = table[1];
- const int* keys = reinterpret_cast<const int*>(&table[2]);
- const int* targets = &keys[entries];
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
+ const int32_t* targets = &keys[entries];
for (int i = 0; i < entries; i++) {
tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]);
}
@@ -852,8 +814,8 @@
*/
uint16_t ident = table[0];
int entries = table[1];
- const int* keys = reinterpret_cast<const int*>(&table[2]);
- const int* targets = &keys[entries];
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
+ const int32_t* targets = &keys[entries];
LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident
<< ", entries: " << std::dec << entries;
for (int i = 0; i < entries; i++) {
@@ -872,7 +834,7 @@
* Total size is (4+size*2) 16-bit code units.
*/
uint16_t ident = table[0];
- const int* targets = reinterpret_cast<const int*>(&table[4]);
+ const int32_t* targets = reinterpret_cast<const int32_t*>(&table[4]);
int entries = table[1];
int low_key = s4FromSwitchData(&table[2]);
LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident
@@ -883,18 +845,10 @@
}
}
-/*
- * Set up special LIR to mark a Dalvik byte-code instruction start and
- * record it in the boundary_map. NOTE: in cases such as kMirOpCheck in
- * which we split a single Dalvik instruction, only the first MIR op
- * associated with a Dalvik PC should be entered into the map.
- */
-LIR* Mir2Lir::MarkBoundary(int offset, const char* inst_str) {
- LIR* res = NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast<uintptr_t>(inst_str));
- if (boundary_map_.find(offset) == boundary_map_.end()) {
- boundary_map_.Put(offset, res);
- }
- return res;
+/* Set up special LIR to mark a Dalvik byte-code instruction start for pretty printing */
+void Mir2Lir::MarkBoundary(DexOffset offset, const char* inst_str) {
+ // NOTE: only used for debug listings.
+ NewLIR1(kPseudoDalvikByteCodeBoundary, WrapPointer(ArenaStrdup(inst_str)));
}
bool Mir2Lir::EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) {
@@ -942,6 +896,7 @@
literal_list_(NULL),
method_literal_list_(NULL),
code_literal_list_(NULL),
+ first_fixup_(NULL),
cu_(cu),
mir_graph_(mir_graph),
switch_tables_(arena, 4, kGrowableArraySwitchTables),
@@ -949,10 +904,14 @@
throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads),
suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads),
intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc),
+ tempreg_info_(arena, 20, kGrowableArrayMisc),
+ reginfo_map_(arena, 64, kGrowableArrayMisc),
+ pointer_storage_(arena, 128, kGrowableArrayMisc),
data_offset_(0),
total_size_(0),
block_label_list_(NULL),
current_dalvik_offset_(0),
+ estimated_native_code_size_(0),
reg_pool_(NULL),
live_sreg_(0),
num_core_spills_(0),
@@ -965,9 +924,13 @@
promotion_map_ = static_cast<PromotionMap*>
(arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) *
sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc));
+ // Reserve pointer id 0 for NULL.
+ size_t null_idx = WrapPointer(NULL);
+ DCHECK_EQ(null_idx, 0U);
}
void Mir2Lir::Materialize() {
+ cu_->NewTimingSplit("RegisterAllocation");
CompilerInitializeRegAlloc(); // Needs to happen after SSA naming
/* Allocate Registers using simple local allocation scheme */
@@ -979,6 +942,7 @@
* special codegen doesn't succeed, first_lir_insn_ will
* set to NULL;
*/
+ cu_->NewTimingSplit("SpecialMIR2LIR");
SpecialMIR2LIR(mir_graph_->GetSpecialCase());
}
@@ -1091,5 +1055,4 @@
new_lir->next->prev = new_lir;
}
-
} // namespace art
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index f018c61..df6493d 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -30,16 +30,17 @@
*/
/*
- * Generate an kPseudoBarrier marker to indicate the boundary of special
+ * Generate a kPseudoBarrier marker to indicate the boundary of special
* blocks.
*/
void Mir2Lir::GenBarrier() {
LIR* barrier = NewLIR0(kPseudoBarrier);
/* Mark all resources as being clobbered */
- barrier->def_mask = -1;
+ DCHECK(!barrier->flags.use_def_invalid);
+ barrier->u.m.def_mask = ENCODE_ALL;
}
-// FIXME: need to do some work to split out targets with
+// TODO: need to do some work to split out targets with
// condition codes and those without
LIR* Mir2Lir::GenCheck(ConditionCode c_code, ThrowKind kind) {
DCHECK_NE(cu_->instruction_set, kMips);
@@ -65,8 +66,7 @@
/* Perform null-check on a register. */
LIR* Mir2Lir::GenNullCheck(int s_reg, int m_reg, int opt_flags) {
- if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
- opt_flags & MIR_IGNORE_NULL_CHECK) {
+ if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) {
return NULL;
}
return GenImmedCheck(kCondEq, m_reg, 0, kThrowNullPointer);
@@ -127,13 +127,11 @@
InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src2))) {
// OK - convert this to a compare immediate and branch
OpCmpImmBranch(cond, rl_src1.low_reg, mir_graph_->ConstantValue(rl_src2), taken);
- OpUnconditionalBranch(fall_through);
return;
}
}
rl_src2 = LoadValue(rl_src2, kCoreReg);
OpCmpBranch(cond, rl_src1.low_reg, rl_src2.low_reg, taken);
- OpUnconditionalBranch(fall_through);
}
void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken,
@@ -164,7 +162,6 @@
LOG(FATAL) << "Unexpected opcode " << opcode;
}
OpCmpImmBranch(cond, rl_src.low_reg, 0, taken);
- OpUnconditionalBranch(fall_through);
}
void Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
@@ -337,8 +334,8 @@
bool is_volatile;
bool is_referrers_class;
bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo(
- field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index,
- is_referrers_class, is_volatile, true);
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), true,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
DCHECK_GE(field_offset, 0);
int rBase;
@@ -423,8 +420,8 @@
bool is_volatile;
bool is_referrers_class;
bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo(
- field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index,
- is_referrers_class, is_volatile, false);
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), false,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
DCHECK_GE(field_offset, 0);
int rBase;
@@ -506,7 +503,7 @@
ResetRegPool();
ResetDefTracking();
LIR* lab = suspend_launchpads_.Get(i);
- LIR* resume_lab = reinterpret_cast<LIR*>(lab->operands[0]);
+ LIR* resume_lab = reinterpret_cast<LIR*>(UnwrapPointer(lab->operands[0]));
current_dalvik_offset_ = lab->operands[1];
AppendLIR(lab);
int r_tgt = CallHelperSetup(helper_offset);
@@ -521,12 +518,12 @@
ResetRegPool();
ResetDefTracking();
LIR* lab = intrinsic_launchpads_.Get(i);
- CallInfo* info = reinterpret_cast<CallInfo*>(lab->operands[0]);
+ CallInfo* info = reinterpret_cast<CallInfo*>(UnwrapPointer(lab->operands[0]));
current_dalvik_offset_ = info->offset;
AppendLIR(lab);
// NOTE: GenInvoke handles MarkSafepointPC
GenInvoke(info);
- LIR* resume_lab = reinterpret_cast<LIR*>(lab->operands[2]);
+ LIR* resume_lab = reinterpret_cast<LIR*>(UnwrapPointer(lab->operands[2]));
if (resume_lab != NULL) {
OpUnconditionalBranch(resume_lab);
}
@@ -626,7 +623,7 @@
int field_offset;
bool is_volatile;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
RegLocation rl_result;
@@ -687,8 +684,7 @@
int field_offset;
bool is_volatile;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile,
- true);
+ bool fast_path = FastInstance(field_idx, true, &field_offset, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
RegisterClass reg_class = oat_reg_class_by_size(size);
DCHECK_GE(field_offset, 0);
@@ -730,6 +726,18 @@
}
}
+void Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src) {
+ bool needs_range_check = !(opt_flags & MIR_IGNORE_RANGE_CHECK);
+ bool needs_null_check = !((cu_->disable_opt & (1 << kNullCheckElimination)) &&
+ (opt_flags & MIR_IGNORE_NULL_CHECK));
+ ThreadOffset helper = needs_range_check
+ ? (needs_null_check ? QUICK_ENTRYPOINT_OFFSET(pAputObjectWithNullAndBoundCheck)
+ : QUICK_ENTRYPOINT_OFFSET(pAputObjectWithBoundCheck))
+ : QUICK_ENTRYPOINT_OFFSET(pAputObject);
+ CallRuntimeHelperRegLocationRegLocationRegLocation(helper, rl_array, rl_index, rl_src, true);
+}
+
void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) {
RegLocation rl_method = LoadCurrMethod();
int res_reg = AllocTemp();
@@ -1113,8 +1121,8 @@
if (!type_known_abstract) {
branch2 = OpCmpBranch(kCondEq, TargetReg(kArg1), class_reg, NULL);
}
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCheckCast), TargetReg(kArg1),
- TargetReg(kArg2), true);
+ CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCheckCast), TargetReg(kArg2),
+ TargetReg(kArg1), true);
/* branch target here */
LIR* target = NewLIR0(kPseudoTargetLabel);
branch1->target = target;
@@ -1299,6 +1307,7 @@
}
StoreValue(rl_dest, rl_result);
} else {
+ bool done = false; // Set to true if we happen to find a way to use a real instruction.
if (cu_->instruction_set == kMips) {
rl_src1 = LoadValue(rl_src1, kCoreReg);
rl_src2 = LoadValue(rl_src2, kCoreReg);
@@ -1306,7 +1315,23 @@
GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero);
}
rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv);
- } else {
+ done = true;
+ } else if (cu_->instruction_set == kThumb2) {
+ if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ // Use ARM SDIV instruction for division. For remainder we also need to
+ // calculate using a MUL and subtract.
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ if (check_zero) {
+ GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero);
+ }
+ rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv);
+ done = true;
+ }
+ }
+
+ // If we haven't already generated the code use the callout function.
+ if (!done) {
ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pIdivmod);
FlushAllRegs(); /* Send everything to home location */
LoadValueDirectFixed(rl_src2, TargetReg(kArg1));
@@ -1315,7 +1340,7 @@
if (check_zero) {
GenImmedCheck(kCondEq, TargetReg(kArg1), 0, kThrowDivZero);
}
- // NOTE: callout here is not a safepoint
+ // NOTE: callout here is not a safepoint.
CallHelper(r_tgt, func_offset, false /* not a safepoint */);
if (op == kOpDiv)
rl_result = GetReturn(false);
@@ -1343,7 +1368,7 @@
}
// Returns the index of the lowest set bit in 'x'.
-static int LowestSetBit(unsigned int x) {
+static int32_t LowestSetBit(uint32_t x) {
int bit_posn = 0;
while ((x & 0xf) == 0) {
bit_posn += 4;
@@ -1553,11 +1578,24 @@
if (HandleEasyDivRem(opcode, is_div, rl_src, rl_dest, lit)) {
return;
}
+
+ bool done = false;
if (cu_->instruction_set == kMips) {
rl_src = LoadValue(rl_src, kCoreReg);
rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div);
- } else {
- FlushAllRegs(); /* Everything to home location */
+ done = true;
+ } else if (cu_->instruction_set == kThumb2) {
+ if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ // Use ARM SDIV instruction for division. For remainder we also need to
+ // calculate using a MUL and subtract.
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div);
+ done = true;
+ }
+ }
+
+ if (!done) {
+ FlushAllRegs(); /* Everything to home location. */
LoadValueDirectFixed(rl_src, TargetReg(kArg0));
Clobber(TargetReg(kArg0));
ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pIdivmod);
@@ -1575,7 +1613,7 @@
}
rl_src = LoadValue(rl_src, kCoreReg);
rl_result = EvalLoc(rl_dest, kCoreReg, true);
- // Avoid shifts by literal 0 - no support in Thumb. Change to copy
+ // Avoid shifts by literal 0 - no support in Thumb. Change to copy.
if (shift_op && (lit == 0)) {
OpRegCopy(rl_result.low_reg, rl_src.low_reg);
} else {
@@ -1651,7 +1689,7 @@
case Instruction::REM_LONG_2ADDR:
call_out = true;
check_zero = true;
- func_offset = QUICK_ENTRYPOINT_OFFSET(pLdivmod);
+ func_offset = QUICK_ENTRYPOINT_OFFSET(pLmod);
/* NOTE - for Arm, result is in kArg2/kArg3 instead of kRet0/kRet1 */
ret_reg = (cu_->instruction_set == kThumb2) ? TargetReg(kArg2) : TargetReg(kRet0);
break;
@@ -1744,8 +1782,8 @@
FlushAllRegs();
LIR* branch = OpTestSuspend(NULL);
LIR* ret_lab = NewLIR0(kPseudoTargetLabel);
- LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget,
- reinterpret_cast<uintptr_t>(ret_lab), current_dalvik_offset_);
+ LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, WrapPointer(ret_lab),
+ current_dalvik_offset_);
branch->target = target;
suspend_launchpads_.Insert(target);
}
@@ -1758,11 +1796,23 @@
}
OpTestSuspend(target);
LIR* launch_pad =
- RawLIR(current_dalvik_offset_, kPseudoSuspendTarget,
- reinterpret_cast<uintptr_t>(target), current_dalvik_offset_);
+ RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, WrapPointer(target),
+ current_dalvik_offset_);
FlushAllRegs();
OpUnconditionalBranch(launch_pad);
suspend_launchpads_.Insert(launch_pad);
}
+/* Call out to helper assembly routine that will null check obj and then lock it. */
+void Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
+ FlushAllRegs();
+ CallRuntimeHelperRegLocation(QUICK_ENTRYPOINT_OFFSET(pLockObject), rl_src, true);
+}
+
+/* Call out to helper assembly routine that will null check obj and then unlock it. */
+void Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
+ FlushAllRegs();
+ CallRuntimeHelperRegLocation(QUICK_ENTRYPOINT_OFFSET(pUnlockObject), rl_src, true);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 2a0a23c..7225262 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -214,6 +214,7 @@
int arg0, RegLocation arg1,
RegLocation arg2, bool safepoint_pc) {
int r_tgt = CallHelperSetup(helper_offset);
+ DCHECK_EQ(arg1.wide, 0U);
LoadValueDirectFixed(arg1, TargetReg(kArg1));
if (arg2.wide == 0) {
LoadValueDirectFixed(arg2, TargetReg(kArg2));
@@ -225,6 +226,21 @@
CallHelper(r_tgt, helper_offset, safepoint_pc);
}
+void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset,
+ RegLocation arg0, RegLocation arg1,
+ RegLocation arg2,
+ bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ DCHECK_EQ(arg0.wide, 0U);
+ LoadValueDirectFixed(arg0, TargetReg(kArg0));
+ DCHECK_EQ(arg1.wide, 0U);
+ LoadValueDirectFixed(arg1, TargetReg(kArg1));
+ DCHECK_EQ(arg1.wide, 0U);
+ LoadValueDirectFixed(arg2, TargetReg(kArg2));
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
/*
* If there are any ins passed in registers that have not been promoted
* to a callee-save register, flush them to the frame. Perform intial
@@ -334,16 +350,13 @@
uintptr_t direct_code, uintptr_t direct_method,
InvokeType type) {
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- if (cu->instruction_set != kThumb2) {
- // Disable sharpening
- direct_code = 0;
- direct_method = 0;
- }
if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_code != static_cast<unsigned int>(-1)) {
- cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
+ if (cu->instruction_set != kX86) {
+ cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
+ }
} else {
CHECK_EQ(cu->dex_file, target_method.dex_file);
LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_,
@@ -389,6 +402,7 @@
cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
} else {
CHECK_EQ(cu->dex_file, target_method.dex_file);
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_,
target_method.dex_method_index, 0);
if (data_target == NULL) {
@@ -477,73 +491,56 @@
}
/*
- * All invoke-interface calls bounce off of art_quick_invoke_interface_trampoline,
- * which will locate the target and continue on via a tail call.
+ * Emit the next instruction in an invoke interface sequence. This will do a lookup in the
+ * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if
+ * more than one interface method map to the same index. Note also that we'll load the first
+ * argument ("this") into kArg1 here rather than the standard LoadArgRegs.
*/
static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
const MethodReference& target_method,
- uint32_t unused, uintptr_t unused2,
- uintptr_t direct_method, InvokeType unused4) {
+ uint32_t method_idx, uintptr_t unused,
+ uintptr_t direct_method, InvokeType unused2) {
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- if (cu->instruction_set != kThumb2) {
- // Disable sharpening
- direct_method = 0;
- }
- ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline);
- if (direct_method != 0) {
- switch (state) {
- case 0: // Load the trampoline target [sets kInvokeTgt].
- if (cu->instruction_set != kX86) {
- cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(),
- cg->TargetReg(kInvokeTgt));
- }
- // Get the interface Method* [sets kArg0]
- if (direct_method != static_cast<unsigned int>(-1)) {
- cg->LoadConstant(cg->TargetReg(kArg0), direct_method);
- } else {
- CHECK_EQ(cu->dex_file, target_method.dex_file);
- LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_,
- target_method.dex_method_index, 0);
- if (data_target == NULL) {
- data_target = cg->AddWordData(&cg->method_literal_list_,
- target_method.dex_method_index);
- data_target->operands[1] = kInterface;
- }
- LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target);
- cg->AppendLIR(load_pc_rel);
- DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
- }
- break;
- default:
- return -1;
- }
- } else {
- switch (state) {
- case 0:
- // Get the current Method* [sets kArg0] - TUNING: remove copy of method if it is promoted.
- cg->LoadCurrMethodDirect(cg->TargetReg(kArg0));
- // Load the trampoline target [sets kInvokeTgt].
- if (cu->instruction_set != kX86) {
- cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(),
- cg->TargetReg(kInvokeTgt));
- }
- break;
- case 1: // Get method->dex_cache_resolved_methods_ [set/use kArg0]
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- cg->TargetReg(kArg0));
- break;
- case 2: // Grab target method* [set/use kArg0]
+ switch (state) {
+ case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)]
CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- (target_method.dex_method_index * 4),
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+ cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index);
+ if (cu->instruction_set == kX86) {
+ cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg));
+ }
+ break;
+ case 1: { // Get "this" [set kArg1]
+ RegLocation rl_arg = info->args[0];
+ cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1));
+ break;
+ }
+ case 2: // Is "this" null? [use kArg1]
+ cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags);
+ // Get this->klass_ [use kArg1, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 4: // Get target method [use kInvokeTgt, set kArg0]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) +
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
cg->TargetReg(kArg0));
break;
+ case 5: // Get the compiled code address [use kArg0, set kInvokeTgt]
+ if (cu->instruction_set != kX86) {
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ }
+ // Intentional fallthrough for X86
default:
return -1;
- }
}
return state + 1;
}
@@ -810,7 +807,7 @@
OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset);
LIR* ld = OpVldm(TargetReg(kArg3), regs_left);
// TUNING: loosen barrier
- ld->def_mask = ENCODE_ALL;
+ ld->u.m.def_mask = ENCODE_ALL;
SetMemRefType(ld, true /* is_load */, kDalvikReg);
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
direct_code, direct_method, type);
@@ -819,7 +816,7 @@
direct_code, direct_method, type);
LIR* st = OpVstm(TargetReg(kArg3), regs_left);
SetMemRefType(st, false /* is_load */, kDalvikReg);
- st->def_mask = ENCODE_ALL;
+ st->u.m.def_mask = ENCODE_ALL;
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
direct_code, direct_method, type);
}
@@ -892,7 +889,7 @@
LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr);
if (range_check) {
// Set up a launch pad to allow retry in case of bounds violation */
- launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpRegReg(kOpCmp, rl_idx.low_reg, reg_max);
FreeTemp(reg_max);
@@ -903,7 +900,7 @@
reg_max = AllocTemp();
LoadWordDisp(rl_obj.low_reg, count_offset, reg_max);
// Set up a launch pad to allow retry in case of bounds violation */
- launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpRegReg(kOpCmp, rl_idx.low_reg, reg_max);
FreeTemp(reg_max);
@@ -961,6 +958,31 @@
return true;
}
+bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_src_i = info->args[0];
+ RegLocation rl_dest = InlineTarget(info); // result reg
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == kLong) {
+ RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg);
+ int reg_tmp = AllocTemp();
+ OpRegCopy(reg_tmp, rl_result.low_reg);
+ OpRegReg(kOpRev, rl_result.low_reg, rl_i.high_reg);
+ OpRegReg(kOpRev, rl_result.high_reg, reg_tmp);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ DCHECK(size == kWord || size == kSignedHalf);
+ OpKind op = (size == kWord) ? kOpRev : kOpRevsh;
+ RegLocation rl_i = LoadValue(rl_src_i, kCoreReg);
+ OpRegReg(op, rl_result.low_reg, rl_i.low_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
if (cu_->instruction_set == kMips) {
// TODO - add Mips implementation
@@ -1069,7 +1091,7 @@
}
int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0;
GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags);
- LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad);
// NOTE: not a safepoint
@@ -1079,7 +1101,7 @@
OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf));
}
LIR* resume_tgt = NewLIR0(kPseudoTargetLabel);
- launch_pad->operands[2] = reinterpret_cast<uintptr_t>(resume_tgt);
+ launch_pad->operands[2] = WrapPointer(resume_tgt);
// Record that we've already inlined & null checked
info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
RegLocation rl_return = GetReturn(false);
@@ -1107,7 +1129,7 @@
LoadHelper(QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)) : 0;
GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags);
// TUNING: check if rl_cmp.s_reg_low is already null checked
- LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad);
// NOTE: not a safepoint
@@ -1219,8 +1241,10 @@
* method. By doing this during basic block construction, we can also
* take advantage of/generate new useful dataflow info.
*/
+ const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index);
+ const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_);
StringPiece tgt_methods_declaring_class(
- cu_->dex_file->GetMethodDeclaringClassDescriptor(cu_->dex_file->GetMethodId(info->index)));
+ cu_->dex_file->StringDataByIdx(declaring_type.descriptor_idx_));
if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") {
@@ -1231,12 +1255,22 @@
}
} else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
- if (tgt_method == "int java.lang.Float.float_to_raw_int_bits(float)") {
+ if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") {
return GenInlinedFloatCvt(info);
}
if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") {
return GenInlinedFloatCvt(info);
}
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Integer;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "int java.lang.Integer.reverseBytes(int)") {
+ return GenInlinedReverseBytes(info, kWord);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Long;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "long java.lang.Long.reverseBytes(long)") {
+ return GenInlinedReverseBytes(info, kLong);
+ }
} else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") ||
tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
@@ -1260,6 +1294,11 @@
tgt_method == "double java.lang.StrictMath.sqrt(double)") {
return GenInlinedSqrt(info);
}
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Short;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "short java.lang.Short.reverseBytes(short)") {
+ return GenInlinedReverseBytes(info, kSignedHalf);
+ }
} else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
if (tgt_method == "char java.lang.String.charAt(int)") {
@@ -1285,6 +1324,32 @@
if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") {
return GenInlinedCurrentThread(info);
}
+ } else if (tgt_methods_declaring_class.starts_with("Llibcore/io/Memory;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "byte libcore.io.Memory.peekByte(long)") {
+ return GenInlinedPeek(info, kSignedByte);
+ }
+ if (tgt_method == "int libcore.io.Memory.peekIntNative(long)") {
+ return GenInlinedPeek(info, kWord);
+ }
+ if (tgt_method == "long libcore.io.Memory.peekLongNative(long)") {
+ return GenInlinedPeek(info, kLong);
+ }
+ if (tgt_method == "short libcore.io.Memory.peekShortNative(long)") {
+ return GenInlinedPeek(info, kSignedHalf);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeByte(long, byte)") {
+ return GenInlinedPoke(info, kSignedByte);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeIntNative(long, int)") {
+ return GenInlinedPoke(info, kWord);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeLongNative(long, long)") {
+ return GenInlinedPoke(info, kLong);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeShortNative(long, short)") {
+ return GenInlinedPoke(info, kSignedHalf);
+ }
} else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") {
@@ -1373,16 +1438,13 @@
bool fast_path =
cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(),
current_dalvik_offset_,
- info->type, target_method,
- vtable_idx,
- direct_code, direct_method,
- true) && !SLOW_INVOKE_PATH;
+ true, true,
+ &info->type, &target_method,
+ &vtable_idx,
+ &direct_code, &direct_method) && !SLOW_INVOKE_PATH;
if (info->type == kInterface) {
- if (fast_path) {
- p_null_ck = &null_ck;
- }
next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck;
- skip_this = false;
+ skip_this = fast_path;
} else if (info->type == kDirect) {
if (fast_path) {
p_null_ck = &null_ck;
@@ -1422,15 +1484,14 @@
if (cu_->instruction_set != kX86) {
call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt));
} else {
- if (fast_path && info->type != kInterface) {
+ if (fast_path) {
call_inst = OpMem(kOpBlx, TargetReg(kArg0),
mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value());
} else {
ThreadOffset trampoline(-1);
switch (info->type) {
case kInterface:
- trampoline = fast_path ? QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline)
- : QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
+ trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
break;
case kDirect:
trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck);
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index 630e990..0f29578 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -21,8 +21,8 @@
#define DEBUG_OPT(X)
/* Check RAW, WAR, and RAW dependency on the register operands */
-#define CHECK_REG_DEP(use, def, check) ((def & check->use_mask) || \
- ((use | def) & check->def_mask))
+#define CHECK_REG_DEP(use, def, check) ((def & check->u.m.use_mask) || \
+ ((use | def) & check->u.m.def_mask))
/* Scheduler heuristics */
#define MAX_HOIST_DISTANCE 20
@@ -30,10 +30,10 @@
#define LD_LATENCY 2
static bool IsDalvikRegisterClobbered(LIR* lir1, LIR* lir2) {
- int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->alias_info);
- int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->alias_info);
- int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->alias_info);
- int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->alias_info);
+ int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->flags.alias_info);
+ int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->flags.alias_info);
+ int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->flags.alias_info);
+ int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->flags.alias_info);
return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
}
@@ -78,7 +78,7 @@
}
for (this_lir = PREV_LIR(tail_lir); this_lir != head_lir; this_lir = PREV_LIR(this_lir)) {
- if (is_pseudo_opcode(this_lir->opcode)) {
+ if (IsPseudoLirOp(this_lir->opcode)) {
continue;
}
@@ -99,15 +99,14 @@
int native_reg_id;
if (cu_->instruction_set == kX86) {
// If x86, location differs depending on whether memory/reg operation.
- native_reg_id = (GetTargetInstFlags(this_lir->opcode) & IS_STORE) ? this_lir->operands[2]
- : this_lir->operands[0];
+ native_reg_id = (target_flags & IS_STORE) ? this_lir->operands[2] : this_lir->operands[0];
} else {
native_reg_id = this_lir->operands[0];
}
- bool is_this_lir_load = GetTargetInstFlags(this_lir->opcode) & IS_LOAD;
+ bool is_this_lir_load = target_flags & IS_LOAD;
LIR* check_lir;
/* Use the mem mask to determine the rough memory location */
- uint64_t this_mem_mask = (this_lir->use_mask | this_lir->def_mask) & ENCODE_MEM;
+ uint64_t this_mem_mask = (this_lir->u.m.use_mask | this_lir->u.m.def_mask) & ENCODE_MEM;
/*
* Currently only eliminate redundant ld/st for constant and Dalvik
@@ -117,10 +116,10 @@
continue;
}
- uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM;
+ uint64_t stop_def_reg_mask = this_lir->u.m.def_mask & ~ENCODE_MEM;
uint64_t stop_use_reg_mask;
if (cu_->instruction_set == kX86) {
- stop_use_reg_mask = (IS_BRANCH | this_lir->use_mask) & ~ENCODE_MEM;
+ stop_use_reg_mask = (IS_BRANCH | this_lir->u.m.use_mask) & ~ENCODE_MEM;
} else {
/*
* Add pc to the resource mask to prevent this instruction
@@ -128,7 +127,7 @@
* region bits since stop_mask is used to check data/control
* dependencies.
*/
- stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->use_mask) & ~ENCODE_MEM;
+ stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->u.m.use_mask) & ~ENCODE_MEM;
}
for (check_lir = NEXT_LIR(this_lir); check_lir != tail_lir; check_lir = NEXT_LIR(check_lir)) {
@@ -136,11 +135,11 @@
* Skip already dead instructions (whose dataflow information is
* outdated and misleading).
*/
- if (check_lir->flags.is_nop || is_pseudo_opcode(check_lir->opcode)) {
+ if (check_lir->flags.is_nop || IsPseudoLirOp(check_lir->opcode)) {
continue;
}
- uint64_t check_mem_mask = (check_lir->use_mask | check_lir->def_mask) & ENCODE_MEM;
+ uint64_t check_mem_mask = (check_lir->u.m.use_mask | check_lir->u.m.def_mask) & ENCODE_MEM;
uint64_t alias_condition = this_mem_mask & check_mem_mask;
bool stop_here = false;
@@ -160,7 +159,7 @@
*/
DCHECK(!(check_flags & IS_STORE));
/* Same value && same register type */
- if (check_lir->alias_info == this_lir->alias_info &&
+ if (check_lir->flags.alias_info == this_lir->flags.alias_info &&
SameRegType(check_lir->operands[0], native_reg_id)) {
/*
* Different destination register - insert
@@ -169,11 +168,11 @@
if (check_lir->operands[0] != native_reg_id) {
ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id);
}
- check_lir->flags.is_nop = true;
+ NopLIR(check_lir);
}
} else if (alias_condition == ENCODE_DALVIK_REG) {
/* Must alias */
- if (check_lir->alias_info == this_lir->alias_info) {
+ if (check_lir->flags.alias_info == this_lir->flags.alias_info) {
/* Only optimize compatible registers */
bool reg_compatible = SameRegType(check_lir->operands[0], native_reg_id);
if ((is_this_lir_load && is_check_lir_load) ||
@@ -188,7 +187,7 @@
native_reg_id) {
ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id);
}
- check_lir->flags.is_nop = true;
+ NopLIR(check_lir);
} else {
/*
* Destinaions are of different types -
@@ -202,7 +201,7 @@
stop_here = true;
} else if (!is_this_lir_load && !is_check_lir_load) {
/* WAW - nuke the earlier store */
- this_lir->flags.is_nop = true;
+ NopLIR(this_lir);
stop_here = true;
}
/* Partial overlap */
@@ -257,7 +256,7 @@
* top-down order.
*/
InsertLIRBefore(check_lir, new_store_lir);
- this_lir->flags.is_nop = true;
+ NopLIR(this_lir);
}
break;
} else if (!check_lir->flags.is_nop) {
@@ -286,7 +285,7 @@
/* Start from the second instruction */
for (this_lir = NEXT_LIR(head_lir); this_lir != tail_lir; this_lir = NEXT_LIR(this_lir)) {
- if (is_pseudo_opcode(this_lir->opcode)) {
+ if (IsPseudoLirOp(this_lir->opcode)) {
continue;
}
@@ -298,7 +297,7 @@
continue;
}
- uint64_t stop_use_all_mask = this_lir->use_mask;
+ uint64_t stop_use_all_mask = this_lir->u.m.use_mask;
if (cu_->instruction_set != kX86) {
/*
@@ -314,7 +313,7 @@
/* Similar as above, but just check for pure register dependency */
uint64_t stop_use_reg_mask = stop_use_all_mask & ~ENCODE_MEM;
- uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM;
+ uint64_t stop_def_reg_mask = this_lir->u.m.def_mask & ~ENCODE_MEM;
int next_slot = 0;
bool stop_here = false;
@@ -329,7 +328,7 @@
continue;
}
- uint64_t check_mem_mask = check_lir->def_mask & ENCODE_MEM;
+ uint64_t check_mem_mask = check_lir->u.m.def_mask & ENCODE_MEM;
uint64_t alias_condition = stop_use_all_mask & check_mem_mask;
stop_here = false;
@@ -338,7 +337,7 @@
/* We can fully disambiguate Dalvik references */
if (alias_condition == ENCODE_DALVIK_REG) {
/* Must alias or partually overlap */
- if ((check_lir->alias_info == this_lir->alias_info) ||
+ if ((check_lir->flags.alias_info == this_lir->flags.alias_info) ||
IsDalvikRegisterClobbered(this_lir, check_lir)) {
stop_here = true;
}
@@ -363,7 +362,7 @@
* Store the dependent or non-pseudo/indepedent instruction to the
* list.
*/
- if (stop_here || !is_pseudo_opcode(check_lir->opcode)) {
+ if (stop_here || !IsPseudoLirOp(check_lir->opcode)) {
prev_inst_list[next_slot++] = check_lir;
if (next_slot == MAX_HOIST_DISTANCE) {
break;
@@ -394,7 +393,7 @@
int slot;
LIR* dep_lir = prev_inst_list[next_slot-1];
/* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
- if (!is_pseudo_opcode(dep_lir->opcode) &&
+ if (!IsPseudoLirOp(dep_lir->opcode) &&
(GetTargetInstFlags(dep_lir->opcode) & IS_LOAD)) {
first_slot -= LDLD_DISTANCE;
}
@@ -407,7 +406,7 @@
LIR* prev_lir = prev_inst_list[slot+1];
/* Check the highest instruction */
- if (prev_lir->def_mask == ENCODE_ALL) {
+ if (prev_lir->u.m.def_mask == ENCODE_ALL) {
/*
* If the first instruction is a load, don't hoist anything
* above it since it is unlikely to be beneficial.
@@ -435,9 +434,9 @@
* Try to find two instructions with load/use dependency until
* the remaining instructions are less than LD_LATENCY.
*/
- bool prev_is_load = is_pseudo_opcode(prev_lir->opcode) ? false :
+ bool prev_is_load = IsPseudoLirOp(prev_lir->opcode) ? false :
(GetTargetInstFlags(prev_lir->opcode) & IS_LOAD);
- if (((cur_lir->use_mask & prev_lir->def_mask) && prev_is_load) || (slot < LD_LATENCY)) {
+ if (((cur_lir->u.m.use_mask & prev_lir->u.m.def_mask) && prev_is_load) || (slot < LD_LATENCY)) {
break;
}
}
@@ -453,7 +452,7 @@
* is never the first LIR on the list
*/
InsertLIRBefore(cur_lir, new_load_lir);
- this_lir->flags.is_nop = true;
+ NopLIR(this_lir);
}
}
}
@@ -468,41 +467,4 @@
}
}
-/*
- * Nop any unconditional branches that go to the next instruction.
- * Note: new redundant branches may be inserted later, and we'll
- * use a check in final instruction assembly to nop those out.
- */
-void Mir2Lir::RemoveRedundantBranches() {
- LIR* this_lir;
-
- for (this_lir = first_lir_insn_; this_lir != last_lir_insn_; this_lir = NEXT_LIR(this_lir)) {
- /* Branch to the next instruction */
- if (IsUnconditionalBranch(this_lir)) {
- LIR* next_lir = this_lir;
-
- while (true) {
- next_lir = NEXT_LIR(next_lir);
-
- /*
- * Is the branch target the next instruction?
- */
- if (next_lir == this_lir->target) {
- this_lir->flags.is_nop = true;
- break;
- }
-
- /*
- * Found real useful stuff between the branch and the target.
- * Need to explicitly check the last_lir_insn_ here because it
- * might be the last real instruction.
- */
- if (!is_pseudo_opcode(next_lir->opcode) ||
- (next_lir == last_lir_insn_))
- break;
- }
- }
- }
-}
-
} // namespace art
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
index cd25232..5f5e5e4 100644
--- a/compiler/dex/quick/mips/assemble_mips.cc
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -489,12 +489,12 @@
LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC);
InsertLIRBefore(lir, curr_pc);
LIR* anchor = RawLIR(dalvik_offset, kPseudoTargetLabel);
- LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0,
- reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target);
+ LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0, WrapPointer(anchor), 0, 0,
+ lir->target);
InsertLIRBefore(lir, delta_hi);
InsertLIRBefore(lir, anchor);
- LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0,
- reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target);
+ LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0, WrapPointer(anchor), 0, 0,
+ lir->target);
InsertLIRBefore(lir, delta_lo);
LIR* addu = RawLIR(dalvik_offset, kMipsAddu, r_AT, r_AT, r_RA);
InsertLIRBefore(lir, addu);
@@ -503,7 +503,7 @@
if (!unconditional) {
InsertLIRBefore(lir, hop_target);
}
- lir->flags.is_nop = true;
+ NopLIR(lir);
}
/*
@@ -512,7 +512,7 @@
* instruction. In those cases we will try to substitute a new code
* sequence or request that the trace be shortened and retried.
*/
-AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
+AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) {
LIR *lir;
AssemblerStatus res = kSuccess; // Assume success
@@ -526,7 +526,7 @@
continue;
}
- if (lir->flags.pcRelFixup) {
+ if (lir->flags.fixup != kFixupNone) {
if (lir->opcode == kMipsDelta) {
/*
* The "Delta" pseudo-ops load the difference between
@@ -538,8 +538,8 @@
* and is found in lir->target. If operands[3] is non-NULL,
* then it is a Switch/Data table.
*/
- int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset1 = (reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2])))->offset;
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
int delta = offset2 - offset1;
if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) {
@@ -561,25 +561,25 @@
RawLIR(lir->dalvik_offset, kMipsAddu,
lir->operands[0], lir->operands[0], r_RA);
InsertLIRBefore(lir, new_addu);
- lir->flags.is_nop = true;
+ NopLIR(lir);
res = kRetryAll;
}
} else if (lir->opcode == kMipsDeltaLo) {
- int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset1 = (reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2])))->offset;
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
int delta = offset2 - offset1;
lir->operands[1] = delta & 0xffff;
} else if (lir->opcode == kMipsDeltaHi) {
- int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset1 = (reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2])))->offset;
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
int delta = offset2 - offset1;
lir->operands[1] = (delta >> 16) & 0xffff;
} else if (lir->opcode == kMipsB || lir->opcode == kMipsBal) {
LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (delta & 0x3) {
LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
@@ -592,8 +592,8 @@
}
} else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) {
LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (delta & 0x3) {
LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
@@ -606,8 +606,8 @@
}
} else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) {
LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (delta & 0x3) {
LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
@@ -619,8 +619,8 @@
lir->operands[2] = delta >> 2;
}
} else if (lir->opcode == kMipsJal) {
- uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
- uintptr_t target = lir->operands[0];
+ CodeOffset cur_pc = (start_addr + lir->offset + 4) & ~3;
+ CodeOffset target = lir->operands[0];
/* ensure PC-region branch can be used */
DCHECK_EQ((cur_pc & 0xF0000000), (target & 0xF0000000));
if (target & 0x3) {
@@ -629,11 +629,11 @@
lir->operands[0] = target >> 2;
} else if (lir->opcode == kMipsLahi) { /* ld address hi (via lui) */
LIR *target_lir = lir->target;
- uintptr_t target = start_addr + target_lir->offset;
+ CodeOffset target = start_addr + target_lir->offset;
lir->operands[1] = target >> 16;
} else if (lir->opcode == kMipsLalo) { /* ld address lo (via ori) */
LIR *target_lir = lir->target;
- uintptr_t target = start_addr + target_lir->offset;
+ CodeOffset target = start_addr + target_lir->offset;
lir->operands[2] = lir->operands[2] + target;
}
}
@@ -646,6 +646,7 @@
if (res != kSuccess) {
continue;
}
+ DCHECK(!IsPseudoLirOp(lir->opcode));
const MipsEncodingMap *encoder = &EncodingMap[lir->opcode];
uint32_t bits = encoder->skeleton;
int i;
@@ -695,6 +696,7 @@
code_buffer_.push_back((bits >> 24) & 0xff);
// TUNING: replace with proper delay slot handling
if (encoder->size == 8) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
const MipsEncodingMap *encoder = &EncodingMap[kMipsNop];
uint32_t bits = encoder->skeleton;
code_buffer_.push_back(bits & 0xff);
@@ -707,7 +709,105 @@
}
int MipsMir2Lir::GetInsnSize(LIR* lir) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
return EncodingMap[lir->opcode].size;
}
+// LIR offset assignment.
+// TODO: consolidate w/ Arm assembly mechanism.
+int MipsMir2Lir::AssignInsnOffsets() {
+ LIR* lir;
+ int offset = 0;
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (LIKELY(lir->opcode >= 0)) {
+ if (!lir->flags.is_nop) {
+ offset += lir->flags.size;
+ }
+ } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) {
+ if (offset & 0x2) {
+ offset += 2;
+ lir->operands[0] = 1;
+ } else {
+ lir->operands[0] = 0;
+ }
+ }
+ /* Pseudo opcodes don't consume space */
+ }
+ return offset;
+}
+
+/*
+ * Walk the compilation unit and assign offsets to instructions
+ * and literals and compute the total size of the compiled unit.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void MipsMir2Lir::AssignOffsets() {
+ int offset = AssignInsnOffsets();
+
+ /* Const values have to be word aligned */
+ offset = (offset + 3) & ~3;
+
+ /* Set up offsets for literals */
+ data_offset_ = offset;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ offset = AssignFillArrayDataOffset(offset);
+
+ total_size_ = offset;
+}
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void MipsMir2Lir::AssembleLIR() {
+ cu_->NewTimingSplit("Assemble");
+ AssignOffsets();
+ int assembler_retries = 0;
+ /*
+ * Assemble here. Note that we generate code with optimistic assumptions
+ * and if found now to work, we'll have to redo the sequence and retry.
+ */
+
+ while (true) {
+ AssemblerStatus res = AssembleInstructions(0);
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ // Redo offsets and try again
+ AssignOffsets();
+ code_buffer_.clear();
+ }
+ }
+
+ // Install literals
+ cu_->NewTimingSplit("LiteralData");
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ cu_->NewTimingSplit("PcMappingTable");
+ CreateMappingTables();
+
+ cu_->NewTimingSplit("GcMap");
+ CreateNativeGcMap();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index d53c012..18c8cf8 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -59,14 +59,14 @@
* done:
*
*/
-void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+void MipsMir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
// Add the table to the list - we'll process it later
- SwitchTable *tab_rec =
+ SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
@@ -101,8 +101,7 @@
// Remember base label so offsets can be computed later
tab_rec->anchor = base_label;
int rBase = AllocTemp();
- NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR4(kMipsDelta, rBase, 0, WrapPointer(base_label), WrapPointer(tab_rec));
OpRegRegReg(kOpAdd, rEnd, rEnd, rBase);
// Grab switch test value
@@ -138,20 +137,20 @@
* jr r_RA
* done:
*/
-void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+void MipsMir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
// Add the table to the list - we'll process it later
- SwitchTable *tab_rec =
+ SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
- ArenaAllocator::kAllocLIR));
+ ArenaAllocator::kAllocLIR));
switch_tables_.Insert(tab_rec);
// Get the switch value
@@ -196,8 +195,7 @@
// Materialize the table base pointer
int rBase = AllocTemp();
- NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR4(kMipsDelta, rBase, 0, WrapPointer(base_label), WrapPointer(tab_rec));
// Load the displacement from the switch table
int r_disp = AllocTemp();
@@ -222,10 +220,10 @@
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
+void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
// Add the table to the list - we'll process it later
- FillArrayData *tab_rec =
+ FillArrayData* tab_rec =
reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
ArenaAllocator::kAllocData));
tab_rec->table = table;
@@ -252,8 +250,7 @@
LIR* base_label = NewLIR0(kPseudoTargetLabel);
// Materialize a pointer to the fill data image
- NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(base_label),
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR4(kMipsDelta, rMIPS_ARG1, 0, WrapPointer(base_label), WrapPointer(tab_rec));
// And go...
ClobberCalleeSave();
@@ -261,36 +258,6 @@
MarkSafepointPC(call_inst);
}
-/*
- * TODO: implement fast path to short-circuit thin-lock case
- */
-void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
- // Go expensive route - artLockObjectFromCode(self, obj);
- int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pLockObject));
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, r_tgt);
- MarkSafepointPC(call_inst);
-}
-
-/*
- * TODO: implement fast path to short-circuit thin-lock case
- */
-void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
- // Go expensive route - UnlockObjectFromCode(obj);
- int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pUnlockObject));
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, r_tgt);
- MarkSafepointPC(call_inst);
-}
-
void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
int ex_offset = Thread::ExceptionOffset().Int32Value();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
@@ -318,6 +285,7 @@
FreeTemp(reg_card_base);
FreeTemp(reg_card_no);
}
+
void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
int spill_count = num_core_spills_ + num_fp_spills_;
/*
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index b9cb720..88b244b 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -52,7 +52,6 @@
int AllocTypedTempPair(bool fp_hint, int reg_class);
int S2d(int low_reg, int high_reg);
int TargetReg(SpecialTargetRegister reg);
- RegisterInfo* GetRegInfo(int reg);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -72,9 +71,12 @@
void CompilerInitializeRegAlloc();
// Required for target - miscellaneous.
- AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void AssembleLIR();
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ AssemblerStatus AssembleInstructions(CodeOffset start_addr);
void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
- void SetupTargetResourceMasks(LIR* lir);
+ void SetupTargetResourceMasks(LIR* lir, uint64_t flags);
const char* GetTargetInstFmt(int opcode);
const char* GetTargetInstName(int opcode);
std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
@@ -86,12 +88,10 @@
// Required for target - Dalvik-level generators.
void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2);
- void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
- RegLocation rl_src, int scale);
void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale);
+ RegLocation rl_index, RegLocation rl_dest, int scale);
void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark);
void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_shift);
void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -107,6 +107,8 @@
bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
bool GenInlinedSqrt(CallInfo* info);
+ bool GenInlinedPeek(CallInfo* info, OpSize size);
+ bool GenInlinedPoke(CallInfo* info, OpSize size);
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -124,8 +126,6 @@
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
void GenMemBarrier(MemBarrierKind barrier_kind);
- void GenMonitorEnter(int opt_flags, RegLocation rl_src);
- void GenMonitorExit(int opt_flags, RegLocation rl_src);
void GenMoveException(RegLocation rl_dest);
void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
int first_bit, int second_bit);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 6ce5750..5229429 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -268,6 +268,37 @@
return false;
}
+bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ if (size != kSignedByte) {
+ // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
+ return false;
+ }
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ DCHECK(size == kSignedByte);
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
+ if (size != kSignedByte) {
+ // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
+ return false;
+ }
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_src_value = info->args[2]; // [size] value
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ DCHECK(size == kSignedByte);
+ RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
+ return true;
+}
+
LIR* MipsMir2Lir::OpPcRelLoad(int reg, LIR* target) {
LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
return NULL;
@@ -484,7 +515,7 @@
*
*/
void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -498,12 +529,14 @@
rl_array = LoadValue(rl_array, kCoreReg);
rl_index = LoadValue(rl_index, kCoreReg);
int reg_ptr = INVALID_REG;
- if (IsTemp(rl_array.low_reg)) {
+ bool allocated_reg_ptr_temp = false;
+ if (IsTemp(rl_array.low_reg) && !card_mark) {
Clobber(rl_array.low_reg);
reg_ptr = rl_array.low_reg;
} else {
reg_ptr = AllocTemp();
OpRegCopy(reg_ptr, rl_array.low_reg);
+ allocated_reg_ptr_temp = true;
}
/* null object? */
@@ -538,8 +571,6 @@
}
StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg);
-
- FreeTemp(reg_ptr);
} else {
rl_src = LoadValue(rl_src, reg_class);
if (needs_range_check) {
@@ -549,65 +580,11 @@
StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
scale, size);
}
-}
-
-/*
- * Generate array store
- *
- */
-void MipsMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
- int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
-
- FlushAllRegs(); // Use explicit registers
- LockCallTemps();
-
- int r_value = TargetReg(kArg0); // Register holding value
- int r_array_class = TargetReg(kArg1); // Register holding array's Class
- int r_array = TargetReg(kArg2); // Register holding array
- int r_index = TargetReg(kArg3); // Register holding index into array
-
- LoadValueDirectFixed(rl_array, r_array); // Grab array
- LoadValueDirectFixed(rl_src, r_value); // Grab value
- LoadValueDirectFixed(rl_index, r_index); // Grab index
-
- GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
-
- // Store of null?
- LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
-
- // Get the array's class.
- LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
- r_array_class, true);
- // Redo LoadValues in case they didn't survive the call.
- LoadValueDirectFixed(rl_array, r_array); // Reload array
- LoadValueDirectFixed(rl_index, r_index); // Reload index
- LoadValueDirectFixed(rl_src, r_value); // Reload value
- r_array_class = INVALID_REG;
-
- // Branch here if value to be stored == null
- LIR* target = NewLIR0(kPseudoTargetLabel);
- null_value_check->target = target;
-
- bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
- int reg_len = INVALID_REG;
- if (needs_range_check) {
- reg_len = TargetReg(kArg1);
- LoadWordDisp(r_array, len_offset, reg_len); // Get len
+ if (allocated_reg_ptr_temp) {
+ FreeTemp(reg_ptr);
}
- /* r_ptr -> array data */
- int r_ptr = AllocTemp();
- OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
- if (needs_range_check) {
- GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
- }
- StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
- FreeTemp(r_ptr);
- FreeTemp(r_index);
- if (!mir_graph_->IsConstantNullRef(rl_src)) {
- MarkGCCard(r_value, r_array);
+ if (card_mark) {
+ MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 4ee5b23..9c598e6 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -76,6 +76,8 @@
case kRet0: res = rMIPS_RET0; break;
case kRet1: res = rMIPS_RET1; break;
case kInvokeTgt: res = rMIPS_INVOKE_TGT; break;
+ case kHiddenArg: res = r_T0; break;
+ case kHiddenFpArg: res = INVALID_REG; break;
case kCount: res = rMIPS_COUNT; break;
}
return res;
@@ -120,22 +122,21 @@
}
-void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir) {
+void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) {
DCHECK_EQ(cu_->instruction_set, kMips);
+ DCHECK(!lir->flags.use_def_invalid);
// Mips-specific resource map setup here.
- uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags;
-
if (flags & REG_DEF_SP) {
- lir->def_mask |= ENCODE_MIPS_REG_SP;
+ lir->u.m.def_mask |= ENCODE_MIPS_REG_SP;
}
if (flags & REG_USE_SP) {
- lir->use_mask |= ENCODE_MIPS_REG_SP;
+ lir->u.m.use_mask |= ENCODE_MIPS_REG_SP;
}
if (flags & REG_DEF_LR) {
- lir->def_mask |= ENCODE_MIPS_REG_LR;
+ lir->u.m.def_mask |= ENCODE_MIPS_REG_LR;
}
}
@@ -269,8 +270,8 @@
}
/* Memory bits */
if (mips_lir && (mask & ENCODE_DALVIK_REG)) {
- sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff,
- (mips_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(mips_lir->flags.alias_info),
+ DECODE_ALIAS_INFO_WIDE(mips_lir->flags.alias_info) ? "(+1)" : "");
}
if (mask & ENCODE_LITERAL) {
strcat(buf, "lit ");
@@ -399,11 +400,6 @@
return res;
}
-MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg) {
- return MIPS_FPREG(reg) ? ®_pool_->FPRegs[reg & MIPS_FP_REG_MASK]
- : ®_pool_->core_regs[reg];
-}
-
/* To be used when explicitly managing register use */
void MipsMir2Lir::LockCallTemps() {
LockTemp(rMIPS_ARG0);
@@ -559,14 +555,17 @@
}
uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return MipsMir2Lir::EncodingMap[opcode].flags;
}
const char* MipsMir2Lir::GetTargetInstName(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return MipsMir2Lir::EncodingMap[opcode].name;
}
const char* MipsMir2Lir::GetTargetInstFmt(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return MipsMir2Lir::EncodingMap[opcode].fmt;
}
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 5d9ae33..2ba2c84 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -93,7 +93,7 @@
} else if ((value < 0) && (value >= -32768)) {
res = NewLIR3(kMipsAddiu, r_dest, r_ZERO, value);
} else {
- res = NewLIR2(kMipsLui, r_dest, value>>16);
+ res = NewLIR2(kMipsLui, r_dest, value >> 16);
if (value & 0xffff)
NewLIR3(kMipsOri, r_dest, r_dest, value);
}
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 440df2a..1a30b7a 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -33,12 +33,17 @@
p->def_end = NULL;
if (p->pair) {
p->pair = false;
- Clobber(p->partner);
+ p = GetRegInfo(p->partner);
+ p->pair = false;
+ p->live = false;
+ p->s_reg = INVALID_SREG;
+ p->def_start = NULL;
+ p->def_end = NULL;
}
}
}
-inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0,
+inline LIR* Mir2Lir::RawLIR(DexOffset dalvik_offset, int opcode, int op0,
int op1, int op2, int op3, int op4, LIR* target) {
LIR* insn = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
insn->dalvik_offset = dalvik_offset;
@@ -53,7 +58,8 @@
if ((opcode == kPseudoTargetLabel) || (opcode == kPseudoSafepointPC) ||
(opcode == kPseudoExportedPC)) {
// Always make labels scheduling barriers
- insn->use_mask = insn->def_mask = ENCODE_ALL;
+ DCHECK(!insn->flags.use_def_invalid);
+ insn->u.m.use_mask = insn->u.m.def_mask = ENCODE_ALL;
}
return insn;
}
@@ -63,7 +69,7 @@
* operands.
*/
inline LIR* Mir2Lir::NewLIR0(int opcode) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -73,7 +79,7 @@
}
inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -83,7 +89,7 @@
}
inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -93,7 +99,7 @@
}
inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -103,7 +109,7 @@
}
inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -114,7 +120,7 @@
inline LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1,
int info2) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -136,20 +142,23 @@
inline void Mir2Lir::SetupResourceMasks(LIR* lir) {
int opcode = lir->opcode;
- if (opcode <= 0) {
- lir->use_mask = lir->def_mask = 0;
+ if (IsPseudoLirOp(opcode)) {
+ if (opcode != kPseudoBarrier) {
+ lir->flags.fixup = kFixupLabel;
+ }
return;
}
uint64_t flags = GetTargetInstFlags(opcode);
if (flags & NEEDS_FIXUP) {
- lir->flags.pcRelFixup = true;
+ // Note: target-specific setup may specialize the fixup kind.
+ lir->flags.fixup = kFixupLabel;
}
/* Get the starting size of the instruction's template */
lir->flags.size = GetInsnSize(lir);
-
+ estimated_native_code_size_ += lir->flags.size;
/* Set up the mask for resources that are updated */
if (flags & (IS_LOAD | IS_STORE)) {
/* Default to heap - will catch specialized classes later */
@@ -161,39 +170,49 @@
* turn will trash everything.
*/
if (flags & IS_BRANCH) {
- lir->def_mask = lir->use_mask = ENCODE_ALL;
+ lir->u.m.def_mask = lir->u.m.use_mask = ENCODE_ALL;
return;
}
if (flags & REG_DEF0) {
- SetupRegMask(&lir->def_mask, lir->operands[0]);
+ SetupRegMask(&lir->u.m.def_mask, lir->operands[0]);
}
if (flags & REG_DEF1) {
- SetupRegMask(&lir->def_mask, lir->operands[1]);
+ SetupRegMask(&lir->u.m.def_mask, lir->operands[1]);
}
+ if (flags & REG_USE0) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[0]);
+ }
+
+ if (flags & REG_USE1) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[1]);
+ }
+
+ if (flags & REG_USE2) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[2]);
+ }
+
+ if (flags & REG_USE3) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[3]);
+ }
if (flags & SETS_CCODES) {
- lir->def_mask |= ENCODE_CCODE;
- }
-
- if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
- int i;
-
- for (i = 0; i < 4; i++) {
- if (flags & (1 << (kRegUse0 + i))) {
- SetupRegMask(&lir->use_mask, lir->operands[i]);
- }
- }
+ lir->u.m.def_mask |= ENCODE_CCODE;
}
if (flags & USES_CCODES) {
- lir->use_mask |= ENCODE_CCODE;
+ lir->u.m.use_mask |= ENCODE_CCODE;
}
// Handle target-specific actions
- SetupTargetResourceMasks(lir);
+ SetupTargetResourceMasks(lir, flags);
+}
+
+inline art::Mir2Lir::RegisterInfo* Mir2Lir::GetRegInfo(int reg) {
+ DCHECK(reginfo_map_.Get(reg) != NULL);
+ return reginfo_map_.Get(reg);
}
} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index c41feb1..fa9a3ad 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 "mir_to_lir-inl.h"
#include "object_utils.h"
+#include "thread-inl.h"
namespace art {
@@ -240,9 +241,9 @@
case Instruction::GOTO_16:
case Instruction::GOTO_32:
if (mir_graph_->IsBackedge(bb, bb->taken)) {
- GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken->id]);
+ GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken]);
} else {
- OpUnconditionalBranch(&label_list[bb->taken->id]);
+ OpUnconditionalBranch(&label_list[bb->taken]);
}
break;
@@ -271,23 +272,22 @@
case Instruction::IF_GE:
case Instruction::IF_GT:
case Instruction::IF_LE: {
- LIR* taken = &label_list[bb->taken->id];
- LIR* fall_through = &label_list[bb->fall_through->id];
+ LIR* taken = &label_list[bb->taken];
+ LIR* fall_through = &label_list[bb->fall_through];
// Result known at compile time?
if (rl_src[0].is_const && rl_src[1].is_const) {
bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg),
mir_graph_->ConstantValue(rl_src[1].orig_sreg));
- BasicBlock* target = is_taken ? bb->taken : bb->fall_through;
- if (mir_graph_->IsBackedge(bb, target)) {
+ BasicBlockId target_id = is_taken ? bb->taken : bb->fall_through;
+ if (mir_graph_->IsBackedge(bb, target_id)) {
GenSuspendTest(opt_flags);
}
- OpUnconditionalBranch(&label_list[target->id]);
+ OpUnconditionalBranch(&label_list[target_id]);
} else {
if (mir_graph_->IsBackwardsBranch(bb)) {
GenSuspendTest(opt_flags);
}
- GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken,
- fall_through);
+ GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken, fall_through);
}
break;
}
@@ -298,16 +298,16 @@
case Instruction::IF_GEZ:
case Instruction::IF_GTZ:
case Instruction::IF_LEZ: {
- LIR* taken = &label_list[bb->taken->id];
- LIR* fall_through = &label_list[bb->fall_through->id];
+ LIR* taken = &label_list[bb->taken];
+ LIR* fall_through = &label_list[bb->fall_through];
// Result known at compile time?
if (rl_src[0].is_const) {
bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), 0);
- BasicBlock* target = is_taken ? bb->taken : bb->fall_through;
- if (mir_graph_->IsBackedge(bb, target)) {
+ BasicBlockId target_id = is_taken ? bb->taken : bb->fall_through;
+ if (mir_graph_->IsBackedge(bb, target_id)) {
GenSuspendTest(opt_flags);
}
- OpUnconditionalBranch(&label_list[target->id]);
+ OpUnconditionalBranch(&label_list[target_id]);
} else {
if (mir_graph_->IsBackwardsBranch(bb)) {
GenSuspendTest(opt_flags);
@@ -337,22 +337,35 @@
GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
break;
case Instruction::APUT_WIDE:
- GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3);
+ GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3, false);
break;
case Instruction::APUT:
- GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2);
+ GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, false);
break;
- case Instruction::APUT_OBJECT:
- GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0], 2);
+ case Instruction::APUT_OBJECT: {
+ bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]);
+ bool is_safe = is_null; // Always safe to store null.
+ if (!is_safe) {
+ // Check safety from verifier type information.
+ const MethodReference mr(cu_->dex_file, cu_->method_idx);
+ is_safe = cu_->compiler_driver->IsSafeCast(mr, mir->offset);
+ }
+ if (is_null || is_safe) {
+ // Store of constant null doesn't require an assignability test and can be generated inline
+ // without fixed register usage or a card mark.
+ GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, !is_null);
+ } else {
+ GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0]);
+ }
break;
+ }
case Instruction::APUT_SHORT:
case Instruction::APUT_CHAR:
- GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1);
+ GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1, false);
break;
case Instruction::APUT_BYTE:
case Instruction::APUT_BOOLEAN:
- GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2],
- rl_src[0], 0);
+ GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2], rl_src[0], 0, false);
break;
case Instruction::IGET_OBJECT:
@@ -696,6 +709,7 @@
// Insert the block label.
block_label_list_[block_id].opcode = kPseudoNormalBlockLabel;
+ block_label_list_[block_id].flags.fixup = kFixupLabel;
AppendLIR(&block_label_list_[block_id]);
LIR* head_lir = NULL;
@@ -706,16 +720,15 @@
}
// Free temp registers and reset redundant store tracking.
- ResetRegPool();
- ResetDefTracking();
-
ClobberAllRegs();
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()]);
} else if (bb->block_type == kExitBlock) {
+ ResetRegPool();
GenExitSequence();
}
@@ -736,17 +749,18 @@
current_dalvik_offset_ = mir->offset;
int opcode = mir->dalvikInsn.opcode;
- LIR* boundary_lir;
// Mark the beginning of a Dalvik instruction for line tracking.
- char* inst_str = cu_->verbose ?
- mir_graph_->GetDalvikDisassembly(mir) : NULL;
- boundary_lir = MarkBoundary(mir->offset, inst_str);
+ if (cu_->verbose) {
+ char* inst_str = mir_graph_->GetDalvikDisassembly(mir);
+ MarkBoundary(mir->offset, inst_str);
+ }
// Remember the first LIR for this block.
if (head_lir == NULL) {
- head_lir = boundary_lir;
- // Set the first boundary_lir as a scheduling barrier.
- head_lir->def_mask = ENCODE_ALL;
+ head_lir = &block_label_list_[bb->id];
+ // Set the first label as a scheduling barrier.
+ DCHECK(!head_lir->flags.use_def_invalid);
+ head_lir->u.m.def_mask = ENCODE_ALL;
}
if (opcode == kMirOpCheck) {
@@ -771,11 +785,6 @@
if (head_lir) {
// Eliminate redundant loads/stores and delay stores into later slots.
ApplyLocalOptimizations(head_lir, last_lir_insn_);
-
- // Generate an unconditional branch to the fallthrough block.
- if (bb->fall_through) {
- OpUnconditionalBranch(&block_label_list_[bb->fall_through->id]);
- }
}
return false;
}
@@ -810,25 +819,34 @@
}
void Mir2Lir::MethodMIR2LIR() {
+ cu_->NewTimingSplit("MIR2LIR");
+
// Hold the labels of each block.
block_label_list_ =
static_cast<LIR*>(arena_->Alloc(sizeof(LIR) * mir_graph_->GetNumBlocks(),
ArenaAllocator::kAllocLIR));
- PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
- for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
- MethodBlockCodeGen(bb);
+ PreOrderDfsIterator iter(mir_graph_);
+ BasicBlock* curr_bb = iter.Next();
+ BasicBlock* next_bb = iter.Next();
+ while (curr_bb != NULL) {
+ MethodBlockCodeGen(curr_bb);
+ // If the fall_through block is no longer laid out consecutively, drop in a branch.
+ BasicBlock* curr_bb_fall_through = mir_graph_->GetBasicBlock(curr_bb->fall_through);
+ if ((curr_bb_fall_through != NULL) && (curr_bb_fall_through != next_bb)) {
+ OpUnconditionalBranch(&block_label_list_[curr_bb->fall_through]);
+ }
+ curr_bb = next_bb;
+ do {
+ next_bb = iter.Next();
+ } while ((next_bb != NULL) && (next_bb->block_type == kDead));
}
-
+ cu_->NewTimingSplit("Launchpads");
HandleSuspendLaunchPads();
HandleThrowLaunchPads();
HandleIntrinsicLaunchPads();
-
- if (!(cu_->disable_opt & (1 << kSafeOptimizations))) {
- RemoveRedundantBranches();
- }
}
} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index a37ebd1..4c56b74 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -30,6 +30,14 @@
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
@@ -95,6 +103,7 @@
struct CallInfo;
struct CompilationUnit;
struct MIR;
+struct LIR;
struct RegLocation;
struct RegisterInfo;
class MIRGraph;
@@ -107,24 +116,36 @@
typedef std::vector<uint8_t> CodeBuffer;
+struct UseDefMasks {
+ uint64_t use_mask; // Resource mask for use.
+ uint64_t def_mask; // Resource mask for def.
+};
+
+struct AssemblyInfo {
+ LIR* pcrel_next; // Chain of LIR nodes needing pc relative fixups.
+ uint8_t bytes[16]; // Encoded instruction bytes.
+};
struct LIR {
- int offset; // Offset of this instruction.
- int dalvik_offset; // Offset of Dalvik opcode.
+ CodeOffset offset; // Offset of this instruction.
+ NarrowDexOffset dalvik_offset; // Offset of Dalvik opcode in code units (16-bit words).
+ int16_t opcode;
LIR* next;
LIR* prev;
LIR* target;
- int opcode;
- int operands[5]; // [0..4] = [dest, src1, src2, extra, extra2].
struct {
- bool is_nop:1; // LIR is optimized away.
- bool pcRelFixup:1; // May need pc-relative fixup.
- unsigned int size:5; // Note: size is in bytes.
- unsigned int unused:25;
+ unsigned int alias_info:17; // For Dalvik register disambiguation.
+ bool is_nop:1; // LIR is optimized away.
+ unsigned int size:4; // Note: size of encoded instruction is in bytes.
+ bool use_def_invalid:1; // If true, masks should not be used.
+ unsigned int generation:1; // Used to track visitation state during fixup pass.
+ unsigned int fixup:8; // Fixup kind.
} flags;
- int alias_info; // For Dalvik register & litpool disambiguation.
- uint64_t use_mask; // Resource mask for use.
- uint64_t def_mask; // Resource mask for def.
+ union {
+ UseDefMasks m; // Use & Def masks used during optimization.
+ AssemblyInfo a; // Instruction encoding used during assembly phase.
+ } u;
+ int32_t operands[5]; // [0..4] = [dest, src1, src2, extra, extra2].
};
// Target-specific initialization.
@@ -141,7 +162,7 @@
// Defines for alias_info (tracks Dalvik register references).
#define DECODE_ALIAS_INFO_REG(X) (X & 0xffff)
-#define DECODE_ALIAS_INFO_WIDE_FLAG (0x80000000)
+#define DECODE_ALIAS_INFO_WIDE_FLAG (0x10000)
#define DECODE_ALIAS_INFO_WIDE(X) ((X & DECODE_ALIAS_INFO_WIDE_FLAG) ? 1 : 0)
#define ENCODE_ALIAS_INFO(REG, ISWIDE) (REG | (ISWIDE ? DECODE_ALIAS_INFO_WIDE_FLAG : 0))
@@ -158,36 +179,42 @@
#define ENCODE_ALL (~0ULL)
#define ENCODE_MEM (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+
+// Mask to denote sreg as the start of a double. Must not interfere with low 16 bits.
+#define STARTING_DOUBLE_SREG 0x10000
+
// TODO: replace these macros
#define SLOW_FIELD_PATH (cu_->enable_debug & (1 << kDebugSlowFieldPath))
#define SLOW_INVOKE_PATH (cu_->enable_debug & (1 << kDebugSlowInvokePath))
#define SLOW_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowStringPath))
#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
-#define is_pseudo_opcode(opcode) (static_cast<int>(opcode) < 0)
class Mir2Lir : public Backend {
public:
- struct SwitchTable {
- int offset;
- const uint16_t* table; // Original dex table.
- int vaddr; // Dalvik offset of switch opcode.
- LIR* anchor; // Reference instruction for relative offsets.
- LIR** targets; // Array of case targets.
+ /*
+ * Auxiliary information describing the location of data embedded in the Dalvik
+ * byte code stream.
+ */
+ struct EmbeddedData {
+ CodeOffset offset; // Code offset of data block.
+ const uint16_t* table; // Original dex data.
+ DexOffset vaddr; // Dalvik offset of parent opcode.
};
- struct FillArrayData {
- int offset;
- const uint16_t* table; // Original dex table.
- int size;
- int vaddr; // Dalvik offset of FILL_ARRAY_DATA opcode.
+ struct FillArrayData : EmbeddedData {
+ int32_t size;
+ };
+
+ struct SwitchTable : EmbeddedData {
+ LIR* anchor; // Reference instruction for relative offsets.
+ LIR** targets; // Array of case targets.
};
/* Static register use counts */
struct RefCounts {
int count;
int s_reg;
- bool double_start; // Starting v_reg for a double
};
/*
@@ -241,6 +268,38 @@
return code_buffer_.size() / sizeof(code_buffer_[0]);
}
+ bool IsPseudoLirOp(int opcode) {
+ return (opcode < 0);
+ }
+
+ /*
+ * LIR operands are 32-bit integers. Sometimes, (especially for managing
+ * instructions which require PC-relative fixups), we need the operands to carry
+ * pointers. To do this, we assign these pointers an index in pointer_storage_, and
+ * hold that index in the operand array.
+ * TUNING: If use of these utilities becomes more common on 32-bit builds, it
+ * 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);
+ return res;
+ }
+
+ void* UnwrapPointer(size_t index) {
+ return pointer_storage_.Get(index);
+ }
+
+ // strdup(), but allocates from the arena.
+ char* ArenaStrdup(const char* str) {
+ size_t len = strlen(str) + 1;
+ char* res = reinterpret_cast<char*>(arena_->Alloc(len, ArenaAllocator::kAllocMisc));
+ if (res != NULL) {
+ strncpy(res, str, len);
+ }
+ return res;
+ }
+
// Shared by all targets - implemented in codegen_util.cc
void AppendLIR(LIR* lir);
void InsertLIRBefore(LIR* current_lir, LIR* new_lir);
@@ -250,16 +309,15 @@
virtual void Materialize();
virtual CompiledMethod* GetCompiledMethod();
void MarkSafepointPC(LIR* inst);
- bool FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put);
+ bool FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile);
void SetupResourceMasks(LIR* lir);
- void AssembleLIR();
void SetMemRefType(LIR* lir, bool is_load, int mem_type);
void AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, bool is64bit);
void SetupRegMask(uint64_t* mask, int reg);
void DumpLIRInsn(LIR* arg, unsigned char* base_addr);
void DumpPromotionMap();
void CodegenDump();
- LIR* RawLIR(int dalvik_offset, int opcode, int op0 = 0, int op1 = 0,
+ LIR* RawLIR(DexOffset dalvik_offset, int opcode, int op0 = 0, int op1 = 0,
int op2 = 0, int op3 = 0, int op4 = 0, LIR* target = NULL);
LIR* NewLIR0(int opcode);
LIR* NewLIR1(int opcode, int dest);
@@ -274,13 +332,14 @@
void ProcessSwitchTables();
void DumpSparseSwitchTable(const uint16_t* table);
void DumpPackedSwitchTable(const uint16_t* table);
- LIR* MarkBoundary(int offset, const char* inst_str);
+ void MarkBoundary(DexOffset offset, const char* inst_str);
void NopLIR(LIR* lir);
+ void UnlinkLIR(LIR* lir);
bool EvaluateBranch(Instruction::Code opcode, int src1, int src2);
bool IsInexpensiveConstant(RegLocation rl_src);
ConditionCode FlipComparisonOrder(ConditionCode before);
- void DumpMappingTable(const char* table_name, const std::string& descriptor,
- const std::string& name, const std::string& signature,
+ void DumpMappingTable(const char* table_name, const char* descriptor,
+ const char* name, const Signature& signature,
const std::vector<uint32_t>& v);
void InstallLiteralPools();
void InstallSwitchTables();
@@ -288,21 +347,18 @@
bool VerifyCatchEntries();
void CreateMappingTables();
void CreateNativeGcMap();
- int AssignLiteralOffset(int offset);
- int AssignSwitchTablesOffset(int offset);
- int AssignFillArrayDataOffset(int offset);
- int AssignInsnOffsets();
- void AssignOffsets();
- LIR* InsertCaseLabel(int vaddr, int keyVal);
- void MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec);
- void MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec);
+ int AssignLiteralOffset(CodeOffset offset);
+ int AssignSwitchTablesOffset(CodeOffset offset);
+ int AssignFillArrayDataOffset(CodeOffset offset);
+ LIR* InsertCaseLabel(DexOffset vaddr, int keyVal);
+ void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec);
+ void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec);
// Shared by all targets - implemented in local_optimizations.cc
void ConvertMemOpIntoMove(LIR* orig_lir, int dest, int src);
void ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir);
void ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir);
void ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir);
- void RemoveRedundantBranches();
// Shared by all targets - implemented in ralloc_util.cc
int GetSRegHi(int lowSreg);
@@ -324,11 +380,9 @@
void RecordCorePromotion(int reg, int s_reg);
int AllocPreservedCoreReg(int s_reg);
void RecordFpPromotion(int reg, int s_reg);
- int AllocPreservedSingle(int s_reg, bool even);
+ int AllocPreservedSingle(int s_reg);
int AllocPreservedDouble(int s_reg);
- int AllocPreservedFPReg(int s_reg, bool double_start);
- int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp,
- bool required);
+ int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp, bool required);
int AllocTempDouble();
int AllocFreeTemp();
int AllocTemp();
@@ -367,13 +421,14 @@
RegLocation UpdateRawLoc(RegLocation loc);
RegLocation EvalLocWide(RegLocation loc, int reg_class, bool update);
RegLocation EvalLoc(RegLocation loc, int reg_class, bool update);
- void CountRefs(RefCounts* core_counts, RefCounts* fp_counts);
+ void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs);
void DumpCounts(const RefCounts* arr, int size, const char* msg);
void DoPromotion();
int VRegOffset(int v_reg);
int SRegOffset(int s_reg);
RegLocation GetReturnWide(bool is_double);
RegLocation GetReturn(bool is_float);
+ RegisterInfo* GetRegInfo(int reg);
// Shared by all targets - implemented in gen_common.cc.
bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
@@ -407,6 +462,9 @@
RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object);
void GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+ void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src);
+
void GenConstClass(uint32_t type_idx, RegLocation rl_dest);
void GenConstString(uint32_t string_idx, RegLocation rl_dest);
void GenNewInstance(uint32_t type_idx, RegLocation rl_dest);
@@ -463,6 +521,10 @@
void CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_offset,
int arg0, RegLocation arg1, RegLocation arg2,
bool safepoint_pc);
+ void CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset,
+ RegLocation arg0, RegLocation arg1,
+ RegLocation arg2,
+ bool safepoint_pc);
void GenInvoke(CallInfo* info);
void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
@@ -482,6 +544,7 @@
bool GenInlinedCharAt(CallInfo* info);
bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty);
+ bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
bool GenInlinedAbsInt(CallInfo* info);
bool GenInlinedAbsLong(CallInfo* info);
bool GenInlinedFloatCvt(CallInfo* info);
@@ -550,7 +613,6 @@
virtual int AllocTypedTempPair(bool fp_hint, int reg_class) = 0;
virtual int S2d(int low_reg, int high_reg) = 0;
virtual int TargetReg(SpecialTargetRegister reg) = 0;
- virtual RegisterInfo* GetRegInfo(int reg) = 0;
virtual RegLocation GetReturnAlt() = 0;
virtual RegLocation GetReturnWideAlt() = 0;
virtual RegLocation LocCReturn() = 0;
@@ -570,9 +632,9 @@
virtual void CompilerInitializeRegAlloc() = 0;
// Required for target - miscellaneous.
- virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr) = 0;
+ virtual void AssembleLIR() = 0;
virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix) = 0;
- virtual void SetupTargetResourceMasks(LIR* lir) = 0;
+ virtual void SetupTargetResourceMasks(LIR* lir, uint64_t flags) = 0;
virtual const char* GetTargetInstFmt(int opcode) = 0;
virtual const char* GetTargetInstName(int opcode) = 0;
virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr) = 0;
@@ -602,6 +664,8 @@
virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier) = 0;
virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min) = 0;
virtual bool GenInlinedSqrt(CallInfo* info) = 0;
+ virtual bool GenInlinedPeek(CallInfo* info, OpSize size) = 0;
+ virtual bool GenInlinedPoke(CallInfo* info, OpSize size) = 0;
virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src) = 0;
virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2) = 0;
@@ -621,46 +685,40 @@
virtual void GenEntrySequence(RegLocation* ArgLocs,
RegLocation rl_method) = 0;
virtual void GenExitSequence() = 0;
- virtual void GenFillArrayData(uint32_t table_offset,
+ virtual void GenFillArrayData(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;
virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0;
virtual void GenMemBarrier(MemBarrierKind barrier_kind) = 0;
- virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src) = 0;
- virtual void GenMonitorExit(int opt_flags, RegLocation rl_src) = 0;
virtual void GenMoveException(RegLocation rl_dest) = 0;
virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
RegLocation rl_result, int lit, int first_bit,
int second_bit) = 0;
virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src) = 0;
virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) = 0;
- virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ virtual void GenPackedSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) = 0;
- virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) = 0;
virtual void GenSpecialCase(BasicBlock* bb, MIR* mir,
SpecialCaseHandler special_case) = 0;
- virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) = 0;
virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale) = 0;
virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) = 0;
+ RegLocation rl_index, RegLocation rl_src, int scale,
+ bool card_mark) = 0;
virtual void GenShiftImmOpLong(Instruction::Code opcode,
RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_shift) = 0;
// Required for target - single operation generators.
virtual LIR* OpUnconditionalBranch(LIR* target) = 0;
- virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2,
- LIR* target) = 0;
- virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
- LIR* target) = 0;
+ virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target) = 0;
+ virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target) = 0;
virtual LIR* OpCondBranch(ConditionCode cc, LIR* target) = 0;
- virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg,
- LIR* target) = 0;
+ virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) = 0;
virtual LIR* OpFpRegCopy(int r_dest, int r_src) = 0;
virtual LIR* OpIT(ConditionCode cond, const char* guide) = 0;
virtual LIR* OpMem(OpKind op, int rBase, int disp) = 0;
@@ -672,22 +730,23 @@
virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset) = 0;
virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2) = 0;
virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) = 0;
- virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1,
- int r_src2) = 0;
+ virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) = 0;
virtual LIR* OpTestSuspend(LIR* target) = 0;
virtual LIR* OpThreadMem(OpKind op, ThreadOffset thread_offset) = 0;
virtual LIR* OpVldm(int rBase, int count) = 0;
virtual LIR* OpVstm(int rBase, int count) = 0;
- virtual void OpLea(int rBase, int reg1, int reg2, int scale,
- int offset) = 0;
- virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
- int src_hi) = 0;
+ virtual void OpLea(int rBase, int reg1, int reg2, int scale, int offset) = 0;
+ virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi) = 0;
virtual void OpTlsCmp(ThreadOffset offset, int val) = 0;
virtual bool InexpensiveConstantInt(int32_t value) = 0;
virtual bool InexpensiveConstantFloat(int32_t value) = 0;
virtual bool InexpensiveConstantLong(int64_t value) = 0;
virtual bool InexpensiveConstantDouble(int64_t value) = 0;
+ // May be optimized by targets.
+ virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src);
+ virtual void GenMonitorExit(int opt_flags, RegLocation rl_src);
+
// Temp workaround
void Workaround7250540(RegLocation rl_dest, int value);
@@ -718,6 +777,7 @@
LIR* literal_list_; // Constants.
LIR* method_literal_list_; // Method literals requiring patching.
LIR* code_literal_list_; // Code literals requiring patching.
+ LIR* first_fixup_; // Doubly-linked list of LIR nodes requiring fixups.
protected:
CompilationUnit* const cu_;
@@ -727,7 +787,9 @@
GrowableArray<LIR*> throw_launchpads_;
GrowableArray<LIR*> suspend_launchpads_;
GrowableArray<LIR*> intrinsic_launchpads_;
- SafeMap<unsigned int, LIR*> boundary_map_; // boundary lookup cache.
+ GrowableArray<RegisterInfo*> tempreg_info_;
+ GrowableArray<RegisterInfo*> reginfo_map_;
+ GrowableArray<void*> pointer_storage_;
/*
* Holds mapping from native PC to dex PC for safepoints where we may deoptimize.
* Native PC is on the return address of the safepointed operation. Dex PC is for
@@ -739,8 +801,9 @@
* immediately preceed the instruction.
*/
std::vector<uint32_t> dex2pc_mapping_table_;
- int data_offset_; // starting offset of literal pool.
- int total_size_; // header + code size.
+ 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.
LIR* block_label_list_;
PromotionMap* promotion_map_;
/*
@@ -752,7 +815,8 @@
* in the CompilationUnit struct before codegen for each instruction.
* The low-level LIR creation utilites will pull it from here. Rework this.
*/
- int current_dalvik_offset_;
+ DexOffset current_dalvik_offset_;
+ size_t estimated_native_code_size_; // Just an estimate; used to reserve code_buffer_ size.
RegisterPool* reg_pool_;
/*
* Sanity checking for the register temp tracking. The same ssa
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index 71b74a4..41a57af 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -28,13 +28,9 @@
* live until it is either explicitly killed or reallocated.
*/
void Mir2Lir::ResetRegPool() {
- for (int i = 0; i < reg_pool_->num_core_regs; i++) {
- if (reg_pool_->core_regs[i].is_temp)
- reg_pool_->core_regs[i].in_use = false;
- }
- for (int i = 0; i < reg_pool_->num_fp_regs; i++) {
- if (reg_pool_->FPRegs[i].is_temp)
- reg_pool_->FPRegs[i].in_use = false;
+ GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
+ for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ info->in_use = false;
}
// Reset temp tracking sanity check.
if (kIsDebugBuild) {
@@ -48,13 +44,21 @@
*/
void Mir2Lir::CompilerInitPool(RegisterInfo* regs, int* reg_nums, int num) {
for (int i = 0; i < num; i++) {
- regs[i].reg = reg_nums[i];
+ uint32_t reg_number = reg_nums[i];
+ regs[i].reg = reg_number;
regs[i].in_use = false;
regs[i].is_temp = false;
regs[i].pair = false;
regs[i].live = false;
regs[i].dirty = false;
regs[i].s_reg = INVALID_SREG;
+ size_t map_size = reginfo_map_.Size();
+ if (reg_number >= map_size) {
+ for (uint32_t i = 0; i < ((reg_number - map_size) + 1); i++) {
+ reginfo_map_.Insert(NULL);
+ }
+ }
+ reginfo_map_.Put(reg_number, ®s[i]);
}
}
@@ -62,10 +66,9 @@
LOG(INFO) << "================================================";
for (int i = 0; i < num_regs; i++) {
LOG(INFO) << StringPrintf(
- "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+ "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d",
p[i].reg, p[i].is_temp, p[i].in_use, p[i].pair, p[i].partner,
- p[i].live, p[i].dirty, p[i].s_reg, reinterpret_cast<uintptr_t>(p[i].def_start),
- reinterpret_cast<uintptr_t>(p[i].def_end));
+ p[i].live, p[i].dirty, p[i].s_reg);
}
LOG(INFO) << "================================================";
}
@@ -170,17 +173,12 @@
promotion_map_[p_map_idx].FpReg = reg;
}
-/*
- * Reserve a callee-save fp single register. Try to fullfill request for
- * even/odd allocation, but go ahead and allocate anything if not
- * available. If nothing's available, return -1.
- */
-int Mir2Lir::AllocPreservedSingle(int s_reg, bool even) {
- int res = -1;
+// Reserve a callee-save fp single register.
+int Mir2Lir::AllocPreservedSingle(int s_reg) {
+ int res = -1; // Return code if none available.
RegisterInfo* FPRegs = reg_pool_->FPRegs;
for (int i = 0; i < reg_pool_->num_fp_regs; i++) {
- if (!FPRegs[i].is_temp && !FPRegs[i].in_use &&
- ((FPRegs[i].reg & 0x1) == 0) == even) {
+ if (!FPRegs[i].is_temp && !FPRegs[i].in_use) {
res = FPRegs[i].reg;
RecordFpPromotion(res, s_reg);
break;
@@ -246,26 +244,6 @@
return res;
}
-
-/*
- * Reserve a callee-save fp register. If this register can be used
- * as the first of a double, attempt to allocate an even pair of fp
- * single regs (but if can't still attempt to allocate a single, preferring
- * first to allocate an odd register.
- */
-int Mir2Lir::AllocPreservedFPReg(int s_reg, bool double_start) {
- int res = -1;
- if (double_start) {
- res = AllocPreservedDouble(s_reg);
- }
- if (res == -1) {
- res = AllocPreservedSingle(s_reg, false /* try odd # */);
- }
- if (res == -1)
- res = AllocPreservedSingle(s_reg, true /* try even # */);
- return res;
-}
-
int Mir2Lir::AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp,
bool required) {
int next = *next_temp;
@@ -379,7 +357,7 @@
if (s_reg == -1)
return NULL;
for (int i = 0; i < num_regs; i++) {
- if (p[i].live && (p[i].s_reg == s_reg)) {
+ if ((p[i].s_reg == s_reg) && p[i].live) {
if (p[i].is_temp)
p[i].in_use = true;
return &p[i];
@@ -412,47 +390,16 @@
}
void Mir2Lir::FreeTemp(int reg) {
- RegisterInfo* p = reg_pool_->core_regs;
- int num_regs = reg_pool_->num_core_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- if (p[i].is_temp) {
- p[i].in_use = false;
- }
- p[i].pair = false;
- return;
- }
+ RegisterInfo* p = GetRegInfo(reg);
+ if (p->is_temp) {
+ p->in_use = false;
}
- p = reg_pool_->FPRegs;
- num_regs = reg_pool_->num_fp_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- if (p[i].is_temp) {
- p[i].in_use = false;
- }
- p[i].pair = false;
- return;
- }
- }
- LOG(FATAL) << "Tried to free a non-existant temp: r" << reg;
+ p->pair = false;
}
Mir2Lir::RegisterInfo* Mir2Lir::IsLive(int reg) {
- RegisterInfo* p = reg_pool_->core_regs;
- int num_regs = reg_pool_->num_core_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- return p[i].live ? &p[i] : NULL;
- }
- }
- p = reg_pool_->FPRegs;
- num_regs = reg_pool_->num_fp_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- return p[i].live ? &p[i] : NULL;
- }
- }
- return NULL;
+ RegisterInfo* p = GetRegInfo(reg);
+ return p->live ? p : NULL;
}
Mir2Lir::RegisterInfo* Mir2Lir::IsTemp(int reg) {
@@ -476,27 +423,10 @@
* allocated. Use with caution.
*/
void Mir2Lir::LockTemp(int reg) {
- RegisterInfo* p = reg_pool_->core_regs;
- int num_regs = reg_pool_->num_core_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- DCHECK(p[i].is_temp);
- p[i].in_use = true;
- p[i].live = false;
- return;
- }
- }
- p = reg_pool_->FPRegs;
- num_regs = reg_pool_->num_fp_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- DCHECK(p[i].is_temp);
- p[i].in_use = true;
- p[i].live = false;
- return;
- }
- }
- LOG(FATAL) << "Tried to lock a non-existant temp: r" << reg;
+ RegisterInfo* p = GetRegInfo(reg);
+ DCHECK(p->is_temp);
+ p->in_use = true;
+ p->live = false;
}
void Mir2Lir::ResetDef(int reg) {
@@ -599,11 +529,13 @@
}
void Mir2Lir::ClobberAllRegs() {
- for (int i = 0; i< reg_pool_->num_core_regs; i++) {
- ClobberBody(®_pool_->core_regs[i]);
- }
- for (int i = 0; i< reg_pool_->num_fp_regs; i++) {
- ClobberBody(®_pool_->FPRegs[i]);
+ GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
+ for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ info->live = false;
+ info->s_reg = INVALID_SREG;
+ info->def_start = NULL;
+ info->def_end = NULL;
+ info->pair = false;
}
}
@@ -659,11 +591,13 @@
void Mir2Lir::MarkTemp(int reg) {
RegisterInfo* info = GetRegInfo(reg);
+ tempreg_info_.Insert(info);
info->is_temp = true;
}
void Mir2Lir::UnmarkTemp(int reg) {
RegisterInfo* info = GetRegInfo(reg);
+ tempreg_info_.Delete(info);
info->is_temp = false;
}
@@ -834,9 +768,9 @@
RegLocation Mir2Lir::EvalLocWide(RegLocation loc, int reg_class, bool update) {
DCHECK(loc.wide);
- int new_regs;
- int low_reg;
- int high_reg;
+ int32_t new_regs;
+ int32_t low_reg;
+ int32_t high_reg;
loc = UpdateLocWide(loc);
@@ -912,18 +846,22 @@
}
/* USE SSA names to count references of base Dalvik v_regs. */
-void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts) {
+void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) {
for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
RegLocation loc = mir_graph_->reg_location_[i];
RefCounts* counts = loc.fp ? fp_counts : core_counts;
int p_map_idx = SRegToPMap(loc.s_reg_low);
- // Don't count easily regenerated immediates
- if (loc.fp || !IsInexpensiveConstant(loc)) {
+ if (loc.fp) {
+ if (loc.wide) {
+ // Treat doubles as a unit, using upper half of fp_counts array.
+ counts[p_map_idx + num_regs].count += mir_graph_->GetUseCount(i);
+ i++;
+ } else {
+ counts[p_map_idx].count += mir_graph_->GetUseCount(i);
+ }
+ } else if (!IsInexpensiveConstant(loc)) {
counts[p_map_idx].count += mir_graph_->GetUseCount(i);
}
- if (loc.wide && loc.fp && !loc.high_word) {
- counts[p_map_idx].double_start = true;
- }
}
}
@@ -942,7 +880,11 @@
void Mir2Lir::DumpCounts(const RefCounts* arr, int size, const char* msg) {
LOG(INFO) << msg;
for (int i = 0; i < size; i++) {
- LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count;
+ if ((arr[i].s_reg & STARTING_DOUBLE_SREG) != 0) {
+ LOG(INFO) << "s_reg[D" << (arr[i].s_reg & ~STARTING_DOUBLE_SREG) << "]: " << arr[i].count;
+ } else {
+ LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count;
+ }
}
}
@@ -965,7 +907,7 @@
* count based on original Dalvik register name. Count refs
* separately based on type in order to give allocation
* preference to fp doubles - which must be allocated sequential
- * physical single fp registers started with an even-numbered
+ * physical single fp registers starting with an even-numbered
* reg.
* TUNING: replace with linear scan once we have the ability
* to describe register live ranges for GC.
@@ -974,7 +916,7 @@
static_cast<RefCounts*>(arena_->Alloc(sizeof(RefCounts) * num_regs,
ArenaAllocator::kAllocRegAlloc));
RefCounts *FpRegs =
- static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * num_regs,
+ static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * num_regs * 2,
ArenaAllocator::kAllocRegAlloc));
// Set ssa names for original Dalvik registers
for (int i = 0; i < dalvik_regs; i++) {
@@ -982,46 +924,49 @@
}
// Set ssa name for Method*
core_regs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg();
- FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy
+ FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy.
+ FpRegs[dalvik_regs + num_regs].s_reg = mir_graph_->GetMethodSReg(); // for consistency.
// Set ssa names for compiler_temps
for (int i = 1; i <= cu_->num_compiler_temps; i++) {
CompilerTemp* ct = mir_graph_->compiler_temps_.Get(i);
core_regs[dalvik_regs + i].s_reg = ct->s_reg;
FpRegs[dalvik_regs + i].s_reg = ct->s_reg;
+ FpRegs[num_regs + dalvik_regs + i].s_reg = ct->s_reg;
+ }
+
+ // Duplicate in upper half to represent possible fp double starting sregs.
+ for (int i = 0; i < num_regs; i++) {
+ FpRegs[num_regs + i].s_reg = FpRegs[i].s_reg | STARTING_DOUBLE_SREG;
}
// Sum use counts of SSA regs by original Dalvik vreg.
- CountRefs(core_regs, FpRegs);
+ CountRefs(core_regs, FpRegs, num_regs);
- /*
- * Ideally, we'd allocate doubles starting with an even-numbered
- * register. Bias the counts to try to allocate any vreg that's
- * used as the start of a pair first.
- */
- for (int i = 0; i < num_regs; i++) {
- if (FpRegs[i].double_start) {
- FpRegs[i].count *= 2;
- }
- }
// Sort the count arrays
qsort(core_regs, num_regs, sizeof(RefCounts), SortCounts);
- qsort(FpRegs, num_regs, sizeof(RefCounts), SortCounts);
+ qsort(FpRegs, num_regs * 2, sizeof(RefCounts), SortCounts);
if (cu_->verbose) {
DumpCounts(core_regs, num_regs, "Core regs after sort");
- DumpCounts(FpRegs, num_regs, "Fp regs after sort");
+ DumpCounts(FpRegs, num_regs * 2, "Fp regs after sort");
}
if (!(cu_->disable_opt & (1 << kPromoteRegs))) {
// Promote FpRegs
- for (int i = 0; (i < num_regs) && (FpRegs[i].count >= promotion_threshold); i++) {
- int p_map_idx = SRegToPMap(FpRegs[i].s_reg);
- if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) {
- int reg = AllocPreservedFPReg(FpRegs[i].s_reg,
- FpRegs[i].double_start);
+ for (int i = 0; (i < (num_regs * 2)) && (FpRegs[i].count >= promotion_threshold); i++) {
+ int p_map_idx = SRegToPMap(FpRegs[i].s_reg & ~STARTING_DOUBLE_SREG);
+ if ((FpRegs[i].s_reg & STARTING_DOUBLE_SREG) != 0) {
+ if ((promotion_map_[p_map_idx].fp_location != kLocPhysReg) &&
+ (promotion_map_[p_map_idx + 1].fp_location != kLocPhysReg)) {
+ int low_sreg = FpRegs[i].s_reg & ~STARTING_DOUBLE_SREG;
+ // Ignore result - if can't alloc double may still be able to alloc singles.
+ AllocPreservedDouble(low_sreg);
+ }
+ } else if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) {
+ int reg = AllocPreservedSingle(FpRegs[i].s_reg);
if (reg < 0) {
- break; // No more left
+ break; // No more left.
}
}
}
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index e883432..2047f30 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -246,6 +246,8 @@
UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"),
#undef UNARY_ENCODING_MAP
+ { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" },
+
#define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \
{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \
{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RM", "!0r,[!1r+!2d]" }, \
@@ -362,6 +364,7 @@
}
int X86Mir2Lir::GetInsnSize(LIR* lir) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
const X86EncodingMap* entry = &X86Mir2Lir::EncodingMap[lir->opcode];
switch (entry->kind) {
case kData:
@@ -370,6 +373,8 @@
return lir->operands[0]; // length of nop is sole operand
case kNullary:
return 1; // 1 byte of opcode
+ case kRegOpcode: // lir operands - 0: reg
+ return ComputeSize(entry, 0, 0, false) - 1; // substract 1 for modrm
case kReg: // lir operands - 0: reg
return ComputeSize(entry, 0, 0, false);
case kMem: // lir operands - 0: base, 1: disp
@@ -513,6 +518,33 @@
}
}
+void X86Mir2Lir::EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ // There's no 3-byte instruction with +rd
+ DCHECK_NE(0x38, entry->skeleton.extra_opcode1);
+ DCHECK_NE(0x3A, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ DCHECK(!X86_FPREG(reg));
+ DCHECK_LT(reg, 8);
+ code_buffer_.back() += reg;
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) {
if (entry->skeleton.prefix1 != 0) {
code_buffer_.push_back(entry->skeleton.prefix1);
@@ -525,7 +557,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -582,7 +614,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -595,7 +627,9 @@
reg = reg & X86_FP_REG_MASK;
}
if (reg >= 4) {
- DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast<int>(reg)
+ DCHECK(strchr(entry->name, '8') == NULL ||
+ entry->opcode == kX86Movzx8RM || entry->opcode == kX86Movsx8RM)
+ << entry->name << " " << static_cast<int>(reg)
<< " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
}
DCHECK_LT(reg, 8);
@@ -631,7 +665,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -672,7 +706,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -712,7 +746,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -749,7 +783,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -808,7 +842,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -858,7 +892,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -923,7 +957,7 @@
}
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -1037,7 +1071,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -1066,7 +1100,7 @@
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -1089,11 +1123,13 @@
int base_or_table, uint8_t index, int scale, int table_or_disp) {
int disp;
if (entry->opcode == kX86PcRelLoadRA) {
- Mir2Lir::SwitchTable *tab_rec = reinterpret_cast<Mir2Lir::SwitchTable*>(table_or_disp);
+ Mir2Lir::EmbeddedData *tab_rec =
+ reinterpret_cast<Mir2Lir::EmbeddedData*>(UnwrapPointer(table_or_disp));
disp = tab_rec->offset;
} else {
DCHECK(entry->opcode == kX86PcRelAdr);
- Mir2Lir::FillArrayData *tab_rec = reinterpret_cast<Mir2Lir::FillArrayData*>(base_or_table);
+ Mir2Lir::EmbeddedData *tab_rec =
+ reinterpret_cast<Mir2Lir::EmbeddedData*>(UnwrapPointer(base_or_table));
disp = tab_rec->offset;
}
if (entry->skeleton.prefix1 != 0) {
@@ -1160,13 +1196,13 @@
* instruction. In those cases we will try to substitute a new code
* sequence or request that the trace be shortened and retried.
*/
-AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
+AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) {
LIR *lir;
AssemblerStatus res = kSuccess; // Assume success
const bool kVerbosePcFixup = false;
for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
- if (lir->opcode < 0) {
+ if (IsPseudoLirOp(lir->opcode)) {
continue;
}
@@ -1174,19 +1210,19 @@
continue;
}
- if (lir->flags.pcRelFixup) {
+ if (lir->flags.fixup != kFixupNone) {
switch (lir->opcode) {
case kX86Jcc8: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
int delta = 0;
- uintptr_t pc;
+ CodeOffset pc;
if (IS_SIMM8(lir->operands[0])) {
pc = lir->offset + 2 /* opcode + rel8 */;
} else {
pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
}
- uintptr_t target = target_lir->offset;
+ CodeOffset target = target_lir->offset;
delta = target - pc;
if (IS_SIMM8(delta) != IS_SIMM8(lir->operands[0])) {
if (kVerbosePcFixup) {
@@ -1210,8 +1246,8 @@
case kX86Jcc32: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
- uintptr_t pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (kVerbosePcFixup) {
LOG(INFO) << "Source:";
@@ -1227,17 +1263,17 @@
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
int delta = 0;
- uintptr_t pc;
+ CodeOffset pc;
if (IS_SIMM8(lir->operands[0])) {
pc = lir->offset + 2 /* opcode + rel8 */;
} else {
pc = lir->offset + 5 /* opcode + rel32 */;
}
- uintptr_t target = target_lir->offset;
+ CodeOffset target = target_lir->offset;
delta = target - pc;
if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && delta == 0) {
// Useless branch
- lir->flags.is_nop = true;
+ NopLIR(lir);
if (kVerbosePcFixup) {
LOG(INFO) << "Retry for useless branch at " << lir->offset;
}
@@ -1256,8 +1292,8 @@
case kX86Jmp32: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
- uintptr_t pc = lir->offset + 5 /* opcode + rel32 */;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 5 /* opcode + rel32 */;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
lir->operands[0] = delta;
break;
@@ -1298,6 +1334,9 @@
DCHECK_EQ(0, entry->skeleton.ax_opcode);
DCHECK_EQ(0, entry->skeleton.immediate_bytes);
break;
+ case kRegOpcode: // lir operands - 0: reg
+ EmitOpRegOpcode(entry, lir->operands[0]);
+ break;
case kReg: // lir operands - 0: reg
EmitOpReg(entry, lir->operands[0]);
break;
@@ -1385,4 +1424,101 @@
return res;
}
+// LIR offset assignment.
+// TODO: consolidate w/ Arm assembly mechanism.
+int X86Mir2Lir::AssignInsnOffsets() {
+ LIR* lir;
+ int offset = 0;
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (LIKELY(!IsPseudoLirOp(lir->opcode))) {
+ if (!lir->flags.is_nop) {
+ offset += lir->flags.size;
+ }
+ } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) {
+ if (offset & 0x2) {
+ offset += 2;
+ lir->operands[0] = 1;
+ } else {
+ lir->operands[0] = 0;
+ }
+ }
+ /* Pseudo opcodes don't consume space */
+ }
+ return offset;
+}
+
+/*
+ * Walk the compilation unit and assign offsets to instructions
+ * and literals and compute the total size of the compiled unit.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void X86Mir2Lir::AssignOffsets() {
+ int offset = AssignInsnOffsets();
+
+ /* Const values have to be word aligned */
+ offset = (offset + 3) & ~3;
+
+ /* Set up offsets for literals */
+ data_offset_ = offset;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ offset = AssignFillArrayDataOffset(offset);
+
+ total_size_ = offset;
+}
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void X86Mir2Lir::AssembleLIR() {
+ cu_->NewTimingSplit("Assemble");
+ AssignOffsets();
+ int assembler_retries = 0;
+ /*
+ * Assemble here. Note that we generate code with optimistic assumptions
+ * and if found now to work, we'll have to redo the sequence and retry.
+ */
+
+ while (true) {
+ AssemblerStatus res = AssembleInstructions(0);
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ // Redo offsets and try again
+ AssignOffsets();
+ code_buffer_.clear();
+ }
+ }
+
+ cu_->NewTimingSplit("LiteralData");
+ // Install literals
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ cu_->NewTimingSplit("PcMappingTable");
+ CreateMappingTables();
+
+ cu_->NewTimingSplit("GcMap");
+ CreateNativeGcMap();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 2be2aa9..17924b0 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -31,15 +31,15 @@
* The sparse table in the literal pool is an array of <key,displacement>
* pairs.
*/
-void X86Mir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+void X86Mir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
int entries = table[1];
- const int* keys = reinterpret_cast<const int*>(&table[2]);
- const int* targets = &keys[entries];
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
+ const int32_t* targets = &keys[entries];
rl_src = LoadValue(rl_src, kCoreReg);
for (int i = 0; i < entries; i++) {
int key = keys[i];
@@ -66,15 +66,15 @@
* jmp r_start_of_method
* done:
*/
-void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+void X86Mir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
// Add the table to the list - we'll process it later
- SwitchTable *tab_rec =
- static_cast<SwitchTable *>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
+ SwitchTable* tab_rec =
+ static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int size = table[1];
@@ -103,8 +103,7 @@
// Load the displacement from the switch table
int disp_reg = AllocTemp();
- NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2,
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2, WrapPointer(tab_rec));
// Add displacement to start of method
OpRegReg(kOpAdd, start_of_method_reg, disp_reg);
// ..and go!
@@ -126,10 +125,10 @@
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
+void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
// Add the table to the list - we'll process it later
- FillArrayData *tab_rec =
+ FillArrayData* tab_rec =
static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
@@ -144,49 +143,12 @@
LoadValueDirectFixed(rl_src, rX86_ARG0);
// Materialize a pointer to the fill data image
NewLIR1(kX86StartOfMethod, rX86_ARG2);
- NewLIR2(kX86PcRelAdr, rX86_ARG1, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR2(kX86PcRelAdr, rX86_ARG1, WrapPointer(tab_rec));
NewLIR2(kX86Add32RR, rX86_ARG1, rX86_ARG2);
CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData), rX86_ARG0,
rX86_ARG1, true);
}
-void X86Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rCX); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rCX, opt_flags);
- // If lock is unheld, try to grab it quickly with compare and exchange
- // TODO: copy and clear hash state?
- NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value());
- NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT);
- NewLIR2(kX86Xor32RR, rAX, rAX);
- NewLIR3(kX86LockCmpxchgMR, rCX, mirror::Object::MonitorOffset().Int32Value(), rDX);
- LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
- // If lock is held, go the expensive route - artLockObjectFromCode(self, obj);
- CallRuntimeHelperReg(QUICK_ENTRYPOINT_OFFSET(pLockObject), rCX, true);
- branch->target = NewLIR0(kPseudoTargetLabel);
-}
-
-void X86Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rAX); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rAX, opt_flags);
- // If lock is held by the current thread, clear it to quickly release it
- // TODO: clear hash state?
- NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value());
- NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT);
- NewLIR3(kX86Mov32RM, rCX, rAX, mirror::Object::MonitorOffset().Int32Value());
- OpRegReg(kOpSub, rCX, rDX);
- LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
- NewLIR3(kX86Mov32MR, rAX, mirror::Object::MonitorOffset().Int32Value(), rCX);
- LIR* branch2 = NewLIR1(kX86Jmp8, 0);
- branch->target = NewLIR0(kPseudoTargetLabel);
- // Otherwise, go the expensive route - UnlockObjectFromCode(obj);
- CallRuntimeHelperReg(QUICK_ENTRYPOINT_OFFSET(pUnlockObject), rAX, true);
- branch2->target = NewLIR0(kPseudoTargetLabel);
-}
-
void X86Mir2Lir::GenMoveException(RegLocation rl_dest) {
int ex_offset = Thread::ExceptionOffset().Int32Value();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 478654d..1d6509e 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -52,7 +52,6 @@
int AllocTypedTempPair(bool fp_hint, int reg_class);
int S2d(int low_reg, int high_reg);
int TargetReg(SpecialTargetRegister reg);
- RegisterInfo* GetRegInfo(int reg);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -72,9 +71,12 @@
void CompilerInitializeRegAlloc();
// Required for target - miscellaneous.
- AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void AssembleLIR();
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ AssemblerStatus AssembleInstructions(CodeOffset start_addr);
void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
- void SetupTargetResourceMasks(LIR* lir);
+ void SetupTargetResourceMasks(LIR* lir, uint64_t flags);
const char* GetTargetInstFmt(int opcode);
const char* GetTargetInstName(int opcode);
std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
@@ -86,14 +88,12 @@
// Required for target - Dalvik-level generators.
void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2);
- void GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale);
void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark);
void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
- RegLocation rl_src1, RegLocation rl_shift);
+ RegLocation rl_src1, RegLocation rl_shift);
void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -107,6 +107,8 @@
bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
bool GenInlinedSqrt(CallInfo* info);
+ bool GenInlinedPeek(CallInfo* info, OpSize size);
+ bool GenInlinedPoke(CallInfo* info, OpSize size);
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -119,20 +121,18 @@
void GenDivZeroCheck(int reg_lo, int reg_hi);
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
- void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFillArrayData(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);
void GenMemBarrier(MemBarrierKind barrier_kind);
- void GenMonitorEnter(int opt_flags, RegLocation rl_src);
- void GenMonitorExit(int opt_flags, RegLocation rl_src);
void GenMoveException(RegLocation rl_dest);
void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result,
int lit, int first_bit, int second_bit);
void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
- void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
- void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
// Single operation generators.
@@ -172,6 +172,7 @@
private:
void EmitDisp(int base, int disp);
+ void EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg);
void EmitOpReg(const X86EncodingMap* entry, uint8_t reg);
void EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp);
void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index f736b5e..c9d6bfc 100644
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -284,8 +284,8 @@
void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
bool is_double) {
- LIR* taken = &block_label_list_[bb->taken->id];
- LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* taken = &block_label_list_[bb->taken];
+ LIR* not_taken = &block_label_list_[bb->fall_through];
LIR* branch = NULL;
RegLocation rl_src1;
RegLocation rl_src2;
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 14be7dd..499547b 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -166,7 +166,7 @@
}
void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
- LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* taken = &block_label_list_[bb->taken];
RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
FlushAllRegs();
@@ -236,6 +236,43 @@
return true;
}
+bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == kLong) {
+ // Unaligned access is allowed on x86.
+ LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned access is allowed on x86.
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
+bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_src_value = info->args[2]; // [size] value
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ if (size == kLong) {
+ // Unaligned access is allowed on x86.
+ RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
+ StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned access is allowed on x86.
+ RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
+ }
+ return true;
+}
+
void X86Mir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
NewLIR5(kX86Lea32RA, rBase, reg1, reg2, scale, offset);
}
@@ -419,7 +456,7 @@
* Generate array load
*/
void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale) {
+ RegLocation rl_index, RegLocation rl_dest, int scale) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -466,7 +503,7 @@
*
*/
void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -502,59 +539,10 @@
StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_src.low_reg,
rl_src.high_reg, size, INVALID_SREG);
}
-}
-
-/*
- * Generate array store
- *
- */
-void X86Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
- int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
-
- FlushAllRegs(); // Use explicit registers
- LockCallTemps();
-
- int r_value = TargetReg(kArg0); // Register holding value
- int r_array_class = TargetReg(kArg1); // Register holding array's Class
- int r_array = TargetReg(kArg2); // Register holding array
- int r_index = TargetReg(kArg3); // Register holding index into array
-
- LoadValueDirectFixed(rl_array, r_array); // Grab array
- LoadValueDirectFixed(rl_src, r_value); // Grab value
- LoadValueDirectFixed(rl_index, r_index); // Grab index
-
- GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
-
- // Store of null?
- LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
-
- // Get the array's class.
- LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
- r_array_class, true);
- // Redo LoadValues in case they didn't survive the call.
- LoadValueDirectFixed(rl_array, r_array); // Reload array
- LoadValueDirectFixed(rl_index, r_index); // Reload index
- LoadValueDirectFixed(rl_src, r_value); // Reload value
- r_array_class = INVALID_REG;
-
- // Branch here if value to be stored == null
- LIR* target = NewLIR0(kPseudoTargetLabel);
- null_value_check->target = target;
-
- // make an extra temp available for card mark below
- FreeTemp(TargetReg(kArg1));
- if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
- /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
- GenRegMemCheck(kCondUge, r_index, r_array, len_offset, kThrowArrayBounds);
- }
- StoreBaseIndexedDisp(r_array, r_index, scale,
- data_offset, r_value, INVALID_REG, kWord, INVALID_SREG);
- FreeTemp(r_index);
- if (!mir_graph_->IsConstantNullRef(rl_src)) {
- MarkGCCard(r_value, r_array);
+ if (card_mark) {
+ // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
+ FreeTemp(rl_index.low_reg);
+ MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 26accab..878fa76 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -85,6 +85,8 @@
case kRet0: res = rX86_RET0; break;
case kRet1: res = rX86_RET1; break;
case kInvokeTgt: res = rX86_INVOKE_TGT; break;
+ case kHiddenArg: res = rAX; break;
+ case kHiddenFpArg: res = fr0; break;
case kCount: res = rX86_COUNT; break;
}
return res;
@@ -132,37 +134,36 @@
return 0ULL;
}
-void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir) {
+void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) {
DCHECK_EQ(cu_->instruction_set, kX86);
+ DCHECK(!lir->flags.use_def_invalid);
// X86-specific resource map setup here.
- uint64_t flags = X86Mir2Lir::EncodingMap[lir->opcode].flags;
-
if (flags & REG_USE_SP) {
- lir->use_mask |= ENCODE_X86_REG_SP;
+ lir->u.m.use_mask |= ENCODE_X86_REG_SP;
}
if (flags & REG_DEF_SP) {
- lir->def_mask |= ENCODE_X86_REG_SP;
+ lir->u.m.def_mask |= ENCODE_X86_REG_SP;
}
if (flags & REG_DEFA) {
- SetupRegMask(&lir->def_mask, rAX);
+ SetupRegMask(&lir->u.m.def_mask, rAX);
}
if (flags & REG_DEFD) {
- SetupRegMask(&lir->def_mask, rDX);
+ SetupRegMask(&lir->u.m.def_mask, rDX);
}
if (flags & REG_USEA) {
- SetupRegMask(&lir->use_mask, rAX);
+ SetupRegMask(&lir->u.m.use_mask, rAX);
}
if (flags & REG_USEC) {
- SetupRegMask(&lir->use_mask, rCX);
+ SetupRegMask(&lir->u.m.use_mask, rCX);
}
if (flags & REG_USED) {
- SetupRegMask(&lir->use_mask, rDX);
+ SetupRegMask(&lir->u.m.use_mask, rDX);
}
}
@@ -224,7 +225,7 @@
buf += StringPrintf("%d", operand);
break;
case 'p': {
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(operand);
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(operand));
buf += StringPrintf("0x%08x", tab_rec->offset);
break;
}
@@ -239,7 +240,7 @@
break;
case 't':
buf += StringPrintf("0x%08x (L%p)",
- reinterpret_cast<uint32_t>(base_addr)
+ reinterpret_cast<uintptr_t>(base_addr)
+ lir->offset + operand, lir->target);
break;
default:
@@ -275,8 +276,8 @@
}
/* Memory bits */
if (x86LIR && (mask & ENCODE_DALVIK_REG)) {
- sprintf(buf + strlen(buf), "dr%d%s", x86LIR->alias_info & 0xffff,
- (x86LIR->alias_info & 0x80000000) ? "(+1)" : "");
+ sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(x86LIR->flags.alias_info),
+ (DECODE_ALIAS_INFO_WIDE(x86LIR->flags.alias_info)) ? "(+1)" : "");
}
if (mask & ENCODE_LITERAL) {
strcat(buf, "lit ");
@@ -375,11 +376,6 @@
return res;
}
-X86Mir2Lir::RegisterInfo* X86Mir2Lir::GetRegInfo(int reg) {
- return X86_FPREG(reg) ? ®_pool_->FPRegs[reg & X86_FP_REG_MASK]
- : ®_pool_->core_regs[reg];
-}
-
/* To be used when explicitly managing register use */
void X86Mir2Lir::LockCallTemps() {
LockTemp(rX86_ARG0);
@@ -530,14 +526,17 @@
}
uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return X86Mir2Lir::EncodingMap[opcode].flags;
}
const char* X86Mir2Lir::GetTargetInstName(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return X86Mir2Lir::EncodingMap[opcode].name;
}
const char* X86Mir2Lir::GetTargetInstFmt(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return X86Mir2Lir::EncodingMap[opcode].fmt;
}
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index c519bfe..6ec7ebb 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -117,6 +117,7 @@
switch (op) {
case kOpNeg: opcode = kX86Neg32R; break;
case kOpNot: opcode = kX86Not32R; break;
+ case kOpRev: opcode = kX86Bswap32R; break;
case kOpBlx: opcode = kX86CallR; break;
default:
LOG(FATAL) << "Bad case in OpReg " << op;
@@ -161,6 +162,13 @@
case kOpNeg:
OpRegCopy(r_dest_src1, r_src2);
return OpReg(kOpNeg, r_dest_src1);
+ case kOpRev:
+ OpRegCopy(r_dest_src1, r_src2);
+ return OpReg(kOpRev, r_dest_src1);
+ case kOpRevsh:
+ OpRegCopy(r_dest_src1, r_src2);
+ OpReg(kOpRev, r_dest_src1);
+ return OpRegImm(kOpAsr, r_dest_src1, 16);
// X86 binary opcodes
case kOpSub: opcode = kX86Sub32RR; break;
case kOpSbc: opcode = kX86Sbb32RR; break;
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 643a3d5..3518131 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -243,7 +243,7 @@
// - lir operands - 0: base, 1: disp, 2: immediate
// AI - Array Immediate - opcode [base + index * scale + disp], #immediate
// - lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate
- // TI - Thread Register - opcode fs:[disp], imm - where fs: is equal to Thread::Current()
+ // TI - Thread Immediate - opcode fs:[disp], imm - where fs: is equal to Thread::Current()
// - lir operands - 0: disp, 1: imm
#define BinaryOpCode(opcode) \
opcode ## 8MR, opcode ## 8AR, opcode ## 8TR, \
@@ -313,6 +313,7 @@
UnaryOpcode(kX86Imul, DaR, DaM, DaA),
UnaryOpcode(kX86Divmod, DaR, DaM, DaA),
UnaryOpcode(kX86Idivmod, DaR, DaM, DaA),
+ kX86Bswap32R,
#undef UnaryOpcode
#define Binary0fOpCode(opcode) \
opcode ## RR, opcode ## RM, opcode ## RA
@@ -381,6 +382,7 @@
kData, // Special case for raw data.
kNop, // Special case for variable length nop.
kNullary, // Opcode that takes no arguments.
+ kRegOpcode, // Shorter form of R instruction kind (opcode+rd)
kReg, kMem, kArray, // R, M and A instruction kinds.
kMemReg, kArrayReg, kThreadReg, // MR, AR and TR instruction kinds.
kRegReg, kRegMem, kRegArray, kRegThread, // RR, RM, RA and RT instruction kinds.
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index cd1602f..0d8bd07 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -22,7 +22,7 @@
namespace art {
void MIRGraph::ClearAllVisitedFlags() {
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
bb->visited = false;
}
@@ -38,18 +38,18 @@
}
BasicBlock* MIRGraph::NextUnvisitedSuccessor(BasicBlock* bb) {
- BasicBlock* res = NeedsVisit(bb->fall_through);
+ BasicBlock* res = NeedsVisit(GetBasicBlock(bb->fall_through));
if (res == NULL) {
- res = NeedsVisit(bb->taken);
+ res = NeedsVisit(GetBasicBlock(bb->taken));
if (res == NULL) {
- if (bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ if (bb->successor_block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
while (true) {
SuccessorBlockInfo *sbi = iterator.Next();
if (sbi == NULL) {
break;
}
- res = NeedsVisit(sbi->block);
+ res = NeedsVisit(GetBasicBlock(sbi->block));
if (res != NULL) {
break;
}
@@ -63,7 +63,9 @@
void MIRGraph::MarkPreOrder(BasicBlock* block) {
block->visited = true;
/* Enqueue the pre_order block id */
- dfs_order_->Insert(block->id);
+ if (block->id != NullBasicBlockId) {
+ dfs_order_->Insert(block->id);
+ }
}
void MIRGraph::RecordDFSOrders(BasicBlock* block) {
@@ -79,7 +81,9 @@
continue;
}
curr->dfs_id = dfs_post_order_->Size();
- dfs_post_order_->Insert(curr->id);
+ if (curr->id != NullBasicBlockId) {
+ dfs_post_order_->Insert(curr->id);
+ }
succ.pop_back();
}
}
@@ -88,7 +92,8 @@
void MIRGraph::ComputeDFSOrders() {
/* Initialize or reset the DFS pre_order list */
if (dfs_order_ == NULL) {
- dfs_order_ = new (arena_) GrowableArray<int>(arena_, GetNumBlocks(), kGrowableArrayDfsOrder);
+ dfs_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
+ kGrowableArrayDfsOrder);
} else {
/* Just reset the used length on the counter */
dfs_order_->Reset();
@@ -96,7 +101,8 @@
/* Initialize or reset the DFS post_order list */
if (dfs_post_order_ == NULL) {
- dfs_post_order_ = new (arena_) GrowableArray<int>(arena_, GetNumBlocks(), kGrowableArrayDfsPostOrder);
+ dfs_post_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
+ kGrowableArrayDfsPostOrder);
} else {
/* Just reset the used length on the counter */
dfs_post_order_->Reset();
@@ -145,11 +151,11 @@
def_block_matrix_[i] =
new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix);
}
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
FindLocalLiveIn(bb);
}
- AllNodesIterator iter2(this, false /* not iterative */);
+ AllNodesIterator iter2(this);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
FillDefBlockMatrix(bb);
}
@@ -169,7 +175,7 @@
if (dom_post_order_traversal_ == NULL) {
// First time - create the array.
dom_post_order_traversal_ =
- new (arena_) GrowableArray<int>(arena_, num_reachable_blocks_,
+ new (arena_) GrowableArray<BasicBlockId>(arena_, num_reachable_blocks_,
kGrowableArrayDomPostOrderTraversal);
} else {
dom_post_order_traversal_->Reset();
@@ -177,9 +183,9 @@
ClearAllVisitedFlags();
std::vector<std::pair<BasicBlock*, ArenaBitVector::Iterator*> > work_stack;
bb->visited = true;
- work_stack.push_back(std::make_pair(bb, new (arena_) ArenaBitVector::Iterator(bb->i_dominated)));
+ work_stack.push_back(std::make_pair(bb, bb->i_dominated->GetIterator()));
while (!work_stack.empty()) {
- std::pair<BasicBlock*, ArenaBitVector::Iterator*> curr = work_stack.back();
+ const std::pair<BasicBlock*, ArenaBitVector::Iterator*>& curr = work_stack.back();
BasicBlock* curr_bb = curr.first;
ArenaBitVector::Iterator* curr_idom_iter = curr.second;
int bb_idx = curr_idom_iter->Next();
@@ -190,14 +196,17 @@
BasicBlock* new_bb = GetBasicBlock(bb_idx);
new_bb->visited = true;
work_stack.push_back(
- std::make_pair(new_bb, new (arena_) ArenaBitVector::Iterator(new_bb->i_dominated)));
+ std::make_pair(new_bb, new_bb->i_dominated->GetIterator()));
} else {
// no successor/next
- dom_post_order_traversal_->Insert(curr_bb->id);
+ if (curr_bb->id != NullBasicBlockId) {
+ dom_post_order_traversal_->Insert(curr_bb->id);
+ }
work_stack.pop_back();
/* hacky loop detection */
- if (curr_bb->taken && curr_bb->dominators->IsBitSet(curr_bb->taken->id)) {
+ if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) {
+ curr_bb->nesting_depth++;
attributes_ |= METHOD_HAS_LOOP;
}
}
@@ -210,7 +219,7 @@
* TODO - evaluate whether phi will ever need to be inserted into exit
* blocks.
*/
- if (succ_bb->i_dom != dom_bb &&
+ if (succ_bb->i_dom != dom_bb->id &&
succ_bb->block_type == kDalvikByteCode &&
succ_bb->hidden == false) {
dom_bb->dom_frontier->SetBit(succ_bb->id);
@@ -220,20 +229,20 @@
/* Worker function to compute the dominance frontier */
bool MIRGraph::ComputeDominanceFrontier(BasicBlock* bb) {
/* Calculate DF_local */
- if (bb->taken) {
- CheckForDominanceFrontier(bb, bb->taken);
+ if (bb->taken != NullBasicBlockId) {
+ CheckForDominanceFrontier(bb, GetBasicBlock(bb->taken));
}
- if (bb->fall_through) {
- CheckForDominanceFrontier(bb, bb->fall_through);
+ if (bb->fall_through != NullBasicBlockId) {
+ CheckForDominanceFrontier(bb, GetBasicBlock(bb->fall_through));
}
- if (bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ 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 = successor_block_info->block;
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
CheckForDominanceFrontier(bb, succ_bb);
}
}
@@ -306,17 +315,17 @@
/* Worker function to compute each block's immediate dominator */
bool MIRGraph::ComputeblockIDom(BasicBlock* bb) {
/* Special-case entry block */
- if (bb == GetEntryBlock()) {
+ if ((bb->id == NullBasicBlockId) || (bb == GetEntryBlock())) {
return false;
}
/* Iterate through the predecessors */
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
/* Find the first processed predecessor */
int idom = -1;
while (true) {
- BasicBlock* pred_bb = iter.Next();
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
CHECK(pred_bb != NULL);
if (i_dom_list_[pred_bb->dfs_id] != NOTVISITED) {
idom = pred_bb->dfs_id;
@@ -326,7 +335,7 @@
/* Scan the rest of the predecessors */
while (true) {
- BasicBlock* pred_bb = iter.Next();
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) {
break;
}
@@ -352,7 +361,7 @@
if (bb == GetEntryBlock()) {
bb->dominators->ClearAllBits();
} else {
- bb->dominators->Copy(bb->i_dom->dominators);
+ bb->dominators->Copy(GetBasicBlock(bb->i_dom)->dominators);
}
bb->dominators->SetBit(bb->id);
return false;
@@ -364,7 +373,7 @@
DCHECK_NE(idom_dfs_idx, NOTVISITED);
int i_dom_idx = dfs_post_order_->Get(idom_dfs_idx);
BasicBlock* i_dom = GetBasicBlock(i_dom_idx);
- bb->i_dom = i_dom;
+ bb->i_dom = i_dom->id;
/* Add bb to the i_dominated set of the immediate dominator block */
i_dom->i_dominated->SetBit(bb->id);
}
@@ -377,7 +386,7 @@
int num_total_blocks = GetBasicBlockListCount();
/* Initialize domination-related data structures */
- ReachableNodesIterator iter(this, false /* not iterative */);
+ PreOrderDfsIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
InitializeDominationInfo(bb);
}
@@ -396,7 +405,7 @@
i_dom_list_[GetEntryBlock()->dfs_id] = GetEntryBlock()->dfs_id;
/* Compute the immediate dominators */
- ReversePostOrderDfsIterator iter2(this, true /* iterative */);
+ RepeatingReversePostOrderDfsIterator iter2(this);
bool change = false;
for (BasicBlock* bb = iter2.Next(false); bb != NULL; bb = iter2.Next(change)) {
change = ComputeblockIDom(bb);
@@ -412,21 +421,21 @@
} else {
temp_block_v_->ClearAllBits();
}
- GetEntryBlock()->i_dom = NULL;
+ GetEntryBlock()->i_dom = 0;
- ReachableNodesIterator iter3(this, false /* not iterative */);
+ PreOrderDfsIterator iter3(this);
for (BasicBlock* bb = iter3.Next(); bb != NULL; bb = iter3.Next()) {
SetDominators(bb);
}
- ReversePostOrderDfsIterator iter4(this, false /* not iterative */);
+ ReversePostOrderDfsIterator iter4(this);
for (BasicBlock* bb = iter4.Next(); bb != NULL; bb = iter4.Next()) {
ComputeBlockDominators(bb);
}
// Compute the dominance frontier for each block.
ComputeDomPostOrderTraversal(GetEntryBlock());
- PostOrderDOMIterator iter5(this, false /* not iterative */);
+ PostOrderDOMIterator iter5(this);
for (BasicBlock* bb = iter5.Next(); bb != NULL; bb = iter5.Next()) {
ComputeDominanceFrontier(bb);
}
@@ -463,20 +472,22 @@
return false;
}
temp_dalvik_register_v->Copy(bb->data_flow_info->live_in_v);
- if (bb->taken && bb->taken->data_flow_info)
- ComputeSuccLineIn(temp_dalvik_register_v, bb->taken->data_flow_info->live_in_v,
+ BasicBlock* bb_taken = GetBasicBlock(bb->taken);
+ BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through);
+ if (bb_taken && bb_taken->data_flow_info)
+ ComputeSuccLineIn(temp_dalvik_register_v, bb_taken->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
- if (bb->fall_through && bb->fall_through->data_flow_info)
- ComputeSuccLineIn(temp_dalvik_register_v, bb->fall_through->data_flow_info->live_in_v,
+ if (bb_fall_through && bb_fall_through->data_flow_info)
+ 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.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ 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 = successor_block_info->block;
+ 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,
bb->data_flow_info->def_v);
@@ -503,7 +514,7 @@
temp_dalvik_register_v_ =
new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapRegisterV);
- PostOrderDfsIterator iter(this, true /* iterative */);
+ RepeatingPostOrderDfsIterator iter(this);
bool change = false;
for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
change = ComputeBlockLiveIns(bb);
@@ -579,50 +590,37 @@
* predecessor blocks
*/
bool MIRGraph::InsertPhiNodeOperands(BasicBlock* bb) {
- MIR *mir;
- std::vector<int> uses;
- std::vector<int> incoming_arc;
-
/* Phi nodes are at the beginning of each block */
- for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
if (mir->dalvikInsn.opcode != static_cast<Instruction::Code>(kMirOpPhi))
return true;
int ssa_reg = mir->ssa_rep->defs[0];
DCHECK_GE(ssa_reg, 0); // Shouldn't see compiler temps here
int v_reg = SRegToVReg(ssa_reg);
- uses.clear();
- incoming_arc.clear();
-
/* Iterate through the predecessors */
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
+ size_t num_uses = bb->predecessors->Size();
+ mir->ssa_rep->num_uses = num_uses;
+ int* uses = static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->uses = uses;
+ mir->ssa_rep->fp_use =
+ static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo));
+ BasicBlockId* incoming =
+ static_cast<BasicBlockId*>(arena_->Alloc(sizeof(BasicBlockId) * num_uses,
+ ArenaAllocator::kAllocDFInfo));
+ mir->meta.phi_incoming = incoming;
+ int idx = 0;
while (true) {
- BasicBlock* pred_bb = iter.Next();
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) {
break;
}
int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map[v_reg];
- uses.push_back(ssa_reg);
- incoming_arc.push_back(pred_bb->id);
- }
-
- /* Count the number of SSA registers for a Dalvik register */
- int num_uses = uses.size();
- mir->ssa_rep->num_uses = num_uses;
- mir->ssa_rep->uses =
- static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo));
- mir->ssa_rep->fp_use =
- static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo));
- int* incoming =
- static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo));
- // TODO: Ugly, rework (but don't burden each MIR/LIR for Phi-only needs)
- mir->dalvikInsn.vB = reinterpret_cast<uintptr_t>(incoming);
-
- /* Set the uses array for the phi node */
- int *use_ptr = mir->ssa_rep->uses;
- for (int i = 0; i < num_uses; i++) {
- *use_ptr++ = uses[i];
- *incoming++ = incoming_arc[i];
+ uses[idx] = ssa_reg;
+ incoming[idx] = pred_bb->id;
+ idx++;
}
}
@@ -644,24 +642,24 @@
static_cast<int*>(arena_->Alloc(map_size, ArenaAllocator::kAllocDalvikToSSAMap));
memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
- if (block->fall_through) {
- DoDFSPreOrderSSARename(block->fall_through);
+ if (block->fall_through != NullBasicBlockId) {
+ DoDFSPreOrderSSARename(GetBasicBlock(block->fall_through));
/* Restore SSA map snapshot */
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
}
- if (block->taken) {
- DoDFSPreOrderSSARename(block->taken);
+ if (block->taken != NullBasicBlockId) {
+ DoDFSPreOrderSSARename(GetBasicBlock(block->taken));
/* Restore SSA map snapshot */
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
}
- if (block->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(block->successor_block_list.blocks);
+ 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;
}
- BasicBlock* succ_bb = successor_block_info->block;
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
DoDFSPreOrderSSARename(succ_bb);
/* Restore SSA map snapshot */
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
@@ -700,7 +698,7 @@
new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapTempSSARegisterV);
/* Insert phi-operands with latest SSA names from predecessor blocks */
- ReachableNodesIterator iter2(this, false /* not iterative */);
+ PreOrderDfsIterator iter2(this);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
InsertPhiNodeOperands(bb);
}
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index 07f37bb..32fac0b 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -29,6 +29,16 @@
return change;
}
+bool MIRGraph::SetFp(int index) {
+ bool change = false;
+ if (!reg_location_[index].fp) {
+ reg_location_[index].fp = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetCore(int index, bool is_core) {
bool change = false;
if (is_core && !reg_location_[index].defined) {
@@ -39,6 +49,16 @@
return change;
}
+bool MIRGraph::SetCore(int index) {
+ bool change = false;
+ if (!reg_location_[index].defined) {
+ reg_location_[index].core = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetRef(int index, bool is_ref) {
bool change = false;
if (is_ref && !reg_location_[index].defined) {
@@ -49,6 +69,16 @@
return change;
}
+bool MIRGraph::SetRef(int index) {
+ bool change = false;
+ if (!reg_location_[index].defined) {
+ reg_location_[index].ref = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetWide(int index, bool is_wide) {
bool change = false;
if (is_wide && !reg_location_[index].wide) {
@@ -58,6 +88,15 @@
return change;
}
+bool MIRGraph::SetWide(int index) {
+ bool change = false;
+ if (!reg_location_[index].wide) {
+ reg_location_[index].wide = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetHigh(int index, bool is_high) {
bool change = false;
if (is_high && !reg_location_[index].high_word) {
@@ -67,6 +106,16 @@
return change;
}
+bool MIRGraph::SetHigh(int index) {
+ bool change = false;
+ if (!reg_location_[index].high_word) {
+ reg_location_[index].high_word = true;
+ change = true;
+ }
+ return change;
+}
+
+
/*
* Infer types and sizes. We don't need to track change on sizes,
* as it doesn't propagate. We're guaranteed at least one pass through
@@ -84,21 +133,23 @@
SSARepresentation *ssa_rep = mir->ssa_rep;
if (ssa_rep) {
int attrs = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+ const int* uses = ssa_rep->uses;
+ const int* defs = ssa_rep->defs;
// Handle defs
if (attrs & DF_DA) {
if (attrs & DF_CORE_A) {
- changed |= SetCore(ssa_rep->defs[0], true);
+ changed |= SetCore(defs[0]);
}
if (attrs & DF_REF_A) {
- changed |= SetRef(ssa_rep->defs[0], true);
+ changed |= SetRef(defs[0]);
}
if (attrs & DF_A_WIDE) {
- reg_location_[ssa_rep->defs[0]].wide = true;
- reg_location_[ssa_rep->defs[1]].wide = true;
- reg_location_[ssa_rep->defs[1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->defs[0])+1,
- SRegToVReg(ssa_rep->defs[1]));
+ reg_location_[defs[0]].wide = true;
+ reg_location_[defs[1]].wide = true;
+ reg_location_[defs[1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(defs[0])+1,
+ SRegToVReg(defs[1]));
}
}
@@ -106,17 +157,17 @@
int next = 0;
if (attrs & DF_UA) {
if (attrs & DF_CORE_A) {
- changed |= SetCore(ssa_rep->uses[next], true);
+ changed |= SetCore(uses[next]);
}
if (attrs & DF_REF_A) {
- changed |= SetRef(ssa_rep->uses[next], true);
+ changed |= SetRef(uses[next]);
}
if (attrs & DF_A_WIDE) {
- reg_location_[ssa_rep->uses[next]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
- SRegToVReg(ssa_rep->uses[next + 1]));
+ reg_location_[uses[next]].wide = true;
+ reg_location_[uses[next + 1]].wide = true;
+ reg_location_[uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[next])+1,
+ SRegToVReg(uses[next + 1]));
next += 2;
} else {
next++;
@@ -124,17 +175,17 @@
}
if (attrs & DF_UB) {
if (attrs & DF_CORE_B) {
- changed |= SetCore(ssa_rep->uses[next], true);
+ changed |= SetCore(uses[next]);
}
if (attrs & DF_REF_B) {
- changed |= SetRef(ssa_rep->uses[next], true);
+ changed |= SetRef(uses[next]);
}
if (attrs & DF_B_WIDE) {
- reg_location_[ssa_rep->uses[next]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
- SRegToVReg(ssa_rep->uses[next + 1]));
+ reg_location_[uses[next]].wide = true;
+ reg_location_[uses[next + 1]].wide = true;
+ reg_location_[uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[next])+1,
+ SRegToVReg(uses[next + 1]));
next += 2;
} else {
next++;
@@ -142,17 +193,17 @@
}
if (attrs & DF_UC) {
if (attrs & DF_CORE_C) {
- changed |= SetCore(ssa_rep->uses[next], true);
+ changed |= SetCore(uses[next]);
}
if (attrs & DF_REF_C) {
- changed |= SetRef(ssa_rep->uses[next], true);
+ changed |= SetRef(uses[next]);
}
if (attrs & DF_C_WIDE) {
- reg_location_[ssa_rep->uses[next]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
- SRegToVReg(ssa_rep->uses[next + 1]));
+ reg_location_[uses[next]].wide = true;
+ reg_location_[uses[next + 1]].wide = true;
+ reg_location_[uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[next])+1,
+ SRegToVReg(uses[next + 1]));
}
}
@@ -162,27 +213,27 @@
(mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) {
switch (cu_->shorty[0]) {
case 'I':
- changed |= SetCore(ssa_rep->uses[0], true);
+ changed |= SetCore(uses[0]);
break;
case 'J':
- changed |= SetCore(ssa_rep->uses[0], true);
- changed |= SetCore(ssa_rep->uses[1], true);
- reg_location_[ssa_rep->uses[0]].wide = true;
- reg_location_[ssa_rep->uses[1]].wide = true;
- reg_location_[ssa_rep->uses[1]].high_word = true;
+ changed |= SetCore(uses[0]);
+ changed |= SetCore(uses[1]);
+ reg_location_[uses[0]].wide = true;
+ reg_location_[uses[1]].wide = true;
+ reg_location_[uses[1]].high_word = true;
break;
case 'F':
- changed |= SetFp(ssa_rep->uses[0], true);
+ changed |= SetFp(uses[0]);
break;
case 'D':
- changed |= SetFp(ssa_rep->uses[0], true);
- changed |= SetFp(ssa_rep->uses[1], true);
- reg_location_[ssa_rep->uses[0]].wide = true;
- reg_location_[ssa_rep->uses[1]].wide = true;
- reg_location_[ssa_rep->uses[1]].high_word = true;
+ changed |= SetFp(uses[0]);
+ changed |= SetFp(uses[1]);
+ reg_location_[uses[0]].wide = true;
+ reg_location_[uses[1]].wide = true;
+ reg_location_[uses[1]].high_word = true;
break;
case 'L':
- changed |= SetRef(ssa_rep->uses[0], true);
+ changed |= SetRef(uses[0]);
break;
default: break;
}
@@ -206,10 +257,10 @@
SSARepresentation* tgt_rep = move_result_mir->ssa_rep;
DCHECK(tgt_rep != NULL);
tgt_rep->fp_def[0] = true;
- changed |= SetFp(tgt_rep->defs[0], true);
+ changed |= SetFp(tgt_rep->defs[0]);
if (shorty[0] == 'D') {
tgt_rep->fp_def[1] = true;
- changed |= SetFp(tgt_rep->defs[1], true);
+ changed |= SetFp(tgt_rep->defs[1]);
}
}
}
@@ -217,8 +268,8 @@
// If this is a non-static invoke, mark implicit "this"
if (((mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC) &&
(mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC_RANGE))) {
- reg_location_[ssa_rep->uses[next]].defined = true;
- reg_location_[ssa_rep->uses[next]].ref = true;
+ reg_location_[uses[next]].defined = true;
+ reg_location_[uses[next]].ref = true;
next++;
}
uint32_t cpos = 1;
@@ -229,28 +280,28 @@
case 'D':
ssa_rep->fp_use[i] = true;
ssa_rep->fp_use[i+1] = true;
- reg_location_[ssa_rep->uses[i]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1]));
+ reg_location_[uses[i]].wide = true;
+ reg_location_[uses[i+1]].wide = true;
+ reg_location_[uses[i+1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1]));
i++;
break;
case 'J':
- reg_location_[ssa_rep->uses[i]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1]));
- changed |= SetCore(ssa_rep->uses[i], true);
+ reg_location_[uses[i]].wide = true;
+ reg_location_[uses[i+1]].wide = true;
+ reg_location_[uses[i+1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1]));
+ changed |= SetCore(uses[i]);
i++;
break;
case 'F':
ssa_rep->fp_use[i] = true;
break;
case 'L':
- changed |= SetRef(ssa_rep->uses[i], true);
+ changed |= SetRef(uses[i]);
break;
default:
- changed |= SetCore(ssa_rep->uses[i], true);
+ changed |= SetCore(uses[i]);
break;
}
i++;
@@ -260,11 +311,11 @@
for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) {
if (ssa_rep->fp_use[i])
- changed |= SetFp(ssa_rep->uses[i], true);
+ changed |= SetFp(uses[i]);
}
for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) {
if (ssa_rep->fp_def[i])
- changed |= SetFp(ssa_rep->defs[i], true);
+ changed |= SetFp(defs[i]);
}
// Special-case handling for moves & Phi
if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) {
@@ -276,14 +327,14 @@
*/
bool is_phi = (static_cast<int>(mir->dalvikInsn.opcode) ==
kMirOpPhi);
- RegLocation rl_temp = reg_location_[ssa_rep->defs[0]];
+ RegLocation rl_temp = reg_location_[defs[0]];
bool defined_fp = rl_temp.defined && rl_temp.fp;
bool defined_core = rl_temp.defined && rl_temp.core;
bool defined_ref = rl_temp.defined && rl_temp.ref;
bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0);
bool is_high = is_phi && rl_temp.wide && rl_temp.high_word;
for (int i = 0; i < ssa_rep->num_uses; i++) {
- rl_temp = reg_location_[ssa_rep->uses[i]];
+ rl_temp = reg_location_[uses[i]];
defined_fp |= rl_temp.defined && rl_temp.fp;
defined_core |= rl_temp.defined && rl_temp.core;
defined_ref |= rl_temp.defined && rl_temp.ref;
@@ -303,26 +354,26 @@
<< " has both fp and core/ref uses for same def.";
cu_->disable_opt |= (1 << kPromoteRegs);
}
- changed |= SetFp(ssa_rep->defs[0], defined_fp);
- changed |= SetCore(ssa_rep->defs[0], defined_core);
- changed |= SetRef(ssa_rep->defs[0], defined_ref);
- changed |= SetWide(ssa_rep->defs[0], is_wide);
- changed |= SetHigh(ssa_rep->defs[0], is_high);
+ changed |= SetFp(defs[0], defined_fp);
+ changed |= SetCore(defs[0], defined_core);
+ changed |= SetRef(defs[0], defined_ref);
+ changed |= SetWide(defs[0], is_wide);
+ changed |= SetHigh(defs[0], is_high);
if (attrs & DF_A_WIDE) {
- changed |= SetWide(ssa_rep->defs[1], true);
- changed |= SetHigh(ssa_rep->defs[1], true);
+ changed |= SetWide(defs[1]);
+ changed |= SetHigh(defs[1]);
}
for (int i = 0; i < ssa_rep->num_uses; i++) {
- changed |= SetFp(ssa_rep->uses[i], defined_fp);
- changed |= SetCore(ssa_rep->uses[i], defined_core);
- changed |= SetRef(ssa_rep->uses[i], defined_ref);
- changed |= SetWide(ssa_rep->uses[i], is_wide);
- changed |= SetHigh(ssa_rep->uses[i], is_high);
+ changed |= SetFp(uses[i], defined_fp);
+ changed |= SetCore(uses[i], defined_core);
+ changed |= SetRef(uses[i], defined_ref);
+ changed |= SetWide(uses[i], is_wide);
+ changed |= SetHigh(uses[i], is_high);
}
if (attrs & DF_A_WIDE) {
DCHECK_EQ(ssa_rep->num_uses, 2);
- changed |= SetWide(ssa_rep->uses[1], true);
- changed |= SetHigh(ssa_rep->uses[1], true);
+ changed |= SetWide(uses[1]);
+ changed |= SetHigh(uses[1]);
}
}
}
@@ -444,7 +495,7 @@
}
/* Do type & size inference pass */
- PreOrderDfsIterator iter(this, true /* iterative */);
+ RepeatingPreOrderDfsIterator iter(this);
bool change = false;
for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
change = InferTypeAndSize(bb);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index a77c820..9cc94e8 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -64,7 +64,7 @@
if (x == 0 && y == 0) {
return;
}
- VLOG(compiler) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases";
+ LOG(INFO) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases";
}
class AOTCompilationStats {
@@ -336,10 +336,12 @@
std::string const& filename);
CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes, size_t thread_count,
bool dump_stats)
: compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
+ instruction_set_features_(instruction_set_features),
freezing_constructor_lock_("freezing constructor lock"),
compiled_classes_lock_("compiled classes lock"),
compiled_methods_lock_("compiled method lock"),
@@ -355,7 +357,11 @@
jni_compiler_(NULL),
compiler_enable_auto_elf_loading_(NULL),
compiler_get_method_code_addr_(NULL),
- support_boot_image_fixup_(true) {
+ support_boot_image_fixup_(instruction_set == kThumb2),
+ dedupe_code_("dedupe code"),
+ dedupe_mapping_table_("dedupe mapping table"),
+ dedupe_vmap_table_("dedupe vmap table"),
+ dedupe_gc_map_("dedupe gc map") {
CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key");
@@ -465,6 +471,11 @@
return CreateTrampoline(instruction_set_, kJniAbi, JNI_ENTRYPOINT_OFFSET(pDlsymLookup));
}
+const std::vector<uint8_t>* CompilerDriver::CreatePortableImtConflictTrampoline() const {
+ return CreateTrampoline(instruction_set_, kPortableAbi,
+ PORTABLE_ENTRYPOINT_OFFSET(pPortableImtConflictTrampoline));
+}
+
const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
return CreateTrampoline(instruction_set_, kPortableAbi,
PORTABLE_ENTRYPOINT_OFFSET(pPortableResolutionTrampoline));
@@ -475,6 +486,11 @@
PORTABLE_ENTRYPOINT_OFFSET(pPortableToInterpreterBridge));
}
+const std::vector<uint8_t>* CompilerDriver::CreateQuickImtConflictTrampoline() const {
+ return CreateTrampoline(instruction_set_, kQuickAbi,
+ QUICK_ENTRYPOINT_OFFSET(pQuickImtConflictTrampoline));
+}
+
const std::vector<uint8_t>* CompilerDriver::CreateQuickResolutionTrampoline() const {
return CreateTrampoline(instruction_set_, kQuickAbi,
QUICK_ENTRYPOINT_OFFSET(pQuickResolutionTrampoline));
@@ -597,7 +613,6 @@
}
bool CompilerDriver::IsImageClass(const char* descriptor) const {
- DCHECK(descriptor != NULL);
if (!IsImage()) {
return true;
} else {
@@ -776,7 +791,8 @@
bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file,
uint32_t type_idx) {
- if (IsImage() && IsImageClass(dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)))) {
+ if (IsImage() &&
+ IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) {
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
@@ -912,9 +928,9 @@
}
static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjectAccess& soa,
- const DexCompilationUnit* mUnit,
- uint32_t method_idx,
- InvokeType type)
+ const DexCompilationUnit* mUnit,
+ uint32_t method_idx,
+ InvokeType type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile());
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader());
@@ -923,11 +939,11 @@
}
bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, bool& is_volatile, bool is_put) {
+ bool is_put, int* field_offset, bool* is_volatile) {
ScopedObjectAccess soa(Thread::Current());
// Conservative defaults.
- field_offset = -1;
- is_volatile = true;
+ *field_offset = -1;
+ *is_volatile = true;
// Try to resolve field and ignore if an Incompatible Class Change Error (ie is static).
mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx);
if (resolved_field != NULL && !resolved_field->IsStatic()) {
@@ -954,8 +970,8 @@
bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() &&
fields_class != referrer_class;
if (access_ok && !is_write_to_final_from_wrong_class) {
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedInstanceField();
return true; // Fast path.
}
@@ -970,15 +986,14 @@
}
bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, int& ssb_index,
- bool& is_referrers_class, bool& is_volatile,
- bool is_put) {
+ bool is_put, int* field_offset, int* ssb_index,
+ bool* is_referrers_class, bool* is_volatile) {
ScopedObjectAccess soa(Thread::Current());
// Conservative defaults.
- field_offset = -1;
- ssb_index = -1;
- is_referrers_class = false;
- is_volatile = true;
+ *field_offset = -1;
+ *ssb_index = -1;
+ *is_referrers_class = false;
+ *is_volatile = true;
// Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static).
mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx);
if (resolved_field != NULL && resolved_field->IsStatic()) {
@@ -988,9 +1003,9 @@
if (referrer_class != NULL) {
mirror::Class* fields_class = resolved_field->GetDeclaringClass();
if (fields_class == referrer_class) {
- is_referrers_class = true; // implies no worrying about class initialization
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *is_referrers_class = true; // implies no worrying about class initialization
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedLocalStaticField();
return true; // fast path
} else {
@@ -1021,9 +1036,9 @@
if (fields_class->GetDexCache() == dex_cache) {
// common case where the dex cache of both the referrer and the field are the same,
// no need to search the dex file
- ssb_index = fields_class->GetDexTypeIndex();
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *ssb_index = fields_class->GetDexTypeIndex();
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedStaticField();
return true;
}
@@ -1036,9 +1051,9 @@
mUnit->GetDexFile()->FindTypeId(mUnit->GetDexFile()->GetIndexForStringId(*string_id));
if (type_id != NULL) {
// medium path, needs check of static storage base being initialized
- ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id);
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id);
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedStaticField();
return true;
}
@@ -1055,81 +1070,138 @@
return false; // Incomplete knowledge needs slow path.
}
-void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type,
+void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
+ bool no_guarantee_of_dex_cache_entry,
mirror::Class* referrer_class,
mirror::ArtMethod* method,
- uintptr_t& direct_code,
- uintptr_t& direct_method,
- bool update_stats) {
+ bool update_stats,
+ MethodReference* target_method,
+ uintptr_t* direct_code,
+ uintptr_t* direct_method) {
// For direct and static methods compute possible direct_code and direct_method values, ie
// an address for the Method* being invoked and an address of the code for that Method*.
// For interface calls compute a value for direct_method that is the interface method being
// invoked, so this can be passed to the out-of-line runtime support code.
- direct_code = 0;
- direct_method = 0;
+ *direct_code = 0;
+ *direct_method = 0;
+ bool use_dex_cache = false;
+ bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1;
if (compiler_backend_ == kPortable) {
if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
+ use_dex_cache = true;
} else {
- if (sharp_type != kStatic && sharp_type != kDirect && sharp_type != kInterface) {
+ if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
+ // TODO: support patching on all architectures.
+ use_dex_cache = compiling_boot && !support_boot_image_fixup_;
}
- bool method_code_in_boot = method->GetDeclaringClass()->GetClassLoader() == NULL;
- if (!method_code_in_boot) {
- return;
- }
- bool has_clinit_trampoline = method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
- if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
- // Ensure we run the clinit trampoline unless we are invoking a static method in the same class.
- return;
- }
- if (update_stats) {
- if (sharp_type != kInterface) { // Interfaces always go via a trampoline.
- stats_->DirectCallsToBoot(type);
- }
- stats_->DirectMethodsToBoot(type);
- }
- bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1;
- if (compiling_boot) {
- if (support_boot_image_fixup_) {
- MethodHelper mh(method);
- if (IsImageClass(mh.GetDeclaringClassDescriptor())) {
- // We can only branch directly to Methods that are resolved in the DexCache.
- // Otherwise we won't invoke the resolution trampoline.
- direct_method = -1;
- direct_code = -1;
+ bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
+ if (!use_dex_cache) {
+ if (!method_code_in_boot) {
+ use_dex_cache = true;
+ } else {
+ bool has_clinit_trampoline =
+ method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
+ if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
+ // Ensure we run the clinit trampoline unless we are invoking a static method in the same
+ // class.
+ use_dex_cache = true;
}
}
- } else {
- if (Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace()) {
- direct_method = reinterpret_cast<uintptr_t>(method);
+ }
+ if (update_stats && method_code_in_boot) {
+ stats_->DirectCallsToBoot(*type);
+ stats_->DirectMethodsToBoot(*type);
+ }
+ if (!use_dex_cache && compiling_boot) {
+ MethodHelper mh(method);
+ if (!IsImageClass(mh.GetDeclaringClassDescriptor())) {
+ // We can only branch directly to Methods that are resolved in the DexCache.
+ // Otherwise we won't invoke the resolution trampoline.
+ use_dex_cache = true;
}
- direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
+ }
+ // The method is defined not within this dex file. We need a dex cache slot within the current
+ // dex file or direct pointers.
+ bool must_use_direct_pointers = false;
+ if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) {
+ target_method->dex_method_index = method->GetDexMethodIndex();
+ } else {
+ // TODO: support patching from one dex file to another in the boot image.
+ use_dex_cache = use_dex_cache || compiling_boot;
+ if (no_guarantee_of_dex_cache_entry) {
+ // See if the method is also declared in this dex cache.
+ uint32_t dex_method_idx = MethodHelper(method).FindDexMethodIndexInOtherDexFile(
+ *referrer_class->GetDexCache()->GetDexFile());
+ if (dex_method_idx != DexFile::kDexNoIndex) {
+ target_method->dex_method_index = dex_method_idx;
+ } else {
+ must_use_direct_pointers = true;
+ }
+ }
+ }
+ if (use_dex_cache) {
+ if (must_use_direct_pointers) {
+ // Fail. Test above showed the only safe dispatch was via the dex cache, however, the direct
+ // pointers are required as the dex cache lacks an appropriate entry.
+ VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
+ } else {
+ *type = sharp_type;
+ }
+ } else {
+ if (compiling_boot) {
+ *type = sharp_type;
+ *direct_method = -1;
+ *direct_code = -1;
+ } else {
+ bool method_in_image =
+ Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
+ if (method_in_image) {
+ CHECK(!method->IsAbstract());
+ *type = sharp_type;
+ *direct_method = reinterpret_cast<uintptr_t>(method);
+ *direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ target_method->dex_method_index = method->GetDexMethodIndex();
+ } else if (!must_use_direct_pointers) {
+ // Set the code and rely on the dex cache for the method.
+ *type = sharp_type;
+ *direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
+ } else {
+ // Direct pointers were required but none were available.
+ VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
+ }
+ }
}
}
bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
- InvokeType& invoke_type,
- MethodReference& target_method,
- int& vtable_idx,
- uintptr_t& direct_code, uintptr_t& direct_method,
- bool update_stats) {
+ bool update_stats, bool enable_devirtualization,
+ InvokeType* invoke_type, MethodReference* target_method,
+ int* vtable_idx, uintptr_t* direct_code,
+ uintptr_t* direct_method) {
ScopedObjectAccess soa(Thread::Current());
- vtable_idx = -1;
- direct_code = 0;
- direct_method = 0;
+ *vtable_idx = -1;
+ *direct_code = 0;
+ *direct_method = 0;
mirror::ArtMethod* resolved_method =
- ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method.dex_method_index,
- invoke_type);
+ ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method->dex_method_index,
+ *invoke_type);
if (resolved_method != NULL) {
+ if (*invoke_type == kVirtual || *invoke_type == kSuper) {
+ *vtable_idx = resolved_method->GetMethodIndex();
+ } else if (*invoke_type == kInterface) {
+ *vtable_idx = resolved_method->GetDexMethodIndex();
+ }
// Don't try to fast-path if we don't understand the caller's class or this appears to be an
// Incompatible Class Change Error.
mirror::Class* referrer_class =
ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(),
mUnit);
- bool icce = resolved_method->CheckIncompatibleClassChange(invoke_type);
+ bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type);
if (referrer_class != NULL && !icce) {
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
if (!referrer_class->CanAccess(methods_class) ||
@@ -1140,42 +1212,43 @@
// method public. Resort to the dex file to determine the correct class for the access
// check.
uint16_t class_idx =
- target_method.dex_file->GetMethodId(target_method.dex_method_index).class_idx_;
- methods_class = mUnit->GetClassLinker()->ResolveType(*target_method.dex_file,
+ target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_;
+ methods_class = mUnit->GetClassLinker()->ResolveType(*target_method->dex_file,
class_idx, referrer_class);
}
if (referrer_class->CanAccess(methods_class) &&
referrer_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags())) {
- const bool kEnableFinalBasedSharpening = true;
+ const bool enableFinalBasedSharpening = enable_devirtualization;
// Sharpen a virtual call into a direct call when the target is known not to have been
// overridden (ie is final).
bool can_sharpen_virtual_based_on_type =
- (invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal());
+ (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal());
// For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of
// the super class.
- bool can_sharpen_super_based_on_type = (invoke_type == kSuper) &&
+ bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) &&
(referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() &&
(methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method);
- if (kEnableFinalBasedSharpening && (can_sharpen_virtual_based_on_type ||
+ if (enableFinalBasedSharpening && (can_sharpen_virtual_based_on_type ||
can_sharpen_super_based_on_type)) {
// Sharpen a virtual call into a direct call. The method_idx is into referrer's
// dex cache, check that this resolved method is where we expect it.
- CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method.dex_method_index) ==
+ CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) ==
resolved_method) << PrettyMethod(resolved_method);
- if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
- stats_->VirtualMadeDirect(invoke_type);
+ InvokeType orig_invoke_type = *invoke_type;
+ GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method,
+ update_stats, target_method, direct_code, direct_method);
+ if (update_stats && (*invoke_type == kDirect)) {
+ stats_->ResolvedMethod(orig_invoke_type);
+ stats_->VirtualMadeDirect(orig_invoke_type);
}
- GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, resolved_method,
- direct_code, direct_method, update_stats);
- invoke_type = kDirect;
+ DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method);
return true;
}
- const bool kEnableVerifierBasedSharpening = true;
- if (kEnableVerifierBasedSharpening && (invoke_type == kVirtual ||
- invoke_type == kInterface)) {
+ const bool enableVerifierBasedSharpening = enable_devirtualization;
+ if (enableVerifierBasedSharpening && (*invoke_type == kVirtual ||
+ *invoke_type == kInterface)) {
// Did the verifier record a more precise invoke target based on its type information?
const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex());
const MethodReference* devirt_map_target =
@@ -1192,88 +1265,27 @@
kVirtual);
CHECK(called_method != NULL);
CHECK(!called_method->IsAbstract());
- GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, called_method,
- direct_code, direct_method, update_stats);
- bool compiler_needs_dex_cache =
- (GetCompilerBackend() == kPortable) ||
- (GetCompilerBackend() == kQuick && instruction_set_ != kThumb2) ||
- (direct_code == 0) || (direct_code == static_cast<unsigned int>(-1)) ||
- (direct_method == 0) || (direct_method == static_cast<unsigned int>(-1));
- if ((devirt_map_target->dex_file != target_method.dex_file) &&
- compiler_needs_dex_cache) {
- // We need to use the dex cache to find either the method or code, and the dex file
- // containing the method isn't the one expected for the target method. Try to find
- // the method within the expected target dex file.
- // TODO: the -1 could be handled as direct code if the patching new the target dex
- // file.
- // TODO: quick only supports direct pointers with Thumb2.
- // TODO: the following should be factored into a common helper routine to find
- // one dex file's method within another.
- const DexFile* dexfile = target_method.dex_file;
- const DexFile* cm_dexfile =
- called_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
- const DexFile::MethodId& cm_method_id =
- cm_dexfile->GetMethodId(called_method->GetDexMethodIndex());
- const char* cm_descriptor = cm_dexfile->StringByTypeIdx(cm_method_id.class_idx_);
- const DexFile::StringId* descriptor = dexfile->FindStringId(cm_descriptor);
- if (descriptor != NULL) {
- const DexFile::TypeId* type_id =
- dexfile->FindTypeId(dexfile->GetIndexForStringId(*descriptor));
- if (type_id != NULL) {
- const char* cm_name = cm_dexfile->GetMethodName(cm_method_id);
- const DexFile::StringId* name = dexfile->FindStringId(cm_name);
- if (name != NULL) {
- uint16_t return_type_idx;
- std::vector<uint16_t> param_type_idxs;
- bool success = dexfile->CreateTypeList(&return_type_idx, ¶m_type_idxs,
- cm_dexfile->GetMethodSignature(cm_method_id));
- if (success) {
- const DexFile::ProtoId* sig =
- dexfile->FindProtoId(return_type_idx, param_type_idxs);
- if (sig != NULL) {
- const DexFile::MethodId* method_id = dexfile->FindMethodId(*type_id,
- *name, *sig);
- if (method_id != NULL) {
- if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
- stats_->VirtualMadeDirect(invoke_type);
- stats_->PreciseTypeDevirtualization();
- }
- target_method.dex_method_index = dexfile->GetIndexForMethodId(*method_id);
- invoke_type = kDirect;
- return true;
- }
- }
- }
- }
- }
- }
- // TODO: the stats for direct code and method are off as we failed to find the direct
- // method in the referring method's dex cache/file.
- } else {
- if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
- stats_->VirtualMadeDirect(invoke_type);
- stats_->PreciseTypeDevirtualization();
- }
- target_method = *devirt_map_target;
- invoke_type = kDirect;
- return true;
+ InvokeType orig_invoke_type = *invoke_type;
+ GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method,
+ update_stats, target_method, direct_code, direct_method);
+ if (update_stats && (*invoke_type == kDirect)) {
+ stats_->ResolvedMethod(orig_invoke_type);
+ stats_->VirtualMadeDirect(orig_invoke_type);
+ stats_->PreciseTypeDevirtualization();
}
+ DCHECK_NE(*invoke_type, kSuper);
+ return true;
}
}
- if (invoke_type == kSuper) {
+ if (*invoke_type == kSuper) {
// Unsharpened super calls are suspicious so go slow-path.
} else {
// Sharpening failed so generate a regular resolved method dispatch.
if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
+ stats_->ResolvedMethod(*invoke_type);
}
- if (invoke_type == kVirtual || invoke_type == kSuper) {
- vtable_idx = resolved_method->GetMethodIndex();
- }
- GetCodeAndMethodForDirectCall(invoke_type, invoke_type, referrer_class, resolved_method,
- direct_code, direct_method, update_stats);
+ GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method,
+ update_stats, target_method, direct_code, direct_method);
return true;
}
}
@@ -1284,7 +1296,7 @@
soa.Self()->ClearException();
}
if (update_stats) {
- stats_->UnresolvedMethod(invoke_type);
+ stats_->UnresolvedMethod(*invoke_type);
}
return false; // Incomplete knowledge needs slow path.
}
@@ -1432,22 +1444,47 @@
DISALLOW_COPY_AND_ASSIGN(ParallelCompilationManager);
};
-// Return true if the class should be skipped during compilation. We
-// never skip classes in the boot class loader. However, if we have a
-// non-boot class loader and we can resolve the class in the boot
-// class loader, we do skip the class. This happens if an app bundles
-// classes found in the boot classpath. Since at runtime we will
-// select the class from the boot classpath, do not attempt to resolve
-// or compile it now.
+// Return true if the class should be skipped during compilation.
+//
+// The first case where we skip is for redundant class definitions in
+// the boot classpath. We skip all but the first definition in that case.
+//
+// The second case where we skip is when an app bundles classes found
+// in the boot classpath. Since at runtime we will select the class from
+// the boot classpath, we ignore the one from the app.
static bool SkipClass(ClassLinker* class_linker, jobject class_loader, const DexFile& dex_file,
const DexFile::ClassDef& class_def) {
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
if (class_loader == NULL) {
+ DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, class_linker->GetBootClassPath());
+ CHECK(pair.second != NULL);
+ if (pair.first != &dex_file) {
+ LOG(WARNING) << "Skipping class " << descriptor << " from " << dex_file.GetLocation()
+ << " previously found in " << pair.first->GetLocation();
+ return true;
+ }
return false;
}
- const char* descriptor = dex_file.GetClassDescriptor(class_def);
return class_linker->IsInBootClassPath(descriptor);
}
+// A fast version of SkipClass above if the class pointer is available
+// that avoids the expensive FindInClassPath search.
+static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(klass != NULL);
+ const DexFile& original_dex_file = *klass->GetDexCache()->GetDexFile();
+ if (&dex_file != &original_dex_file) {
+ if (class_loader == NULL) {
+ LOG(WARNING) << "Skipping class " << PrettyDescriptor(klass) << " from "
+ << dex_file.GetLocation() << " previously found in "
+ << original_dex_file.GetLocation();
+ }
+ return true;
+ }
+ return false;
+}
+
static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager,
size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
@@ -1569,8 +1606,8 @@
CHECK(soa.Self()->IsExceptionPending());
mirror::Throwable* exception = soa.Self()->GetException(NULL);
VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
- if (strcmp(ClassHelper(exception->GetClass()).GetDescriptor(),
- "Ljava/lang/OutOfMemoryError;") == 0) {
+ if (strcmp("Ljava/lang/OutOfMemoryError;",
+ ClassHelper(exception->GetClass()).GetDescriptor()) == 0) {
// There's little point continuing compilation if the heap is exhausted.
LOG(FATAL) << "Out of memory during type resolution for compilation";
}
@@ -1589,13 +1626,11 @@
if (IsImage()) {
// For images we resolve all types, such as array, whereas for applications just those with
// classdefs are resolved by ResolveClassFieldsAndMethods.
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
+ timings.NewSplit("Resolve Types");
context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
}
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str()));
+ timings.NewSplit("Resolve MethodsAndFields");
context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_);
}
@@ -1612,11 +1647,13 @@
LOCKS_EXCLUDED(Locks::mutator_lock_) {
ATRACE_CALL();
ScopedObjectAccess soa(Thread::Current());
- const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index);
- const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def);
- mirror::Class* klass =
- manager->GetClassLinker()->FindClass(descriptor,
- soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()));
+ const DexFile& dex_file = *manager->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ClassLinker* class_linker = manager->GetClassLinker();
+ jobject jclass_loader = manager->GetClassLoader();
+ mirror::Class* klass = class_linker->FindClass(descriptor,
+ soa.Decode<mirror::ClassLoader*>(jclass_loader));
if (klass == NULL) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
@@ -1626,23 +1663,18 @@
* This is to ensure the class is structurally sound for compilation. An unsound class
* will be rejected by the verifier and later skipped during compilation in the compiler.
*/
- mirror::DexCache* dex_cache = manager->GetClassLinker()->FindDexCache(*manager->GetDexFile());
+ mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file);
std::string error_msg;
- const DexFile* dex_file = manager->GetDexFile();
- const DexFile::ClassDef* class_def = &dex_file->GetClassDef(class_def_index);
- if (verifier::MethodVerifier::VerifyClass(dex_file,
- dex_cache,
- soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()),
- class_def, true, &error_msg) ==
+ if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache,
+ soa.Decode<mirror::ClassLoader*>(jclass_loader),
+ &class_def, true, &error_msg) ==
verifier::MethodVerifier::kHardFailure) {
- const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index);
- LOG(ERROR) << "Verification failed on class "
- << PrettyDescriptor(manager->GetDexFile()->GetClassDescriptor(class_def))
+ LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
<< " because: " << error_msg;
}
- } else {
+ } else if (!SkipClass(jclass_loader, dex_file, klass)) {
CHECK(klass->IsResolved()) << PrettyClass(klass);
- manager->GetClassLinker()->VerifyClass(klass);
+ class_linker->VerifyClass(klass);
if (klass->IsErroneous()) {
// ClassLinker::VerifyClass throws, which isn't useful in the compiler.
@@ -1658,8 +1690,7 @@
void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str()));
+ timings.NewSplit("Verify Dex File");
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_);
@@ -1746,6 +1777,7 @@
"Landroid/opengl/GLUtils;", // Calls android.opengl.GLUtils.nativeClassInit.
"Landroid/os/Build;", // Calls -..-> android.os.SystemProperties.native_get.
"Landroid/os/Build$VERSION;", // Requires Build.
+ "Landroid/os/Bundle;", // Calls android.os.Parcel.obtain -..> Parcel.nativeCreate.
"Landroid/os/Debug;", // Requires android.os.Environment.
"Landroid/os/Environment;", // Calls System.getenv.
"Landroid/os/FileUtils;", // Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl.
@@ -2085,12 +2117,17 @@
static void InitializeClass(const ParallelCompilationManager* manager, size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
ATRACE_CALL();
- const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index);
+ jobject jclass_loader = manager->GetClassLoader();
+ const DexFile& dex_file = *manager->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
+ const DexFile::TypeId& class_type_id = dex_file.GetTypeId(class_def.class_idx_);
+ const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_);
+
ScopedObjectAccess soa(Thread::Current());
- mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
- const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def);
+ mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader);
- if (klass != NULL) {
+
+ if (klass != NULL && !SkipClass(jclass_loader, dex_file, klass)) {
// Only try to initialize classes that were successfully verified.
if (klass->IsVerified()) {
// Attempt to initialize the class but bail if we either need to initialize the super-class
@@ -2119,7 +2156,7 @@
bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;");
if (!is_black_listed) {
for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
- if (StringPiece(descriptor) == class_initializer_black_list[i]) {
+ if (strcmp(descriptor, class_initializer_black_list[i]) == 0) {
is_black_listed = true;
break;
}
@@ -2127,7 +2164,7 @@
}
if (!is_black_listed) {
VLOG(compiler) << "Initializing: " << descriptor;
- if (StringPiece(descriptor) == "Ljava/lang/Void;") {
+ if (strcmp("Ljava/lang/Void;", descriptor) == 0) {
// Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
ObjectLock lock(soa.Self(), klass);
mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
@@ -2158,8 +2195,7 @@
void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str()));
+ timings.NewSplit("InitializeNoClinit");
#ifndef NDEBUG
// Sanity check blacklist descriptors.
if (IsImage()) {
@@ -2266,8 +2302,7 @@
void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Compile " + dex_file.GetLocation()).c_str()));
+ timings.NewSplit("Compile Dex File");
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
&dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 3852acf..9321f06 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -91,6 +91,7 @@
// can assume will be in the image, with NULL implying all available
// classes.
explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes,
size_t thread_count, bool dump_stats);
@@ -104,10 +105,14 @@
void CompileOne(const mirror::ArtMethod* method, base::TimingLogger& timings)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- InstructionSet GetInstructionSet() const {
+ const InstructionSet& GetInstructionSet() const {
return instruction_set_;
}
+ const InstructionSetFeatures& GetInstructionSetFeatures() const {
+ return instruction_set_features_;
+ }
+
CompilerBackend GetCompilerBackend() const {
return compiler_backend_;
}
@@ -130,10 +135,14 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateJniDlsymLookup() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreatePortableImtConflictTrampoline() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreatePortableToInterpreterBridge() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const
@@ -170,22 +179,23 @@
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fast path instance field access? Computes field's offset and volatility.
- bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, bool& is_volatile, bool is_put)
+ bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
+ int* field_offset, bool* is_volatile)
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fastpath static field access? Computes field's offset, volatility and whether the
// field is within the referrer (which can avoid checking class initialization).
- bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, int& ssb_index,
- bool& is_referrers_class, bool& is_volatile, bool is_put)
+ bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
+ int* field_offset, int* ssb_index,
+ bool* is_referrers_class, bool* is_volatile)
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fastpath a interface, super class or virtual method call? Computes method's vtable
// index.
bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
- InvokeType& type, MethodReference& target_method, int& vtable_idx,
- uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats)
+ bool update_stats, bool enable_devirtualization,
+ InvokeType* type, MethodReference* target_method, int* vtable_idx,
+ uintptr_t* direct_code, uintptr_t* direct_method)
LOCKS_EXCLUDED(Locks::mutator_lock_);
bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc);
@@ -320,11 +330,13 @@
private:
// Compute constant code and method pointers when possible
- void GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type,
+ void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
+ bool no_guarantee_of_dex_cache_entry,
mirror::Class* referrer_class,
mirror::ArtMethod* method,
- uintptr_t& direct_code, uintptr_t& direct_method,
- bool update_stats)
+ bool update_stats,
+ MethodReference* target_method,
+ uintptr_t* direct_code, uintptr_t* direct_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
@@ -379,7 +391,8 @@
CompilerBackend compiler_backend_;
- InstructionSet instruction_set_;
+ const InstructionSet instruction_set_;
+ const InstructionSetFeatures instruction_set_features_;
// All class references that require
mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -458,27 +471,40 @@
class DedupeHashFunc {
public:
size_t operator()(const std::vector<uint8_t>& array) const {
- // Take a random sample of bytes.
+ // For small arrays compute a hash using every byte.
static const size_t kSmallArrayThreshold = 16;
- static const size_t kRandomHashCount = 16;
- size_t hash = 0;
- if (array.size() < kSmallArrayThreshold) {
- for (auto c : array) {
- hash = hash * 54 + c;
+ size_t hash = 0x811c9dc5;
+ if (array.size() <= kSmallArrayThreshold) {
+ for (uint8_t b : array) {
+ hash = (hash * 16777619) ^ b;
}
} else {
- for (size_t i = 0; i < kRandomHashCount; ++i) {
+ // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
+ // instruction field for quick generated code on ARM) and then select a number of other
+ // values at random.
+ static const size_t kRandomHashCount = 16;
+ for (size_t i = 0; i < 2; ++i) {
+ uint8_t b = array[i + 6];
+ hash = (hash * 16777619) ^ b;
+ }
+ for (size_t i = 2; i < kRandomHashCount; ++i) {
size_t r = i * 1103515245 + 12345;
- hash = hash * 54 + array[r % array.size()];
+ uint8_t b = array[r % array.size()];
+ hash = (hash * 16777619) ^ b;
}
}
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
return hash;
}
};
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_code_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_mapping_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_vmap_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_gc_map_;
+ 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_;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc
index 359c493..c571288 100644
--- a/compiler/elf_fixup.cc
+++ b/compiler/elf_fixup.cc
@@ -27,8 +27,9 @@
static const bool DEBUG_FIXUP = false;
bool ElfFixup::Fixup(File* file, uintptr_t oat_data_begin) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
- CHECK(elf_file.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
+ CHECK(elf_file.get() != nullptr) << error_msg;
// Lookup "oatdata" symbol address.
::llvm::ELF::Elf32_Addr oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
diff --git a/compiler/elf_stripper.cc b/compiler/elf_stripper.cc
index 7fc662c..7ee8d3c 100644
--- a/compiler/elf_stripper.cc
+++ b/compiler/elf_stripper.cc
@@ -27,9 +27,11 @@
namespace art {
-bool ElfStripper::Strip(File* file) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
- CHECK(elf_file.get() != NULL);
+bool ElfStripper::Strip(File* file, std::string* error_msg) {
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
+ if (elf_file.get() == nullptr) {
+ return false;
+ }
// ELF files produced by MCLinker look roughly like this
//
@@ -120,7 +122,8 @@
elf_file->GetHeader().e_shoff = shoff;
int result = ftruncate(file->Fd(), offset);
if (result != 0) {
- PLOG(ERROR) << "Failed to truncate while stripping ELF file: " << file->GetPath();
+ *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
+ file->GetPath().c_str(), strerror(errno));
return false;
}
return true;
diff --git a/compiler/elf_stripper.h b/compiler/elf_stripper.h
index 6015b30..f1a1d46 100644
--- a/compiler/elf_stripper.h
+++ b/compiler/elf_stripper.h
@@ -17,6 +17,8 @@
#ifndef ART_COMPILER_ELF_STRIPPER_H_
#define ART_COMPILER_ELF_STRIPPER_H_
+#include <string>
+
#include "base/macros.h"
#include "os.h"
@@ -26,7 +28,7 @@
public:
// Strip an ELF file of unneeded debugging information.
// Returns true on success, false on failure.
- static bool Strip(File* file);
+ static bool Strip(File* file, std::string* error_msg);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper);
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
index d3c13dd..0bfe4a4 100644
--- a/compiler/elf_writer.cc
+++ b/compiler/elf_writer.cc
@@ -47,8 +47,9 @@
void ElfWriter::GetOatElfInformation(File* file,
size_t& oat_loaded_size,
size_t& oat_data_offset) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(file, false, false));
- CHECK(elf_file.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, false, false, &error_msg));
+ CHECK(elf_file.get() != NULL) << error_msg;
oat_loaded_size = elf_file->GetLoadedSize();
CHECK_NE(0U, oat_loaded_size);
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
index e496ace..8e19ef6 100644
--- a/compiler/elf_writer_mclinker.cc
+++ b/compiler/elf_writer_mclinker.cc
@@ -153,8 +153,9 @@
void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) {
// Add an artificial memory input. Based on LinkerTest.
- UniquePtr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath()));
- CHECK(oat_file.get() != NULL) << elf_file_->GetPath();
+ std::string error_msg;
+ UniquePtr<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();
@@ -344,8 +345,9 @@
#if defined(ART_USE_PORTABLE_COMPILER)
void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false));
- CHECK(elf_file.get() != NULL) << elf_file_->GetPath();
+ std::string error_msg;
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg));
+ CHECK(elf_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get());
DexMethodIterator it(dex_files);
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index ffe1f72..eca67a8 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -65,23 +65,26 @@
UniquePtr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
ASSERT_TRUE(file.get() != NULL);
{
- UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false));
- CHECK(ef.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", false);
}
{
- UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false));
- CHECK(ef.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true);
}
{
- UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, true));
- CHECK(ef.get() != NULL);
- ef->Load(false);
+ std::string error_msg;
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, true, &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
+ CHECK(ef->Load(false, &error_msg)) << error_msg;
EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata"));
EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec"));
EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword"));
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 6464a4c..a8b7c88 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -23,6 +23,8 @@
#include "compiler/oat_writer.h"
#include "gc/space/image_space.h"
#include "image.h"
+#include "lock_word.h"
+#include "mirror/object-inl.h"
#include "signal_catcher.h"
#include "UniquePtr.h"
#include "utils.h"
@@ -110,8 +112,11 @@
runtime_.reset();
java_lang_dex_file_ = NULL;
- UniquePtr<const DexFile> dex(DexFile::Open(GetLibCoreDexFileName(), GetLibCoreDexFileName()));
- ASSERT_TRUE(dex.get() != NULL);
+ std::string error_msg;
+ UniquePtr<const DexFile> dex(DexFile::Open(GetLibCoreDexFileName().c_str(),
+ GetLibCoreDexFileName().c_str(),
+ &error_msg));
+ ASSERT_TRUE(dex.get() != nullptr) << error_msg;
// Remove the reservation of the memory for use to load the image.
UnreserveImageSpace();
@@ -158,7 +163,7 @@
// non image classes should be in a space after the image.
EXPECT_GT(reinterpret_cast<byte*>(klass), image_end) << descriptor;
}
- EXPECT_TRUE(Monitor::IsValidLockWord(*klass->GetRawLockWordAddress()));
+ EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord()));
}
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f82c6fb..75be2c9 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -36,6 +36,7 @@
#include "globals.h"
#include "image.h"
#include "intern_table.h"
+#include "lock_word.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/array-inl.h"
@@ -82,12 +83,14 @@
LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
return false;
}
- oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location);
- if (oat_file_ == NULL) {
- LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location;
+ std::string error_msg;
+ oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location, &error_msg);
+ if (oat_file_ == nullptr) {
+ LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
+ << ": " << error_msg;
return false;
}
- class_linker->RegisterOatFile(*oat_file_);
+ CHECK_EQ(class_linker->RegisterOatFile(oat_file_), oat_file_);
interpreter_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetInterpreterToInterpreterBridgeOffset();
@@ -96,11 +99,15 @@
jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset();
+ portable_imt_conflict_trampoline_offset_ =
+ oat_file_->GetOatHeader().GetPortableImtConflictTrampolineOffset();
portable_resolution_trampoline_offset_ =
oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
portable_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset();
+ quick_imt_conflict_trampoline_offset_ =
+ oat_file_->GetOatHeader().GetQuickImtConflictTrampolineOffset();
quick_resolution_trampoline_offset_ =
oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
quick_to_interpreter_bridge_offset_ =
@@ -192,9 +199,10 @@
int prot = PROT_READ | PROT_WRITE;
size_t length = RoundUp(size, kPageSize);
- image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot));
- if (image_.get() == NULL) {
- LOG(ERROR) << "Failed to allocate memory for image file generation";
+ std::string error_msg;
+ image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot, &error_msg));
+ if (UNLIKELY(image_.get() == nullptr)) {
+ LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
return false;
}
return true;
@@ -387,6 +395,8 @@
ObjectArray<Object>::Alloc(self, object_array_class,
ImageHeader::kImageRootsMax));
image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod());
+ image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod());
+ image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt());
image_roots->Set(ImageHeader::kCalleeSaveMethod,
runtime->GetCalleeSaveMethod(Runtime::kSaveAll));
image_roots->Set(ImageHeader::kRefsOnlySaveMethod,
@@ -486,7 +496,35 @@
DCHECK_LT(offset + n, image_writer->image_->Size());
memcpy(dst, src, n);
Object* copy = reinterpret_cast<Object*>(dst);
- copy->SetField32(Object::MonitorOffset(), 0, false); // We may have inflated the lock during compilation.
+ // Write in a hash code of objects which have inflated monitors or a hash code in their monitor
+ // word.
+ LockWord lw(copy->GetLockWord());
+ switch (lw.GetState()) {
+ case LockWord::kFatLocked: {
+ Monitor* monitor = lw.FatLockMonitor();
+ CHECK(monitor != nullptr);
+ CHECK(!monitor->IsLocked());
+ if (monitor->HasHashCode()) {
+ copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode()));
+ } else {
+ copy->SetLockWord(LockWord());
+ }
+ break;
+ }
+ case LockWord::kThinLocked: {
+ LOG(FATAL) << "Thin locked object " << obj << " found during object copy";
+ break;
+ }
+ case LockWord::kUnlocked:
+ break;
+ case LockWord::kHashCode:
+ // Do nothing since we can just keep the same hash code.
+ CHECK_NE(lw.GetHashCode(), 0);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable.";
+ break;
+ }
image_writer->FixupObject(obj, copy);
}
@@ -524,6 +562,12 @@
#else
copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
#endif
+ } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
+#if defined(ART_USE_PORTABLE_COMPILER)
+ copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_imt_conflict_trampoline_offset_));
+#else
+ copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+#endif
} 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
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 0d85f36..0b408e8 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -40,7 +40,8 @@
explicit ImageWriter(const CompilerDriver& compiler_driver)
: compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
- interpreter_to_compiled_code_bridge_offset_(0), portable_resolution_trampoline_offset_(0),
+ interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
+ portable_resolution_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0),
quick_resolution_trampoline_offset_(0) {}
~ImageWriter() {}
@@ -204,8 +205,10 @@
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
+ uint32_t portable_imt_conflict_trampoline_offset_;
uint32_t portable_resolution_trampoline_offset_;
uint32_t portable_to_interpreter_bridge_offset_;
+ uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index a653ab4..667b913 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -152,7 +152,7 @@
std::string reason;
ASSERT_TRUE(
Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode<mirror::ClassLoader*>(class_loader_),
- reason)) << reason;
+ &reason)) << reason;
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
EXPECT_EQ(25, result);
@@ -167,7 +167,7 @@
std::string reason;
ASSERT_TRUE(
Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode<mirror::ClassLoader*>(class_loader_),
- reason)) << reason;
+ &reason)) << reason;
jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
EXPECT_EQ(43, result);
diff --git a/compiler/jni/portable/jni_compiler.cc b/compiler/jni/portable/jni_compiler.cc
index 43408a7..0c14346 100644
--- a/compiler/jni/portable/jni_compiler.cc
+++ b/compiler/jni/portable/jni_compiler.cc
@@ -50,9 +50,9 @@
using ::art::llvm::runtime_support::RuntimeId;
JniCompiler::JniCompiler(LlvmCompilationUnit* cunit,
- CompilerDriver& driver,
+ CompilerDriver* driver,
const DexCompilationUnit* dex_compilation_unit)
- : cunit_(cunit), driver_(&driver), module_(cunit_->GetModule()),
+ : cunit_(cunit), driver_(driver), module_(cunit_->GetModule()),
context_(cunit_->GetLLVMContext()), irb_(*cunit_->GetIRBuilder()),
dex_compilation_unit_(dex_compilation_unit),
func_(NULL), elf_func_idx_(0) {
diff --git a/compiler/jni/portable/jni_compiler.h b/compiler/jni/portable/jni_compiler.h
index d20c63b..ffabfe6 100644
--- a/compiler/jni/portable/jni_compiler.h
+++ b/compiler/jni/portable/jni_compiler.h
@@ -54,7 +54,7 @@
class JniCompiler {
public:
JniCompiler(LlvmCompilationUnit* cunit,
- CompilerDriver& driver,
+ CompilerDriver* driver,
const DexCompilationUnit* dex_compilation_unit);
CompiledMethod* Compile();
@@ -67,7 +67,7 @@
private:
LlvmCompilationUnit* cunit_;
- CompilerDriver* driver_;
+ CompilerDriver* const driver_;
::llvm::Module* module_;
::llvm::LLVMContext* context_;
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 1417fb9..1c9aed8 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -24,7 +24,6 @@
#include "compiled_method.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
-#include "disassembler.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "jni_internal.h"
#include "utils/assembler.h"
@@ -82,10 +81,8 @@
UniquePtr<JniCallingConvention> end_jni_conv(
JniCallingConvention::Create(is_static, is_synchronized, jni_end_shorty, instruction_set));
-
// Assembler that holds generated instructions
UniquePtr<Assembler> jni_asm(Assembler::Create(instruction_set));
- bool should_disassemble = false;
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
@@ -356,9 +353,9 @@
// 15. Process pending exceptions from JNI call or monitor exit.
__ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), 0);
- // 16. Remove activation - no need to restore callee save registers because we didn't clobber
+ // 16. Remove activation - need to restore callee save registers since the GC may have changed
// them.
- __ RemoveFrame(frame_size, std::vector<ManagedRegister>());
+ __ RemoveFrame(frame_size, callee_save_regs);
// 17. Finalize code generation
__ EmitSlowPaths();
@@ -366,10 +363,6 @@
std::vector<uint8_t> managed_code(cs);
MemoryRegion code(&managed_code[0], managed_code.size());
__ FinalizeInstructions(code);
- if (should_disassemble) {
- UniquePtr<Disassembler> disassembler(Disassembler::Create(instruction_set));
- disassembler->Dump(LOG(INFO), &managed_code[0], &managed_code[managed_code.size()]);
- }
return new CompiledMethod(compiler,
instruction_set,
managed_code,
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
index a917cdc..d59afd4 100644
--- a/compiler/llvm/compiler_llvm.cc
+++ b/compiler/llvm/compiler_llvm.cc
@@ -26,6 +26,7 @@
#include "ir_builder.h"
#include "jni/portable/jni_compiler.h"
#include "llvm_compilation_unit.h"
+#include "thread-inl.h"
#include "utils_llvm.h"
#include "verifier/method_verifier.h"
@@ -164,7 +165,7 @@
UniquePtr<LlvmCompilationUnit> cunit(AllocateCompilationUnit());
UniquePtr<JniCompiler> jni_compiler(
- new JniCompiler(cunit.get(), *compiler_driver_, dex_compilation_unit));
+ new JniCompiler(cunit.get(), compiler_driver_, dex_compilation_unit));
return jni_compiler->Compile();
}
diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc
index 4f6fa0a..b206a25 100644
--- a/compiler/llvm/gbc_expander.cc
+++ b/compiler/llvm/gbc_expander.cc
@@ -846,10 +846,10 @@
uintptr_t direct_code = 0;
uintptr_t direct_method = 0;
bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc,
- invoke_type, target_method,
- vtable_idx,
- direct_code, direct_method,
- true);
+ true, true,
+ &invoke_type, &target_method,
+ &vtable_idx,
+ &direct_code, &direct_method);
// Load the method object
llvm::Value* callee_method_object_addr = NULL;
@@ -1630,7 +1630,7 @@
int field_offset;
bool is_volatile;
bool is_fast_path = driver_->ComputeInstanceFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, is_volatile, false);
+ field_idx, dex_compilation_unit_, false, &field_offset, &is_volatile);
if (!is_fast_path) {
llvm::Function* runtime_func;
@@ -1692,7 +1692,7 @@
int field_offset;
bool is_volatile;
bool is_fast_path = driver_->ComputeInstanceFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, is_volatile, true);
+ field_idx, dex_compilation_unit_, true, &field_offset, &is_volatile);
if (!is_fast_path) {
llvm::Function* runtime_func;
@@ -1897,8 +1897,8 @@
bool is_volatile;
bool is_fast_path = driver_->ComputeStaticFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, ssb_index,
- is_referrers_class, is_volatile, false);
+ field_idx, dex_compilation_unit_, false,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
llvm::Value* static_field_value;
@@ -1981,8 +1981,8 @@
bool is_volatile;
bool is_fast_path = driver_->ComputeStaticFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, ssb_index,
- is_referrers_class, is_volatile, true);
+ field_idx, dex_compilation_unit_, true,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
if (!is_fast_path) {
llvm::Function* runtime_func;
diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc
index 139100b..038f5dc 100644
--- a/compiler/llvm/llvm_compilation_unit.cc
+++ b/compiler/llvm/llvm_compilation_unit.cc
@@ -82,7 +82,6 @@
#include "ir_builder.h"
#include "os.h"
#include "runtime_support_builder_arm.h"
-#include "runtime_support_builder_thumb2.h"
#include "runtime_support_builder_x86.h"
#include "utils_llvm.h"
@@ -118,12 +117,10 @@
default:
runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
break;
+ case kThumb2:
case kArm:
runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
break;
- case kThumb2:
- runtime_support_.reset(new RuntimeSupportBuilderThumb2(*context_, *module_, *irb_));
- break;
case kX86:
runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
break;
diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc
index 24e283d..c825fbf 100644
--- a/compiler/llvm/runtime_support_builder.cc
+++ b/compiler/llvm/runtime_support_builder.cc
@@ -164,89 +164,13 @@
/* Monitor */
void RuntimeSupportBuilder::EmitLockObject(::llvm::Value* object) {
- Value* monitor =
- irb_.LoadFromObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAARuntimeInfo);
-
- Value* real_monitor =
- irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
-
- // Is thin lock, unheld and not recursively acquired.
- Value* unheld = irb_.CreateICmpEQ(real_monitor, irb_.getInt32(0));
-
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* bb_fast = BasicBlock::Create(context_, "lock_fast", parent_func);
- BasicBlock* bb_slow = BasicBlock::Create(context_, "lock_slow", parent_func);
- BasicBlock* bb_cont = BasicBlock::Create(context_, "lock_cont", parent_func);
- irb_.CreateCondBr(unheld, bb_fast, bb_slow, kLikely);
-
- irb_.SetInsertPoint(bb_fast);
-
- // Calculate new monitor: new = old | (lock_id << LW_LOCK_OWNER_SHIFT)
- Value* lock_id =
- EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(),
- irb_.getInt32Ty(), kTBAARuntimeInfo);
-
- Value* owner = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT);
- Value* new_monitor = irb_.CreateOr(monitor, owner);
-
- // Atomically update monitor.
- Value* old_monitor =
- irb_.CompareExchangeObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- monitor, new_monitor, kTBAARuntimeInfo);
-
- Value* retry_slow_path = irb_.CreateICmpEQ(old_monitor, monitor);
- irb_.CreateCondBr(retry_slow_path, bb_cont, bb_slow, kLikely);
-
- irb_.SetInsertPoint(bb_slow);
Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_cont);
}
void RuntimeSupportBuilder::EmitUnlockObject(::llvm::Value* object) {
- Value* lock_id =
- EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAARuntimeInfo);
- Value* monitor =
- irb_.LoadFromObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAARuntimeInfo);
-
- Value* my_monitor = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT);
- Value* hash_state = irb_.CreateAnd(monitor, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
- Value* real_monitor = irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
-
- // Is thin lock, held by us and not recursively acquired
- Value* is_fast_path = irb_.CreateICmpEQ(real_monitor, my_monitor);
-
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* bb_fast = BasicBlock::Create(context_, "unlock_fast", parent_func);
- BasicBlock* bb_slow = BasicBlock::Create(context_, "unlock_slow", parent_func);
- BasicBlock* bb_cont = BasicBlock::Create(context_, "unlock_cont", parent_func);
- irb_.CreateCondBr(is_fast_path, bb_fast, bb_slow, kLikely);
-
- irb_.SetInsertPoint(bb_fast);
- // Set all bits to zero (except hash state)
- irb_.StoreToObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- hash_state,
- kTBAARuntimeInfo);
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_slow);
Function* slow_func = GetRuntimeSupportFunction(runtime_support::UnlockObject);
irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_cont);
}
diff --git a/compiler/llvm/runtime_support_builder.h b/compiler/llvm/runtime_support_builder.h
index e92ac0a..898611a 100644
--- a/compiler/llvm/runtime_support_builder.h
+++ b/compiler/llvm/runtime_support_builder.h
@@ -64,8 +64,8 @@
virtual void EmitTestSuspend();
/* Monitor */
- virtual void EmitLockObject(::llvm::Value* object);
- virtual void EmitUnlockObject(::llvm::Value* object);
+ void EmitLockObject(::llvm::Value* object);
+ void EmitUnlockObject(::llvm::Value* object);
/* MarkGCCard */
virtual void EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr);
diff --git a/compiler/llvm/runtime_support_builder_arm.cc b/compiler/llvm/runtime_support_builder_arm.cc
index 569d825..cad4624 100644
--- a/compiler/llvm/runtime_support_builder_arm.cc
+++ b/compiler/llvm/runtime_support_builder_arm.cc
@@ -116,24 +116,5 @@
return old_thread_register;
}
-
-/* Monitor */
-
-void RuntimeSupportBuilderARM::EmitLockObject(Value* object) {
- RuntimeSupportBuilder::EmitLockObject(object);
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*isVarArg=*/false);
- InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true);
- irb_.CreateCall(func);
-}
-
-void RuntimeSupportBuilderARM::EmitUnlockObject(Value* object) {
- RuntimeSupportBuilder::EmitUnlockObject(object);
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*isVarArg=*/false);
- InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true);
- irb_.CreateCall(func);
-}
-
} // namespace llvm
} // namespace art
diff --git a/compiler/llvm/runtime_support_builder_arm.h b/compiler/llvm/runtime_support_builder_arm.h
index 5a353d7..0d01509 100644
--- a/compiler/llvm/runtime_support_builder_arm.h
+++ b/compiler/llvm/runtime_support_builder_arm.h
@@ -34,10 +34,6 @@
virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
TBAASpecialType s_ty);
virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-
- /* Monitor */
- virtual void EmitLockObject(::llvm::Value* object);
- virtual void EmitUnlockObject(::llvm::Value* object);
};
} // namespace llvm
diff --git a/compiler/llvm/runtime_support_builder_thumb2.cc b/compiler/llvm/runtime_support_builder_thumb2.cc
deleted file mode 100644
index eff29c8..0000000
--- a/compiler/llvm/runtime_support_builder_thumb2.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-#include "runtime_support_builder_thumb2.h"
-
-#include "ir_builder.h"
-#include "mirror/object.h"
-#include "monitor.h"
-#include "thread.h"
-#include "utils_llvm.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/InlineAsm.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <inttypes.h>
-#include <vector>
-
-using ::llvm::BasicBlock;
-using ::llvm::Function;
-using ::llvm::FunctionType;
-using ::llvm::InlineAsm;
-using ::llvm::Type;
-using ::llvm::Value;
-
-namespace art {
-namespace llvm {
-
-
-void RuntimeSupportBuilderThumb2::EmitLockObject(Value* object) {
- FunctionType* func_ty = FunctionType::get(/*Result=*/irb_.getInt32Ty(),
- /*Params=*/irb_.getJObjectTy(),
- /*isVarArg=*/false);
- // $0: result
- // $1: object
- // $2: temp
- // $3: temp
- std::string asms;
- StringAppendF(&asms, "add $3, $1, #%" PRId32 "\n", mirror::Object::MonitorOffset().Int32Value());
- StringAppendF(&asms, "ldr $2, [r9, #%" PRId32 "]\n", Thread::ThinLockIdOffset().Int32Value());
- StringAppendF(&asms, "ldrex $0, [$3]\n");
- StringAppendF(&asms, "lsl $2, $2, %d\n", LW_LOCK_OWNER_SHIFT);
- StringAppendF(&asms, "bfi $2, $0, #0, #%d\n", LW_LOCK_OWNER_SHIFT - 1);
- StringAppendF(&asms, "bfc $0, #%d, #%d\n", LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
- StringAppendF(&asms, "cmp $0, #0\n");
- StringAppendF(&asms, "it eq\n");
- StringAppendF(&asms, "strexeq $0, $2, [$3]\n");
-
- InlineAsm* func = InlineAsm::get(func_ty, asms, "=&l,l,~l,~l", true);
-
- Value* retry_slow_path = irb_.CreateCall(func, object);
- retry_slow_path = irb_.CreateICmpNE(retry_slow_path, irb_.getJInt(0));
-
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* basic_block_lock = BasicBlock::Create(context_, "lock", parent_func);
- BasicBlock* basic_block_cont = BasicBlock::Create(context_, "lock_cont", parent_func);
- irb_.CreateCondBr(retry_slow_path, basic_block_lock, basic_block_cont, kUnlikely);
-
- irb_.SetInsertPoint(basic_block_lock);
- Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
- irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
- irb_.CreateBr(basic_block_cont);
-
- irb_.SetInsertPoint(basic_block_cont);
- { // Memory barrier
- FunctionType* asm_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*isVarArg=*/false);
- InlineAsm* func = InlineAsm::get(asm_ty, "dmb sy", "", true);
- irb_.CreateCall(func);
- }
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/llvm/runtime_support_builder_thumb2.h b/compiler/llvm/runtime_support_builder_thumb2.h
deleted file mode 100644
index c47a274..0000000
--- a/compiler/llvm/runtime_support_builder_thumb2.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
-
-#include "runtime_support_builder_arm.h"
-
-namespace art {
-namespace llvm {
-
-class RuntimeSupportBuilderThumb2 : public RuntimeSupportBuilderARM {
- public:
- RuntimeSupportBuilderThumb2(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb)
- : RuntimeSupportBuilderARM(context, module, irb) {}
-
- /* Monitor */
- virtual void EmitLockObject(::llvm::Value* object);
-};
-
-} // namespace llvm
-} // namespace art
-
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index bfba9c0..6213b45 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -28,6 +28,8 @@
class OatTest : public CommonTest {
protected:
+ static const bool kCompile = false; // DISABLED_ due to the time to compile libcore
+
void CheckMethod(mirror::ArtMethod* method,
const OatFile::OatMethod& oat_method,
const DexFile* dex_file)
@@ -40,7 +42,7 @@
EXPECT_TRUE(oat_method.GetCode() == NULL) << PrettyMethod(method) << " "
<< oat_method.GetCode();
#if !defined(ART_USE_PORTABLE_COMPILER)
- EXPECT_EQ(oat_method.GetFrameSizeInBytes(), static_cast<uint32_t>(kStackAlignment));
+ EXPECT_EQ(oat_method.GetFrameSizeInBytes(), kCompile ? kStackAlignment : 0);
EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
#endif
@@ -65,7 +67,6 @@
};
TEST_F(OatTest, WriteRead) {
- const bool compile = false; // DISABLED_ due to the time to compile libcore
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// TODO: make selectable
@@ -75,9 +76,12 @@
CompilerBackend compiler_backend = kQuick;
#endif
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
- compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set, false, NULL, 2, true));
+
+ InstructionSetFeatures insn_features;
+ compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set,
+ insn_features, false, NULL, 2, true));
jobject class_loader = NULL;
- if (compile) {
+ if (kCompile) {
base::TimingLogger timings("OatTest::WriteRead", false, false);
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
}
@@ -96,37 +100,42 @@
tmp.GetFile());
ASSERT_TRUE(success);
- if (compile) { // OatWriter strips the code, regenerate to compare
+ if (kCompile) { // OatWriter strips the code, regenerate to compare
base::TimingLogger timings("CommonTest::WriteRead", false, false);
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
}
- UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false));
- ASSERT_TRUE(oat_file.get() != NULL);
+ std::string error_msg;
+ UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false,
+ &error_msg));
+ ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
- ASSERT_EQ(2U, oat_header.GetDexFileCount()); // core and conscrypt
+ ASSERT_EQ(1U, oat_header.GetDexFileCount()); // core
ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
ASSERT_EQ("lue.art", oat_header.GetImageFileLocation());
const DexFile* dex_file = java_lang_dex_file_;
uint32_t dex_file_checksum = dex_file->GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation(),
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation().c_str(),
&dex_file_checksum);
+ ASSERT_TRUE(oat_dex_file != nullptr);
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
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);
- size_t num_virtual_methods =0;
+ size_t num_virtual_methods = 0;
if (class_data != NULL) {
ClassDataItemIterator it(*dex_file, class_data);
num_virtual_methods = it.NumVirtualMethods();
}
const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ mirror::Class* klass = class_linker->FindClass(descriptor, NULL);
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i));
-
- mirror::Class* klass = class_linker->FindClass(descriptor, NULL);
+ CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor;
+ CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled,
+ oat_class->GetType()) << descriptor;
size_t method_index = 0;
for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) {
@@ -143,17 +152,19 @@
TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
- EXPECT_EQ(64U, sizeof(OatHeader));
+ EXPECT_EQ(76U, sizeof(OatHeader));
EXPECT_EQ(28U, sizeof(OatMethodOffsets));
}
TEST_F(OatTest, OatHeaderIsValid) {
InstructionSet instruction_set = kX86;
+ InstructionSetFeatures instruction_set_features;
std::vector<const DexFile*> dex_files;
uint32_t image_file_location_oat_checksum = 0;
uint32_t image_file_location_oat_begin = 0;
const std::string image_file_location;
OatHeader oat_header(instruction_set,
+ instruction_set_features,
&dex_files,
image_file_location_oat_checksum,
image_file_location_oat_begin,
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f9d6e41..f3bb112 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,6 +18,7 @@
#include <zlib.h>
+#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
@@ -54,8 +55,10 @@
size_interpreter_to_interpreter_bridge_(0),
size_interpreter_to_compiled_code_bridge_(0),
size_jni_dlsym_lookup_(0),
+ size_portable_imt_conflict_trampoline_(0),
size_portable_resolution_trampoline_(0),
size_portable_to_interpreter_bridge_(0),
+ size_quick_imt_conflict_trampoline_(0),
size_quick_resolution_trampoline_(0),
size_quick_to_interpreter_bridge_(0),
size_trampoline_alignment_(0),
@@ -70,7 +73,9 @@
size_oat_dex_file_location_checksum_(0),
size_oat_dex_file_offset_(0),
size_oat_dex_file_methods_offsets_(0),
+ size_oat_class_type_(0),
size_oat_class_status_(0),
+ size_oat_class_method_bitmaps_(0),
size_oat_class_method_offsets_(0) {
size_t offset = InitOatHeader();
offset = InitOatDexFiles(offset);
@@ -93,6 +98,7 @@
size_t OatWriter::InitOatHeader() {
// create the OatHeader
oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
dex_files_,
image_file_location_oat_checksum_,
image_file_location_oat_begin_,
@@ -142,12 +148,48 @@
oat_dex_files_[i]->methods_offsets_[class_def_index] = offset;
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
const byte* class_data = dex_file->GetClassData(class_def);
- uint32_t num_methods = 0;
+ uint32_t num_non_null_compiled_methods = 0;
+ UniquePtr<std::vector<CompiledMethod*> > compiled_methods(new std::vector<CompiledMethod*>());
if (class_data != NULL) { // ie not an empty class, such as a marker interface
ClassDataItemIterator it(*dex_file, class_data);
size_t num_direct_methods = it.NumDirectMethods();
size_t num_virtual_methods = it.NumVirtualMethods();
- num_methods = num_direct_methods + num_virtual_methods;
+ size_t num_methods = num_direct_methods + num_virtual_methods;
+
+ // Fill in the compiled_methods_ array for methods that have a
+ // CompiledMethod. We track the number of non-null entries in
+ // num_non_null_compiled_methods since we only want to allocate
+ // OatMethodOffsets for the compiled methods.
+ compiled_methods->reserve(num_methods);
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+ size_t class_def_method_index = 0;
+ while (it.HasNextDirectMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
+ compiled_methods->push_back(compiled_method);
+ if (compiled_method != NULL) {
+ num_non_null_compiled_methods++;
+ }
+ class_def_method_index++;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
+ compiled_methods->push_back(compiled_method);
+ if (compiled_method != NULL) {
+ num_non_null_compiled_methods++;
+ }
+ class_def_method_index++;
+ it.Next();
+ }
}
ClassReference class_ref(dex_file, class_def_index);
@@ -161,7 +203,8 @@
status = mirror::Class::kStatusNotReady;
}
- OatClass* oat_class = new OatClass(offset, status, num_methods);
+ OatClass* oat_class = new OatClass(offset, compiled_methods.release(),
+ num_non_null_compiled_methods, status);
oat_classes_.push_back(oat_class);
offset += oat_class->SizeOf();
}
@@ -189,8 +232,10 @@
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_, InterpreterToInterpreterBridge);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_, InterpreterToCompiledCodeBridge);
DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
+ DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline);
DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline);
DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge);
@@ -199,8 +244,10 @@
oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
oat_header_->SetJniDlsymLookupOffset(0);
+ oat_header_->SetPortableImtConflictTrampolineOffset(0);
oat_header_->SetPortableResolutionTrampolineOffset(0);
oat_header_->SetPortableToInterpreterBridgeOffset(0);
+ oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
oat_header_->SetQuickToInterpreterBridgeOffset(0);
}
@@ -212,20 +259,20 @@
for (size_t i = 0; i != dex_files_->size(); ++i) {
const DexFile* dex_file = (*dex_files_)[i];
CHECK(dex_file != NULL);
- offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file);
+ offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file);
}
return offset;
}
size_t OatWriter::InitOatCodeDexFile(size_t offset,
- size_t& oat_class_index,
+ size_t* oat_class_index,
const DexFile& dex_file) {
for (size_t class_def_index = 0;
class_def_index < dex_file.NumClassDefs();
- class_def_index++, oat_class_index++) {
+ class_def_index++, (*oat_class_index)++) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- offset = InitOatCodeClassDef(offset, oat_class_index, class_def_index, dex_file, class_def);
- oat_classes_[oat_class_index]->UpdateChecksum(*oat_header_);
+ offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def);
+ oat_classes_[*oat_class_index]->UpdateChecksum(*oat_header_);
}
return offset;
}
@@ -240,7 +287,7 @@
return offset;
}
ClassDataItemIterator it(dex_file, class_data);
- CHECK_EQ(oat_classes_[oat_class_index]->method_offsets_.size(),
+ CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(),
it.NumDirectMethods() + it.NumVirtualMethods());
// Skip fields
while (it.HasNextStaticField()) {
@@ -251,32 +298,35 @@
}
// Process methods
size_t class_def_method_index = 0;
+ size_t method_offsets_index = 0;
while (it.HasNextDirectMethod()) {
bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
- is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(),
- &dex_file);
+ &method_offsets_index, is_native,
+ it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
class_def_method_index++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
- is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(),
- &dex_file);
+ &method_offsets_index, is_native,
+ it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
class_def_method_index++;
it.Next();
}
DCHECK(!it.HasNext());
+ CHECK_LE(method_offsets_index, class_def_method_index);
return offset;
}
size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
size_t __attribute__((unused)) class_def_index,
size_t class_def_method_index,
+ size_t* method_offsets_index,
bool __attribute__((unused)) is_native,
InvokeType invoke_type,
- uint32_t method_idx, const DexFile* dex_file) {
+ uint32_t method_idx, const DexFile& dex_file) {
// derived from CompiledMethod if available
uint32_t code_offset = 0;
uint32_t frame_size_in_bytes = kStackAlignment;
@@ -292,8 +342,7 @@
oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
#endif
- CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != NULL) {
#if defined(ART_USE_PORTABLE_COMPILER)
compiled_method->AddOatdataOffsetToCompliledCodeOffset(
@@ -358,7 +407,7 @@
#if !defined(NDEBUG)
// We expect GC maps except when the class hasn't been verified or the method is native
- ClassReference class_ref(dex_file, class_def_index);
+ ClassReference class_ref(&dex_file, class_def_index);
CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref);
mirror::Class::Status status;
if (compiled_class != NULL) {
@@ -371,7 +420,7 @@
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(method_idx, *dex_file);
+ << PrettyMethod(method_idx, dex_file);
#endif
// Deduplicate GC maps
@@ -384,24 +433,26 @@
offset += gc_map_size;
oat_header_->UpdateChecksum(&gc_map[0], gc_map_size);
}
+
+ oat_class->method_offsets_[*method_offsets_index] =
+ OatMethodOffsets(code_offset,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ mapping_table_offset,
+ vmap_table_offset,
+ gc_map_offset);
+ (*method_offsets_index)++;
}
- oat_class->method_offsets_[class_def_method_index] =
- OatMethodOffsets(code_offset,
- frame_size_in_bytes,
- core_spill_mask,
- fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
- gc_map_offset);
if (compiler_driver_->IsImage()) {
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file);
+ mirror::DexCache* dex_cache = linker->FindDexCache(dex_file);
// Unchecked as we hold mutator_lock_ on entry.
ScopedObjectAccessUnchecked soa(Thread::Current());
- mirror::ArtMethod* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache,
- NULL, NULL, invoke_type);
+ mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache,
+ NULL, NULL, invoke_type);
CHECK(method != NULL);
method->SetFrameSizeInBytes(frame_size_in_bytes);
method->SetCoreSpillMask(core_spill_mask);
@@ -475,8 +526,10 @@
DO_STAT(size_interpreter_to_interpreter_bridge_);
DO_STAT(size_interpreter_to_compiled_code_bridge_);
DO_STAT(size_jni_dlsym_lookup_);
+ DO_STAT(size_portable_imt_conflict_trampoline_);
DO_STAT(size_portable_resolution_trampoline_);
DO_STAT(size_portable_to_interpreter_bridge_);
+ DO_STAT(size_quick_imt_conflict_trampoline_);
DO_STAT(size_quick_resolution_trampoline_);
DO_STAT(size_quick_to_interpreter_bridge_);
DO_STAT(size_trampoline_alignment_);
@@ -491,7 +544,9 @@
DO_STAT(size_oat_dex_file_location_checksum_);
DO_STAT(size_oat_dex_file_offset_);
DO_STAT(size_oat_dex_file_methods_offsets_);
+ DO_STAT(size_oat_class_type_);
DO_STAT(size_oat_class_status_);
+ DO_STAT(size_oat_class_method_bitmaps_);
DO_STAT(size_oat_class_method_offsets_);
#undef DO_STAT
@@ -570,8 +625,10 @@
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_);
DO_TRAMPOLINE(jni_dlsym_lookup_);
+ DO_TRAMPOLINE(portable_imt_conflict_trampoline_);
DO_TRAMPOLINE(portable_resolution_trampoline_);
DO_TRAMPOLINE(portable_to_interpreter_bridge_);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
DO_TRAMPOLINE(quick_resolution_trampoline_);
DO_TRAMPOLINE(quick_to_interpreter_bridge_);
#undef DO_TRAMPOLINE
@@ -586,7 +643,7 @@
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
const DexFile* dex_file = (*dex_files_)[i];
CHECK(dex_file != NULL);
- relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, oat_class_index,
+ relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index,
*dex_file);
if (relative_offset == 0) {
return 0;
@@ -596,12 +653,12 @@
}
size_t OatWriter::WriteCodeDexFile(OutputStream& out, const size_t file_offset,
- size_t relative_offset, size_t& oat_class_index,
+ size_t relative_offset, size_t* oat_class_index,
const DexFile& dex_file) {
for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs();
- class_def_index++, oat_class_index++) {
+ class_def_index++, (*oat_class_index)++) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, oat_class_index,
+ relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index,
dex_file, class_def);
if (relative_offset == 0) {
return 0;
@@ -637,11 +694,12 @@
}
// Process methods
size_t class_def_method_index = 0;
+ size_t method_offsets_index = 0;
while (it.HasNextDirectMethod()) {
bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0;
relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
- class_def_method_index, is_static, it.GetMemberIndex(),
- dex_file);
+ class_def_method_index, &method_offsets_index, is_static,
+ it.GetMemberIndex(), dex_file);
if (relative_offset == 0) {
return 0;
}
@@ -650,28 +708,30 @@
}
while (it.HasNextVirtualMethod()) {
relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
- class_def_method_index, false, it.GetMemberIndex(), dex_file);
+ class_def_method_index, &method_offsets_index, false,
+ it.GetMemberIndex(), dex_file);
if (relative_offset == 0) {
return 0;
}
class_def_method_index++;
it.Next();
}
+ DCHECK(!it.HasNext());
+ CHECK_LE(method_offsets_index, class_def_method_index);
return relative_offset;
}
size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset,
size_t relative_offset, size_t oat_class_index,
- size_t class_def_method_index, bool is_static,
- uint32_t method_idx, const DexFile& dex_file) {
- const CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx));
-
- OatMethodOffsets method_offsets =
- oat_classes_[oat_class_index]->method_offsets_[class_def_method_index];
-
+ size_t class_def_method_index, size_t* method_offsets_index,
+ bool is_static, uint32_t method_idx, const DexFile& dex_file) {
+ OatClass* oat_class = oat_classes_[oat_class_index];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != NULL) { // ie. not an abstract method
+ const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index];
+ (*method_offsets_index)++;
+
#if !defined(ART_USE_PORTABLE_COMPILER)
uint32_t aligned_offset = compiled_method->AlignCode(relative_offset);
uint32_t aligned_code_delta = aligned_offset - relative_offset;
@@ -854,29 +914,96 @@
return true;
}
-OatWriter::OatClass::OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count) {
+OatWriter::OatClass::OatClass(size_t offset,
+ std::vector<CompiledMethod*>* compiled_methods,
+ uint32_t num_non_null_compiled_methods,
+ mirror::Class::Status status) {
+ CHECK(compiled_methods != NULL);
+ uint32_t num_methods = compiled_methods->size();
+ CHECK_LE(num_non_null_compiled_methods, num_methods);
+
offset_ = offset;
+ compiled_methods_ = compiled_methods;
+ oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
+
+ // Since both kOatClassNoneCompiled and kOatClassAllCompiled could
+ // apply when there are 0 methods, we just arbitrarily say that 0
+ // methods means kOatClassNoneCompiled and that we won't use
+ // kOatClassAllCompiled unless there is at least one compiled
+ // method. This means in an interpretter only system, we can assert
+ // that all classes are kOatClassNoneCompiled.
+ if (num_non_null_compiled_methods == 0) {
+ type_ = kOatClassNoneCompiled;
+ } else if (num_non_null_compiled_methods == num_methods) {
+ type_ = kOatClassAllCompiled;
+ } else {
+ type_ = kOatClassSomeCompiled;
+ }
+
status_ = status;
- method_offsets_.resize(methods_count);
+ method_offsets_.resize(num_non_null_compiled_methods);
+
+ uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
+ if (type_ == kOatClassSomeCompiled) {
+ method_bitmap_ = new BitVector(num_methods, false, Allocator::GetMallocAllocator());
+ method_bitmap_size_ = method_bitmap_->GetSizeOf();
+ oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
+ oat_method_offsets_offset_from_oat_class += method_bitmap_size_;
+ } else {
+ method_bitmap_ = NULL;
+ method_bitmap_size_ = 0;
+ }
+
+ for (size_t i = 0; i < num_methods; i++) {
+ CompiledMethod* compiled_method = (*compiled_methods_)[i];
+ if (compiled_method == NULL) {
+ oat_method_offsets_offsets_from_oat_class_[i] = 0;
+ } else {
+ oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class;
+ oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets);
+ if (type_ == kOatClassSomeCompiled) {
+ method_bitmap_->SetBit(i);
+ }
+ }
+ }
}
+OatWriter::OatClass::~OatClass() {
+ delete compiled_methods_;
+}
+
+#if defined(ART_USE_PORTABLE_COMPILER)
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
size_t class_def_method_index_) const {
- return offset_ + GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
+ uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
+ if (method_offset == 0) {
+ return 0;
+ }
+ return offset_ + method_offset;
}
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass(
size_t class_def_method_index_) const {
- return sizeof(status_)
- + (sizeof(method_offsets_[0]) * class_def_method_index_);
+ return oat_method_offsets_offsets_from_oat_class_[class_def_method_index_];
}
+#endif
size_t OatWriter::OatClass::SizeOf() const {
- return GetOatMethodOffsetsOffsetFromOatClass(method_offsets_.size());
+ return sizeof(status_)
+ + sizeof(type_)
+ + ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+ + method_bitmap_size_
+ + (sizeof(method_offsets_[0]) * method_offsets_.size());
}
void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const {
oat_header.UpdateChecksum(&status_, sizeof(status_));
+ oat_header.UpdateChecksum(&type_, sizeof(type_));
+ if (method_bitmap_size_ != 0) {
+ CHECK_EQ(kOatClassSomeCompiled, type_);
+ oat_header.UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_));
+ oat_header.UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_);
+ }
oat_header.UpdateChecksum(&method_offsets_[0],
sizeof(method_offsets_[0]) * method_offsets_.size());
}
@@ -890,17 +1017,30 @@
return false;
}
oat_writer->size_oat_class_status_ += sizeof(status_);
- DCHECK_EQ(static_cast<off_t>(file_offset + GetOatMethodOffsetsOffsetFromOatHeader(0)),
- out.Seek(0, kSeekCurrent));
+ if (!out.WriteFully(&type_, sizeof(type_))) {
+ PLOG(ERROR) << "Failed to write oat class type to " << out.GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_type_ += sizeof(type_);
+ if (method_bitmap_size_ != 0) {
+ CHECK_EQ(kOatClassSomeCompiled, type_);
+ if (!out.WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
+ PLOG(ERROR) << "Failed to write method bitmap size to " << out.GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
+ if (!out.WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+ PLOG(ERROR) << "Failed to write method bitmap to " << out.GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
+ }
if (!out.WriteFully(&method_offsets_[0],
sizeof(method_offsets_[0]) * method_offsets_.size())) {
PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation();
return false;
}
oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
- DCHECK_EQ(static_cast<off_t>(file_offset +
- GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())),
- out.Seek(0, kSeekCurrent));
return true;
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index d5f7e21..5d947cf 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -30,6 +30,7 @@
namespace art {
+class BitVector;
class OutputStream;
// OatHeader variable length with count of D OatDexFiles
@@ -90,7 +91,7 @@
size_t InitOatCodeDexFiles(size_t offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeDexFile(size_t offset,
- size_t& oat_class_index,
+ size_t* oat_class_index,
const DexFile& dex_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeClassDef(size_t offset,
@@ -99,21 +100,22 @@
const DexFile::ClassDef& class_def)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index,
- size_t class_def_method_index, bool is_native, InvokeType type,
- uint32_t method_idx, const DexFile*)
+ size_t class_def_method_index, size_t* method_offsets_index,
+ bool is_native, InvokeType type, uint32_t method_idx, const DexFile&)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool WriteTables(OutputStream& out, const size_t file_offset);
size_t WriteCode(OutputStream& out, const size_t file_offset);
size_t WriteCodeDexFiles(OutputStream& out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFile(OutputStream& out, const size_t file_offset, size_t relative_offset,
- size_t& oat_class_index, const DexFile& dex_file);
+ size_t* oat_class_index, const DexFile& dex_file);
size_t WriteCodeClassDef(OutputStream& out, const size_t file_offset, size_t relative_offset,
size_t oat_class_index, const DexFile& dex_file,
const DexFile::ClassDef& class_def);
size_t WriteCodeMethod(OutputStream& out, const size_t file_offset, size_t relative_offset,
- size_t oat_class_index, size_t class_def_method_index, bool is_static,
- uint32_t method_idx, const DexFile& dex_file);
+ size_t oat_class_index, size_t class_def_method_index,
+ size_t* method_offsets_index, bool is_static, uint32_t method_idx,
+ const DexFile& dex_file);
void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file,
OutputStream& out) const;
@@ -142,13 +144,24 @@
class OatClass {
public:
- explicit OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count);
+ explicit OatClass(size_t offset,
+ std::vector<CompiledMethod*>* compiled_methods,
+ uint32_t num_non_null_compiled_methods,
+ mirror::Class::Status status);
+ ~OatClass();
+#if defined(ART_USE_PORTABLE_COMPILER)
size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
+#endif
size_t SizeOf() const;
void UpdateChecksum(OatHeader& oat_header) const;
bool Write(OatWriter* oat_writer, OutputStream& out, const size_t file_offset) const;
+ CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
+ DCHECK(compiled_methods_ != NULL);
+ return (*compiled_methods_)[class_def_method_index];
+ }
+
// Offset of start of OatClass from beginning of OatHeader. It is
// used to validate file position when writing. For Portable, it
// is also used to calculate the position of the OatMethodOffsets
@@ -156,8 +169,37 @@
// patched to point to code in the Portable .o ELF objects.
size_t offset_;
+ // CompiledMethods for each class_def_method_index, or NULL if no method is available.
+ std::vector<CompiledMethod*>* compiled_methods_;
+
+ // Offset from OatClass::offset_ to the OatMethodOffsets for the
+ // class_def_method_index. If 0, it means the corresponding
+ // CompiledMethod entry in OatClass::compiled_methods_ should be
+ // NULL and that the OatClass::type_ should be kOatClassBitmap.
+ std::vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
+
// data to write
- mirror::Class::Status status_;
+
+ COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits);
+ int16_t status_;
+
+ COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits);
+ uint16_t type_;
+
+ uint32_t method_bitmap_size_;
+
+ // bit vector indexed by ClassDef method index. When
+ // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
+ // method has an OatMethodOffsets in methods_offsets_, otherwise
+ // the entry was ommited to save space. If OatClassType::type_ is
+ // not is kOatClassBitmap, the bitmap will be NULL.
+ BitVector* method_bitmap_;
+
+ // OatMethodOffsets for each CompiledMethod present in the
+ // OatClass. Note that some may be missing if
+ // OatClass::compiled_methods_ contains NULL values (and
+ // oat_method_offsets_offsets_from_oat_class_ should contain 0
+ // values in this case).
std::vector<OatMethodOffsets> method_offsets_;
private:
@@ -184,8 +226,10 @@
UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_bridge_;
UniquePtr<const std::vector<uint8_t> > interpreter_to_compiled_code_bridge_;
UniquePtr<const std::vector<uint8_t> > jni_dlsym_lookup_;
+ UniquePtr<const std::vector<uint8_t> > portable_imt_conflict_trampoline_;
UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_;
UniquePtr<const std::vector<uint8_t> > portable_to_interpreter_bridge_;
+ UniquePtr<const std::vector<uint8_t> > quick_imt_conflict_trampoline_;
UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_;
UniquePtr<const std::vector<uint8_t> > quick_to_interpreter_bridge_;
@@ -198,8 +242,10 @@
uint32_t size_interpreter_to_interpreter_bridge_;
uint32_t size_interpreter_to_compiled_code_bridge_;
uint32_t size_jni_dlsym_lookup_;
+ uint32_t size_portable_imt_conflict_trampoline_;
uint32_t size_portable_resolution_trampoline_;
uint32_t size_portable_to_interpreter_bridge_;
+ uint32_t size_quick_imt_conflict_trampoline_;
uint32_t size_quick_resolution_trampoline_;
uint32_t size_quick_to_interpreter_bridge_;
uint32_t size_trampoline_alignment_;
@@ -214,7 +260,9 @@
uint32_t size_oat_dex_file_location_checksum_;
uint32_t size_oat_dex_file_offset_;
uint32_t size_oat_dex_file_methods_offsets_;
+ uint32_t size_oat_class_type_;
uint32_t size_oat_class_status_;
+ uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
// Code mappings for deduplication. Deduplication is already done on a pointer basis by the
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index f3d35d7..638e0ec 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -18,62 +18,66 @@
#define ART_COMPILER_UTILS_DEDUPE_SET_H_
#include <set>
+#include <string>
#include "base/mutex.h"
#include "base/stl_util.h"
+#include "base/stringprintf.h"
namespace art {
-// A simple data structure to handle hashed deduplication. Add is thread safe.
-template <typename Key, typename HashType, typename HashFunc>
+// A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
+// Add method. The data-structure is thread-safe through the use of internal locks, it also
+// supports the lock being sharded.
+template <typename Key, typename HashType, typename HashFunc, HashType kShard = 1>
class DedupeSet {
typedef std::pair<HashType, Key*> HashedKey;
class Comparator {
public:
bool operator()(const HashedKey& a, const HashedKey& b) const {
- if (a.first < b.first) return true;
- if (a.first > b.first) return true;
- return *a.second < *b.second;
+ if (a.first != b.first) {
+ return a.first < b.first;
+ } else {
+ return *a.second < *b.second;
+ }
}
};
- typedef std::set<HashedKey, Comparator> Keys;
-
public:
- typedef typename Keys::iterator iterator;
- typedef typename Keys::const_iterator const_iterator;
- typedef typename Keys::size_type size_type;
- typedef typename Keys::value_type value_type;
-
- iterator begin() { return keys_.begin(); }
- const_iterator begin() const { return keys_.begin(); }
- iterator end() { return keys_.end(); }
- const_iterator end() const { return keys_.end(); }
-
Key* Add(Thread* self, const Key& key) {
- HashType hash = HashFunc()(key);
- HashedKey hashed_key(hash, const_cast<Key*>(&key));
- MutexLock lock(self, lock_);
- auto it = keys_.find(hashed_key);
- if (it != keys_.end()) {
+ HashType raw_hash = HashFunc()(key);
+ HashType shard_hash = raw_hash / kShard;
+ HashType shard_bin = raw_hash % kShard;
+ HashedKey hashed_key(shard_hash, const_cast<Key*>(&key));
+ MutexLock lock(self, *lock_[shard_bin]);
+ auto it = keys_[shard_bin].find(hashed_key);
+ if (it != keys_[shard_bin].end()) {
return it->second;
}
hashed_key.second = new Key(key);
- keys_.insert(hashed_key);
+ keys_[shard_bin].insert(hashed_key);
return hashed_key.second;
}
- DedupeSet() : lock_("dedupe lock") {
+ explicit DedupeSet(const char* set_name) {
+ for (HashType i = 0; i < kShard; ++i) {
+ lock_name_[i] = StringPrintf("%s lock %d", set_name, i);
+ lock_[i].reset(new Mutex(lock_name_[i].c_str()));
+ }
}
~DedupeSet() {
- STLDeleteValues(&keys_);
+ for (HashType i = 0; i < kShard; ++i) {
+ STLDeleteValues(&keys_[i]);
+ }
}
private:
- Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- Keys keys_;
+ std::string lock_name_[kShard];
+ UniquePtr<Mutex> lock_[kShard];
+ std::set<HashedKey, Comparator> keys_[kShard];
+
DISALLOW_COPY_AND_ASSIGN(DedupeSet);
};
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 9f5e292..8abe6de 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-#include "common_test.h"
#include "dedupe_set.h"
+#include "gtest/gtest.h"
+#include "thread-inl.h"
namespace art {
-class DedupeSetTest : public testing::Test {
- public:
-};
-
class DedupeHashFunc {
public:
size_t operator()(const std::vector<uint8_t>& array) const {
@@ -35,10 +32,10 @@
return hash;
}
};
-TEST_F(DedupeSetTest, Test) {
+TEST(DedupeSetTest, Test) {
Thread* self = Thread::Current();
typedef std::vector<uint8_t> ByteArray;
- DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator;
+ DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator("test");
ByteArray* array1;
{
ByteArray test1;
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 52584cf..a046391 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -38,7 +38,7 @@
LOCAL_SRC_FILES := dalvikvm.cc
LOCAL_CFLAGS := $(dalvikvm_cflags)
LOCAL_SHARED_LIBRARIES := libnativehelper
-LOCAL_LDFLAGS := -ldl
+LOCAL_LDFLAGS := -ldl -lpthread
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_EXECUTABLE)
ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index c4cce2f..3781921 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -131,9 +131,13 @@
UsageError(" Example: --instruction-set=x86");
UsageError(" Default: arm");
UsageError("");
+ UsageError(" --instruction-set-features=...,: Specify instruction set features");
+ UsageError(" Example: --instruction-set-features=div");
+ UsageError(" Default: default");
+ UsageError("");
UsageError(" --compiler-backend=(Quick|QuickGBC|Portable): select compiler backend");
UsageError(" set.");
- UsageError(" Example: --instruction-set=Portable");
+ UsageError(" Example: --compiler-backend=Portable");
UsageError(" Default: Quick");
UsageError("");
UsageError(" --host: used with Portable backend to link against host runtime libraries");
@@ -155,13 +159,15 @@
Runtime::Options& options,
CompilerBackend compiler_backend,
InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
size_t thread_count)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
if (!CreateRuntime(options, instruction_set)) {
*p_dex2oat = NULL;
return false;
}
- *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set, thread_count);
+ *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set,
+ instruction_set_features, thread_count);
return true;
}
@@ -200,21 +206,24 @@
}
// Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
- CompilerDriver::DescriptorSet* ReadImageClassesFromZip(const std::string& zip_filename,
- const char* image_classes_filename) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename));
+ CompilerDriver::DescriptorSet* ReadImageClassesFromZip(const char* zip_filename,
+ const char* image_classes_filename,
+ std::string* error_msg) {
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
if (zip_archive.get() == NULL) {
- LOG(ERROR) << "Failed to open zip file " << zip_filename;
return NULL;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename));
if (zip_entry.get() == NULL) {
- LOG(ERROR) << "Failed to find " << image_classes_filename << " within " << zip_filename;
+ *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
+ zip_filename, error_msg->c_str());
return NULL;
}
- UniquePtr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(image_classes_filename));
+ UniquePtr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(image_classes_filename,
+ error_msg));
if (image_classes_file.get() == NULL) {
- LOG(ERROR) << "Failed to extract " << image_classes_filename << " from " << zip_filename;
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
+ zip_filename, error_msg->c_str());
return NULL;
}
const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
@@ -254,6 +263,7 @@
UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_backend_,
instruction_set_,
+ instruction_set_features_,
image,
image_classes.release(),
thread_count_,
@@ -327,9 +337,11 @@
explicit Dex2Oat(Runtime* runtime,
CompilerBackend compiler_backend,
InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
size_t thread_count)
: compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
+ instruction_set_features_(instruction_set_features),
runtime_(runtime),
thread_count_(thread_count),
start_ns_(NanoTime()) {
@@ -342,10 +354,6 @@
return false;
}
Runtime* runtime = Runtime::Current();
- // if we loaded an existing image, we will reuse values from the image roots.
- if (!runtime->HasResolutionMethod()) {
- runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
- }
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
if (!runtime->HasCalleeSaveMethod(type)) {
@@ -368,9 +376,10 @@
if (DexFilesContains(dex_files, parsed[i])) {
continue;
}
- const DexFile* dex_file = DexFile::Open(parsed[i], parsed[i]);
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg);
if (dex_file == NULL) {
- LOG(WARNING) << "Failed to open dex file " << parsed[i];
+ LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
} else {
dex_files.push_back(dex_file);
}
@@ -391,6 +400,7 @@
const CompilerBackend compiler_backend_;
const InstructionSet instruction_set_;
+ const InstructionSetFeatures instruction_set_features_;
Runtime* runtime_;
size_t thread_count_;
@@ -416,9 +426,14 @@
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i];
const char* dex_location = dex_locations[i];
- const DexFile* dex_file = DexFile::Open(dex_filename, dex_location);
+ std::string error_msg;
+ if (!OS::FileExists(dex_filename)) {
+ LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'";
+ continue;
+ }
+ const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg);
if (dex_file == NULL) {
- LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "'\n";
+ LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
} else {
dex_files.push_back(dex_file);
@@ -558,6 +573,32 @@
const unsigned int WatchDog::kWatchDogWarningSeconds;
const unsigned int WatchDog::kWatchDogTimeoutSeconds;
+// Given a set of instruction features from the build, parse it. The
+// input 'str' is a comma separated list of feature names. Parse it and
+// return the InstructionSetFeatures object.
+static InstructionSetFeatures ParseFeatureList(std::string str) {
+ InstructionSetFeatures result;
+ typedef std::vector<std::string> FeatureList;
+ FeatureList features;
+ Split(str, ',', features);
+ for (FeatureList::iterator i = features.begin(); i != features.end(); i++) {
+ std::string feature = Trim(*i);
+ if (feature == "default") {
+ // Nothing to do.
+ } else if (feature == "div") {
+ // Supports divide instruction.
+ result.SetHasDivideInstruction(true);
+ } else if (feature == "nodiv") {
+ // Turn off support for divide instruction.
+ result.SetHasDivideInstruction(false);
+ } else {
+ Usage("Unknown instruction set feature: '%s'", feature.c_str());
+ }
+ }
+ // others...
+ return result;
+}
+
static int dex2oat(int argc, char** argv) {
base::TimingLogger timings("compiler", false, false);
@@ -594,6 +635,11 @@
#else
CompilerBackend compiler_backend = kQuick;
#endif
+
+ // Take the default set of instruction features from the build.
+ InstructionSetFeatures instruction_set_features =
+ ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES));
+
#if defined(__arm__)
InstructionSet instruction_set = kThumb2;
#elif defined(__i386__)
@@ -603,8 +649,10 @@
#else
#error "Unsupported architecture"
#endif
+
+
bool is_host = false;
- bool dump_stats = kIsDebugBuild;
+ bool dump_stats = false;
bool dump_timing = false;
bool dump_slow_timing = kIsDebugBuild;
bool watch_dog_enabled = !kIsTargetBuild;
@@ -677,6 +725,9 @@
} else if (instruction_set_str == "x86") {
instruction_set = kX86;
}
+ } else if (option.starts_with("--instruction-set-features=")) {
+ StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+ instruction_set_features = ParseFeatureList(str.as_string());
} else if (option.starts_with("--compiler-backend=")) {
StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
if (backend_str == "Quick") {
@@ -696,6 +747,8 @@
runtime_args.push_back(argv[i]);
} else if (option == "--dump-timing") {
dump_timing = true;
+ } else if (option == "--dump-stats") {
+ dump_stats = true;
} else {
Usage("Unknown argument %s", option.data());
}
@@ -867,7 +920,8 @@
#endif
Dex2Oat* p_dex2oat;
- if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count)) {
+ if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
+ instruction_set_features, thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
return EXIT_FAILURE;
}
@@ -885,14 +939,17 @@
// If --image-classes was specified, calculate the full list of classes to include in the image
UniquePtr<CompilerDriver::DescriptorSet> image_classes(NULL);
if (image_classes_filename != NULL) {
+ std::string error_msg;
if (image_classes_zip_filename != NULL) {
image_classes.reset(dex2oat->ReadImageClassesFromZip(image_classes_zip_filename,
- image_classes_filename));
+ image_classes_filename,
+ &error_msg));
} else {
image_classes.reset(dex2oat->ReadImageClassesFromFile(image_classes_filename));
}
if (image_classes.get() == NULL) {
- LOG(ERROR) << "Failed to create list of image classes from " << image_classes_filename;
+ LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
+ "': " << error_msg;
return EXIT_FAILURE;
}
}
@@ -902,14 +959,18 @@
dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath();
} else {
if (dex_filenames.empty()) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd));
+ std::string error_msg;
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd, zip_location.c_str(),
+ &error_msg));
if (zip_archive.get() == NULL) {
- LOG(ERROR) << "Failed to open zip from file descriptor for " << zip_location;
+ LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
+ << error_msg;
return EXIT_FAILURE;
}
- const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location);
+ const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location, &error_msg);
if (dex_file == NULL) {
- LOG(ERROR) << "Failed to open dex from file descriptor for zip file: " << zip_location;
+ LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
+ << "': " << error_msg;
return EXIT_FAILURE;
}
dex_files.push_back(dex_file);
@@ -1061,7 +1122,8 @@
// Strip unneeded sections for target
off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
CHECK_EQ(0, seek_actual);
- ElfStripper::Strip(oat_file.get());
+ std::string error_msg;
+ CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
// We wrote the oat file successfully, and want to keep it.
@@ -1082,8 +1144,6 @@
return EXIT_SUCCESS;
}
-
-
} // namespace art
int main(int argc, char** argv) {
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
new file mode 100644
index 0000000..f8001a4
--- /dev/null
+++ b/disassembler/Android.mk
@@ -0,0 +1,120 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common.mk
+
+LIBART_DISASSEMBLER_SRC_FILES := \
+ disassembler.cc \
+ disassembler_arm.cc \
+ disassembler_mips.cc \
+ disassembler_x86.cc
+
+# $(1): target or host
+# $(2): ndebug or debug
+define build-libart-disassembler
+ ifneq ($(1),target)
+ ifneq ($(1),host)
+ $$(error expected target or host for argument 1, received $(1))
+ endif
+ endif
+ ifneq ($(2),ndebug)
+ ifneq ($(2),debug)
+ $$(error expected ndebug or debug for argument 2, received $(2))
+ endif
+ endif
+
+ art_target_or_host := $(1)
+ art_ndebug_or_debug := $(2)
+
+ include $(CLEAR_VARS)
+ ifeq ($$(art_target_or_host),target)
+ include external/stlport/libstlport.mk
+ else
+ LOCAL_IS_HOST_MODULE := true
+ endif
+ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+ ifeq ($$(art_ndebug_or_debug),ndebug)
+ LOCAL_MODULE := libart-disassembler
+ else # debug
+ LOCAL_MODULE := libartd-disassembler
+ endif
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+ LOCAL_SRC_FILES := $$(LIBART_DISASSEMBLER_SRC_FILES)
+
+ GENERATED_SRC_DIR := $$(call intermediates-dir-for,$$(LOCAL_MODULE_CLASS),$$(LOCAL_MODULE),$$(LOCAL_IS_HOST_MODULE),)
+
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_CLANG := $(ART_TARGET_CLANG)
+ LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+ else # host
+ LOCAL_CLANG := $(ART_HOST_CLANG)
+ LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+ endif
+
+ LOCAL_SHARED_LIBRARIES += liblog
+ ifeq ($$(art_ndebug_or_debug),debug)
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS)
+ else # host
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ endif
+ LOCAL_SHARED_LIBRARIES += libartd
+ else
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS)
+ else # host
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ endif
+ LOCAL_SHARED_LIBRARIES += libart
+ endif
+
+ LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+
+ LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_SHARED_LIBRARIES += libcutils
+ include $(LLVM_GEN_INTRINSICS_MK)
+ include $(LLVM_DEVICE_BUILD_MK)
+ include $(BUILD_SHARED_LIBRARY)
+ else # host
+ LOCAL_STATIC_LIBRARIES += libcutils
+ include $(LLVM_GEN_INTRINSICS_MK)
+ include $(LLVM_HOST_BUILD_MK)
+ include $(BUILD_HOST_SHARED_LIBRARY)
+ endif
+endef
+
+ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
+ $(eval $(call build-libart-disassembler,target,ndebug))
+endif
+ifeq ($(ART_BUILD_TARGET_DEBUG),true)
+ $(eval $(call build-libart-disassembler,target,debug))
+endif
+ifeq ($(WITH_HOST_DALVIK),true)
+ # 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)
+ $(eval $(call build-libart-disassembler,host,ndebug))
+ endif
+ ifeq ($(ART_BUILD_DEBUG),true)
+ $(eval $(call build-libart-disassembler,host,debug))
+ endif
+endif
diff --git a/runtime/disassembler.cc b/disassembler/disassembler.cc
similarity index 100%
rename from runtime/disassembler.cc
rename to disassembler/disassembler.cc
diff --git a/runtime/disassembler.h b/disassembler/disassembler.h
similarity index 90%
rename from runtime/disassembler.h
rename to disassembler/disassembler.h
index 805ff4d..7547ab7 100644
--- a/runtime/disassembler.h
+++ b/disassembler/disassembler.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_H_
-#define ART_RUNTIME_DISASSEMBLER_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_H_
#include <stdint.h>
@@ -45,4 +45,4 @@
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_H_
diff --git a/runtime/disassembler_arm.cc b/disassembler/disassembler_arm.cc
similarity index 83%
rename from runtime/disassembler_arm.cc
rename to disassembler/disassembler_arm.cc
index 879d3ac..8d4f3ce 100644
--- a/runtime/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -103,6 +103,10 @@
"tst", "rsb", "cmp", "cmn", "orr", "mul", "bic", "mvn",
};
+static const char* kThumbReverseOperations[] = {
+ "rev", "rev16", "rbit", "revsh"
+};
+
struct ArmRegister {
explicit ArmRegister(uint32_t r) : r(r) { CHECK_LE(r, 15U); }
ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) { CHECK_LE(r, 15U); }
@@ -278,6 +282,26 @@
os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
}
+int32_t ThumbExpand(int32_t imm12) {
+ if ((imm12 & 0xC00) == 0) {
+ switch ((imm12 >> 8) & 3) {
+ case 0:
+ return imm12 & 0xFF;
+ case 1:
+ return ((imm12 & 0xFF) << 16) | (imm12 & 0xFF);
+ case 2:
+ return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 8);
+ default: // 3
+ return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 16) | ((imm12 & 0xFF) << 8) |
+ (imm12 & 0xFF);
+ }
+ } else {
+ uint32_t val = 0x80 | (imm12 & 0x7F);
+ int32_t rotate = (imm12 >> 7) & 0x1F;
+ return (val >> rotate) | (val << (32 - rotate));
+ }
+}
+
size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2);
// |111|1 1|1000000|0000|1111110000000000|
@@ -350,7 +374,102 @@
// uint32_t op5 = (instr >> 4) & 0xF;
ArmRegister Rn(instr, 16);
ArmRegister Rt(instr, 12);
+ ArmRegister Rd(instr, 8);
uint32_t imm8 = instr & 0xFF;
+ if ((op3 & 2) == 2) { // 1x
+ int W = (instr >> 21) & 1;
+ int U = (instr >> 23) & 1;
+ int P = (instr >> 24) & 1;
+
+ if ((op4 & 1) == 1) {
+ opcode << "ldrd";
+ } else {
+ opcode << "strd";
+ }
+ args << Rt << "," << Rd << ", [" << Rn;
+ const char *sign = U ? "+" : "-";
+ if (P == 0 && W == 1) {
+ args << "], #" << sign << imm8;
+ } else {
+ args << ", #" << sign << imm8 << "]";
+ if (W == 1) {
+ args << "!";
+ }
+ }
+ } else { // 0x
+ switch (op4) {
+ case 0:
+ if (op3 == 0) { // op3 is 00, op4 is 00
+ opcode << "strex";
+ args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
+ } else { // op3 is 01, op4 is 00
+ // this is one of strexb, strexh or strexd
+ int op5 = (instr >> 4) & 0xf;
+ switch (op5) {
+ case 4:
+ opcode << "strexb";
+ break;
+ case 5:
+ opcode << "strexh";
+ break;
+ case 7:
+ opcode << "strexd";
+ break;
+ }
+ }
+ break;
+ case 1:
+ if (op3 == 0) { // op3 is 00, op4 is 01
+ opcode << "ldrex";
+ args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
+ } else { // op3 is 01, op4 is 01
+ // this is one of strexb, strexh or strexd
+ int op5 = (instr >> 4) & 0xf;
+ switch (op5) {
+ case 0:
+ opcode << "tbb";
+ break;
+ case 1:
+ opcode << "tbh";
+ break;
+ case 4:
+ opcode << "ldrexb";
+ break;
+ case 5:
+ opcode << "ldrexh";
+ break;
+ case 7:
+ opcode << "ldrexd";
+ break;
+ }
+ }
+ break;
+ case 2: // op3 is 0x, op4 is 10
+ case 3: // op3 is 0x, op4 is 11
+ if (op4 == 2) {
+ opcode << "strd";
+ } else {
+ opcode << "ldrd";
+ }
+ int W = (instr >> 21) & 1;
+ int U = (instr >> 23) & 1;
+ int P = (instr >> 24) & 1;
+
+ args << Rt << "," << Rd << ", [" << Rn;
+ const char *sign = U ? "+" : "-";
+ if (P == 0 && W == 1) {
+ args << "], #" << sign << imm8;
+ } else {
+ args << ", #" << sign << imm8 << "]";
+ if (W == 1) {
+ args << "!";
+ }
+ }
+ break;
+ }
+ }
+
+
if (op3 == 0 && op4 == 0) { // STREX
ArmRegister Rd(instr, 8);
opcode << "strex";
@@ -495,19 +614,11 @@
uint32_t op3 = (instr >> 20) & 0x3F;
uint32_t coproc = (instr >> 8) & 0xF;
uint32_t op4 = (instr >> 4) & 0x1;
- if ((op3 == 2 || op3 == 2 || op3 == 6 || op3 == 7) || // 00x1x
- (op3 >= 8 && op3 <= 15) || (op3 >= 16 && op3 <= 31)) { // 001xxx, 01xxxx
- // Extension register load/store instructions
- // |111|1|110|00000|0000|1111|110|000000000|
- // |5 3|2|109|87654|3 0|54 2|10 |87 54 0|
- // |---|-|---|-----|----|----|---|---------|
- // |332|2|222|22222|1111|1111|110|000000000|
- // |1 9|8|765|43210|9 6|54 2|10 |87 54 0|
- // |---|-|---|-----|----|----|---|---------|
- // |111|T|110| op3 | Rn | |101| |
- // 111 0 110 01001 0011 0000 101 000000011 - ec930a03
- if (op3 == 9 || op3 == 0xD) { // VLDM
- // 1110 110 PUDW1 nnnn dddd 101S iiii iiii
+
+ if (coproc == 10 || coproc == 11) { // 101x
+ if (op3 < 0x20 && (op3 >> 1) != 2) { // 0xxxxx and not 00010x
+ // extension load/store instructions
+ int op = op3 & 0x1f;
uint32_t P = (instr >> 24) & 1;
uint32_t U = (instr >> 23) & 1;
uint32_t D = (instr >> 22) & 1;
@@ -517,20 +628,49 @@
uint32_t Vd = (instr >> 12) & 0xF;
uint32_t imm8 = instr & 0xFF;
uint32_t d = (S == 0 ? ((Vd << 1) | D) : (Vd | (D << 4)));
- if (P == 0 && U == 0 && W == 0) {
- // TODO: 64bit transfers between ARM core and extension registers.
- } else if (P == 0 && U == 1 && Rn.r == 13) { // VPOP
- opcode << "vpop" << (S == 0 ? ".f64" : ".f32");
- args << d << " .. " << (d + imm8);
- } else if (P == 1 && W == 0) { // VLDR
- opcode << "vldr" << (S == 0 ? ".f64" : ".f32");
- args << d << ", [" << Rn << ", #" << imm8 << "]";
- } else { // VLDM
- opcode << "vldm" << (S == 0 ? ".f64" : ".f32");
- args << Rn << ", " << d << " .. " << (d + imm8);
+ ArmRegister Rd(d, 0);
+
+ if (op == 8 || op == 12 || op == 10 || op == 14 ||
+ op == 18 || op == 22) { // 01x00 or 01x10
+ // vector store multiple or vpush
+ if (P == 1 && U == 0 && W == 1 && Rn.r == 13) {
+ opcode << "vpush" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << " .. " << (Rd.r + imm8);
+ } else {
+ opcode << "vstm" << (S == 0 ? ".f64" : ".f32");
+ args << Rn << ", " << Rd << " .. " << (Rd.r + imm8);
+ }
+ } else if (op == 16 || op == 20 || op == 24 || op == 28) {
+ // 1xx00
+ // vector store register
+ opcode << "vstr" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << ", [" << Rn << ", #" << imm8 << "]";
+ } else if (op == 17 || op == 21 || op == 25 || op == 29) {
+ // 1xx01
+ // vector load register
+ opcode << "vldr" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << ", [" << Rn << ", #" << imm8 << "]";
+ } else if (op == 9 || op == 13 || op == 11 || op == 15 ||
+ op == 19 || op == 23 ) { // 01x11 10x11
+ // vldm or vpop
+ if (P == 1 && U == 0 && W == 1 && Rn.r == 13) {
+ opcode << "vpop" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << " .. " << (Rd.r + imm8);
+ } else {
+ opcode << "vldm" << (S == 0 ? ".f64" : ".f32");
+ args << Rn << ", " << Rd << " .. " << (Rd.r + imm8);
+ }
}
+ } else if ((op3 >> 1) == 2) { // 00010x
+ // 64 bit transfers
+ } else if ((op3 >> 4) == 2 && op4 == 0) { // 10xxxx, op = 0
+ // fp data processing
+ } else if ((op3 >> 4) == 2 && op4 == 1) { // 10xxxx, op = 1
+ // 8,16,32 bit transfers
}
- } else if ((op3 & 0x30) == 0x20 && op4 == 0) { // 10 xxxx ... 0
+ }
+
+ if ((op3 & 0x30) == 0x20 && op4 == 0) { // 10 xxxx ... 0
if ((coproc & 0xE) == 0xA) {
// VFP data-processing instructions
// |111|1|1100|0000|0000|1111|110|0|00 |0|0|0000|
@@ -628,7 +768,7 @@
opcode << "s";
}
}
- args << Rd << ", ThumbExpand(" << imm32 << ")";
+ args << Rd << ", #" << ThumbExpand(imm32);
} else if (Rd.r == 0xF && S == 1 &&
(op3 == 0x0 || op3 == 0x4 || op3 == 0x8 || op3 == 0xD)) {
if (op3 == 0x0) {
@@ -640,7 +780,7 @@
} else {
opcode << "cmp.w";
}
- args << Rn << ", ThumbExpand(" << imm32 << ")";
+ args << Rn << ", #" << ThumbExpand(imm32);
} else {
switch (op3) {
case 0x0: opcode << "and"; break;
@@ -658,7 +798,7 @@
if (S == 1) {
opcode << "s";
}
- args << Rd << ", " << Rn << ", ThumbExpand(" << imm32 << ")";
+ args << Rd << ", " << Rn << ", #" << ThumbExpand(imm32);
}
} else if ((instr & 0x8000) == 0 && (op2 & 0x20) != 0) {
// Data-processing (plain binary immediate)
@@ -975,6 +1115,31 @@
}
break;
}
+ case 0x29: { // 0101001
+ // |111|11|1000000|0000|1111|1100|00|0 0|0000|
+ // |5 3|21|0 4|3 0|5 2|1 8|76|5 4|3 0|
+ // |---|--|-------|----|----|----|--|---|----|
+ // |332|22|2222222|1111|1111|1100|00|0 0|0000|
+ // |1 9|87|6 0|9 6|5 2|1 8|76|5 4|3 0|
+ // |---|--|-------|----|----|----|--|---|----|
+ // |111|11|0101001| Rm |1111| Rd |11|op3| Rm |
+ // REV - 111 11 0101001 mmmm 1111 dddd 1000 mmmm
+ // REV16 - 111 11 0101001 mmmm 1111 dddd 1001 mmmm
+ // RBIT - 111 11 0101001 mmmm 1111 dddd 1010 mmmm
+ // REVSH - 111 11 0101001 mmmm 1111 dddd 1011 mmmm
+ if ((instr & 0xf0c0) == 0xf080) {
+ uint32_t op3 = (instr >> 4) & 3;
+ opcode << kThumbReverseOperations[op3];
+ ArmRegister Rm(instr, 0);
+ ArmRegister Rd(instr, 8);
+ args << Rd << ", " << Rm;
+ ArmRegister Rm2(instr, 16);
+ if (Rm.r != Rm2.r || Rm.r == 13 || Rm.r == 15 || Rd.r == 13 || Rd.r == 15) {
+ args << " (UNPREDICTABLE)";
+ }
+ } // else unknown instruction
+ break;
+ }
case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101
// Load word
// |111|11|10|0 0|00|0|0000|1111|110000|000000|
@@ -1021,6 +1186,72 @@
}
break;
}
+ default: // more formats
+ if ((op2 >> 4) == 2) { // 010xxxx
+ // data processing (register)
+ } else if ((op2 >> 3) == 6) { // 0110xxx
+ // Multiply, multiply accumulate, and absolute difference
+ op1 = (instr >> 20) & 0x7;
+ op2 = (instr >> 4) & 0x2;
+ ArmRegister Ra(instr, 12);
+ ArmRegister Rn(instr, 16);
+ ArmRegister Rm(instr, 0);
+ ArmRegister Rd(instr, 8);
+ switch (op1) {
+ case 0:
+ if (op2 == 0) {
+ if (Ra.r == 0xf) {
+ opcode << "mul";
+ args << Rd << ", " << Rn << ", " << Rm;
+ } else {
+ opcode << "mla";
+ args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
+ }
+ } else {
+ opcode << "mls";
+ args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ break; // do these sometime
+ }
+ } else if ((op2 >> 3) == 7) { // 0111xxx
+ // Long multiply, long multiply accumulate, and divide
+ op1 = (instr >> 20) & 0x7;
+ op2 = (instr >> 4) & 0xf;
+ ArmRegister Rn(instr, 16);
+ ArmRegister Rm(instr, 0);
+ ArmRegister Rd(instr, 8);
+ ArmRegister RdHi(instr, 8);
+ ArmRegister RdLo(instr, 12);
+ switch (op1) {
+ case 0:
+ opcode << "smull";
+ args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
+ break;
+ case 1:
+ opcode << "sdiv";
+ args << Rd << ", " << Rn << ", " << Rm;
+ break;
+ case 2:
+ opcode << "umull";
+ args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
+ break;
+ case 3:
+ opcode << "udiv";
+ args << Rd << ", " << Rn << ", " << Rm;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ break; // TODO: when we generate these...
+ }
+ }
}
default:
break;
@@ -1265,6 +1496,16 @@
DumpBranchTarget(args, instr_ptr + 4, imm32);
break;
}
+ case 0x50: case 0x51: // 101000x
+ case 0x52: case 0x53: // 101001x
+ case 0x56: case 0x57: { // 101011x
+ uint16_t op = (instr >> 6) & 3;
+ opcode << kThumbReverseOperations[op];
+ ThumbRegister Rm(instr, 3);
+ ThumbRegister Rd(instr, 0);
+ args << Rd << ", " << Rm;
+ break;
+ }
case 0x78: case 0x79: case 0x7A: case 0x7B: // 1111xxx
case 0x7C: case 0x7D: case 0x7E: case 0x7F: {
// If-Then, and hints
diff --git a/runtime/disassembler_arm.h b/disassembler/disassembler_arm.h
similarity index 90%
rename from runtime/disassembler_arm.h
rename to disassembler/disassembler_arm.h
index cab9150..2e699ff 100644
--- a/runtime/disassembler_arm.h
+++ b/disassembler/disassembler_arm.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_ARM_H_
-#define ART_RUNTIME_DISASSEMBLER_ARM_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_ARM_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_ARM_H_
#include <vector>
@@ -48,4 +48,4 @@
} // namespace arm
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_ARM_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_ARM_H_
diff --git a/runtime/disassembler_mips.cc b/disassembler/disassembler_mips.cc
similarity index 100%
rename from runtime/disassembler_mips.cc
rename to disassembler/disassembler_mips.cc
diff --git a/runtime/disassembler_mips.h b/disassembler/disassembler_mips.h
similarity index 87%
rename from runtime/disassembler_mips.h
rename to disassembler/disassembler_mips.h
index e248503..d386267 100644
--- a/runtime/disassembler_mips.h
+++ b/disassembler/disassembler_mips.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_MIPS_H_
-#define ART_RUNTIME_DISASSEMBLER_MIPS_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_
#include <vector>
@@ -37,4 +37,4 @@
} // namespace mips
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_MIPS_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_
diff --git a/runtime/disassembler_x86.cc b/disassembler/disassembler_x86.cc
similarity index 98%
rename from runtime/disassembler_x86.cc
rename to disassembler/disassembler_x86.cc
index e5cdb7b..9ed65cd 100644
--- a/runtime/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -520,6 +520,10 @@
case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break;
case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; break;
case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
+ case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
+ opcode << "bswap";
+ reg_in_opcode = true;
+ break;
default:
opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
break;
diff --git a/runtime/disassembler_x86.h b/disassembler/disassembler_x86.h
similarity index 87%
rename from runtime/disassembler_x86.h
rename to disassembler/disassembler_x86.h
index ff4322c..9adaff7 100644
--- a/runtime/disassembler_x86.h
+++ b/disassembler/disassembler_x86.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_X86_H_
-#define ART_RUNTIME_DISASSEMBLER_X86_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_X86_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_X86_H_
#include "disassembler.h"
@@ -35,4 +35,4 @@
} // namespace x86
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_X86_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_X86_H_
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index a63b229..7cee00e 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@
include art/build/Android.executable.mk
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils,,target,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils,,target,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
endif
ifeq ($(WITH_HOST_DALVIK),true)
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),,,host,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),,,host,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
endif
endif
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 304222a..b9716d5 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -86,6 +86,8 @@
const char* image_roots_descriptions_[] = {
"kResolutionMethod",
+ "kImtConflictMethod",
+ "kDefaultImt",
"kCalleeSaveMethod",
"kRefsOnlySaveMethod",
"kRefsAndArgsSaveMethod",
@@ -116,6 +118,9 @@
os << "INSTRUCTION SET:\n";
os << oat_header.GetInstructionSet() << "\n\n";
+ os << "INSTRUCTION SET FEATURES:\n";
+ os << oat_header.GetInstructionSetFeatures().GetFeatureString() << "\n\n";
+
os << "DEX FILE COUNT:\n";
os << oat_header.GetDexFileCount() << "\n\n";
@@ -173,9 +178,13 @@
MethodHelper mh(m);
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);
- UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
- if (dex_file.get() != NULL) {
+ CHECK(oat_dex_file != nullptr);
+ std::string error_msg;
+ UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
+ << "': " << error_msg;
+ } else {
const DexFile::ClassDef* class_def =
dex_file->FindClassDef(mh.GetDeclaringClassDescriptor());
if (class_def != NULL) {
@@ -199,8 +208,11 @@
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);
- UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
- if (dex_file.get() == NULL) {
+ std::string error_msg;
+ UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
+ << "': " << error_msg;
continue;
}
offsets_.insert(reinterpret_cast<uint32_t>(&dex_file->GetHeader()));
@@ -245,9 +257,10 @@
os << "OAT DEX FILE:\n";
os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
- UniquePtr<const DexFile> dex_file(oat_dex_file.OpenDexFile());
+ std::string error_msg;
+ UniquePtr<const DexFile> dex_file(oat_dex_file.OpenDexFile(&error_msg));
if (dex_file.get() == NULL) {
- os << "NOT FOUND\n\n";
+ os << "NOT FOUND: " << error_msg << "\n\n";
return;
}
for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) {
@@ -255,8 +268,10 @@
const char* descriptor = dex_file->GetClassDescriptor(class_def);
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file.GetOatClass(class_def_index));
CHECK(oat_class.get() != NULL);
- os << StringPrintf("%zd: %s (type_idx=%d) (", class_def_index, descriptor, class_def.class_idx_)
- << oat_class->GetStatus() << ")\n";
+ os << StringPrintf("%zd: %s (type_idx=%d)", class_def_index, descriptor, class_def.class_idx_)
+ << " (" << oat_class->GetStatus() << ")"
+ << " (" << oat_class->GetType() << ")\n";
+ // TODO: include bitmap here if type is kOatClassBitmap?
Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indented_os(&indent_filter);
DumpOatClass(indented_os, *oat_class.get(), *(dex_file.get()), class_def);
@@ -727,9 +742,10 @@
os << " (" << oat_location << ")";
}
os << "\n";
- const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location);
+ std::string error_msg;
+ const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location, &error_msg);
if (oat_file == NULL) {
- os << "NOT FOUND\n";
+ os << "NOT FOUND: " << error_msg << "\n";
return;
}
os << "\n";
@@ -775,7 +791,7 @@
os << "STATS:\n" << std::flush;
UniquePtr<File> file(OS::OpenFileForReading(image_filename_.c_str()));
if (file.get() == NULL) {
- std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_));
+ std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_.c_str()));
file.reset(OS::OpenFileForReading(cache_location.c_str()));
if (file.get() == NULL) {
LOG(WARNING) << "Failed to find image in " << image_filename_
@@ -994,7 +1010,8 @@
indent_os << StringPrintf("OAT CODE: %p\n", oat_code);
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
- method->IsResolutionMethod() || MethodHelper(method).IsClassInitializer()) {
+ method->IsResolutionMethod() || method->IsImtConflictMethod() ||
+ MethodHelper(method).IsClassInitializer()) {
DCHECK(method->GetNativeGcMap() == NULL) << PrettyMethod(method);
DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
} else {
@@ -1126,7 +1143,7 @@
typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
SizeAndCountTable sizes_and_counts;
- void Update(const std::string& descriptor, size_t object_bytes) {
+ void Update(const char* descriptor, size_t object_bytes) {
SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor);
if (it != sizes_and_counts.end()) {
it->second.bytes += object_bytes;
@@ -1412,10 +1429,11 @@
}
if (oat_filename != NULL) {
+ std::string error_msg;
OatFile* oat_file =
- OatFile::Open(oat_filename, oat_filename, NULL, false);
+ OatFile::Open(oat_filename, oat_filename, NULL, false, &error_msg);
if (oat_file == NULL) {
- fprintf(stderr, "Failed to open oat file from %s\n", oat_filename);
+ fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
}
OatDumper oat_dumper(*host_prefix.get(), *oat_file);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index a0ae4bf..bef4381 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -21,6 +21,8 @@
LIBART_COMMON_SRC_FILES := \
atomic.cc.arm \
barrier.cc \
+ base/allocator.cc \
+ base/bit_vector.cc \
base/logging.cc \
base/mutex.cc \
base/stringpiece.cc \
@@ -38,10 +40,6 @@
dex_file.cc \
dex_file_verifier.cc \
dex_instruction.cc \
- disassembler.cc \
- disassembler_arm.cc \
- disassembler_mips.cc \
- disassembler_x86.cc \
elf_file.cc \
gc/allocator/dlmalloc.cc \
gc/accounting/card_table.cc \
@@ -64,6 +62,9 @@
instrumentation.cc \
intern_table.cc \
interpreter/interpreter.cc \
+ interpreter/interpreter_common.cc \
+ interpreter/interpreter_goto_table_impl.cc \
+ interpreter/interpreter_switch_impl.cc \
jdwp/jdwp_event.cc \
jdwp/jdwp_expand_buf.cc \
jdwp/jdwp_handler.cc \
@@ -178,6 +179,7 @@
runtime_android.cc \
thread_android.cc
+LIBART_LDFLAGS :=
ifeq ($(TARGET_ARCH),arm)
LIBART_TARGET_SRC_FILES += \
arch/arm/context_arm.cc.arm \
@@ -185,6 +187,7 @@
arch/arm/jni_entrypoints_arm.S \
arch/arm/portable_entrypoints_arm.S \
arch/arm/quick_entrypoints_arm.S \
+ arch/arm/arm_sdiv.S \
arch/arm/thread_arm.cc
else # TARGET_ARCH != arm
ifeq ($(TARGET_ARCH),x86)
@@ -195,6 +198,7 @@
arch/x86/portable_entrypoints_x86.S \
arch/x86/quick_entrypoints_x86.S \
arch/x86/thread_x86.cc
+LIBART_LDFLAGS += -Wl,--no-fatal-warnings
else # TARGET_ARCH != x86
ifeq ($(TARGET_ARCH),mips)
LIBART_TARGET_SRC_FILES += \
@@ -244,7 +248,9 @@
jdwp/jdwp.h \
jdwp/jdwp_constants.h \
locks.h \
+ lock_word.h \
mirror/class.h \
+ oat.h \
thread.h \
thread_state.h \
verifier/method_verifier.h
@@ -304,6 +310,7 @@
LOCAL_GENERATED_SOURCES += $$(ENUM_OPERATOR_OUT_GEN)
LOCAL_CFLAGS := $(LIBART_CFLAGS)
+ LOCAL_LDFLAGS := $(LIBART_LDFLAGS)
ifeq ($$(art_target_or_host),target)
LOCAL_CLANG := $(ART_TARGET_CLANG)
LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
@@ -328,7 +335,7 @@
endif
LOCAL_C_INCLUDES += $(ART_C_INCLUDES)
LOCAL_SHARED_LIBRARIES += liblog libnativehelper
- LOCAL_SHARED_LIBRARIES += libcorkscrew # native stack trace support
+ LOCAL_SHARED_LIBRARIES += libbacktrace # native stack trace support
ifeq ($$(art_target_or_host),target)
LOCAL_SHARED_LIBRARIES += libcutils libz libdl libselinux
else # host
diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/arm_sdiv.S
new file mode 100644
index 0000000..925e428
--- /dev/null
+++ b/runtime/arch/arm/arm_sdiv.S
@@ -0,0 +1,24 @@
+// This function is used to check for the CPU's support for the sdiv
+// instruction at runtime. It will either return the value 1 or
+// will cause an invalid instruction trap (SIGILL signal). The
+// caller must arrange for the signal handler to set the r0
+// register to 0 and move the pc forward by 4 bytes (to skip
+// the invalid instruction).
+
+
+#include "asm_support_arm.S"
+
+.section .text
+ENTRY CheckForARMSDIVInstruction
+ mov r1,#1
+ // depending on the architecture, the assembler will not allow an
+ // sdiv instruction, so we will have to output the bytes directly.
+
+ // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0. We need little endian.
+ .byte 0x91,0xfb,0xf1,0xf0
+
+ // if the divide worked, r0 will have the value #1 (result of sdiv).
+ // It will have 0 otherwise (set by the signal handler)
+ // the value is just returned from this function.
+ bx lr
+ END CheckForARMSDIVInstruction
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index ed3d476..cfffbea 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -25,7 +25,11 @@
#define rSELF r9
// Offset of field Thread::suspend_count_ verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
+// Offset of field Thread::card_table_ verified in InitCpu
+#define THREAD_CARD_TABLE_OFFSET 8
// Offset of field Thread::exception_ verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 12
+// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
+#define THREAD_ID_OFFSET 60
#endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 9e6902d..3dac636 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -42,10 +42,16 @@
extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t);
extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+
// Cast entrypoints.
extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
const mirror::Class* ref_class);
-extern "C" void art_quick_can_put_array_element(void*, void*);
extern "C" void art_quick_check_cast(void*, void*);
// DexCache entrypoints.
@@ -71,7 +77,10 @@
extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
extern "C" void* art_quick_get_obj_static(uint32_t);
-// FillArray entrypoint.
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
@@ -112,10 +121,10 @@
extern "C" int32_t art_quick_string_compareto(void*, void*);
// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
@@ -133,6 +142,30 @@
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+static bool quick_alloc_entry_points_instrumented = false;
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented) {
+ quick_alloc_entry_points_instrumented = instrumented;
+}
+
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
+ if (quick_alloc_entry_points_instrumented) {
+ qpoints->pAllocArray = art_quick_alloc_array_instrumented;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented;
+ qpoints->pAllocObject = art_quick_alloc_object_instrumented;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented;
+ } else {
+ qpoints->pAllocArray = art_quick_alloc_array;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
+ qpoints->pAllocObject = art_quick_alloc_object;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ }
+}
+
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
// Interpreter
@@ -147,16 +180,10 @@
ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
// Alloc
- qpoints->pAllocArray = art_quick_alloc_array;
- qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
- qpoints->pAllocObject = art_quick_alloc_object;
- qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ ResetQuickAllocEntryPoints(qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
- qpoints->pCanPutArrayElement = art_quick_can_put_array_element;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -179,7 +206,10 @@
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
- // FillArray
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
@@ -210,7 +240,7 @@
qpoints->pD2l = art_d2l;
qpoints->pF2l = art_f2l;
qpoints->pLdiv = __aeabi_ldivmod;
- qpoints->pLdivmod = __aeabi_ldivmod; // result returned in r2:r3
+ qpoints->pLmod = __aeabi_ldivmod; // result returned in r2:r3
qpoints->pLmul = art_quick_mul_long;
qpoints->pShlLong = art_quick_shl_long;
qpoints->pShrLong = art_quick_shr_long;
@@ -223,10 +253,10 @@
qpoints->pMemcpy = memcpy;
// Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a77ce01..9a853d0 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -48,7 +48,7 @@
/*
* Macro that sets up the callee save frame to conform with
- * Runtime::CreateCalleeSaveMethod(kRefsOnly). Restoration assumes non-moving GC.
+ * Runtime::CreateCalleeSaveMethod(kRefsOnly).
*/
.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
push {r5-r8, r10-r11, lr} @ 7 words of callee saves
@@ -67,21 +67,21 @@
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- ldr lr, [sp, #28] @ restore lr for return
- add sp, #32 @ unwind stack
+ add sp, #4 @ bottom word holds Method*
+ pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
.cfi_adjust_cfa_offset -32
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
- ldr lr, [sp, #28] @ restore lr for return
- add sp, #32 @ unwind stack
+ add sp, #4 @ bottom word holds Method*
+ pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
.cfi_adjust_cfa_offset -32
- bx lr @ return
+ bx lr @ return
.endm
/*
* Macro that sets up the callee save frame to conform with
- * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes non-moving GC.
+ * Runtime::CreateCalleeSaveMethod(kRefsAndArgs).
*/
.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
@@ -103,10 +103,8 @@
.endm
.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
- ldr r1, [sp, #8] @ restore non-callee save r1
- ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ rewind sp
+ add sp, #8 @ rewind sp
+ pop {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
.cfi_adjust_cfa_offset -48
.endm
@@ -152,6 +150,7 @@
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
b \cxx_name @ \cxx_name(Thread*, SP)
+ bkpt
END \c_name
.endm
@@ -162,6 +161,7 @@
mov r2, r9 @ pass Thread::Current
mov r3, sp @ pass SP
b \cxx_name @ \cxx_name(Thread*, SP)
+ bkpt
END \c_name
.endm
@@ -318,22 +318,68 @@
END art_quick_handle_fill_data
/*
- * Entry from managed code that calls artLockObjectFromCode, may block for GC.
+ * Entry from managed code that calls artLockObjectFromCode, may block for GC. r0 holds the
+ * possibly null object to lock.
*/
.extern artLockObjectFromCode
ENTRY art_quick_lock_object
+ cbz r0, slow_lock
+retry_lock:
+ ldr r2, [r9, #THREAD_ID_OFFSET]
+ ldrex r1, [r0, #LOCK_WORD_OFFSET]
+ cbnz r1, not_unlocked @ already thin locked
+ @ unlocked case - r2 holds thread id with count of 0
+ strex r3, r2, [r0, #LOCK_WORD_OFFSET]
+ cbnz r3, strex_fail @ store failed, retry
+ bx lr
+strex_fail:
+ b retry_lock @ unlikely forward branch, need to reload and recheck r1/r2
+not_unlocked:
+ lsr r3, r1, 30
+ cbnz r3, slow_lock @ if either of the top two bits are set, go slow path
+ eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId()
+ uxth r2, r2 @ zero top 16 bits
+ cbnz r2, slow_lock @ lock word and self thread id's match -> recursive lock
+ @ else contention, go to slow path
+ add r2, r1, #65536 @ increment count in lock word placing in r2 for storing
+ lsr r1, r2, 30 @ if either of the top two bits are set, we overflowed.
+ cbnz r1, slow_lock @ if we overflow the count go slow path
+ str r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock
+ bx lr
+slow_lock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case we block
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
bl artLockObjectFromCode @ (Object* obj, Thread*, SP)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_ZERO
+ DELIVER_PENDING_EXCEPTION
END art_quick_lock_object
/*
* Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
+ * r0 holds the possibly null object to lock.
*/
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object
+ cbz r0, slow_unlock
+ ldr r1, [r0, #LOCK_WORD_OFFSET]
+ lsr r2, r1, 30
+ cbnz r2, slow_unlock @ if either of the top two bits are set, go slow path
+ ldr r2, [r9, #THREAD_ID_OFFSET]
+ eor r3, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId()
+ uxth r3, r3 @ zero top 16 bits
+ cbnz r3, slow_unlock @ do lock word and self thread id's match?
+ cmp r1, #65536
+ bpl recursive_thin_unlock
+ @ transition to unlocked, r3 holds 0
+ str r3, [r0, #LOCK_WORD_OFFSET]
+ bx lr
+recursive_thin_unlock:
+ sub r1, r1, #65536
+ str r1, [r0, #LOCK_WORD_OFFSET]
+ bx lr
+slow_unlock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
@@ -344,33 +390,96 @@
END art_quick_unlock_object
/*
- * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
+ * Entry from managed code that calls artIsAssignableFromCode and on failure calls
+ * artThrowClassCastException.
*/
- .extern artCheckCastFromCode
+ .extern artThrowClassCastException
ENTRY art_quick_check_cast
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- bl artCheckCastFromCode @ (Class* a, Class* b, Thread*, SP)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ push {r0-r1, lr} @ save arguments, link register and pad
+ .save {r0-r1, lr}
+ .cfi_adjust_cfa_offset 12
+ .cfi_rel_offset r0, 0
+ .cfi_rel_offset r1, 4
+ .cfi_rel_offset lr, 8
+ sub sp, #4
+ .pad #4
+ .cfi_adjust_cfa_offset 4
+ bl artIsAssignableFromCode
+ cbz r0, throw_class_cast_exception
+ add sp, #4
+ .cfi_adjust_cfa_offset -4
+ pop {r0-r1, pc}
+throw_class_cast_exception:
+ add sp, #4
+ .cfi_adjust_cfa_offset -4
+ pop {r0-r1, lr}
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ b artThrowClassCastException @ (Class*, Class*, Thread*, SP)
+ bkpt
END art_quick_check_cast
/*
- * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on
- * failure.
+ * Entry from managed code for array put operations of objects where the value being stored
+ * needs to be checked for compatibility.
+ * r0 = array, r1 = index, r2 = value
*/
- .extern artCanPutArrayElementFromCode
-ENTRY art_quick_can_put_array_element
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- bl artCanPutArrayElementFromCode @ (Object* element, Class* array_class, Thread*, SP)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_can_put_array_element
+ENTRY art_quick_aput_obj_with_null_and_bound_check
+ tst r0, r0
+ bne art_quick_aput_obj_with_bound_check
+ b art_quick_throw_null_pointer_exception
+END art_quick_aput_obj_with_null_and_bound_check
+
+ENTRY art_quick_aput_obj_with_bound_check
+ ldr r3, [r0, #ARRAY_LENGTH_OFFSET]
+ cmp r3, r1
+ bhi art_quick_aput_obj
+ mov r0, r1
+ mov r1, r3
+ b art_quick_throw_array_bounds
+END art_quick_aput_obj_with_bound_check
+
+ENTRY art_quick_aput_obj
+ cbz r2, do_aput_null
+ ldr r3, [r0, #CLASS_OFFSET]
+ ldr ip, [r2, #CLASS_OFFSET]
+ ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET]
+ cmp r3, ip @ value's type == array's component type - trivial assignability
+ bne check_assignability
+do_aput:
+ add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+ str r2, [r3, r1, lsl #2]
+ ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
+ lsr r0, r0, #7
+ strb r3, [r3, r0]
+ blx lr
+do_aput_null:
+ add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+ str r2, [r3, r1, lsl #2]
+ blx lr
+check_assignability:
+ push {r0-r2, lr} @ save arguments
+ mov r1, ip
+ mov r0, r3
+ bl artIsAssignableFromCode
+ cbz r0, throw_array_store_exception
+ pop {r0-r2, lr}
+ add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+ str r2, [r3, r1, lsl #2]
+ ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
+ lsr r0, r0, #7
+ strb r3, [r3, r0]
+ blx lr
+throw_array_store_exception:
+ pop {r0-r2, lr}
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ mov r1, r2
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ b artThrowArrayStoreException @ (Class*, Class*, Thread*, SP)
+ bkpt @ unreached
+END art_quick_aput_obj
/*
* Entry from managed code when uninitialized static storage, this stub will run the class
@@ -706,6 +815,17 @@
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_object
+ .extern artAllocObjectFromCodeInstrumented
+ENTRY art_quick_alloc_object_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ bl artAllocObjectFromCodeInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_object_instrumented
+
/*
* Called by managed code to allocate an object when the caller doesn't know whether it has
* access to the created type.
@@ -721,6 +841,17 @@
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_object_with_access_check
+ .extern artAllocObjectFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_object_with_access_check_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ bl artAllocObjectFromCodeWithAccessCheckInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_object_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array.
*/
@@ -741,6 +872,23 @@
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_array
+ .extern artAllocArrayFromCodeInstrumented
+ENTRY art_quick_alloc_array_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ 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
+ @ artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP)
+ bl artAllocArrayFromCodeInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array when the caller doesn't know whether it has
* access to the created type.
@@ -762,6 +910,23 @@
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_array_with_access_check
+ .extern artAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_array_with_access_check_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ 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
+ @ artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, SP)
+ bl artAllocArrayFromCodeWithAccessCheckInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -782,6 +947,23 @@
DELIVER_PENDING_EXCEPTION
END art_quick_check_and_alloc_array
+ .extern artCheckAndAllocArrayFromCodeInstrumented
+ENTRY art_quick_check_and_alloc_array_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ 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
+ @ artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , SP)
+ bl artCheckAndAllocArrayFromCodeInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_check_and_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -802,6 +984,23 @@
DELIVER_PENDING_EXCEPTION
END art_quick_check_and_alloc_array_with_access_check
+ .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ 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
+ @ artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , SP)
+ bl artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_check_and_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
*/
@@ -832,15 +1031,28 @@
mov r3, sp @ pass SP
blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP)
ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ pop frame
- .cfi_adjust_cfa_offset -48
+ add sp, #16 @ skip r1-r3, 4 bytes padding.
+ .cfi_adjust_cfa_offset -16
cbnz r2, 1f @ success if no exception is pending
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bx lr @ return on success
1:
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+ /*
+ * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+ENTRY art_quick_imt_conflict_trampoline
+ ldr r0, [sp, #0] @ load caller Method*
+ ldr r0, [r0, #METHOD_DEX_CACHE_METHODS_OFFSET] @ load dex_cache_resolved_methods
+ add r0, #OBJECT_ARRAY_DATA_OFFSET @ get starting address of data
+ ldr r0, [r0, r12, lsl 2] @ load the target method
+ b art_quick_invoke_interface_trampoline
+END art_quick_imt_conflict_trampoline
+
.extern artQuickResolutionTrampoline
ENTRY art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -850,11 +1062,7 @@
cbz r0, 1f @ is code pointer null? goto exception
mov r12, r0
ldr r0, [sp, #0] @ load resolved method in r0
- ldr r1, [sp, #8] @ restore non-callee save r1
- ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ rewind sp
- .cfi_adjust_cfa_offset -48
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
bx r12 @ tail-call into actual code
1:
RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -868,12 +1076,13 @@
mov r2, sp @ pass SP
blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP)
ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ pop frame
- .cfi_adjust_cfa_offset -48
+ add sp, #16 @ skip r1-r3, 4 bytes padding.
+ .cfi_adjust_cfa_offset -16
cbnz r2, 1f @ success if no exception is pending
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bx lr @ return on success
1:
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc
index ea908be..8c1efeb 100644
--- a/runtime/arch/arm/thread_arm.cc
+++ b/runtime/arch/arm/thread_arm.cc
@@ -23,7 +23,9 @@
void Thread::InitCpu() {
CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_));
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
+ CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
}
} // namespace art
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 9a66352..5307997 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -25,6 +25,8 @@
#define rSELF $s1
// Offset of field Thread::suspend_count_ verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
+// Offset of field Thread::card_table_ verified in InitCpu
+#define THREAD_CARD_TABLE_OFFSET 8
// Offset of field Thread::exception_ verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 12
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 40d7cd9..331a461 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -41,10 +41,16 @@
extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t);
extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+
// Cast entrypoints.
extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
const mirror::Class* ref_class);
-extern "C" void art_quick_can_put_array_element(void*, void*);
extern "C" void art_quick_check_cast(void*, void*);
// DexCache entrypoints.
@@ -70,7 +76,10 @@
extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
extern "C" void* art_quick_get_obj_static(uint32_t);
-// FillArray entrypoint.
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
@@ -82,9 +91,9 @@
extern int32_t CmplDouble(double a, double b);
extern int32_t CmpgFloat(float a, float b);
extern int32_t CmplFloat(float a, float b);
-extern "C" int64_t artLmulFromCode(int64_t a, int64_t b);
-extern "C" int64_t artLdivFromCode(int64_t a, int64_t b);
-extern "C" int64_t artLdivmodFromCode(int64_t a, int64_t b);
+extern "C" int64_t artLmul(int64_t a, int64_t b);
+extern "C" int64_t artLdiv(int64_t a, int64_t b);
+extern "C" int64_t artLmod(int64_t a, int64_t b);
// Math conversions.
extern "C" int32_t __fixsfsi(float op1); // FLOAT_TO_INT
@@ -113,10 +122,10 @@
extern "C" int32_t art_quick_string_compareto(void*, void*);
// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
@@ -134,6 +143,30 @@
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+static bool quick_alloc_entry_points_instrumented = false;
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented) {
+ quick_alloc_entry_points_instrumented = instrumented;
+}
+
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
+ if (quick_alloc_entry_points_instrumented) {
+ qpoints->pAllocArray = art_quick_alloc_array_instrumented;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented;
+ qpoints->pAllocObject = art_quick_alloc_object_instrumented;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented;
+ } else {
+ qpoints->pAllocArray = art_quick_alloc_array;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
+ qpoints->pAllocObject = art_quick_alloc_object;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ }
+}
+
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
// Interpreter
@@ -148,16 +181,10 @@
ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
// Alloc
- qpoints->pAllocArray = art_quick_alloc_array;
- qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
- qpoints->pAllocObject = art_quick_alloc_object;
- qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ ResetQuickAllocEntryPoints(qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
- qpoints->pCanPutArrayElement = art_quick_can_put_array_element;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -180,7 +207,10 @@
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
- // FillArray
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
@@ -209,9 +239,9 @@
qpoints->pIdivmod = NULL;
qpoints->pD2l = art_d2l;
qpoints->pF2l = art_f2l;
- qpoints->pLdiv = artLdivFromCode;
- qpoints->pLdivmod = artLdivmodFromCode;
- qpoints->pLmul = artLmulFromCode;
+ qpoints->pLdiv = artLdiv;
+ qpoints->pLmod = artLmod;
+ qpoints->pLmul = artLmul;
qpoints->pShlLong = art_quick_shl_long;
qpoints->pShrLong = art_quick_shr_long;
qpoints->pUshrLong = art_quick_ushr_long;
@@ -223,10 +253,10 @@
qpoints->pMemcpy = memcpy;
// Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3d63ccc..451b1bb 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -88,15 +88,29 @@
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- lw $gp, 52($sp)
lw $ra, 60($sp)
+ lw $s8, 56($sp)
+ lw $gp, 52($sp)
+ lw $s7, 48($sp)
+ lw $s6, 44($sp)
+ lw $s5, 40($sp)
+ lw $s4, 36($sp)
+ lw $s3, 32($sp)
+ lw $s2, 28($sp)
addiu $sp, $sp, 64
.cfi_adjust_cfa_offset -64
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
- lw $gp, 52($sp)
lw $ra, 60($sp)
+ lw $s8, 56($sp)
+ lw $gp, 52($sp)
+ lw $s7, 48($sp)
+ lw $s6, 44($sp)
+ lw $s5, 40($sp)
+ lw $s4, 36($sp)
+ lw $s3, 32($sp)
+ lw $s2, 28($sp)
jr $ra
addiu $sp, $sp, 64
.cfi_adjust_cfa_offset -64
@@ -138,11 +152,18 @@
.endm
.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
- lw $ra, 60($sp) # restore $ra
- lw $gp, 52($sp) # restore $gp
- lw $a1, 4($sp) # restore non-callee save $a1
- lw $a2, 8($sp) # restore non-callee save $a2
- lw $a3, 12($sp) # restore non-callee save $a3
+ lw $ra, 60($sp)
+ lw $s8, 56($sp)
+ lw $gp, 52($sp)
+ lw $s7, 48($sp)
+ lw $s6, 44($sp)
+ lw $s5, 40($sp)
+ lw $s4, 36($sp)
+ lw $s3, 32($sp)
+ lw $s2, 28($sp)
+ lw $a3, 12($sp)
+ lw $a2, 8($sp)
+ lw $a1, 4($sp)
addiu $sp, $sp, 64 # pop frame
.cfi_adjust_cfa_offset -64
.endm
@@ -283,6 +304,7 @@
.extern artThrowNullPointerExceptionFromCode
ENTRY art_quick_throw_null_pointer_exception
GENERATE_GLOBAL_POINTER
+art_quick_throw_null_pointer_exception_gp_set:
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a0, rSELF # pass Thread::Current
la $t9, artThrowNullPointerExceptionFromCode
@@ -309,6 +331,7 @@
.extern artThrowArrayBoundsFromCode
ENTRY art_quick_throw_array_bounds
GENERATE_GLOBAL_POINTER
+art_quick_throw_array_bounds_gp_set:
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a2, rSELF # pass Thread::Current
la $t9, artThrowArrayBoundsFromCode
@@ -481,11 +504,13 @@
.extern artLockObjectFromCode
ENTRY art_quick_lock_object
GENERATE_GLOBAL_POINTER
+ beqz $a0, art_quick_throw_null_pointer_exception_gp_set
+ nop
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block
move $a1, rSELF # pass Thread::Current
jal artLockObjectFromCode # (Object* obj, Thread*, $sp)
move $a2, $sp # pass $sp
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+ RETURN_IF_ZERO
END art_quick_lock_object
/*
@@ -494,6 +519,8 @@
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object
GENERATE_GLOBAL_POINTER
+ beqz $a0, art_quick_throw_null_pointer_exception_gp_set
+ nop
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
move $a1, rSELF # pass Thread::Current
jal artUnlockObjectFromCode # (Object* obj, Thread*, $sp)
@@ -504,29 +531,116 @@
/*
* Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
*/
- .extern artCheckCastFromCode
+ .extern artThrowClassCastException
ENTRY art_quick_check_cast
GENERATE_GLOBAL_POINTER
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
- move $a2, rSELF # pass Thread::Current
- jal artCheckCastFromCode # (Class* a, Class* b, Thread*, $sp)
- move $a3, $sp # pass $sp
- RETURN_IF_ZERO
+ addiu $sp, $sp, -16
+ .cfi_adjust_cfa_offset 16
+ sw $ra, 12($sp)
+ .cfi_rel_offset 31, 12
+ sw $t9, 8($sp)
+ sw $a1, 4($sp)
+ sw $a0, 0($sp)
+ jal artIsAssignableFromCode
+ nop
+ beqz $v0, throw_class_cast_exception
+ lw $ra, 12($sp)
+ jr $ra
+ addiu $sp, $sp, 16
+ .cfi_adjust_cfa_offset -16
+throw_class_cast_exception:
+ lw $t9, 8($sp)
+ lw $a1, 4($sp)
+ lw $a0, 0($sp)
+ addiu $sp, $sp, 16
+ .cfi_adjust_cfa_offset -16
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ move $a2, rSELF # pass Thread::Current
+ la $t9, artThrowClassCastException
+ jr $t9 # artThrowClassCastException (Class*, Class*, Thread*, SP)
+ move $a3, $sp # pass $sp
END art_quick_check_cast
/*
- * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on
- * failure.
+ * Entry from managed code for array put operations of objects where the value being stored
+ * needs to be checked for compatibility.
+ * a0 = array, a1 = index, a2 = value
*/
- .extern artCanPutArrayElementFromCode
-ENTRY art_quick_can_put_array_element
+ENTRY art_quick_aput_obj_with_null_and_bound_check
GENERATE_GLOBAL_POINTER
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
- move $a2, rSELF # pass Thread::Current
- jal artCanPutArrayElementFromCode # (Object* element, Class* array_class, Thread*, $sp)
- move $a3, $sp # pass $sp
- RETURN_IF_ZERO
-END art_quick_can_put_array_element
+ bnez $a0, art_quick_aput_obj_with_bound_check_gp_set
+ nop
+ b art_quick_throw_null_pointer_exception_gp_set
+ nop
+END art_quick_aput_obj_with_null_and_bound_check
+
+ENTRY art_quick_aput_obj_with_bound_check
+ GENERATE_GLOBAL_POINTER
+art_quick_aput_obj_with_bound_check_gp_set:
+ lw $t0, ARRAY_LENGTH_OFFSET($a0)
+ sltu $t1, $a1, $t0
+ bnez $t1, art_quick_aput_obj_gp_set
+ nop
+ move $a0, $a1
+ b art_quick_throw_array_bounds_gp_set
+ move $a1, $t0
+END art_quick_aput_obj_with_bound_check
+
+ENTRY art_quick_aput_obj
+ GENERATE_GLOBAL_POINTER
+art_quick_aput_obj_gp_set:
+ beqz $a2, do_aput_null
+ nop
+ lw $t0, CLASS_OFFSET($a0)
+ lw $t1, CLASS_OFFSET($a2)
+ lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0)
+ bne $t1, $t0, check_assignability # value's type == array's component type - trivial assignability
+ nop
+do_aput:
+ sll $a1, $a1, 2
+ add $t0, $a0, $a1
+ sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
+ lw $t0, THREAD_CARD_TABLE_OFFSET(rSELF)
+ srl $t1, $a0, 7
+ add $t1, $t1, $t0
+ sb $t0, ($t1)
+ jr $ra
+ nop
+do_aput_null:
+ sll $a1, $a1, 2
+ add $t0, $a0, $a1
+ sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
+ jr $ra
+ nop
+check_assignability:
+ addiu $sp, $sp, -32
+ .cfi_adjust_cfa_offset 32
+ sw $ra, 28($sp)
+ .cfi_rel_offset 31, 28
+ sw $t9, 12($sp)
+ sw $a2, 8($sp)
+ sw $a1, 4($sp)
+ sw $a0, 0($sp)
+ move $a1, $t1
+ move $a0, $t0
+ jal artIsAssignableFromCode # (Class*, Class*)
+ nop
+ lw $ra, 28($sp)
+ lw $t9, 12($sp)
+ lw $a2, 8($sp)
+ lw $a1, 4($sp)
+ lw $a0, 0($sp)
+ add $sp, 32
+ .cfi_adjust_cfa_offset -32
+ bnez $v0, do_aput
+ nop
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ move $a1, $a2
+ move $a2, rSELF # pass Thread::Current
+ la $t9, artThrowArrayStoreException
+ jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*, SP)
+ move $a3, $sp # pass $sp
+END art_quick_aput_obj
/*
* Entry from managed code when uninitialized static storage, this stub will run the class
@@ -770,6 +884,16 @@
RETURN_IF_NONZERO
END art_quick_alloc_object
+ .extern artAllocObjectFromCodeInstrumented
+ENTRY art_quick_alloc_object_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a2, rSELF # pass Thread::Current
+ jal artAllocObjectFromCodeInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_object_instrumented
+
/*
* Called by managed code to allocate an object when the caller doesn't know whether it has
* access to the created type.
@@ -784,6 +908,16 @@
RETURN_IF_NONZERO
END art_quick_alloc_object_with_access_check
+ .extern artAllocObjectFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_object_with_access_check_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a2, rSELF # pass Thread::Current
+ jal artAllocObjectFromCodeWithAccessCheckInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_object_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array.
*/
@@ -798,6 +932,17 @@
RETURN_IF_NONZERO
END art_quick_alloc_array
+ .extern artAllocArrayFromCodeInstrumented
+ENTRY art_quick_alloc_array_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, $sp)
+ jal artAllocArrayFromCodeInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array when the caller doesn't know whether it has
* access to the created type.
@@ -813,6 +958,17 @@
RETURN_IF_NONZERO
END art_quick_alloc_array_with_access_check
+ .extern artAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_array_with_access_check_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, $sp)
+ jal artAllocArrayFromCodeWithAccessCheckInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -827,6 +983,17 @@
RETURN_IF_NONZERO
END art_quick_check_and_alloc_array
+ .extern artCheckAndAllocArrayFromCodeInstrumented
+ENTRY art_quick_check_and_alloc_array_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , $sp)
+ jal artCheckAndAllocArrayFromCodeInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_check_and_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -841,6 +1008,17 @@
RETURN_IF_NONZERO
END art_quick_check_and_alloc_array_with_access_check
+ .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , $sp)
+ jal artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_check_and_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
*/
@@ -873,17 +1051,30 @@
jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP)
move $a3, $sp # pass $sp
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
- lw $gp, 52($sp) # restore $gp
- lw $ra, 60($sp) # restore $ra
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bnez $t0, 1f
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
+ nop
jr $ra
nop
1:
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+ /*
+ * Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+ENTRY art_quick_imt_conflict_trampoline
+ GENERATE_GLOBAL_POINTER
+ lw $a0, 0($sp) # load caller Method*
+ lw $a0, METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods
+ sll $t0, 2 # convert target method offset to bytes
+ add $a0, $t0 # get address of target method
+ lw $a0, OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method
+ la $t9, art_quick_invoke_interface_trampoline
+ jr $t9
+END art_quick_imt_conflict_trampoline
+
.extern artQuickResolutionTrampoline
ENTRY art_quick_resolution_trampoline
GENERATE_GLOBAL_POINTER
@@ -891,18 +1082,14 @@
move $a2, rSELF # pass Thread::Current
jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP)
move $a3, $sp # pass $sp
- lw $gp, 52($sp) # restore $gp
- lw $ra, 60($sp) # restore $ra
beqz $v0, 1f
lw $a0, 0($sp) # load resolved method to $a0
- lw $a1, 4($sp) # restore non-callee save $a1
- lw $a2, 8($sp) # restore non-callee save $a2
- lw $a3, 12($sp) # restore non-callee save $a3
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
move $t9, $v0 # code pointer must be in $t9 to generate the global pointer
jr $v0 # tail call to method
+ nop
1:
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_resolution_trampoline
@@ -914,11 +1101,9 @@
jal artQuickToInterpreterBridge # (Method* method, Thread*, SP)
move $a2, $sp # pass $sp
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
- lw $gp, 52($sp) # restore $gp
- lw $ra, 60($sp) # restore $ra
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bnez $t0, 1f
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
+ nop
jr $ra
nop
1:
diff --git a/runtime/arch/mips/thread_mips.cc b/runtime/arch/mips/thread_mips.cc
index 7364de0..bd54549 100644
--- a/runtime/arch/mips/thread_mips.cc
+++ b/runtime/arch/mips/thread_mips.cc
@@ -23,6 +23,7 @@
void Thread::InitCpu() {
CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_));
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
}
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 1092910..e817ff7 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -21,7 +21,11 @@
// Offset of field Thread::self_ verified in InitCpu
#define THREAD_SELF_OFFSET 40
+// Offset of field Thread::card_table_ verified in InitCpu
+#define THREAD_CARD_TABLE_OFFSET 8
// Offset of field Thread::exception_ verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 12
+// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
+#define THREAD_ID_OFFSET 60
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index abc2990..99b0dd5 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -40,10 +40,16 @@
extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t);
extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+
// Cast entrypoints.
extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass,
const mirror::Class* ref_class);
-extern "C" void art_quick_can_put_array_element(void*, void*);
extern "C" void art_quick_check_cast(void*, void*);
// DexCache entrypoints.
@@ -66,7 +72,10 @@
extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
extern "C" void* art_quick_get_obj_static(uint32_t);
-// FillArray entrypoint.
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
@@ -82,7 +91,7 @@
extern "C" int64_t art_quick_f2l(float);
extern "C" int32_t art_quick_idivmod(int32_t, int32_t);
extern "C" int64_t art_quick_ldiv(int64_t, int64_t);
-extern "C" int64_t art_quick_ldivmod(int64_t, int64_t);
+extern "C" int64_t art_quick_lmod(int64_t, int64_t);
extern "C" int64_t art_quick_lmul(int64_t, int64_t);
extern "C" uint64_t art_quick_lshl(uint64_t, uint32_t);
extern "C" uint64_t art_quick_lshr(uint64_t, uint32_t);
@@ -95,10 +104,10 @@
extern "C" void* art_quick_memcpy(void*, const void*, size_t);
// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
@@ -116,6 +125,30 @@
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+static bool quick_alloc_entry_points_instrumented = false;
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented) {
+ quick_alloc_entry_points_instrumented = instrumented;
+}
+
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
+ if (quick_alloc_entry_points_instrumented) {
+ qpoints->pAllocArray = art_quick_alloc_array_instrumented;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented;
+ qpoints->pAllocObject = art_quick_alloc_object_instrumented;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented;
+ } else {
+ qpoints->pAllocArray = art_quick_alloc_array;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
+ qpoints->pAllocObject = art_quick_alloc_object;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ }
+}
+
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
// Interpreter
@@ -130,16 +163,10 @@
ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
// Alloc
- qpoints->pAllocArray = art_quick_alloc_array;
- qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
- qpoints->pAllocObject = art_quick_alloc_object;
- qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ ResetQuickAllocEntryPoints(qpoints);
// Cast
qpoints->pInstanceofNonTrivial = art_quick_is_assignable;
- qpoints->pCanPutArrayElement = art_quick_can_put_array_element;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -162,7 +189,10 @@
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
- // FillArray
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
@@ -192,7 +222,7 @@
qpoints->pD2l = art_quick_d2l;
qpoints->pF2l = art_quick_f2l;
qpoints->pLdiv = art_quick_ldiv;
- qpoints->pLdivmod = art_quick_ldivmod;
+ qpoints->pLmod = art_quick_lmod;
qpoints->pLmul = art_quick_lmul;
qpoints->pShlLong = art_quick_lshl;
qpoints->pShrLong = art_quick_lshr;
@@ -205,10 +235,10 @@
qpoints->pMemcpy = art_quick_memcpy;
// Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index dbf552f..6fe4993 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -41,7 +41,10 @@
END_MACRO
MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME)
- addl MACRO_LITERAL(28), %esp // Unwind stack up to return address
+ addl MACRO_LITERAL(16), %esp // Unwind stack up to return address
+ POP ebp // Restore callee saves (ebx is saved/restored by the upcall)
+ POP esi
+ POP edi
.cfi_adjust_cfa_offset -28
END_MACRO
@@ -389,26 +392,207 @@
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO
+
TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_EAX_NOT_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_EAX_NOT_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO
-ONE_ARG_DOWNCALL art_quick_lock_object, artLockObjectFromCode, ret
-ONE_ARG_DOWNCALL art_quick_unlock_object, artUnlockObjectFromCode, RETURN_IF_EAX_ZERO
-
TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
+DEFINE_FUNCTION art_quick_lock_object
+ testl %eax, %eax // null check object/eax
+ jz slow_lock
+retry_lock:
+ movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word
+ test LITERAL(0xC0000000), %ecx // test the 2 high bits.
+ jne slow_lock // slow path if either of the two high bits are set.
+ movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id
+ test %ecx, %ecx
+ jnz already_thin // lock word contains a thin lock
+ // unlocked case - %edx holds thread id with count of 0
+ movl %eax, %ecx // remember object in case of retry
+ xor %eax, %eax // eax == 0 for comparison with lock word in cmpxchg
+ lock cmpxchg %edx, LOCK_WORD_OFFSET(%ecx)
+ jnz cmpxchg_fail // cmpxchg failed retry
+ ret
+cmpxchg_fail:
+ movl %ecx, %eax // restore eax
+ jmp retry_lock
+already_thin:
+ cmpw %ax, %dx // do we hold the lock already?
+ jne slow_lock
+ addl LITERAL(65536), %eax // increment recursion count
+ test LITERAL(0xC0000000), %eax // overflowed if either of top two bits are set
+ jne slow_lock // count overflowed so go slow
+ movl %eax, LOCK_WORD_OFFSET(%ecx) // update lockword, cmpxchg not necessary as we hold lock
+ ret
+slow_lock:
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %edx // remember SP
+ // Outgoing argument set up
+ PUSH eax // push padding
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH eax // pass object
+ call artLockObjectFromCode // artLockObjectFromCode(object, 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
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_lock_object
+
+DEFINE_FUNCTION art_quick_unlock_object
+ testl %eax, %eax // null check object/eax
+ jz slow_unlock
+ movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word
+ movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id
+ test %ecx, %ecx
+ jb slow_unlock // lock word contains a monitor
+ cmpw %cx, %dx // does the thread id match?
+ jne slow_unlock
+ cmpl LITERAL(65536), %ecx
+ jae recursive_thin_unlock
+ movl LITERAL(0), LOCK_WORD_OFFSET(%eax)
+ ret
+recursive_thin_unlock:
+ subl LITERAL(65536), %ecx
+ mov %ecx, LOCK_WORD_OFFSET(%eax)
+ ret
+slow_unlock:
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %edx // remember SP
+ // Outgoing argument set up
+ PUSH eax // push padding
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH eax // pass object
+ call artUnlockObjectFromCode // artUnlockObjectFromCode(object, 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
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_unlock_object
+
DEFINE_FUNCTION art_quick_is_assignable
PUSH eax // alignment padding
- PUSH ecx // pass arg2
- PUSH eax // pass arg1
- call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b, Thread*, SP)
+ PUSH ecx // pass arg2 - obj->klass
+ PUSH eax // pass arg1 - checked class
+ call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass)
addl LITERAL(12), %esp // pop arguments
.cfi_adjust_cfa_offset -12
ret
END_FUNCTION art_quick_is_assignable
+DEFINE_FUNCTION art_quick_check_cast
+ PUSH eax // alignment padding
+ PUSH ecx // pass arg2 - obj->klass
+ PUSH eax // pass arg1 - checked class
+ call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass)
+ testl %eax, %eax
+ jz 1f // jump forward if not assignable
+ addl LITERAL(12), %esp // pop arguments
+ .cfi_adjust_cfa_offset -12
+ ret
+1:
+ POP eax // pop arguments
+ POP ecx
+ addl LITERAL(4), %esp
+ .cfi_adjust_cfa_offset -12
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov %esp, %edx
+ // Outgoing argument set up
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH ecx // pass arg2
+ PUSH eax // pass arg1
+ call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+ int3 // unreached
+END_FUNCTION art_quick_check_cast
+
+ /*
+ * Entry from managed code for array put operations of objects where the value being stored
+ * needs to be checked for compatibility.
+ * eax = array, ecx = index, edx = value
+ */
+DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
+ testl %eax, %eax
+ jnz art_quick_aput_obj_with_bound_check
+ jmp art_quick_throw_null_pointer_exception
+END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
+
+DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
+ movl ARRAY_LENGTH_OFFSET(%eax), %ebx
+ cmpl %ebx, %ecx
+ jb art_quick_aput_obj
+ mov %ecx, %eax
+ mov %ebx, %ecx
+ jmp art_quick_throw_array_bounds
+END_FUNCTION art_quick_aput_obj_with_bound_check
+
+DEFINE_FUNCTION art_quick_aput_obj
+ test %edx, %edx // store of null
+ jz do_aput_null
+ movl CLASS_OFFSET(%eax), %ebx
+ movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability
+ jne check_assignability
+do_aput:
+ movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
+ movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
+ shrl LITERAL(7), %eax
+ movb %dl, (%edx, %eax)
+ ret
+do_aput_null:
+ movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
+ ret
+check_assignability:
+ PUSH eax // save arguments
+ PUSH ecx
+ PUSH edx
+ subl LITERAL(8), %esp // alignment padding
+ .cfi_adjust_cfa_offset 8
+ 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
+ call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
+ addl LITERAL(16), %esp // pop arguments
+ .cfi_adjust_cfa_offset -16
+ testl %eax, %eax
+ jz throw_array_store_exception
+ POP edx
+ POP ecx
+ POP eax
+ movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput
+ movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
+ shrl LITERAL(7), %eax
+ movb %dl, (%edx, %eax)
+ ret
+throw_array_store_exception:
+ POP edx
+ POP ecx
+ POP eax
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov %esp, %ecx
+ // Outgoing argument set up
+ PUSH ecx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH edx // pass arg2 - value
+ PUSH eax // pass arg1 - array
+ call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
+ int3 // unreached
+END_FUNCTION art_quick_aput_obj
+
DEFINE_FUNCTION art_quick_memcpy
PUSH edx // pass arg3
PUSH ecx // pass arg2
@@ -419,9 +603,6 @@
ret
END_FUNCTION art_quick_memcpy
-TWO_ARG_DOWNCALL art_quick_check_cast, artCheckCastFromCode, RETURN_IF_EAX_ZERO
-TWO_ARG_DOWNCALL art_quick_can_put_array_element, artCanPutArrayElementFromCode, RETURN_IF_EAX_ZERO
-
NO_ARG_DOWNCALL art_quick_test_suspend, artTestSuspendFromCode, ret
DEFINE_FUNCTION art_quick_fmod
@@ -508,30 +689,30 @@
END_FUNCTION art_quick_idivmod
DEFINE_FUNCTION art_quick_ldiv
- subl LITERAL(12), %esp // alignment padding
+ subl LITERAL(12), %esp // alignment padding
.cfi_adjust_cfa_offset 12
PUSH ebx // pass arg4 b.hi
PUSH edx // pass arg3 b.lo
PUSH ecx // pass arg2 a.hi
- PUSH eax // pass arg1 a.lo
- call SYMBOL(artLdivFromCode) // (jlong a, jlong b)
- addl LITERAL(28), %esp // pop arguments
+ PUSH eax // pass arg1 a.lo
+ call SYMBOL(artLdiv) // (jlong a, jlong b)
+ addl LITERAL(28), %esp // pop arguments
.cfi_adjust_cfa_offset -28
ret
END_FUNCTION art_quick_ldiv
-DEFINE_FUNCTION art_quick_ldivmod
- subl LITERAL(12), %esp // alignment padding
+DEFINE_FUNCTION art_quick_lmod
+ subl LITERAL(12), %esp // alignment padding
.cfi_adjust_cfa_offset 12
PUSH ebx // pass arg4 b.hi
PUSH edx // pass arg3 b.lo
PUSH ecx // pass arg2 a.hi
- PUSH eax // pass arg1 a.lo
- call SYMBOL(artLdivmodFromCode) // (jlong a, jlong b)
- addl LITERAL(28), %esp // pop arguments
+ PUSH eax // pass arg1 a.lo
+ call SYMBOL(artLmod) // (jlong a, jlong b)
+ addl LITERAL(28), %esp // pop arguments
.cfi_adjust_cfa_offset -28
ret
-END_FUNCTION art_quick_ldivmod
+END_FUNCTION art_quick_lmod
DEFINE_FUNCTION art_quick_lmul
imul %eax, %ebx // ebx = a.lo(eax) * b.hi(ebx)
@@ -818,6 +999,20 @@
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_proxy_invoke_handler
+ /*
+ * Called to resolve an imt conflict. xmm0 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+DEFINE_FUNCTION art_quick_imt_conflict_trampoline
+ PUSH ecx
+ movl 8(%esp), %eax // load caller Method*
+ movl METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods
+ movd %xmm0, %ecx // get target method index stored in xmm0
+ movl OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method
+ POP ecx
+ jmp art_quick_invoke_interface_trampoline
+END_FUNCTION art_quick_imt_conflict_trampoline
+
DEFINE_FUNCTION art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
PUSH esp // pass SP
@@ -855,8 +1050,9 @@
movd %eax, %xmm0 // place return value also into floating point return value
movd %edx, %xmm1
punpckldq %xmm1, %xmm0
- addl LITERAL(44), %esp // pop arguments
- .cfi_adjust_cfa_offset -44
+ addl LITERAL(16), %esp // pop arguments
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_to_interpreter_bridge
diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc
index dd3e7dd..39bad58 100644
--- a/runtime/arch/x86/thread_x86.cc
+++ b/runtime/arch/x86/thread_x86.cc
@@ -21,7 +21,7 @@
#include "asm_support_x86.h"
#include "base/macros.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "thread_list.h"
#if defined(__APPLE__)
@@ -134,6 +134,8 @@
// Sanity check other offsets.
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
+ CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
}
} // namespace art
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index aca93a5..e9bbf91 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -21,13 +21,25 @@
// check.
#define SUSPEND_CHECK_INTERVAL (1000)
+// Offsets within java.lang.Object.
+#define CLASS_OFFSET 0
+#define LOCK_WORD_OFFSET 4
+
+// Offsets within java.lang.Class.
+#define CLASS_COMPONENT_TYPE_OFFSET 12
+
+// Array offsets.
+#define ARRAY_LENGTH_OFFSET 8
+#define OBJECT_ARRAY_DATA_OFFSET 12
+
// Offsets within java.lang.String.
#define STRING_VALUE_OFFSET 8
#define STRING_COUNT_OFFSET 12
#define STRING_OFFSET_OFFSET 20
#define STRING_DATA_OFFSET 12
-// Offset of field Method::entry_point_from_compiled_code_
+// Offsets within java.lang.Method.
+#define METHOD_DEX_CACHE_METHODS_OFFSET 16
#define METHOD_CODE_OFFSET 40
#endif // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/atomic.cc b/runtime/atomic.cc
index c91db79..47cee6a 100644
--- a/runtime/atomic.cc
+++ b/runtime/atomic.cc
@@ -23,7 +23,7 @@
#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
-#include "thread.h"
+#include "thread-inl.h"
#endif
namespace art {
diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc
new file mode 100644
index 0000000..4f7753d
--- /dev/null
+++ b/runtime/base/allocator.cc
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#include "allocator.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+
+namespace art {
+
+class MallocAllocator : public Allocator {
+ public:
+ explicit MallocAllocator() {}
+ ~MallocAllocator() {}
+
+ virtual void* Alloc(size_t size) {
+ return calloc(sizeof(uint8_t), size);
+ }
+
+ virtual void Free(void* p) {
+ free(p);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MallocAllocator);
+};
+
+MallocAllocator g_malloc_allocator;
+
+class NoopAllocator : public Allocator {
+ public:
+ explicit NoopAllocator() {}
+ ~NoopAllocator() {}
+
+ virtual void* Alloc(size_t size) {
+ LOG(FATAL) << "NoopAllocator::Alloc should not be called";
+ return NULL;
+ }
+
+ virtual void Free(void* p) {
+ // Noop.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoopAllocator);
+};
+
+NoopAllocator g_noop_allocator;
+
+Allocator* Allocator::GetMallocAllocator() {
+ return &g_malloc_allocator;
+}
+
+Allocator* Allocator::GetNoopAllocator() {
+ return &g_noop_allocator;
+}
+
+
+} // namespace art
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
new file mode 100644
index 0000000..917bf0b
--- /dev/null
+++ b/runtime/base/allocator.h
@@ -0,0 +1,41 @@
+/*
+ * 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_ALLOCATOR_H_
+#define ART_RUNTIME_BASE_ALLOCATOR_H_
+
+#include "base/macros.h"
+
+namespace art {
+
+class Allocator {
+ public:
+ static Allocator* GetMallocAllocator();
+ static Allocator* GetNoopAllocator();
+
+ Allocator() {}
+ virtual ~Allocator() {}
+
+ virtual void* Alloc(size_t) = 0;
+ virtual void Free(void*) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Allocator);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_ALLOCATOR_H_
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
new file mode 100644
index 0000000..3b82651
--- /dev/null
+++ b/runtime/base/bit_vector.cc
@@ -0,0 +1,177 @@
+/*
+ * 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 "bit_vector.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;
+}
+
+// TODO: replace excessive argument defaulting when we are at gcc 4.7
+// or later on host with delegating constructor support. Specifically,
+// starts_bits and storage_size/storage are mutually exclusive.
+BitVector::BitVector(uint32_t start_bits,
+ bool expandable,
+ Allocator* allocator,
+ uint32_t storage_size,
+ uint32_t* storage)
+ : allocator_(allocator),
+ expandable_(expandable),
+ storage_size_(storage_size),
+ storage_(storage) {
+ DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units.
+ if (storage_ == NULL) {
+ storage_size_ = BitsToWords(start_bits);
+ storage_ = static_cast<uint32_t*>(allocator_->Alloc(storage_size_ * sizeof(uint32_t)));
+ }
+}
+
+BitVector::~BitVector() {
+ allocator_->Free(storage_);
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool BitVector::IsBitSet(uint32_t num) const {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+
+ uint32_t val = storage_[num >> 5] & check_masks[num & 0x1f];
+ return (val != 0);
+}
+
+// Mark all bits bit as "clear".
+void BitVector::ClearAllBits() {
+ memset(storage_, 0, storage_size_ * sizeof(uint32_t));
+}
+
+// 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_ * sizeof(uint32_t) * 8) {
+ 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 * sizeof(uint32_t)));
+ memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t));
+ // Zero out the new storage words.
+ memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t));
+ // 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) {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+ storage_[num >> 5] &= ~check_masks[num & 0x1f];
+}
+
+// Intersect with another bit vector. Sizes and expandability must be the same.
+void BitVector::Intersect(const BitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ DCHECK_EQ(expandable_, src->IsExpandable());
+ for (uint32_t idx = 0; idx < storage_size_; idx++) {
+ storage_[idx] &= src->GetRawStorageWord(idx);
+ }
+}
+
+/*
+ * Union with another bit vector. Sizes and expandability must be the same.
+ */
+void BitVector::Union(const BitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ DCHECK_EQ(expandable_, src->IsExpandable());
+ for (uint32_t idx = 0; idx < storage_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++) {
+ count += __builtin_popcount(storage_[word]);
+ }
+ return count;
+}
+
+// Count the number of bits that are set up through and including num.
+uint32_t BitVector::NumSetBits(uint32_t num) const {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+ uint32_t last_word = num >> 5;
+ uint32_t partial_word_bits = num & 0x1f;
+
+ // partial_word_bits | # | | | partial_word_mask
+ // 00000 | 0 | 0xffffffff >> (31 - 0) | (1 << (0 + 1)) - 1 | 0x00000001
+ // 00001 | 1 | 0xffffffff >> (31 - 1) | (1 << (1 + 1)) - 1 | 0x00000003
+ // 00010 | 2 | 0xffffffff >> (31 - 2) | (1 << (2 + 1)) - 1 | 0x00000007
+ // ..... |
+ // 11110 | 30 | 0xffffffff >> (31 - 30) | (1 << (30 + 1)) - 1 | 0x7fffffff
+ // 11111 | 31 | 0xffffffff >> (31 - 31) | last_full_word++ | 0xffffffff
+ uint32_t partial_word_mask = 0xffffffff >> (0x1f - partial_word_bits);
+
+ uint32_t count = 0;
+ for (uint32_t word = 0; word < last_word; word++) {
+ count += __builtin_popcount(storage_[word]);
+ }
+ count += __builtin_popcount(storage_[last_word] & partial_word_mask);
+ return count;
+}
+
+BitVector::Iterator* BitVector::GetIterator() const {
+ return new (allocator_) Iterator(this);
+}
+
+/*
+ * 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) {
+ DCHECK_LE(BitsToWords(num_bits), storage_size_);
+ uint32_t idx;
+ for (idx = 0; idx < (num_bits >> 5); idx++) {
+ storage_[idx] = -1;
+ }
+ uint32_t rem_num_bits = num_bits & 0x1f;
+ if (rem_num_bits) {
+ storage_[idx] = (1 << rem_num_bits) - 1;
+ }
+}
+
+} // namespace art
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
new file mode 100644
index 0000000..74bec08
--- /dev/null
+++ b/runtime/base/bit_vector.h
@@ -0,0 +1,134 @@
+/*
+ * 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_H_
+#define ART_RUNTIME_BASE_BIT_VECTOR_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "allocator.h"
+#include "base/logging.h"
+#include "utils.h"
+
+namespace art {
+
+/*
+ * Expanding bitmap, used for tracking resources. Bits are numbered starting
+ * from zero. All operations on a BitVector are unsynchronized.
+ */
+class BitVector {
+ public:
+ class Iterator {
+ public:
+ explicit Iterator(const BitVector* bit_vector)
+ : p_bits_(bit_vector),
+ bit_storage_(bit_vector->GetRawStorage()),
+ bit_index_(0),
+ bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {}
+
+ // Return the position of the next set bit. -1 means end-of-element reached.
+ int32_t Next() {
+ // Did anything obviously change since we started?
+ DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8);
+ DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage());
+
+ if (UNLIKELY(bit_index_ >= bit_size_)) return -1;
+
+ uint32_t word_index = bit_index_ / 32;
+ uint32_t word = bit_storage_[word_index];
+ // Mask out any bits in the first word we've already considered.
+ word >>= bit_index_ & 0x1f;
+ if (word == 0) {
+ bit_index_ &= ~0x1f;
+ do {
+ word_index++;
+ if (UNLIKELY((word_index * 32) >= bit_size_)) {
+ bit_index_ = bit_size_;
+ return -1;
+ }
+ word = bit_storage_[word_index];
+ bit_index_ += 32;
+ } while (word == 0);
+ }
+ bit_index_ += CTZ(word) + 1;
+ return bit_index_ - 1;
+ }
+
+ static void* operator new(size_t size, Allocator* allocator) {
+ return allocator->Alloc(sizeof(BitVector::Iterator));
+ };
+ static void operator delete(void* p) {
+ Iterator* it = reinterpret_cast<Iterator*>(p);
+ it->p_bits_->allocator_->Free(p);
+ }
+
+ private:
+ const BitVector* const p_bits_;
+ const uint32_t* const bit_storage_;
+ uint32_t bit_index_; // Current index (size in bits).
+ const uint32_t bit_size_; // Size of vector in bits.
+
+ friend class BitVector;
+ };
+
+ BitVector(uint32_t start_bits,
+ bool expandable,
+ Allocator* allocator,
+ uint32_t storage_size = 0,
+ uint32_t* storage = NULL);
+
+ 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(BitVector* src) {
+ memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_);
+ }
+ void Intersect(const BitVector* src2);
+ void Union(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);
+ }
+ uint32_t NumSetBits() const;
+ uint32_t NumSetBits(uint32_t num) const;
+
+ Iterator* GetIterator() const;
+
+ 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_ * sizeof(uint32_t); }
+
+ private:
+ 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_;
+};
+
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_VECTOR_H_
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
new file mode 100644
index 0000000..d99d059
--- /dev/null
+++ b/runtime/base/bit_vector_test.cc
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#include "UniquePtr.h"
+#include "bit_vector.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(BitVector, Test) {
+ const size_t kBits = 32;
+
+ BitVector bv(kBits, false, Allocator::GetMallocAllocator());
+ EXPECT_EQ(1U, bv.GetStorageSize());
+ EXPECT_EQ(kWordSize, bv.GetSizeOf());
+ EXPECT_FALSE(bv.IsExpandable());
+
+ EXPECT_EQ(0U, bv.NumSetBits());
+ EXPECT_EQ(0U, bv.NumSetBits(0));
+ EXPECT_EQ(0U, bv.NumSetBits(kBits - 1));
+ for (size_t i = 0; i < kBits; i++) {
+ EXPECT_FALSE(bv.IsBitSet(i));
+ }
+ EXPECT_EQ(0U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0U, *bv.GetRawStorage());
+
+ BitVector::Iterator empty_iterator(&bv);
+ EXPECT_EQ(-1, empty_iterator.Next());
+
+ UniquePtr<BitVector::Iterator> empty_iterator_on_heap(bv.GetIterator());
+ EXPECT_EQ(-1, empty_iterator_on_heap->Next());
+
+ bv.SetBit(0);
+ bv.SetBit(kBits - 1);
+ EXPECT_EQ(2U, bv.NumSetBits());
+ EXPECT_EQ(1U, bv.NumSetBits(0));
+ EXPECT_EQ(2U, bv.NumSetBits(kBits - 1));
+ EXPECT_TRUE(bv.IsBitSet(0));
+ for (size_t i = 1; i < kBits - 1; i++) {
+ EXPECT_FALSE(bv.IsBitSet(i));
+ }
+ EXPECT_TRUE(bv.IsBitSet(kBits - 1));
+ EXPECT_EQ(0x80000001U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x80000001U, *bv.GetRawStorage());
+
+ BitVector::Iterator iterator(&bv);
+ EXPECT_EQ(0, iterator.Next());
+ EXPECT_EQ(static_cast<int>(kBits - 1), iterator.Next());
+ EXPECT_EQ(-1, iterator.Next());
+}
+
+TEST(BitVector, NoopAllocator) {
+ const uint32_t kWords = 2;
+
+ uint32_t bits[kWords];
+ memset(bits, 0, sizeof(bits));
+
+ BitVector bv(0U, false, Allocator::GetNoopAllocator(), kWords, bits);
+ EXPECT_EQ(kWords, bv.GetStorageSize());
+ EXPECT_EQ(kWords * kWordSize, bv.GetSizeOf());
+ EXPECT_EQ(bits, bv.GetRawStorage());
+ EXPECT_EQ(0U, bv.NumSetBits());
+
+ bv.SetBit(8);
+ EXPECT_EQ(1U, bv.NumSetBits());
+ EXPECT_EQ(0x00000100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(1U, bv.NumSetBits());
+
+ bv.SetBit(16);
+ EXPECT_EQ(2U, bv.NumSetBits());
+ EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(2U, bv.NumSetBits());
+
+ bv.SetBit(32);
+ EXPECT_EQ(3U, bv.NumSetBits());
+ EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00000001U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(3U, bv.NumSetBits());
+
+ bv.SetBit(48);
+ EXPECT_EQ(4U, bv.NumSetBits());
+ EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00010001U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(4U, bv.NumSetBits());
+
+ EXPECT_EQ(0U, bv.NumSetBits(0));
+
+ EXPECT_EQ(0U, bv.NumSetBits(7));
+ EXPECT_EQ(1U, bv.NumSetBits(8));
+ EXPECT_EQ(1U, bv.NumSetBits(9));
+
+ EXPECT_EQ(1U, bv.NumSetBits(15));
+ EXPECT_EQ(2U, bv.NumSetBits(16));
+ EXPECT_EQ(2U, bv.NumSetBits(17));
+
+ EXPECT_EQ(2U, bv.NumSetBits(31));
+ EXPECT_EQ(3U, bv.NumSetBits(32));
+ EXPECT_EQ(3U, bv.NumSetBits(33));
+
+ EXPECT_EQ(3U, bv.NumSetBits(47));
+ EXPECT_EQ(4U, bv.NumSetBits(48));
+ EXPECT_EQ(4U, bv.NumSetBits(49));
+
+ EXPECT_EQ(4U, bv.NumSetBits(63));
+}
+
+} // namespace art
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 7d54baf..3d842a0 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -18,7 +18,7 @@
#include "base/mutex.h"
#include "runtime.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "utils.h"
namespace art {
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 6531858..00a530a 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -130,6 +130,10 @@
#define LIKELY(x) __builtin_expect((x), true)
#define UNLIKELY(x) __builtin_expect((x), false)
+// Stringify the argument.
+#define QUOTE(x) #x
+#define STRINGIFY(x) QUOTE(x)
+
#ifndef NDEBUG
#define ALWAYS_INLINE
#else
@@ -138,8 +142,10 @@
#if defined (__APPLE__)
#define HOT_ATTR
+#define COLD_ATTR
#else
#define HOT_ATTR __attribute__ ((hot))
+#define COLD_ATTR __attribute__ ((cold))
#endif
#define PURE __attribute__ ((__pure__))
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 7e8365e..c0cfee2 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -41,6 +41,54 @@
}
#endif // ART_USE_FUTEXES
+#if defined(__APPLE__)
+
+// This works on Mac OS 10.6 but hasn't been tested on older releases.
+struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
+ long padding0; // NOLINT(runtime/int) exact match to darwin type
+ int padding1;
+ uint32_t padding2;
+ int16_t padding3;
+ int16_t padding4;
+ uint32_t padding5;
+ pthread_t darwin_pthread_mutex_owner;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t {
+ long padding0; // NOLINT(runtime/int) exact match to darwin type
+ pthread_mutex_t padding1;
+ int padding2;
+ pthread_cond_t padding3;
+ pthread_cond_t padding4;
+ int padding5;
+ int padding6;
+ pthread_t darwin_pthread_rwlock_owner;
+ // ...other stuff we don't care about.
+};
+
+#endif // __APPLE__
+
+#if defined(__GLIBC__)
+
+struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
+ int32_t padding0[2];
+ int owner;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t {
+#ifdef __LP64__
+ int32_t padding0[6];
+#else
+ int32_t padding0[7];
+#endif
+ int writer;
+ // ...other stuff we don't care about.
+};
+
+#endif // __GLIBC__
+
class ScopedContentionRecorder {
public:
ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid)
@@ -185,6 +233,84 @@
#endif
}
+inline bool Mutex::IsExclusiveHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
+ if (kDebugLocking) {
+ // Sanity debug check that if we think it is locked we have it in our held mutexes.
+ if (result && self != NULL && level_ != kMonitorLock && !gAborting) {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
+ }
+ return result;
+}
+
+inline uint64_t Mutex::GetExclusiveOwnerTid() const {
+#if ART_USE_FUTEXES
+ return exclusive_owner_;
+#elif defined(__BIONIC__)
+ return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
+#elif defined(__GLIBC__)
+ return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner;
+#elif defined(__APPLE__)
+ const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_);
+ pthread_t owner = dpmutex->darwin_pthread_mutex_owner;
+ // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING
+ // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1?
+ if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) {
+ return 0;
+ }
+ uint64_t tid;
+ CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
+ return tid;
+#else
+#error unsupported C library
+#endif
+}
+
+inline bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
+ if (kDebugLocking) {
+ // Sanity that if the pthread thinks we own the lock the Thread agrees.
+ if (self != NULL && result) {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
+ }
+ return result;
+}
+
+inline uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const {
+#if ART_USE_FUTEXES
+ int32_t state = state_;
+ if (state == 0) {
+ return 0; // No owner.
+ } else if (state > 0) {
+ return -1; // Shared.
+ } else {
+ return exclusive_owner_;
+ }
+#else
+#if defined(__BIONIC__)
+ return rwlock_.writerThreadId;
+#elif defined(__GLIBC__)
+ return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer;
+#elif defined(__APPLE__)
+ const darwin_pthread_rwlock_t*
+ dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_);
+ pthread_t owner = dprwlock->darwin_pthread_rwlock_owner;
+ if (owner == (pthread_t)0) {
+ return 0;
+ }
+ uint64_t tid;
+ CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
+ return tid;
+#else
+#error unsupported C library
+#endif
+#endif
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_MUTEX_INL_H_
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index b99e7c9..249f031 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -31,54 +31,6 @@
namespace art {
-#if defined(__APPLE__)
-
-// This works on Mac OS 10.6 but hasn't been tested on older releases.
-struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
- long padding0; // NOLINT(runtime/int) exact match to darwin type
- int padding1;
- uint32_t padding2;
- int16_t padding3;
- int16_t padding4;
- uint32_t padding5;
- pthread_t darwin_pthread_mutex_owner;
- // ...other stuff we don't care about.
-};
-
-struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t {
- long padding0; // NOLINT(runtime/int) exact match to darwin type
- pthread_mutex_t padding1;
- int padding2;
- pthread_cond_t padding3;
- pthread_cond_t padding4;
- int padding5;
- int padding6;
- pthread_t darwin_pthread_rwlock_owner;
- // ...other stuff we don't care about.
-};
-
-#endif // __APPLE__
-
-#if defined(__GLIBC__)
-
-struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
- int32_t padding0[2];
- int owner;
- // ...other stuff we don't care about.
-};
-
-struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t {
-#ifdef __LP64__
- int32_t padding0[6];
-#else
- int32_t padding0[7];
-#endif
- int writer;
- // ...other stuff we don't care about.
-};
-
-#endif // __GLIBC__
-
#if ART_USE_FUTEXES
static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds.
@@ -102,17 +54,17 @@
std::set<BaseMutex*>* all_mutexes;
AllMutexData() : all_mutexes(NULL) {}
};
-static struct AllMutexData all_mutex_data[kAllMutexDataSize];
+static struct AllMutexData gAllMutexData[kAllMutexDataSize];
class ScopedAllMutexesLock {
public:
explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) {
- while (!all_mutex_data->all_mutexes_guard.compare_and_swap(0, reinterpret_cast<int32_t>(mutex))) {
+ while (!gAllMutexData->all_mutexes_guard.compare_and_swap(0, reinterpret_cast<int32_t>(mutex))) {
NanoSleep(100);
}
}
~ScopedAllMutexesLock() {
- while (!all_mutex_data->all_mutexes_guard.compare_and_swap(reinterpret_cast<int32_t>(mutex_), 0)) {
+ while (!gAllMutexData->all_mutexes_guard.compare_and_swap(reinterpret_cast<int32_t>(mutex_), 0)) {
NanoSleep(100);
}
}
@@ -123,7 +75,7 @@
BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {
if (kLogLockContentions) {
ScopedAllMutexesLock mu(this);
- std::set<BaseMutex*>** all_mutexes_ptr = &all_mutex_data->all_mutexes;
+ std::set<BaseMutex*>** all_mutexes_ptr = &gAllMutexData->all_mutexes;
if (*all_mutexes_ptr == NULL) {
// We leak the global set of all mutexes to avoid ordering issues in global variable
// construction/destruction.
@@ -136,7 +88,7 @@
BaseMutex::~BaseMutex() {
if (kLogLockContentions) {
ScopedAllMutexesLock mu(this);
- all_mutex_data->all_mutexes->erase(this);
+ gAllMutexData->all_mutexes->erase(this);
}
}
@@ -144,13 +96,13 @@
if (kLogLockContentions) {
os << "Mutex logging:\n";
ScopedAllMutexesLock mu(reinterpret_cast<const BaseMutex*>(-1));
- std::set<BaseMutex*>* all_mutexes = all_mutex_data->all_mutexes;
+ std::set<BaseMutex*>* all_mutexes = gAllMutexData->all_mutexes;
if (all_mutexes == NULL) {
// No mutexes have been created yet during at startup.
return;
}
typedef std::set<BaseMutex*>::const_iterator It;
- os << "(Contented)\n";
+ os << "(Contended)\n";
for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) {
BaseMutex* mutex = *it;
if (mutex->HasEverContended()) {
@@ -175,7 +127,8 @@
return;
}
if (kDebugLocking) {
- CHECK(self->GetHeldMutex(level_) == this) << "Waiting on unacquired mutex: " << name_;
+ CHECK(self->GetHeldMutex(level_) == this || level_ == kMonitorLock)
+ << "Waiting on unacquired mutex: " << name_;
bool bad_mutexes_held = false;
for (int i = kLockLevelCount - 1; i >= 0; --i) {
if (i != level_) {
@@ -346,7 +299,7 @@
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == 0) {
+ if (LIKELY(cur_state == 0)) {
// Change state from 0 to 1.
done = android_atomic_acquire_cas(0, 1, &state_) == 0;
} else {
@@ -432,14 +385,14 @@
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == 1) {
+ if (LIKELY(cur_state == 1)) {
// We're no longer the owner.
exclusive_owner_ = 0;
// Change state to 0.
done = android_atomic_release_cas(cur_state, 0, &state_) == 0;
- if (done) { // Spurious fail?
+ if (LIKELY(done)) { // Spurious fail?
// Wake a contender
- if (num_contenders_ > 0) {
+ if (UNLIKELY(num_contenders_ > 0)) {
futex(&state_, FUTEX_WAKE, 1, NULL, NULL, 0);
}
}
@@ -461,41 +414,6 @@
}
}
-bool Mutex::IsExclusiveHeld(const Thread* self) const {
- DCHECK(self == NULL || self == Thread::Current());
- bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
- if (kDebugLocking) {
- // Sanity debug check that if we think it is locked we have it in our held mutexes.
- if (result && self != NULL && level_ != kMonitorLock && !gAborting) {
- CHECK_EQ(self->GetHeldMutex(level_), this);
- }
- }
- return result;
-}
-
-uint64_t Mutex::GetExclusiveOwnerTid() const {
-#if ART_USE_FUTEXES
- return exclusive_owner_;
-#elif defined(__BIONIC__)
- return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
-#elif defined(__GLIBC__)
- return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner;
-#elif defined(__APPLE__)
- const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_);
- pthread_t owner = dpmutex->darwin_pthread_mutex_owner;
- // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING
- // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1?
- if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) {
- return 0;
- }
- uint64_t tid;
- CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
- return tid;
-#else
-#error unsupported C library
-#endif
-}
-
void Mutex::Dump(std::ostream& os) const {
os << (recursive_ ? "recursive " : "non-recursive ")
<< name_
@@ -549,7 +467,7 @@
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == 0) {
+ if (LIKELY(cur_state == 0)) {
// Change state from 0 to -1.
done = android_atomic_acquire_cas(0, -1, &state_) == 0;
} else {
@@ -583,14 +501,14 @@
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == -1) {
+ if (LIKELY(cur_state == -1)) {
// We're no longer the owner.
exclusive_owner_ = 0;
// Change state from -1 to 0.
done = android_atomic_release_cas(-1, 0, &state_) == 0;
- if (done) { // cmpxchg may fail due to noise?
+ if (LIKELY(done)) { // cmpxchg may fail due to noise?
// Wake any waiters.
- if (num_pending_readers_ > 0 || num_pending_writers_ > 0) {
+ if (UNLIKELY(num_pending_readers_ > 0 || num_pending_writers_ > 0)) {
futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0);
}
}
@@ -687,18 +605,6 @@
return true;
}
-bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const {
- DCHECK(self == NULL || self == Thread::Current());
- bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
- if (kDebugLocking) {
- // Sanity that if the pthread thinks we own the lock the Thread agrees.
- if (self != NULL && result) {
- CHECK_EQ(self->GetHeldMutex(level_), this);
- }
- }
- return result;
-}
-
bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const {
DCHECK(self == NULL || self == Thread::Current());
bool result;
@@ -710,37 +616,6 @@
return result;
}
-uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const {
-#if ART_USE_FUTEXES
- int32_t state = state_;
- if (state == 0) {
- return 0; // No owner.
- } else if (state > 0) {
- return -1; // Shared.
- } else {
- return exclusive_owner_;
- }
-#else
-#if defined(__BIONIC__)
- return rwlock_.writerThreadId;
-#elif defined(__GLIBC__)
- return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer;
-#elif defined(__APPLE__)
- const darwin_pthread_rwlock_t*
- dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_);
- pthread_t owner = dprwlock->darwin_pthread_rwlock_owner;
- if (owner == (pthread_t)0) {
- return 0;
- }
- uint64_t tid;
- CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
- return tid;
-#else
-#error unsupported C library
-#endif
-#endif
-}
-
void ReaderWriterMutex::Dump(std::ostream& os) const {
os << name_
<< " level=" << static_cast<int>(level_)
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index ee37388..feb8a6c 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -58,7 +58,7 @@
// futex.
const bool kLogLockContentions = false;
#endif
-const size_t kContentionLogSize = 64;
+const size_t kContentionLogSize = 4;
const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0;
const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0;
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index 11dc542..6df1126 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -22,7 +22,7 @@
#include "timing_logger.h"
#include "base/logging.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "base/stl_util.h"
#include "base/histogram-inl.h"
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 36f8ba7..f48c76d 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -102,10 +102,6 @@
return fd_ >= 0;
}
-std::string FdFile::GetPath() const {
- return file_path_;
-}
-
bool FdFile::ReadFully(void* buffer, int64_t byte_count) {
char* ptr = static_cast<char*>(buffer);
while (byte_count > 0) {
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 79a0db9..19e3511 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -57,7 +57,9 @@
// Bonus API.
int Fd() const;
bool IsOpened() const;
- std::string GetPath() const;
+ const std::string& GetPath() const {
+ return file_path_;
+ }
void DisableAutoClose();
bool ReadFully(void* buffer, int64_t byte_count);
bool WriteFully(const void* buffer, int64_t byte_count);
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 54f3143..54cbfe6 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -90,12 +90,6 @@
reinterpret_cast<JNIEnvExt*>(env)->self->SirtContains(localRef);
}
-// Hack to allow forcecopy to work with jniGetNonMovableArrayElements.
-// The code deliberately uses an invalid sequence of operations, so we
-// need to pass it through unmodified. Review that code before making
-// any changes here.
-#define kNoCopyMagic 0xd5aab57f
-
// Flags passed into ScopedCheck.
#define kFlag_Default 0x0000
@@ -335,7 +329,7 @@
return;
}
mirror::Class* c = soa_.Decode<mirror::Class*>(java_class);
- if (!c->IsAssignableFrom(m->GetDeclaringClass())) {
+ if (!m->GetDeclaringClass()->IsAssignableFrom(c)) {
JniAbortF(function_name_, "can't call static %s on class %s",
PrettyMethod(m).c_str(), PrettyClass(c).c_str());
}
@@ -1098,10 +1092,6 @@
* 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) {
- if (reinterpret_cast<uintptr_t>(dataBuf) == kNoCopyMagic) {
- return;
- }
-
ScopedObjectAccess soa(env);
mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
@@ -1596,9 +1586,7 @@
template<typename ResultT>
ResultT Check(JNIEnv* env, jarray array, jboolean* isCopy, ResultT result) {
if (force_copy && result != NULL) {
- if (no_copy != kNoCopyMagic) {
- result = reinterpret_cast<ResultT>(CreateGuardedPACopy(env, array, isCopy));
- }
+ result = reinterpret_cast<ResultT>(CreateGuardedPACopy(env, array, isCopy));
}
return result;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f072820..184e5d4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -196,7 +196,9 @@
class_table_dirty_(false),
intern_table_(intern_table),
portable_resolution_trampoline_(NULL),
- quick_resolution_trampoline_(NULL) {
+ quick_resolution_trampoline_(NULL),
+ portable_imt_conflict_trampoline_(NULL),
+ quick_imt_conflict_trampoline_(NULL) {
CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
}
@@ -283,7 +285,7 @@
SirtRef<mirror::Class>
java_lang_DexCache(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::DexCacheClass)));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.get());
- java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCacheClass));
+ java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCache));
java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self);
// Constructor, Field, Method, and AbstractMethod are necessary so that FindClass can link members.
@@ -336,6 +338,12 @@
InitializePrimitiveClass(char_class.get(), Primitive::kPrimChar);
SetClassRoot(kPrimitiveChar, char_class.get()); // needs descriptor
+ // Create runtime resolution and imt conflict methods. Also setup the default imt.
+ Runtime* runtime = Runtime::Current();
+ runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
+ runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
+ runtime->SetDefaultImt(runtime->CreateDefaultImt(this));
+
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
@@ -540,9 +548,10 @@
}
}
-bool ClassLinker::GenerateOatFile(const std::string& dex_filename,
+bool ClassLinker::GenerateOatFile(const char* dex_filename,
int oat_fd,
- const std::string& oat_cache_filename) {
+ const char* oat_cache_filename) {
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
std::string dex2oat_string(GetAndroidRoot());
dex2oat_string += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat");
const char* dex2oat = dex2oat_string.c_str();
@@ -567,7 +576,8 @@
const char* oat_location_option = oat_location_option_string.c_str();
std::string oat_compiler_filter_string("-compiler-filter:");
- switch (Runtime::Current()->GetCompilerFilter()) {
+ Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter();
+ switch (filter) {
case Runtime::kInterpretOnly:
oat_compiler_filter_string += "interpret-only";
break;
@@ -584,7 +594,7 @@
oat_compiler_filter_string += "everything";
break;
default:
- LOG(FATAL) << "Unexpected case.";
+ LOG(FATAL) << "Unexpected case: " << filter;
}
const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str();
@@ -633,49 +643,55 @@
int status;
pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (got_pid != pid) {
- PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+ ScopedObjectAccess soa(Thread::Current());
+ ThrowIOException("Failed to create oat file. Waitpid failed: wanted %d, got %d", pid,
+ got_pid);
return false;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << dex2oat << " failed with dex-file=" << dex_filename;
+ ScopedObjectAccess soa(Thread::Current());
+ ThrowIOException("Failed to create oat file. %s failed with dex-file '%s'", dex2oat,
+ dex_filename);
return false;
}
}
return true;
}
-void ClassLinker::RegisterOatFile(const OatFile& oat_file) {
+const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
WriterMutexLock mu(Thread::Current(), dex_lock_);
- RegisterOatFileLocked(oat_file);
-}
-
-void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) {
- dex_lock_.AssertExclusiveHeld(Thread::Current());
- if (kIsDebugBuild) {
- for (size_t i = 0; i < oat_files_.size(); ++i) {
- CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation();
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ if (UNLIKELY(oat_file->GetLocation() == oat_files_[i]->GetLocation())) {
+ VLOG(class_linker) << "Attempt to register oat file that's already registered: "
+ << oat_file->GetLocation();
+ for (size_t j = i; j < oat_files_.size(); ++j) {
+ CHECK_NE(oat_file, oat_files_[j]) << "Attempt to re-register dex file.";
+ }
+ delete oat_file;
+ return oat_files_[i];
}
}
- VLOG(class_linker) << "Registering " << oat_file.GetLocation();
- oat_files_.push_back(&oat_file);
+ VLOG(class_linker) << "Registering " << oat_file->GetLocation();
+ oat_files_.push_back(oat_file);
+ return oat_file;
}
OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) {
VLOG(startup) << "ClassLinker::GetImageOatFile entering";
- OatFile& oat_file = space->ReleaseOatFile();
- WriterMutexLock mu(Thread::Current(), dex_lock_);
- RegisterOatFileLocked(oat_file);
+ OatFile* oat_file = space->ReleaseOatFile();
+ CHECK_EQ(RegisterOatFile(oat_file), oat_file);
VLOG(startup) << "ClassLinker::GetImageOatFile exiting";
- return oat_file;
+ return *oat_file;
}
const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- return FindOpenedOatFileFromDexLocation(dex_file.GetLocation(), dex_file.GetLocationChecksum());
+ return FindOpenedOatFileFromDexLocation(dex_file.GetLocation().c_str(),
+ dex_file.GetLocationChecksum());
}
-const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const std::string& dex_location,
+const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const char* dex_location,
uint32_t dex_location_checksum) {
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (size_t i = 0; i < oat_files_.size(); i++) {
const OatFile* oat_file = oat_files_[i];
DCHECK(oat_file != NULL);
@@ -689,82 +705,83 @@
return NULL;
}
-const DexFile* ClassLinker::FindDexFileInOatLocation(const std::string& dex_location,
+const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location,
uint32_t dex_location_checksum,
- const std::string& oat_location) {
+ const char* oat_location,
+ std::string* error_msg) {
UniquePtr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, NULL,
- !Runtime::Current()->IsCompiler()));
- if (oat_file.get() == NULL) {
- VLOG(class_linker) << "Failed to find existing oat file at " << oat_location;
- return NULL;
+ !Runtime::Current()->IsCompiler(),
+ error_msg));
+ if (oat_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
+ error_msg->c_str());
+ return nullptr;
}
Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader();
uint32_t expected_image_oat_checksum = image_header.GetOatChecksum();
uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum();
if (expected_image_oat_checksum != actual_image_oat_checksum) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location
- << " with expected image oat checksum of " << expected_image_oat_checksum
- << ", found " << actual_image_oat_checksum;
- return NULL;
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of "
+ "0x%x, found 0x%x", oat_location, expected_image_oat_checksum,
+ actual_image_oat_checksum);
+ return nullptr;
}
uint32_t expected_image_oat_offset = reinterpret_cast<uint32_t>(image_header.GetOatDataBegin());
uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin();
if (expected_image_oat_offset != actual_image_oat_offset) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location
- << " with expected image oat offset " << expected_image_oat_offset
- << ", found " << actual_image_oat_offset;
- return NULL;
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %ud, "
+ "found %ud", oat_location, expected_image_oat_offset,
+ actual_image_oat_offset);
+ return nullptr;
}
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
- if (oat_dex_file == NULL) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location << " containing " << dex_location;
- return NULL;
+ // TODO: this registers the oat file now as we may use the oat_dex_file later and we want the
+ // intern behavior of RegisterOatFile. However, if we take an early return we could remove
+ // the oat file.
+ const OatFile* opened_oat_file = RegisterOatFile(oat_file.release());
+ const OatFile::OatDexFile* oat_dex_file = opened_oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location,
+ dex_location);
+ return nullptr;
}
uint32_t expected_dex_checksum = dex_location_checksum;
uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum();
if (expected_dex_checksum != actual_dex_checksum) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location
- << " with expected dex checksum of " << expected_dex_checksum
- << ", found " << actual_dex_checksum;
- return NULL;
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, "
+ "found 0x%x", oat_location, expected_dex_checksum,
+ actual_dex_checksum);
+ return nullptr;
}
- RegisterOatFileLocked(*oat_file.release());
- return oat_dex_file->OpenDexFile();
-}
-
-const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const std::string& dex_location,
- uint32_t dex_location_checksum,
- const std::string& oat_location) {
- WriterMutexLock mu(Thread::Current(), dex_lock_);
- return FindOrCreateOatFileForDexLocationLocked(dex_location, dex_location_checksum, oat_location);
+ return oat_dex_file->OpenDexFile(error_msg);
}
class ScopedFlock {
public:
ScopedFlock() {}
- bool Init(const std::string& filename) {
+ bool Init(const char* filename, std::string* error_msg) {
while (true) {
- file_.reset(OS::OpenFileWithFlags(filename.c_str(), O_CREAT | O_RDWR));
+ file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR));
if (file_.get() == NULL) {
- LOG(ERROR) << "Failed to open file: " << filename;
+ *error_msg = StringPrintf("Failed to open file '%s'", filename);
return false;
}
int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX));
if (flock_result != 0) {
- PLOG(ERROR) << "Failed to lock file: " << filename;
+ *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno));
return false;
}
struct stat fstat_stat;
int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat));
if (fstat_result != 0) {
- PLOG(ERROR) << "Failed to fstat: " << filename;
+ *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno));
return false;
}
struct stat stat_stat;
- int stat_result = TEMP_FAILURE_RETRY(stat(filename.c_str(), &stat_stat));
+ int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat));
if (stat_result != 0) {
PLOG(WARNING) << "Failed to stat, will retry: " << filename;
// ENOENT can happen if someone racing with us unlinks the file we created so just retry.
@@ -795,49 +812,54 @@
DISALLOW_COPY_AND_ASSIGN(ScopedFlock);
};
-const DexFile* ClassLinker::FindOrCreateOatFileForDexLocationLocked(const std::string& dex_location,
- uint32_t dex_location_checksum,
- const std::string& oat_location) {
+const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const char* dex_location,
+ uint32_t dex_location_checksum,
+ const char* oat_location,
+ std::string* error_msg) {
// We play a locking game here so that if two different processes
// race to generate (or worse, one tries to open a partial generated
// file) we will be okay. This is actually common with apps that use
// DexClassLoader to work around the dex method reference limit and
// that have a background service running in a separate process.
ScopedFlock scoped_flock;
- if (!scoped_flock.Init(oat_location)) {
- LOG(ERROR) << "Failed to open locked oat file: " << oat_location;
- return NULL;
+ if (!scoped_flock.Init(oat_location, error_msg)) {
+ return nullptr;
}
// Check if we already have an up-to-date output file
- const DexFile* dex_file = FindDexFileInOatLocation(dex_location,
- dex_location_checksum,
- oat_location);
- if (dex_file != NULL) {
+ const DexFile* dex_file = FindDexFileInOatLocation(dex_location, dex_location_checksum,
+ oat_location, error_msg);
+ if (dex_file != nullptr) {
return dex_file;
}
+ VLOG(class_linker) << "Failed to find dex file '" << dex_location << "' in oat location '"
+ << oat_location << "': " << *error_msg;
+ error_msg->clear();
// Generate the output oat file for the dex file
VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
if (!GenerateOatFile(dex_location, scoped_flock.GetFile().Fd(), oat_location)) {
- LOG(ERROR) << "Failed to generate oat file: " << oat_location;
- return NULL;
+ CHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
}
const OatFile* oat_file = OatFile::Open(oat_location, oat_location, NULL,
- !Runtime::Current()->IsCompiler());
- if (oat_file == NULL) {
- LOG(ERROR) << "Failed to open generated oat file: " << oat_location;
- return NULL;
+ !Runtime::Current()->IsCompiler(),
+ error_msg);
+ if (oat_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open generated oat file '%s': %s",
+ oat_location, error_msg->c_str());
+ return nullptr;
}
- RegisterOatFileLocked(*oat_file);
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
- if (oat_dex_file == NULL) {
- LOG(ERROR) << "Failed to find dex file " << dex_location
- << " (checksum " << dex_location_checksum
- << ") in generated oat file: " << oat_location;
- return NULL;
+ oat_file = RegisterOatFile(oat_file);
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to find dex file '%s' (checksum 0x%x) in generated out file "
+ "'%s'", dex_location, dex_location_checksum, oat_location);
+ return nullptr;
}
- const DexFile* result = oat_dex_file->OpenDexFile();
+ const DexFile* result = oat_dex_file->OpenDexFile(error_msg);
+ CHECK(result != nullptr) << *error_msg;
CHECK_EQ(dex_location_checksum, result->GetLocationChecksum())
<< "dex_location=" << dex_location << " oat_location=" << oat_location << std::hex
<< " dex_location_checksum=" << dex_location_checksum
@@ -846,8 +868,9 @@
}
bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum) {
+ const char* dex_location,
+ uint32_t dex_location_checksum,
+ std::string* error_msg) {
Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader();
uint32_t image_oat_checksum = image_header.GetOatChecksum();
@@ -857,14 +880,14 @@
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
if (oat_dex_file == NULL) {
- LOG(ERROR) << "oat file " << oat_file->GetLocation()
- << " does not contain contents for " << dex_location
- << " with checksum " << dex_location_checksum;
+ *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
+ oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
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];
- LOG(ERROR) << "oat file " << oat_file->GetLocation()
- << " contains contents for " << oat_dex_file->GetDexFileLocation();
+ *error_msg += StringPrintf("\noat file '%s' contains contents for '%s'",
+ oat_file->GetLocation().c_str(),
+ oat_dex_file->GetDexFileLocation().c_str());
}
return false;
}
@@ -875,116 +898,123 @@
}
if (!image_check) {
- std::string image_file(image_header.GetImageRoot(
- ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8());
- LOG(WARNING) << "oat file " << oat_file->GetLocation()
- << " mismatch (" << std::hex << oat_file->GetOatHeader().GetImageFileLocationOatChecksum()
- << ", " << oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
- << ") with " << image_file
- << " (" << image_oat_checksum << ", " << std::hex << image_oat_data_begin << ")";
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::String* oat_location = image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString();
+ std::string image_file(oat_location->ToModifiedUtf8());
+ *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d) with '%s' (0x%x, %d)",
+ oat_file->GetLocation().c_str(),
+ oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
+ oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
+ image_file.c_str(), image_oat_checksum, image_oat_data_begin);
}
if (!dex_check) {
- LOG(WARNING) << "oat file " << oat_file->GetLocation()
- << " mismatch (" << std::hex << oat_dex_file->GetDexFileLocationChecksum()
- << ") with " << dex_location
- << " (" << std::hex << dex_location_checksum << ")";
+ *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;
}
-const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum) {
- bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum);
- if (!verified) {
- delete oat_file;
- return NULL;
+const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location,
+ const char* dex_location,
+ std::string* error_msg,
+ bool* open_failed) {
+ UniquePtr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg));
+ if (oat_file.get() == nullptr) {
+ *open_failed = true;
+ return nullptr;
}
- RegisterOatFileLocked(*oat_file);
- return oat_file->GetOatDexFile(dex_location, &dex_location_checksum)->OpenDexFile();
+ *open_failed = false;
+ uint32_t dex_location_checksum;
+ if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
+ // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
+ // up-to-date. This is the common case in user builds for jar's and apk's in the /system
+ // directory.
+ const OatFile* opened_oat_file = oat_file.release();
+ opened_oat_file = RegisterOatFile(opened_oat_file);
+ const OatFile::OatDexFile* oat_dex_file = opened_oat_file->GetOatDexFile(dex_location, NULL);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
+ "dex file '%s': %s", oat_file_location.c_str(), dex_location,
+ error_msg->c_str());
+ return nullptr;
+ }
+ return oat_dex_file->OpenDexFile(error_msg);
+ }
+
+ bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum,
+ error_msg);
+ if (!verified) {
+ return nullptr;
+ }
+ const OatFile* opened_oat_file = oat_file.release();
+ opened_oat_file = RegisterOatFile(opened_oat_file);
+ return opened_oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum)->OpenDexFile(error_msg);
}
-const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const std::string& dex_location,
- uint32_t dex_location_checksum) {
- WriterMutexLock mu(Thread::Current(), dex_lock_);
-
+const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_location,
+ uint32_t dex_location_checksum,
+ std::string* error_msg) {
const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location,
dex_location_checksum);
- if (open_oat_file != NULL) {
- return open_oat_file->GetOatDexFile(dex_location, &dex_location_checksum)->OpenDexFile();
+ if (open_oat_file != nullptr) {
+ const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum);
+ return oat_dex_file->OpenDexFile(error_msg);
}
// Look for an existing file next to dex. for example, for
// /foo/bar/baz.jar, look for /foo/bar/baz.odex.
std::string odex_filename(OatFile::DexFilenameToOdexFilename(dex_location));
- UniquePtr<const OatFile> oat_file(FindOatFileFromOatLocationLocked(odex_filename));
- if (oat_file.get() != NULL) {
- uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
- // If no classes.dex found in dex_location, it has been stripped, assume oat is up-to-date.
- // This is the common case in user builds for jar's and apk's in the /system directory.
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, NULL);
- CHECK(oat_dex_file != NULL) << odex_filename << " " << dex_location;
- RegisterOatFileLocked(*oat_file);
- return oat_dex_file->OpenDexFile();
- }
- const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(oat_file.release(),
- dex_location,
- dex_location_checksum);
- if (dex_file != NULL) {
- return dex_file;
- }
+ bool open_failed;
+ const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(odex_filename, dex_location,
+ error_msg, &open_failed);
+ if (dex_file != nullptr) {
+ return dex_file;
}
- // Look for an existing file in the dalvik-cache, validating the result if found
- // not found in /foo/bar/baz.odex? try /data/dalvik-cache/foo@bar@baz.jar@classes.dex
+ std::string cache_error_msg;
std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location));
- oat_file.reset(FindOatFileFromOatLocationLocked(cache_location));
- if (oat_file.get() != NULL) {
- uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
- LOG(WARNING) << "Failed to compute checksum: " << dex_location;
- return NULL;
- }
- const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(oat_file.release(),
- dex_location,
- dex_location_checksum);
- if (dex_file != NULL) {
- return dex_file;
- }
- if (TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
- PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location;
- }
+ dex_file = VerifyAndOpenDexFileFromOatFile(cache_location, dex_location, &cache_error_msg,
+ &open_failed);
+ if (dex_file != nullptr) {
+ return dex_file;
}
- LOG(INFO) << "Failed to open oat file from " << odex_filename << " or " << cache_location << ".";
+ if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
+ PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location;
+ }
+ VLOG(class_linker) << "Failed to open oat file from " << odex_filename
+ << " (error '" << *error_msg << "') or " << cache_location
+ << " (error '" << cache_error_msg << "').";
// Try to generate oat file if it wasn't found or was obsolete.
- std::string oat_cache_filename(GetDalvikCacheFilenameOrDie(dex_location));
- return FindOrCreateOatFileForDexLocationLocked(dex_location, dex_location_checksum, oat_cache_filename);
+ error_msg->clear();
+ return FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum,
+ cache_location.c_str(), error_msg);
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (size_t i = 0; i < oat_files_.size(); i++) {
const OatFile* oat_file = oat_files_[i];
- DCHECK(oat_file != NULL);
+ DCHECK(oat_file != nullptr);
if (oat_file->GetLocation() == oat_location) {
return oat_file;
}
}
- return NULL;
+ return nullptr;
}
-const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- return FindOatFileFromOatLocationLocked(oat_location);
-}
-
-const OatFile* ClassLinker::FindOatFileFromOatLocationLocked(const std::string& oat_location) {
+const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
+ std::string* error_msg) {
const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
- if (oat_file != NULL) {
+ if (oat_file != nullptr) {
return oat_file;
}
- oat_file = OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler());
+ oat_file = OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler(),
+ error_msg);
if (oat_file == NULL) {
return NULL;
}
@@ -1023,6 +1053,8 @@
CHECK(oat_file.GetOatHeader().GetImageFileLocation().empty());
portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline();
quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
+ portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
+ quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
@@ -1041,12 +1073,15 @@
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
SirtRef<mirror::DexCache> dex_cache(self, dex_caches->Get(i));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
- const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location, NULL);
+ const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
+ nullptr);
CHECK(oat_dex_file != NULL) << oat_file.GetLocation() << " " << dex_file_location;
- const DexFile* dex_file = oat_dex_file->OpenDexFile();
+ std::string error_msg;
+ const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file == NULL) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
- << " from within oat file " << oat_file.GetLocation();
+ << " from within oat file " << oat_file.GetLocation()
+ << " error '" << error_msg << "'";
}
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
@@ -1094,13 +1129,14 @@
// reinit references to when reinitializing a ClassLinker from a
// mapped image.
void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool clean_dirty) {
- visitor(class_roots_, arg);
+ class_roots_ = down_cast<mirror::ObjectArray<mirror::Class>*>(visitor(class_roots_, arg));
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
if (!only_dirty || dex_caches_dirty_) {
- for (mirror::DexCache* dex_cache : dex_caches_) {
- visitor(dex_cache, arg);
+ for (mirror::DexCache*& dex_cache : dex_caches_) {
+ dex_cache = down_cast<mirror::DexCache*>(visitor(dex_cache, arg));
+ DCHECK(dex_cache != nullptr);
}
if (clean_dirty) {
dex_caches_dirty_ = false;
@@ -1111,8 +1147,9 @@
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
if (!only_dirty || class_table_dirty_) {
- for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
- visitor(it.second, arg);
+ for (std::pair<const size_t, mirror::Class*>& it : class_table_) {
+ it.second = down_cast<mirror::Class*>(visitor(it.second, arg));
+ DCHECK(it.second != nullptr);
}
if (clean_dirty) {
class_table_dirty_ = false;
@@ -1123,7 +1160,8 @@
// handle image roots by using the MS/CMS rescanning of dirty cards.
}
- visitor(array_iftable_, arg);
+ array_iftable_ = reinterpret_cast<mirror::IfTable*>(visitor(array_iftable_, arg));
+ DCHECK(array_iftable_ != nullptr);
}
void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
@@ -1507,7 +1545,7 @@
const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
CHECK(oat_file != NULL) << dex_file.GetLocation();
uint dex_location_checksum = dex_file.GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation(),
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
&dex_location_checksum);
CHECK(oat_dex_file != NULL) << dex_file.GetLocation();
const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_idx);
@@ -1630,21 +1668,22 @@
}
void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) {
- ClassHelper kh(klass);
- const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
- CHECK(dex_class_def != NULL);
- const DexFile& dex_file = kh.GetDexFile();
- const byte* class_data = dex_file.GetClassData(*dex_class_def);
- if (class_data == NULL) {
- return; // no fields or methods - for example a marker interface
+ if (klass->NumDirectMethods() == 0) {
+ return; // No direct methods => no static methods.
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) {
- // OAT file unavailable
- return;
+ return; // OAT file unavailable.
}
+ ClassHelper kh(klass);
+ const DexFile& dex_file = kh.GetDexFile();
+ const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
+ CHECK(dex_class_def != nullptr);
+ const byte* class_data = dex_file.GetClassData(*dex_class_def);
+ // There should always be class data if there were direct methods.
+ CHECK(class_data != nullptr) << PrettyDescriptor(klass);
UniquePtr<const OatFile::OatClass> oat_class(GetOatClass(dex_file, klass->GetDexClassDefIndex()));
- CHECK(oat_class.get() != NULL);
+ CHECK(oat_class.get() != nullptr);
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
while (it.HasNextStaticField()) {
@@ -1653,7 +1692,7 @@
while (it.HasNextInstanceField()) {
it.Next();
}
- // Link the code of methods skipped by LinkCode
+ // 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);
if (!method->IsStatic()) {
@@ -1849,7 +1888,7 @@
SirtRef<mirror::Class>& klass) {
uint32_t dex_method_idx = it.GetMemberIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
- StringPiece method_name(dex_file.GetMethodName(method_id));
+ const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
mirror::ArtMethod* dst = AllocArtMethod(self);
if (UNLIKELY(dst == NULL)) {
@@ -1861,47 +1900,50 @@
const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.get());
-
- if (method_name == "finalize") {
- // Create the prototype for a signature of "()V"
- const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
- if (void_string_id != NULL) {
- const DexFile::TypeId* void_type_id =
- dex_file.FindTypeId(dex_file.GetIndexForStringId(*void_string_id));
- if (void_type_id != NULL) {
- std::vector<uint16_t> no_args;
- const DexFile::ProtoId* finalizer_proto =
- dex_file.FindProtoId(dex_file.GetIndexForTypeId(*void_type_id), no_args);
- if (finalizer_proto != NULL) {
- // We have the prototype in the dex file
- if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged
- klass->SetFinalizable();
- } else {
- ClassHelper kh(klass.get());
- StringPiece klass_descriptor(kh.GetDescriptor());
- // The Enum class declares a "final" finalize() method to prevent subclasses from
- // introducing a finalizer. We don't want to set the finalizable flag for Enum or its
- // subclasses, so we exclude it here.
- // We also want to avoid setting the flag on Object, where we know that finalize() is
- // empty.
- if (klass_descriptor != "Ljava/lang/Object;" &&
- klass_descriptor != "Ljava/lang/Enum;") {
- klass->SetFinalizable();
- }
- }
- }
- }
- }
- }
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
- dst->SetAccessFlags(it.GetMemberAccessFlags());
dst->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
dst->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage());
- CHECK(dst->IsArtMethod());
+ uint32_t access_flags = it.GetMemberAccessFlags();
+
+ if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
+ // Set finalizable flag on declaring class.
+ if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
+ // Void return type.
+ if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged
+ klass->SetFinalizable();
+ } else {
+ ClassHelper kh(klass.get());
+ const char* klass_descriptor = kh.GetDescriptor();
+ // The Enum class declares a "final" finalize() method to prevent subclasses from
+ // introducing a finalizer. We don't want to set the finalizable flag for Enum or its
+ // subclasses, so we exclude it here.
+ // We also want to avoid setting the flag on Object, where we know that finalize() is
+ // empty.
+ if ((strcmp("Ljava/lang/Object;", klass_descriptor) != 0) &&
+ (strcmp("Ljava/lang/Enum;", klass_descriptor) != 0)) {
+ klass->SetFinalizable();
+ }
+ }
+ }
+ } else if (method_name[0] == '<') {
+ // Fix broken access flags for initializers. Bug 11157540.
+ bool is_init = (strcmp("<init>", method_name) == 0);
+ bool is_clinit = !is_init && (strcmp("<clinit>", method_name) == 0);
+ if (UNLIKELY(!is_init && !is_clinit)) {
+ LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
+ } else {
+ if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
+ LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
+ << PrettyDescriptor(klass.get()) << " in dex file " << dex_file.GetLocation();
+ access_flags |= kAccConstructor;
+ }
+ }
+ }
+ dst->SetAccessFlags(access_flags);
self->EndAssertNoThreadSuspension(old_cause);
return dst;
@@ -2229,7 +2271,8 @@
++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
- if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) {
+ if ((klass->GetClassLoader() == class_loader) &&
+ (strcmp(descriptor, kh.GetDescriptor()) == 0)) {
class_table_.erase(it);
return true;
}
@@ -2275,15 +2318,17 @@
for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
- if (klass->GetClassLoader() == class_loader && strcmp(descriptor, kh.GetDescriptor()) == 0) {
+ if ((klass->GetClassLoader() == class_loader) &&
+ (strcmp(descriptor, kh.GetDescriptor()) == 0)) {
if (kIsDebugBuild) {
// Check for duplicates in the table.
for (++it; it != end && it->first == hash; ++it) {
mirror::Class* klass2 = it->second;
kh.ChangeClass(klass2);
- CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader))
- << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
- << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
+ CHECK(!((klass2->GetClassLoader() == class_loader) &&
+ (strcmp(descriptor, kh.GetDescriptor()) == 0)))
+ << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
+ << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
}
}
return klass;
@@ -2547,14 +2592,14 @@
CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
uint dex_location_checksum = dex_file.GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation(),
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
&dex_location_checksum);
CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
- const char* descriptor = ClassHelper(klass).GetDescriptor();
uint16_t class_def_index = klass->GetDexClassDefIndex();
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
CHECK(oat_class.get() != NULL)
- << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor;
+ << dex_file.GetLocation() << " " << PrettyClass(klass) << " "
+ << ClassHelper(klass).GetDescriptor();
oat_file_class_status = oat_class->GetStatus();
if (oat_file_class_status == mirror::Class::kStatusVerified ||
oat_file_class_status == mirror::Class::kStatusInitialized) {
@@ -2593,7 +2638,8 @@
return false;
}
LOG(FATAL) << "Unexpected class status: " << oat_file_class_status
- << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor;
+ << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " "
+ << ClassHelper(klass).GetDescriptor();
return false;
}
@@ -2835,12 +2881,12 @@
CHECK(constructor->IsConstructor());
MethodHelper mh(constructor);
CHECK_STREQ(mh.GetName(), "<init>");
- CHECK_EQ(mh.GetSignature(), std::string("(Ljava/lang/reflect/InvocationHandler;)V"));
+ CHECK_STREQ(mh.GetSignature().ToString().c_str(), "(Ljava/lang/reflect/InvocationHandler;)V");
DCHECK(constructor->IsPublic());
}
mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, SirtRef<mirror::Class>& klass,
- SirtRef<mirror::ArtMethod>& prototype) {
+ SirtRef<mirror::ArtMethod>& prototype) {
// Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
// prototype method
prototype->GetDeclaringClass()->GetDexCache()->SetResolvedMethod(prototype->GetDexMethodIndex(),
@@ -2899,12 +2945,12 @@
static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
bool can_init_parents)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (can_init_statics && can_init_statics) {
+ if (can_init_statics && can_init_parents) {
return true;
}
if (!can_init_statics) {
// Check if there's a class initializer.
- mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
+ mirror::ArtMethod* clinit = klass->FindClassInitializer();
if (clinit != NULL) {
return false;
}
@@ -2923,7 +2969,7 @@
if (!can_init_parents && !super_class->IsInitialized()) {
return false;
} else {
- if (!CanWeInitializeClass(super_class, can_init_statics, true)) {
+ if (!CanWeInitializeClass(super_class, can_init_statics, can_init_parents)) {
return false;
}
}
@@ -3051,7 +3097,7 @@
}
}
- mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
+ mirror::ArtMethod* clinit = klass->FindClassInitializer();
if (clinit != NULL) {
CHECK(can_init_statics);
if (LIKELY(Runtime::Current()->IsStarted())) {
@@ -3480,6 +3526,8 @@
bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass,
mirror::ObjectArray<mirror::Class>* interfaces) {
+ // Set the imt table to be all conflicts by default.
+ klass->SetImTable(Runtime::Current()->GetDefaultImt());
size_t super_ifcount;
if (klass->HasSuperClass()) {
super_ifcount = klass->GetSuperClass()->GetIfTableCount();
@@ -3587,6 +3635,13 @@
if (klass->IsInterface()) {
return true;
}
+ // Allocate imtable
+ bool imtable_changed = false;
+ SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, AllocArtMethodArray(self, kImtSize));
+ if (UNLIKELY(imtable.get() == NULL)) {
+ CHECK(self->IsExceptionPending()); // OOME.
+ return false;
+ }
std::vector<mirror::ArtMethod*> miranda_list;
MethodHelper vtable_mh(NULL, this);
MethodHelper interface_mh(NULL, this);
@@ -3626,6 +3681,14 @@
return false;
}
method_array->Set(j, vtable_method);
+ // Place method in imt if entry is empty, place conflict otherwise.
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize;
+ if (imtable->Get(imt_index) == NULL) {
+ imtable->Set(imt_index, vtable_method);
+ imtable_changed = true;
+ } else {
+ imtable->Set(imt_index, Runtime::Current()->GetImtConflictMethod());
+ }
break;
}
}
@@ -3657,6 +3720,16 @@
}
}
}
+ if (imtable_changed) {
+ // Fill in empty entries in interface method table with conflict.
+ mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
+ for (size_t i = 0; i < kImtSize; i++) {
+ if (imtable->Get(i) == NULL) {
+ imtable->Set(i, imt_conflict_method);
+ }
+ }
+ klass->SetImTable(imtable.get());
+ }
if (!miranda_list.empty()) {
int old_method_count = klass->NumVirtualMethods();
int new_method_count = old_method_count + miranda_list.size();
@@ -3741,10 +3814,10 @@
// same basic group? then sort by string.
fh_->ChangeField(field1);
- StringPiece name1(fh_->GetName());
+ const char* name1 = fh_->GetName();
fh_->ChangeField(field2);
- StringPiece name2(fh_->GetName());
- return name1 < name2;
+ const char* name2 = fh_->GetName();
+ return strcmp(name1, name2) < 0;
}
FieldHelper* fh_;
@@ -3778,7 +3851,9 @@
// minimizes disruption of C++ version such as Class and Method.
std::deque<mirror::ArtField*> grouped_and_sorted_fields;
for (size_t i = 0; i < num_fields; i++) {
- grouped_and_sorted_fields.push_back(fields->Get(i));
+ mirror::ArtField* f = fields->Get(i);
+ CHECK(f != NULL);
+ grouped_and_sorted_fields.push_back(f);
}
FieldHelper fh(NULL, this);
std::sort(grouped_and_sorted_fields.begin(),
@@ -3845,7 +3920,7 @@
// We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
if (!is_static &&
- StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;") {
+ (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)) {
// We know there are no non-reference fields in the Reference classes, and we know
// that 'referent' is alphabetically last, so this is easy...
CHECK_EQ(num_reference_fields, num_fields);
@@ -3854,39 +3929,39 @@
--num_reference_fields;
}
-#ifndef NDEBUG
- // Make sure that all reference fields appear before
- // non-reference fields, and all double-wide fields are aligned.
- bool seen_non_ref = false;
- for (size_t i = 0; i < num_fields; i++) {
- mirror::ArtField* field = fields->Get(i);
- if (false) { // enable to debug field layout
- LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance")
- << " class=" << PrettyClass(klass.get())
- << " field=" << PrettyField(field)
- << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()),
- false);
- }
- fh.ChangeField(field);
- Primitive::Type type = fh.GetTypeAsPrimitiveType();
- bool is_primitive = type != Primitive::kPrimNot;
- if (StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;" &&
- StringPiece(fh.GetName()) == "referent") {
- is_primitive = true; // We lied above, so we have to expect a lie here.
- }
- if (is_primitive) {
- if (!seen_non_ref) {
- seen_non_ref = true;
- DCHECK_EQ(num_reference_fields, i);
+ if (kIsDebugBuild) {
+ // Make sure that all reference fields appear before
+ // non-reference fields, and all double-wide fields are aligned.
+ bool seen_non_ref = false;
+ for (size_t i = 0; i < num_fields; i++) {
+ mirror::ArtField* field = fields->Get(i);
+ if (false) { // enable to debug field layout
+ LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance")
+ << " class=" << PrettyClass(klass.get())
+ << " field=" << PrettyField(field)
+ << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()),
+ false);
}
- } else {
- DCHECK(!seen_non_ref);
+ fh.ChangeField(field);
+ Primitive::Type type = fh.GetTypeAsPrimitiveType();
+ bool is_primitive = type != Primitive::kPrimNot;
+ if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)
+ && (strcmp("referent", fh.GetName()) == 0)) {
+ is_primitive = true; // We lied above, so we have to expect a lie here.
+ }
+ if (is_primitive) {
+ if (!seen_non_ref) {
+ seen_non_ref = true;
+ DCHECK_EQ(num_reference_fields, i);
+ }
+ } else {
+ DCHECK(!seen_non_ref);
+ }
+ }
+ if (!seen_non_ref) {
+ DCHECK_EQ(num_fields, num_reference_fields);
}
}
- if (!seen_non_ref) {
- DCHECK_EQ(num_fields, num_reference_fields);
- }
-#endif
size = field_offset.Uint32Value();
// Update klass
if (is_static) {
@@ -3896,6 +3971,11 @@
klass->SetNumReferenceInstanceFields(num_reference_fields);
if (!klass->IsVariableSize()) {
DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get(), this).GetDescriptor();
+ size_t previous_size = klass->GetObjectSize();
+ if (previous_size != 0) {
+ // Make sure that we didn't originally have an incorrect size.
+ CHECK_EQ(previous_size, size);
+ }
klass->SetObjectSize(size);
}
}
@@ -3961,9 +4041,8 @@
if (resolved != NULL) {
return resolved;
}
- const DexFile::StringId& string_id = dex_file.GetStringId(string_idx);
- int32_t utf16_length = dex_file.GetStringLength(string_id);
- const char* utf8_data = dex_file.GetStringData(string_id);
+ uint32_t utf16_length;
+ const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
mirror::String* string = intern_table_->InternStrong(utf16_length, utf8_data);
dex_cache->SetResolvedString(string_idx, string);
return string;
@@ -4002,15 +4081,15 @@
}
mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
- uint32_t method_idx,
- mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader,
- const mirror::ArtMethod* referrer,
- InvokeType type) {
+ uint32_t method_idx,
+ mirror::DexCache* dex_cache,
+ mirror::ClassLoader* class_loader,
+ const mirror::ArtMethod* referrer,
+ InvokeType type) {
DCHECK(dex_cache != NULL);
// Check for hit in the dex cache.
mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
- if (resolved != NULL) {
+ if (resolved != NULL && !resolved->IsRuntimeMethod()) {
return resolved;
}
// Fail, get the declaring class.
@@ -4041,7 +4120,7 @@
if (resolved == NULL) {
// Search by name, which works across dex files.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL));
+ const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect: // Fall-through.
case kStatic:
@@ -4071,7 +4150,7 @@
// We failed to find the method which means either an access error, an incompatible class
// change, or no such method. First try to find the method among direct and virtual methods.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL));
+ const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect:
case kStatic:
@@ -4149,10 +4228,10 @@
}
mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file,
- uint32_t field_idx,
- mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader,
- bool is_static) {
+ uint32_t field_idx,
+ mirror::DexCache* dex_cache,
+ mirror::ClassLoader* class_loader,
+ bool is_static) {
DCHECK(dex_cache != NULL);
mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx);
if (resolved != NULL) {
@@ -4189,9 +4268,9 @@
}
mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file,
- uint32_t field_idx,
- mirror::DexCache* dex_cache,
- mirror::ClassLoader* class_loader) {
+ uint32_t field_idx,
+ mirror::DexCache* dex_cache,
+ mirror::ClassLoader* class_loader) {
DCHECK(dex_cache != NULL);
mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx);
if (resolved != NULL) {
@@ -4204,8 +4283,9 @@
return NULL;
}
- const char* name = dex_file.GetFieldName(field_id);
- const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
+ StringPiece type(dex_file.StringDataByIdx(
+ dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
resolved = klass->FindField(name, type);
if (resolved != NULL) {
dex_cache->SetResolvedField(field_idx, resolved);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index baeec66..473370d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -51,6 +51,11 @@
class ClassLinker {
public:
+ // Interface method table size. Increasing this value reduces the chance of two interface methods
+ // colliding in the interface method table but increases the size of classes that implement
+ // (non-marker) interfaces.
+ static constexpr size_t kImtSize = 64;
+
// Creates the class linker by bootstrapping from dex files.
static ClassLinker* CreateFromCompiler(const std::vector<const DexFile*>& boot_class_path,
InternTable* intern_table)
@@ -215,7 +220,7 @@
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RegisterOatFile(const OatFile& oat_file)
+ const OatFile* RegisterOatFile(const OatFile* oat_file)
LOCKS_EXCLUDED(dex_lock_);
const std::vector<const DexFile*>& GetBootClassPath() {
@@ -244,43 +249,37 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Generate an oat file from a dex file
- bool GenerateOatFile(const std::string& dex_filename,
+ bool GenerateOatFile(const char* dex_filename,
int oat_fd,
- const std::string& oat_cache_filename);
+ const char* oat_cache_filename);
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
- const OatFile* FindOatFileFromOatLocation(const std::string& location)
+ const OatFile* FindOatFileFromOatLocation(const std::string& location,
+ std::string* error_msg)
LOCKS_EXCLUDED(dex_lock_);
- const OatFile* FindOatFileFromOatLocationLocked(const std::string& location)
- SHARED_LOCKS_REQUIRED(dex_lock_);
-
// Finds the oat file for a dex location, generating the oat file if
// it is missing or out of date. Returns the DexFile from within the
// created oat file.
- const DexFile* FindOrCreateOatFileForDexLocation(const std::string& dex_location,
+ const DexFile* FindOrCreateOatFileForDexLocation(const char* dex_location,
uint32_t dex_location_checksum,
- const std::string& oat_location)
- LOCKS_EXCLUDED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const DexFile* FindOrCreateOatFileForDexLocationLocked(const std::string& dex_location,
- uint32_t dex_location_checksum,
- const std::string& oat_location)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const char* oat_location,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Find a DexFile within an OatFile given a DexFile location. Note
// that this returns null if the location checksum of the DexFile
// does not match the OatFile.
- const DexFile* FindDexFileInOatFileFromDexLocation(const std::string& location,
- uint32_t location_checksum)
- LOCKS_EXCLUDED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const DexFile* FindDexFileInOatFileFromDexLocation(const char* location,
+ uint32_t location_checksum,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Returns true if oat file contains the dex file with the given location and checksum.
static bool VerifyOatFileChecksums(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const char* dex_location,
+ uint32_t dex_location_checksum,
+ std::string* error_msg);
// TODO: replace this with multiple methods that allocate the correct managed type.
template <class T>
@@ -346,6 +345,14 @@
return quick_resolution_trampoline_;
}
+ const void* GetPortableImtConflictTrampoline() const {
+ return portable_imt_conflict_trampoline_;
+ }
+
+ const void* GetQuickImtConflictTrampoline() const {
+ return quick_imt_conflict_trampoline_;
+ }
+
InternTable* GetInternTable() const {
return intern_table_;
}
@@ -434,8 +441,6 @@
EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsDexFileRegisteredLocked(const DexFile& dex_file) const SHARED_LOCKS_REQUIRED(dex_lock_);
- void RegisterOatFileLocked(const OatFile& oat_file) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_);
bool InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_parents)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -497,22 +502,22 @@
const OatFile* FindOpenedOatFileForDexFile(const DexFile& dex_file)
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_location,
+ const OatFile* FindOpenedOatFileFromDexLocation(const char* dex_location,
uint32_t dex_location_checksum)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_);
+ LOCKS_EXCLUDED(dex_lock);
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
- SHARED_LOCKS_REQUIRED(dex_lock_);
- const DexFile* FindDexFileInOatLocation(const std::string& dex_location,
+ LOCKS_EXCLUDED(dex_lock_);
+ const DexFile* FindDexFileInOatLocation(const char* dex_location,
uint32_t dex_location_checksum,
- const std::string& oat_location)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const char* oat_location,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_);
- const DexFile* VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const DexFile* VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location,
+ const char* dex_location,
+ std::string* error_msg,
+ bool* open_failed)
+ LOCKS_EXCLUDED(dex_lock_);
mirror::ArtMethod* CreateProxyConstructor(Thread* self, SirtRef<mirror::Class>& klass,
mirror::Class* proxy_class)
@@ -620,6 +625,8 @@
const void* portable_resolution_trampoline_;
const void* quick_resolution_trampoline_;
+ const void* portable_imt_conflict_trampoline_;
+ const void* quick_imt_conflict_trampoline_;
friend class ImageWriter; // for GetClassRoots
FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index bbc2877..a52b680 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -152,7 +152,7 @@
EXPECT_TRUE(method != NULL);
EXPECT_TRUE(method->GetClass() != NULL);
EXPECT_TRUE(mh.GetName() != NULL);
- EXPECT_TRUE(mh.GetSignature() != NULL);
+ EXPECT_TRUE(mh.GetSignature() != Signature::NoSignature());
EXPECT_TRUE(method->GetDexCacheStrings() != NULL);
EXPECT_TRUE(method->GetDexCacheResolvedMethods() != NULL);
@@ -340,8 +340,9 @@
}
}
- static void TestRootVisitor(const mirror::Object* root, void*) {
+ static mirror::Object* TestRootVisitor(mirror::Object* root, void*) {
EXPECT_TRUE(root != NULL);
+ return root;
}
};
@@ -496,6 +497,7 @@
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_), "directMethods"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, ifields_), "iFields"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, iftable_), "ifTable"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, imtable_), "imTable"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, name_), "name"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, sfields_), "sFields"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass"));
@@ -581,11 +583,11 @@
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, ASCII_), "ASCII"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, CASE_INSENSITIVE_ORDER_), "CASE_INSENSITIVE_ORDER"));
- // alphabetical 64-bit
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID"));
-
// alphabetical 32-bit
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR"));
+
+ // alphabetical 64-bit
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID"));
};
};
@@ -941,15 +943,16 @@
EXPECT_TRUE(K->IsAssignableFrom(B));
EXPECT_TRUE(J->IsAssignableFrom(B));
- mirror::ArtMethod* Ii = I->FindVirtualMethod("i", "()V");
- mirror::ArtMethod* Jj1 = J->FindVirtualMethod("j1", "()V");
- mirror::ArtMethod* Jj2 = J->FindVirtualMethod("j2", "()V");
- mirror::ArtMethod* Kj1 = K->FindInterfaceMethod("j1", "()V");
- mirror::ArtMethod* Kj2 = K->FindInterfaceMethod("j2", "()V");
- mirror::ArtMethod* Kk = K->FindInterfaceMethod("k", "()V");
- mirror::ArtMethod* Ai = A->FindVirtualMethod("i", "()V");
- mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", "()V");
- mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", "()V");
+ const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V");
+ mirror::ArtMethod* Ii = I->FindVirtualMethod("i", void_sig);
+ mirror::ArtMethod* Jj1 = J->FindVirtualMethod("j1", void_sig);
+ mirror::ArtMethod* Jj2 = J->FindVirtualMethod("j2", void_sig);
+ mirror::ArtMethod* Kj1 = K->FindInterfaceMethod("j1", void_sig);
+ mirror::ArtMethod* Kj2 = K->FindInterfaceMethod("j2", void_sig);
+ mirror::ArtMethod* Kk = K->FindInterfaceMethod("k", void_sig);
+ mirror::ArtMethod* Ai = A->FindVirtualMethod("i", void_sig);
+ mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", void_sig);
+ mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", void_sig);
ASSERT_TRUE(Ii != NULL);
ASSERT_TRUE(Jj1 != NULL);
ASSERT_TRUE(Jj2 != NULL);
@@ -994,7 +997,7 @@
CHECK(dex_file != NULL);
mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader.get());
- mirror::ArtMethod* clinit = klass->FindDirectMethod("<clinit>", "()V");
+ mirror::ArtMethod* clinit = klass->FindClassInitializer();
mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;");
const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
ASSERT_TRUE(string_id != NULL);
diff --git a/runtime/common_test.h b/runtime/common_test.h
index dc1f592..643ed1d 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -22,6 +22,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <fstream>
#include "../../external/icu4c/common/unicode/uvernum.h"
#include "base/macros.h"
@@ -152,6 +153,113 @@
UniquePtr<File> file_;
};
+#if defined(__arm__)
+
+
+#include <signal.h>
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+
+
+// A signal handler called when have an illegal instruction. We record the fact in
+// a global boolean and then increment the PC in the signal context to return to
+// the next instruction. We know the instruction is an sdiv (4 bytes long).
+static void baddivideinst(int signo, siginfo *si, void *data) {
+ (void)signo;
+ (void)si;
+ struct ucontext *uc = (struct ucontext *)data;
+ struct sigcontext *sc = &uc->uc_mcontext;
+ sc->arm_r0 = 0; // set R0 to #0 to signal error
+ sc->arm_pc += 4; // skip offending instruction
+}
+
+// This is in arch/arm/arm_sdiv.S. It does the following:
+// mov r1,#1
+// sdiv r0,r1,r1
+// bx lr
+//
+// the result will be the value 1 if sdiv is supported. If it is not supported
+// a SIGILL signal will be raised and the signal handler (baddivideinst) called.
+// The signal handler sets r0 to #0 and then increments pc beyond the failed instruction.
+// Thus if the instruction is not supported, the result of this function will be #0
+
+extern "C" bool CheckForARMSDIVInstruction();
+
+static InstructionSetFeatures GuessInstructionFeatures() {
+ InstructionSetFeatures f;
+
+ // Uncomment this for processing of /proc/cpuinfo.
+ if (false) {
+ // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
+ // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
+ std::ifstream in("/proc/cpuinfo");
+ if (in) {
+ while (!in.eof()) {
+ std::string line;
+ std::getline(in, line);
+ if (!in.eof()) {
+ if (line.find("Features") != std::string::npos) {
+ if (line.find("idivt") != std::string::npos) {
+ f.SetHasDivideInstruction(true);
+ }
+ }
+ }
+ in.close();
+ }
+ } else {
+ LOG(INFO) << "Failed to open /proc/cpuinfo";
+ }
+ }
+
+ // See if have a sdiv instruction. Register a signal handler and try to execute
+ // an sdiv instruction. If we get a SIGILL then it's not supported. We can't use
+ // the /proc/cpuinfo method for this because Krait devices don't always put the idivt
+ // feature in the list.
+ struct sigaction sa, osa;
+ sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
+ sa.sa_sigaction = baddivideinst;
+ sigaction(SIGILL, &sa, &osa);
+
+ if (CheckForARMSDIVInstruction()) {
+ f.SetHasDivideInstruction(true);
+ }
+
+ // Restore the signal handler.
+ sigaction(SIGILL, &osa, NULL);
+
+ // Other feature guesses in here.
+ return f;
+}
+
+#endif
+
+// Given a set of instruction features from the build, parse it. The
+// input 'str' is a comma separated list of feature names. Parse it and
+// return the InstructionSetFeatures object.
+static InstructionSetFeatures ParseFeatureList(std::string str) {
+ LOG(INFO) << "Parsing features " << str;
+ InstructionSetFeatures result;
+ typedef std::vector<std::string> FeatureList;
+ FeatureList features;
+ Split(str, ',', features);
+ for (FeatureList::iterator i = features.begin(); i != features.end(); i++) {
+ std::string feature = Trim(*i);
+ if (feature == "default") {
+ // Nothing to do.
+ } else if (feature == "div") {
+ // Supports divide instruction.
+ result.SetHasDivideInstruction(true);
+ } else if (feature == "nodiv") {
+ // Turn off support for divide instruction.
+ result.SetHasDivideInstruction(false);
+ } else {
+ LOG(FATAL) << "Unknown instruction set feature: '" << feature << "'";
+ }
+ }
+ // Others...
+ return result;
+}
+
class CommonTest : public testing::Test {
public:
static void MakeExecutable(const mirror::ByteArray* code_array) {
@@ -282,16 +390,14 @@
int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
ASSERT_EQ(mkdir_result, 0);
- java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName(), GetLibCoreDexFileName());
+ std::string error_msg;
+ java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName().c_str(),
+ GetLibCoreDexFileName().c_str(), &error_msg);
if (java_lang_dex_file_ == NULL) {
- LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "'\n";
- }
- conscrypt_file_ = DexFile::Open(GetConscryptFileName(), GetConscryptFileName());
- if (conscrypt_file_ == NULL) {
- LOG(FATAL) << "Could not open .dex file '" << GetConscryptFileName() << "'\n";
+ LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "': "
+ << error_msg << "\n";
}
boot_class_path_.push_back(java_lang_dex_file_);
- boot_class_path_.push_back(conscrypt_file_);
std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
@@ -316,8 +422,18 @@
class_linker_ = runtime_->GetClassLinker();
InstructionSet instruction_set = kNone;
+
+ // Take the default set of instruction features from the build.
+ InstructionSetFeatures instruction_set_features =
+ ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES));
+
#if defined(__arm__)
instruction_set = kThumb2;
+ InstructionSetFeatures runtime_features = GuessInstructionFeatures();
+
+ // for ARM, do a runtime check to make sure that the features we are passed from
+ // the build match the features we actually determine at runtime.
+ ASSERT_EQ(instruction_set_features, runtime_features);
#elif defined(__mips__)
instruction_set = kMips;
#elif defined(__i386__)
@@ -331,9 +447,6 @@
CompilerBackend compiler_backend = kQuick;
#endif
- if (!runtime_->HasResolutionMethod()) {
- runtime_->SetResolutionMethod(runtime_->CreateResolutionMethod());
- }
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
if (!runtime_->HasCalleeSaveMethod(type)) {
@@ -343,6 +456,7 @@
}
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set,
+ instruction_set_features,
true, new CompilerDriver::DescriptorSet,
2, true));
}
@@ -398,10 +512,6 @@
return GetDexFileName("core-libart");
}
- std::string GetConscryptFileName() {
- return GetDexFileName("conscrypt");
- }
-
std::string GetDexFileName(const std::string& jar_prefix) {
if (IsHost()) {
const char* host_dir = getenv("ANDROID_HOST_OUT");
@@ -432,8 +542,9 @@
filename += "art-test-dex-";
filename += name;
filename += ".jar";
- const DexFile* dex_file = DexFile::Open(filename, filename);
- CHECK(dex_file != NULL) << "Failed to open " << filename;
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg);
+ CHECK(dex_file != NULL) << "Failed to open '" << filename << "': " << error_msg;
CHECK_EQ(PROT_READ, dex_file->GetPermissions());
CHECK(dex_file->IsReadOnly());
opened_dex_files_.push_back(dex_file);
@@ -507,10 +618,12 @@
void ReserveImageSpace() {
// Reserve where the image will be loaded up front so that other parts of test set up don't
// accidentally end up colliding with the fixed memory address when we need to load the image.
+ std::string error_msg;
image_reservation_.reset(MemMap::MapAnonymous("image reservation",
reinterpret_cast<byte*>(ART_BASE_ADDRESS),
(size_t)100 * 1024 * 1024, // 100MB
- PROT_NONE));
+ PROT_NONE, &error_msg));
+ CHECK(image_reservation_.get() != nullptr) << error_msg;
}
void UnreserveImageSpace() {
@@ -520,7 +633,6 @@
std::string android_data_;
std::string dalvik_cache_;
const DexFile* java_lang_dex_file_; // owned by runtime_
- const DexFile* conscrypt_file_; // owned by runtime_
std::vector<const DexFile*> boot_class_path_;
UniquePtr<Runtime> runtime_;
// Owned by the runtime
@@ -575,7 +687,6 @@
#else
#define TEST_DISABLED_FOR_PORTABLE()
#endif
-
} // namespace art
namespace std {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 26ce5be..0419dab 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -230,6 +230,15 @@
va_end(args);
}
+// IOException
+
+void ThrowIOException(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException(NULL, "Ljava/io/IOException;", NULL, fmt, &args);
+ va_end(args);
+}
+
// LinkageError
void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) {
@@ -265,7 +274,7 @@
// NoSuchMethodError
void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name,
- const StringPiece& signature) {
+ const Signature& signature) {
std::ostringstream msg;
ClassHelper kh(c);
msg << "No " << type << " method " << name << signature
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 99c6343..3164f30 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -22,113 +22,122 @@
namespace art {
namespace mirror {
-class ArtField;
-class ArtMethod;
-class Class;
-class Object;
+ class ArtField;
+ class ArtMethod;
+ class Class;
+ class Object;
} // namespace mirror
+class Signature;
class StringPiece;
class ThrowLocation;
// AbstractMethodError
void ThrowAbstractMethodError(const mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ArithmeticException
-void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ArrayIndexOutOfBoundsException
void ThrowArrayIndexOutOfBoundsException(int index, int length)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ArrayStoreException
void ThrowArrayStoreException(const mirror::Class* element_class,
const mirror::Class* array_class)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ClassCircularityError
-void ThrowClassCircularityError(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowClassCircularityError(mirror::Class* c)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ClassCastException
void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ClassFormatError
void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// IllegalAccessError
void ThrowIllegalAccessErrorClass(mirror::Class* referrer, mirror::Class* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
const mirror::ArtMethod* caller,
const mirror::ArtMethod* called,
InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::ArtMethod* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::ArtField* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorFinalField(const mirror::ArtMethod* referrer,
mirror::ArtField* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// IllegalArgumentException
void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// IncompatibleClassChangeError
void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
mirror::ArtMethod* method,
const mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::ArtMethod* interface_method,
mirror::Object* this_object,
const mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIncompatibleClassChangeErrorField(const mirror::ArtField* resolved_field, bool is_static,
const mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
+
+// IOException
+
+void ThrowIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)))
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// LinkageError
void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// NegativeArraySizeException
-void ThrowNegativeArraySizeException(int size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowNegativeArraySizeException(int size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
-void ThrowNegativeArraySizeException(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowNegativeArraySizeException(const char* msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// NoSuchFieldError
@@ -140,46 +149,46 @@
// NoSuchMethodError
void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name,
- const StringPiece& signature)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const Signature& signature)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNoSuchMethodError(uint32_t method_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// NullPointerException
void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location,
mirror::ArtField* field,
bool is_read)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
uint32_t method_idx,
InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
mirror::ArtMethod* method,
InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// RuntimeException
void ThrowRuntimeException(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// VerifyError
void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
} // namespace art
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 88269e5..bdcf6ac 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -674,15 +674,15 @@
Locks::mutator_lock_->ExclusiveUnlock(self);
Locks::mutator_lock_->SharedLock(self);
- if (monitor_info.owner != NULL) {
- expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner->GetPeer()));
+ if (monitor_info.owner_ != NULL) {
+ expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
} else {
expandBufAddObjectId(reply, gRegistry->Add(NULL));
}
- expandBufAdd4BE(reply, monitor_info.entry_count);
- expandBufAdd4BE(reply, monitor_info.waiters.size());
- for (size_t i = 0; i < monitor_info.waiters.size(); ++i) {
- expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters[i]->GetPeer()));
+ expandBufAdd4BE(reply, monitor_info.entry_count_);
+ expandBufAdd4BE(reply, monitor_info.waiters_.size());
+ for (size_t i = 0; i < monitor_info.waiters_.size(); ++i) {
+ expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters_[i]->GetPeer()));
}
return JDWP::ERR_NONE;
}
@@ -928,13 +928,13 @@
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string& signature) {
+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;
}
- signature = ClassHelper(c).GetDescriptor();
+ *signature = ClassHelper(c).GetDescriptor();
return JDWP::ERR_NONE;
}
@@ -1065,8 +1065,8 @@
LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
return JDWP::ERR_INVALID_LENGTH;
}
- std::string descriptor(ClassHelper(dst->GetClass()).GetDescriptor());
- JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
+ const char* descriptor = ClassHelper(dst->GetClass()).GetDescriptor();
+ JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor + 1);
if (IsPrimitiveTag(tag)) {
size_t width = GetTagWidth(tag);
@@ -1287,7 +1287,7 @@
MethodHelper mh(m);
expandBufAddMethodId(pReply, ToMethodId(m));
expandBufAddUtf8String(pReply, mh.GetName());
- expandBufAddUtf8String(pReply, mh.GetSignature());
+ expandBufAddUtf8String(pReply, mh.GetSignature().ToString());
if (with_generic) {
static const char genericSignature[1] = "";
expandBufAddUtf8String(pReply, genericSignature);
@@ -1324,7 +1324,7 @@
expandBufAdd8BE(pContext->pReply, address);
expandBufAdd4BE(pContext->pReply, line_number);
pContext->numItems++;
- return true;
+ return false;
}
};
mirror::ArtMethod* m = FromMethodId(method_id);
@@ -1935,7 +1935,8 @@
}
// Suspend thread to build stack trace.
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer.get(), request_suspension, &timed_out);
+ Thread* thread = ThreadList::SuspendThreadByPeer(peer.get(), request_suspension, true,
+ &timed_out);
if (thread != NULL) {
return JDWP::ERR_NONE;
} else if (timed_out) {
@@ -2287,7 +2288,8 @@
// since the class may not yet be verified.
int state = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
JDWP::JdwpTypeTag tag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
- gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state);
+ gJdwpState->PostClassPrepare(tag, gRegistry->Add(c),
+ ClassHelper(c).GetDescriptor(), state);
}
void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object,
@@ -2411,7 +2413,8 @@
soa.Self()->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
jobject thread_peer = gRegistry->GetJObject(thread_id);
bool timed_out;
- Thread* suspended_thread = Thread::SuspendForDebugger(thread_peer, true, &timed_out);
+ Thread* suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true,
+ &timed_out);
CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kWaitingForDebuggerSuspension);
if (suspended_thread == NULL) {
// Thread terminated from under us while suspending.
@@ -3011,7 +3014,7 @@
if (type == CHUNK_TYPE("THDE")) {
uint8_t buf[4];
- JDWP::Set4BE(&buf[0], t->GetThinLockId());
+ JDWP::Set4BE(&buf[0], t->GetThreadId());
Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
} else {
CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
@@ -3021,7 +3024,7 @@
const jchar* chars = (name.get() != NULL) ? name->GetCharArray()->GetData() : NULL;
std::vector<uint8_t> bytes;
- JDWP::Append4BE(bytes, t->GetThinLockId());
+ JDWP::Append4BE(bytes, t->GetThreadId());
JDWP::AppendUtf16BE(bytes, chars, char_count);
CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
Dbg::DdmSendChunk(type, bytes);
@@ -3486,7 +3489,9 @@
recent_allocation_records_ = new AllocRecord[gAllocRecordMax];
CHECK(recent_allocation_records_ != NULL);
}
+ Runtime::Current()->InstrumentQuickAllocEntryPoints();
} else {
+ Runtime::Current()->UninstrumentQuickAllocEntryPoints();
delete[] recent_allocation_records_;
recent_allocation_records_ = NULL;
}
@@ -3542,7 +3547,7 @@
AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
record->type = type;
record->byte_count = byte_count;
- record->thin_lock_id = self->GetThinLockId();
+ record->thin_lock_id = self->GetThreadId();
// Fill in the stack trace.
AllocRecordStackVisitor visitor(self, record);
diff --git a/runtime/debugger.h b/runtime/debugger.h
index d0fe445..8574a33 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -149,7 +149,7 @@
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)
+ 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)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index dee8026..3b2135c 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -29,19 +29,89 @@
return DecodeUnsignedLeb128(&ptr);
}
-inline const char* DexFile::GetStringDataAndLength(const StringId& string_id, uint32_t* length) const {
- DCHECK(length != NULL) << GetLocation();
+inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id,
+ uint32_t* utf16_length) const {
+ DCHECK(utf16_length != NULL) << GetLocation();
const byte* ptr = begin_ + string_id.string_data_off_;
- *length = DecodeUnsignedLeb128(&ptr);
+ *utf16_length = DecodeUnsignedLeb128(&ptr);
return reinterpret_cast<const char*>(ptr);
}
+inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const {
+ return Signature(this, GetProtoId(method_id.proto_idx_));
+}
+
inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) {
const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_];
return reinterpret_cast<const TryItem*>
(RoundUp(reinterpret_cast<uint32_t>(insns_end_), 4)) + offset;
}
+static inline bool DexFileStringEquals(const DexFile* df1, uint32_t sidx1,
+ const DexFile* df2, uint32_t sidx2) {
+ uint32_t s1_len; // Note: utf16 length != mutf8 length.
+ const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len);
+ uint32_t s2_len;
+ const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len);
+ return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0);
+}
+
+inline bool Signature::operator==(const Signature& rhs) const {
+ if (dex_file_ == nullptr) {
+ return rhs.dex_file_ == nullptr;
+ }
+ if (rhs.dex_file_ == nullptr) {
+ return false;
+ }
+ if (dex_file_ == rhs.dex_file_) {
+ return proto_id_ == rhs.proto_id_;
+ }
+ uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length.
+ const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_,
+ &lhs_shorty_len);
+ StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len);
+ {
+ uint32_t rhs_shorty_len;
+ const char* rhs_shorty_data =
+ rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_,
+ &rhs_shorty_len);
+ StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len);
+ if (lhs_shorty != rhs_shorty) {
+ return false; // Shorty mismatch.
+ }
+ }
+ if (lhs_shorty[0] == 'L') {
+ const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_);
+ const DexFile::TypeId& rhs_return_type_id =
+ rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_);
+ if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_,
+ rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) {
+ return false; // Return type mismatch.
+ }
+ }
+ if (lhs_shorty.find('L', 1) != StringPiece::npos) {
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
+ // Both lists are empty or have contents, or else shorty is broken.
+ DCHECK_EQ(params == nullptr, rhs_params == nullptr);
+ if (params != nullptr) {
+ uint32_t params_size = params->Size();
+ DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
+ for (uint32_t i = 0; i < params_size; ++i) {
+ const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_);
+ const DexFile::TypeId& rhs_param_id =
+ rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_);
+ if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_,
+ rhs.dex_file_, rhs_param_id.descriptor_idx_)) {
+ return false; // Parameter type mismatch.
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
} // namespace art
#endif // ART_RUNTIME_DEX_FILE_INL_H_
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 9034628..7e09a48 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -38,7 +38,7 @@
#include "safe_map.h"
#include "thread.h"
#include "UniquePtr.h"
-#include "utf.h"
+#include "utf-inl.h"
#include "utils.h"
#include "well_known_classes.h"
#include "zip_archive.h"
@@ -62,72 +62,77 @@
reinterpret_cast<const DexFile::ClassDef*>(NULL));
}
-int OpenAndReadMagic(const std::string& filename, uint32_t* magic) {
+static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
CHECK(magic != NULL);
- int fd = open(filename.c_str(), O_RDONLY, 0);
+ int fd = open(filename, O_RDONLY, 0);
if (fd == -1) {
- PLOG(WARNING) << "Unable to open '" << filename << "'";
+ *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno));
return -1;
}
int n = TEMP_FAILURE_RETRY(read(fd, magic, sizeof(*magic)));
if (n != sizeof(*magic)) {
- PLOG(ERROR) << "Failed to find magic in '" << filename << "'";
+ *error_msg = StringPrintf("Failed to find magic in '%s'", filename);
return -1;
}
if (lseek(fd, 0, SEEK_SET) != 0) {
- PLOG(ERROR) << "Failed to seek to beginning of file '" << filename << "'";
+ *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename,
+ strerror(errno));
return -1;
}
return fd;
}
-bool DexFile::GetChecksum(const std::string& filename, uint32_t* checksum) {
+bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
CHECK(checksum != NULL);
uint32_t magic;
- int fd = OpenAndReadMagic(filename, &magic);
+ int fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd == -1) {
+ DCHECK(!error_msg->empty());
return false;
}
if (IsZipMagic(magic)) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd));
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, filename, error_msg));
if (zip_archive.get() == NULL) {
+ *error_msg = StringPrintf("Failed to open zip archive '%s'", filename);
return false;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex));
if (zip_entry.get() == NULL) {
- LOG(ERROR) << "Zip archive '" << filename << "' doesn't contain " << kClassesDex;
+ *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s", filename, kClassesDex);
return false;
}
*checksum = zip_entry->GetCrc32();
return true;
}
if (IsDexMagic(magic)) {
- UniquePtr<const DexFile> dex_file(DexFile::OpenFile(fd, filename, false));
+ UniquePtr<const DexFile> dex_file(DexFile::OpenFile(fd, filename, false, error_msg));
if (dex_file.get() == NULL) {
return false;
}
*checksum = dex_file->GetHeader().checksum_;
return true;
}
- LOG(ERROR) << "Expected valid zip or dex file: " << filename;
+ *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
return false;
}
-const DexFile* DexFile::Open(const std::string& filename,
- const std::string& location) {
+const DexFile* DexFile::Open(const char* filename,
+ const char* location,
+ std::string* error_msg) {
uint32_t magic;
- int fd = OpenAndReadMagic(filename, &magic);
+ int fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd == -1) {
+ DCHECK(!error_msg->empty());
return NULL;
}
if (IsZipMagic(magic)) {
- return DexFile::OpenZip(fd, location);
+ return DexFile::OpenZip(fd, location, error_msg);
}
if (IsDexMagic(magic)) {
- return DexFile::OpenFile(fd, location, true);
+ return DexFile::OpenFile(fd, location, true, error_msg);
}
- LOG(ERROR) << "Expected valid zip or dex file: " << filename;
- return NULL;
+ *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
+ return nullptr;
}
int DexFile::GetPermissions() const {
@@ -160,46 +165,48 @@
}
}
-const DexFile* DexFile::OpenFile(int fd,
- const std::string& location,
- bool verify) {
- CHECK(!location.empty());
+const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify,
+ std::string* error_msg) {
+ CHECK(location != nullptr);
struct stat sbuf;
memset(&sbuf, 0, sizeof(sbuf));
if (fstat(fd, &sbuf) == -1) {
- PLOG(ERROR) << "fstat \"" << location << "\" failed";
+ *error_msg = StringPrintf("DexFile: fstat \'%s\' failed: %s", location, strerror(errno));
close(fd);
- return NULL;
+ return nullptr;
}
if (S_ISDIR(sbuf.st_mode)) {
- LOG(ERROR) << "attempt to mmap directory \"" << location << "\"";
- return NULL;
+ *error_msg = StringPrintf("Attempt to mmap directory '%s'", location);
+ return nullptr;
}
size_t length = sbuf.st_size;
- UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0));
- if (map.get() == NULL) {
- LOG(ERROR) << "mmap \"" << location << "\" failed";
+ UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0, location,
+ error_msg));
+ if (map.get() == nullptr) {
+ DCHECK(!error_msg->empty());
close(fd);
- return NULL;
+ return nullptr;
}
close(fd);
if (map->Size() < sizeof(DexFile::Header)) {
- LOG(ERROR) << "Failed to open dex file '" << location << "' that is too short to have a header";
- return NULL;
+ *error_msg = StringPrintf(
+ "DexFile: failed to open dex file \'%s\' that is too short to have a header", location);
+ return nullptr;
}
const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
- const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release());
- if (dex_file == NULL) {
- LOG(ERROR) << "Failed to open dex file '" << location << "' from memory";
- return NULL;
+ const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release(), error_msg);
+ if (dex_file == 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())) {
- LOG(ERROR) << "Failed to verify dex file '" << location << "'";
- return NULL;
+ if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size(), location,
+ error_msg)) {
+ return nullptr;
}
return dex_file;
@@ -207,49 +214,55 @@
const char* DexFile::kClassesDex = "classes.dex";
-const DexFile* DexFile::OpenZip(int fd, const std::string& location) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd));
- if (zip_archive.get() == NULL) {
- LOG(ERROR) << "Failed to open " << location << " when looking for classes.dex";
- return NULL;
+const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg) {
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
+ if (zip_archive.get() == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
- return DexFile::Open(*zip_archive.get(), location);
+ return DexFile::Open(*zip_archive.get(), location, error_msg);
}
const DexFile* DexFile::OpenMemory(const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map) {
+ MemMap* mem_map,
+ std::string* error_msg) {
return OpenMemory(mem_map->Begin(),
mem_map->Size(),
location,
location_checksum,
- mem_map);
+ mem_map,
+ error_msg);
}
-const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location) {
+const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location,
+ std::string* error_msg) {
CHECK(!location.empty());
UniquePtr<ZipEntry> zip_entry(zip_archive.Find(kClassesDex));
if (zip_entry.get() == NULL) {
- LOG(ERROR) << "Failed to find classes.dex within '" << location << "'";
- return NULL;
+ *error_msg = StringPrintf("Failed to find classes.dex within '%s'", location.c_str());
+ return nullptr;
}
- UniquePtr<MemMap> map(zip_entry->ExtractToMemMap(kClassesDex));
+ UniquePtr<MemMap> map(zip_entry->ExtractToMemMap(kClassesDex, error_msg));
if (map.get() == NULL) {
- LOG(ERROR) << "Failed to extract '" << kClassesDex << "' from '" << location << "'";
- return NULL;
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", kClassesDex, location.c_str(),
+ error_msg->c_str());
+ return nullptr;
}
- UniquePtr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release()));
- if (dex_file.get() == NULL) {
- LOG(ERROR) << "Failed to open dex file '" << location << "' from memory";
- return NULL;
+ UniquePtr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(),
+ error_msg));
+ if (dex_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
+ error_msg->c_str());
+ return nullptr;
}
- if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size())) {
- LOG(ERROR) << "Failed to verify dex file '" << location << "'";
- return NULL;
+ if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
+ location.c_str(), error_msg)) {
+ return nullptr;
}
if (!dex_file->DisableWrite()) {
- LOG(ERROR) << "Failed to make dex file read only '" << location << "'";
- return NULL;
+ *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
+ return nullptr;
}
CHECK(dex_file->IsReadOnly()) << location;
return dex_file.release();
@@ -259,11 +272,11 @@
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map) {
+ MemMap* mem_map, std::string* error_msg) {
CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned
UniquePtr<DexFile> dex_file(new DexFile(base, size, location, location_checksum, mem_map));
- if (!dex_file->Init()) {
- return NULL;
+ if (!dex_file->Init(error_msg)) {
+ return nullptr;
} else {
return dex_file.release();
}
@@ -276,9 +289,9 @@
// the global reference table is otherwise empty!
}
-bool DexFile::Init() {
+bool DexFile::Init(std::string* error_msg) {
InitMembers();
- if (!CheckMagicAndVersion()) {
+ if (!CheckMagicAndVersion(error_msg)) {
return false;
}
return true;
@@ -296,22 +309,26 @@
class_defs_ = reinterpret_cast<const ClassDef*>(b + h->class_defs_off_);
}
-bool DexFile::CheckMagicAndVersion() const {
+bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
CHECK(header_->magic_ != NULL) << GetLocation();
if (!IsMagicValid(header_->magic_)) {
- LOG(ERROR) << "Unrecognized magic number in " << GetLocation() << ":"
+ std::ostringstream oss;
+ oss << "Unrecognized magic number in " << GetLocation() << ":"
<< " " << header_->magic_[0]
<< " " << header_->magic_[1]
<< " " << header_->magic_[2]
<< " " << header_->magic_[3];
+ *error_msg = oss.str();
return false;
}
if (!IsVersionValid(header_->magic_)) {
- LOG(ERROR) << "Unrecognized version number in " << GetLocation() << ":"
+ std::ostringstream oss;
+ oss << "Unrecognized version number in " << GetLocation() << ":"
<< " " << header_->magic_[4]
<< " " << header_->magic_[5]
<< " " << header_->magic_[6]
<< " " << header_->magic_[7];
+ *error_msg = oss.str();
return false;
}
return true;
@@ -440,9 +457,8 @@
int32_t hi = NumStringIds() - 1;
while (hi >= lo) {
int32_t mid = (hi + lo) / 2;
- uint32_t length;
const DexFile::StringId& str_id = GetStringId(mid);
- const char* str = GetStringDataAndLength(str_id, &length);
+ const char* str = GetStringData(str_id);
int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
if (compare > 0) {
lo = mid + 1;
@@ -460,9 +476,8 @@
int32_t hi = NumStringIds() - 1;
while (hi >= lo) {
int32_t mid = (hi + lo) / 2;
- uint32_t length;
const DexFile::StringId& str_id = GetStringId(mid);
- const char* str = GetStringDataAndLength(str_id, &length);
+ const char* str = GetStringData(str_id);
int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string);
if (compare > 0) {
lo = mid + 1;
@@ -528,8 +543,8 @@
}
// Given a signature place the type ids into the given vector
-bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* param_type_idxs,
- const std::string& signature) const {
+bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
+ std::vector<uint16_t>* param_type_idxs) const {
if (signature[0] != '(') {
return false;
}
@@ -543,6 +558,7 @@
process_return = true;
continue;
}
+ // TODO: avoid building a string.
std::string descriptor;
descriptor += c;
while (c == '[') { // process array prefix
@@ -582,35 +598,18 @@
return false; // failed to correctly parse return type
}
-// Materializes the method descriptor for a method prototype. Method
-// descriptors are not stored directly in the dex file. Instead, one
-// must assemble the descriptor from references in the prototype.
-std::string DexFile::CreateMethodSignature(uint32_t proto_idx, int32_t* unicode_length) const {
- const ProtoId& proto_id = GetProtoId(proto_idx);
- std::string descriptor;
- descriptor.push_back('(');
- const TypeList* type_list = GetProtoParameters(proto_id);
- size_t parameter_length = 0;
- if (type_list != NULL) {
- // A non-zero number of arguments. Append the type names.
- for (size_t i = 0; i < type_list->Size(); ++i) {
- const TypeItem& type_item = type_list->GetTypeItem(i);
- uint32_t type_idx = type_item.type_idx_;
- uint32_t type_length;
- const char* name = StringByTypeIdx(type_idx, &type_length);
- parameter_length += type_length;
- descriptor.append(name);
- }
+const Signature DexFile::CreateSignature(const StringPiece& signature) const {
+ uint16_t return_type_idx;
+ std::vector<uint16_t> param_type_indices;
+ bool success = CreateTypeList(signature, &return_type_idx, ¶m_type_indices);
+ if (!success) {
+ return Signature::NoSignature();
}
- descriptor.push_back(')');
- uint32_t return_type_idx = proto_id.return_type_idx_;
- uint32_t return_type_length;
- const char* name = StringByTypeIdx(return_type_idx, &return_type_length);
- descriptor.append(name);
- if (unicode_length != NULL) {
- *unicode_length = parameter_length + return_type_length + 2; // 2 for ( and )
+ const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices);
+ if (proto_id == NULL) {
+ return Signature::NoSignature();
}
- return descriptor;
+ return Signature(this, *proto_id);
}
int32_t DexFile::GetLineNumFromPC(const mirror::ArtMethod* method, uint32_t rel_pc) const {
@@ -856,6 +855,30 @@
}
}
+std::string Signature::ToString() const {
+ if (dex_file_ == nullptr) {
+ CHECK(proto_id_ == nullptr);
+ return "<no signature>";
+ }
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ std::string result;
+ if (params == nullptr) {
+ result += "()";
+ } else {
+ result += "(";
+ for (uint32_t i = 0; i < params->Size(); ++i) {
+ result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_);
+ }
+ result += ")";
+ }
+ result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_);
+ return result;
+}
+
+std::ostream& operator<<(std::ostream& os, const Signature& sig) {
+ return os << sig.ToString();
+}
+
// Decodes the header section from the class data bytes.
void ClassDataItemIterator::ReadClassDataHeader() {
CHECK(ptr_pos_ != NULL);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 346154c..a9c24e6 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -33,6 +33,8 @@
namespace art {
+// TODO: remove dependencies on mirror classes, primarily by moving
+// EncodedStaticFieldValueIterator to its own file.
namespace mirror {
class ArtField;
class ArtMethod;
@@ -40,6 +42,8 @@
class DexCache;
} // namespace mirror
class ClassLinker;
+class Signature;
+class StringPiece;
class ZipArchive;
// TODO: move all of the macro functionality into the DexCache class.
@@ -346,22 +350,22 @@
// For .dex files, this is the header checksum.
// For zip files, this is the classes.dex zip entry CRC32 checksum.
// Return true if the checksum could be found, false otherwise.
- static bool GetChecksum(const std::string& filename, uint32_t* checksum)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
// Opens .dex file, guessing the container format based on file extension
- static const DexFile* Open(const std::string& filename,
- const std::string& location);
+ static const DexFile* Open(const char* filename, const char* location, std::string* error_msg);
// Opens .dex file, backed by existing memory
static const DexFile* Open(const uint8_t* base, size_t size,
const std::string& location,
- uint32_t location_checksum) {
- return OpenMemory(base, size, location, location_checksum, NULL);
+ uint32_t location_checksum,
+ std::string* error_msg) {
+ return OpenMemory(base, size, location, location_checksum, NULL, error_msg);
}
// Opens .dex file from the classes.dex in a zip archive
- static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location);
+ static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location,
+ std::string* error_msg);
// Closes a .dex file.
virtual ~DexFile();
@@ -414,27 +418,29 @@
int32_t GetStringLength(const StringId& string_id) const;
- // Returns a pointer to the UTF-8 string data referred to by the given string_id.
- const char* GetStringDataAndLength(const StringId& string_id, uint32_t* length) const;
+ // Returns a pointer to the UTF-8 string data referred to by the given string_id as well as the
+ // length of the string when decoded as a UTF-16 string. Note the UTF-16 length is not the same
+ // as the string length of the string data.
+ const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const;
const char* GetStringData(const StringId& string_id) const {
- uint32_t length;
- return GetStringDataAndLength(string_id, &length);
+ uint32_t ignored;
+ return GetStringDataAndUtf16Length(string_id, &ignored);
}
- // return the UTF-8 encoded string with the specified string_id index
- const char* StringDataAndLengthByIdx(uint32_t idx, uint32_t* unicode_length) const {
+ // Index version of GetStringDataAndUtf16Length.
+ const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const {
if (idx == kDexNoIndex) {
- *unicode_length = 0;
+ *utf16_length = 0;
return NULL;
}
const StringId& string_id = GetStringId(idx);
- return GetStringDataAndLength(string_id, unicode_length);
+ return GetStringDataAndUtf16Length(string_id, utf16_length);
}
const char* StringDataByIdx(uint32_t idx) const {
uint32_t unicode_length;
- return StringDataAndLengthByIdx(idx, &unicode_length);
+ return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
}
// Looks up a string id for a given modified utf8 string.
@@ -466,7 +472,7 @@
// Get the descriptor string associated with a given type index.
const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const {
const TypeId& type_id = GetTypeId(idx);
- return StringDataAndLengthByIdx(type_id.descriptor_idx_, unicode_length);
+ return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
}
const char* StringByTypeIdx(uint32_t idx) const {
@@ -556,10 +562,8 @@
return GetProtoId(method_id.proto_idx_);
}
- // Returns the signature of a method id.
- const std::string GetMethodSignature(const MethodId& method_id) const {
- return CreateMethodSignature(method_id.proto_idx_, NULL);
- }
+ // Returns a representation of the signature of a method id.
+ const Signature GetMethodSignature(const MethodId& method_id) const;
// Returns the name of a method id.
const char* GetMethodName(const MethodId& method_id) const {
@@ -571,7 +575,8 @@
return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_);
}
const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const {
- return StringDataAndLengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
+ // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
+ return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
}
// Returns the number of class definitions in the .dex file.
size_t NumClassDefs() const {
@@ -653,15 +658,16 @@
}
// Looks up a proto id for a given return type and signature type list
- const ProtoId* FindProtoId(uint16_t return_type_id,
+ const ProtoId* FindProtoId(uint16_t return_type_idx,
const std::vector<uint16_t>& signature_type_idxs_) const;
// Given a signature place the type ids into the given vector, returns true on success
- bool CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* param_type_idxs,
- const std::string& signature) const;
+ bool CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
+ std::vector<uint16_t>* param_type_idxs) const;
- // Given a proto_idx decode the type list and return type into a method signature
- std::string CreateMethodSignature(uint32_t proto_idx, int32_t* unicode_length) const;
+ // Create a Signature from the given string signature or return Signature::NoSignature if not
+ // possible.
+ const Signature CreateSignature(const StringPiece& signature) const;
// Returns the short form method descriptor for the given prototype.
const char* GetShorty(uint32_t proto_idx) const {
@@ -815,24 +821,24 @@
private:
// Opens a .dex file
- static const DexFile* OpenFile(int fd,
- const std::string& location,
- bool verify);
+ static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg);
// Opens a dex file from within a .jar, .zip, or .apk file
- static const DexFile* OpenZip(int fd, const std::string& location);
+ static const DexFile* OpenZip(int fd, const std::string& location, std::string* error_msg);
// Opens a .dex file at the given address backed by a MemMap
static const DexFile* OpenMemory(const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map);
+ MemMap* mem_map,
+ std::string* error_msg);
// Opens a .dex file at the given address, optionally backed by a MemMap
static const DexFile* OpenMemory(const byte* dex_file,
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map);
+ MemMap* mem_map,
+ std::string* error_msg);
DexFile(const byte* base, size_t size,
const std::string& location,
@@ -856,13 +862,13 @@
}
// Top-level initializer that calls other Init methods.
- bool Init();
+ bool Init(std::string* error_msg);
// Caches pointers into to the various file sections.
void InitMembers();
// Returns true if the header magic and version numbers are of the expected values.
- bool CheckMagicAndVersion() const;
+ bool CheckMagicAndVersion(std::string* error_msg) const;
void DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx,
DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
@@ -938,6 +944,39 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator);
};
+// Abstract the signature of a method.
+class Signature {
+ public:
+ std::string ToString() const;
+
+ static Signature NoSignature() {
+ return Signature();
+ }
+
+ bool operator==(const Signature& rhs) const;
+ bool operator!=(const Signature& rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator==(const StringPiece& rhs) const {
+ // TODO: Avoid temporary string allocation.
+ return ToString() == rhs;
+ }
+
+ private:
+ Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
+ }
+
+ Signature() : dex_file_(nullptr), proto_id_(nullptr) {
+ }
+
+ friend class DexFile;
+
+ const DexFile* const dex_file_;
+ const DexFile::ProtoId* const proto_id_;
+};
+std::ostream& operator<<(std::ostream& os, const Signature& sig);
+
// Iterate and decode class_data_item
class ClassDataItemIterator {
public:
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 272c42d..543a7b0 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -56,7 +56,7 @@
"AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
static const DexFile* OpenDexFileBase64(const char* base64,
- const std::string& location) {
+ const char* location) {
// decode base64
CHECK(base64 != NULL);
size_t length;
@@ -64,7 +64,7 @@
CHECK(dex_bytes.get() != NULL);
// write to provided file
- UniquePtr<File> file(OS::CreateEmptyFile(location.c_str()));
+ UniquePtr<File> file(OS::CreateEmptyFile(location));
CHECK(file.get() != NULL);
if (!file->WriteFully(dex_bytes.get(), length)) {
PLOG(FATAL) << "Failed to write base64 as dex file";
@@ -73,8 +73,9 @@
// read dex file
ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex_file = DexFile::Open(location, location);
- CHECK(dex_file != NULL);
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(location, location, &error_msg);
+ CHECK(dex_file != nullptr) << error_msg;
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
@@ -82,7 +83,7 @@
TEST_F(DexFileTest, Header) {
ScratchFile tmp;
- UniquePtr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename()));
+ UniquePtr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
ASSERT_TRUE(raw.get() != NULL);
const DexFile::Header& header = raw->GetHeader();
@@ -120,7 +121,9 @@
TEST_F(DexFileTest, GetChecksum) {
uint32_t checksum;
ScopedObjectAccess soa(Thread::Current());
- EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName(), &checksum));
+ std::string error_msg;
+ EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName().c_str(), &checksum, &error_msg))
+ << error_msg;
EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
}
@@ -137,14 +140,14 @@
EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1));
}
-TEST_F(DexFileTest, CreateMethodSignature) {
+TEST_F(DexFileTest, GetMethodSignature) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("CreateMethodSignature"));
+ const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
ASSERT_TRUE(raw != NULL);
EXPECT_EQ(1U, raw->NumClassDefs());
const DexFile::ClassDef& class_def = raw->GetClassDef(0);
- ASSERT_STREQ("LCreateMethodSignature;", raw->GetClassDescriptor(class_def));
+ ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def));
const byte* class_data = raw->GetClassData(class_def);
ASSERT_TRUE(class_data != NULL);
@@ -156,11 +159,9 @@
{
ASSERT_EQ(1U, it.NumDirectMethods());
const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
- uint32_t proto_idx = method_id.proto_idx_;
const char* name = raw->StringDataByIdx(method_id.name_idx_);
ASSERT_STREQ("<init>", name);
- int32_t length;
- std::string signature(raw->CreateMethodSignature(proto_idx, &length));
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
ASSERT_EQ("()V", signature);
}
@@ -173,9 +174,7 @@
const char* name = raw->StringDataByIdx(method_id.name_idx_);
ASSERT_STREQ("m1", name);
- uint32_t proto_idx = method_id.proto_idx_;
- int32_t length;
- std::string signature(raw->CreateMethodSignature(proto_idx, &length));
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
ASSERT_EQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", signature);
}
@@ -186,20 +185,18 @@
const char* name = raw->StringDataByIdx(method_id.name_idx_);
ASSERT_STREQ("m2", name);
- uint32_t proto_idx = method_id.proto_idx_;
- int32_t length;
- std::string signature(raw->CreateMethodSignature(proto_idx, &length));
- ASSERT_EQ("(ZSC)LCreateMethodSignature;", signature);
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
+ ASSERT_EQ("(ZSC)LGetMethodSignature;", signature);
}
}
TEST_F(DexFileTest, FindStringId) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("CreateMethodSignature"));
+ const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
ASSERT_TRUE(raw != NULL);
EXPECT_EQ(1U, raw->NumClassDefs());
- const char* strings[] = { "LCreateMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
+ const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
"D", "I", "J", NULL };
for (size_t i = 0; strings[i] != NULL; i++) {
const char* str = strings[i];
@@ -245,11 +242,10 @@
const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_);
const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_);
const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature);
- int32_t length;
ASSERT_TRUE(found != NULL) << "Didn't find method " << i << ": "
<< java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "."
<< java_lang_dex_file_->GetStringData(name)
- << java_lang_dex_file_->CreateMethodSignature(to_find.proto_idx_, &length);
+ << java_lang_dex_file_->GetMethodSignature(to_find);
EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i);
}
}
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 5b076e0..56bf21d 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -21,7 +21,7 @@
#include "leb128.h"
#include "safe_map.h"
#include "UniquePtr.h"
-#include "utf.h"
+#include "utf-inl.h"
#include "utils.h"
#include "zip_archive.h"
@@ -65,12 +65,22 @@
return true;
}
-static bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
- bool is_return_type) {
+bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size,
+ const char* location, std::string* error_msg) {
+ UniquePtr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location));
+ if (!verifier->Verify()) {
+ *error_msg = verifier->FailureReason();
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
+ bool is_return_type) {
switch (shorty_char) {
case 'V':
- if (!is_return_type) {
- LOG(ERROR) << "Invalid use of void";
+ if (UNLIKELY(!is_return_type)) {
+ ErrorStringPrintf("Invalid use of void");
return false;
}
// Intentional fallthrough.
@@ -82,62 +92,58 @@
case 'J':
case 'S':
case 'Z':
- if ((descriptor[0] != shorty_char) || (descriptor[1] != '\0')) {
- LOG(ERROR) << StringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'", shorty_char, descriptor);
+ if (UNLIKELY((descriptor[0] != shorty_char) || (descriptor[1] != '\0'))) {
+ ErrorStringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'",
+ shorty_char, descriptor);
return false;
}
break;
case 'L':
- if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
- LOG(ERROR) << StringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor);
+ if (UNLIKELY((descriptor[0] != 'L') && (descriptor[0] != '['))) {
+ ErrorStringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor);
return false;
}
break;
default:
- LOG(ERROR) << "Bad shorty character: '" << shorty_char << "'";
+ ErrorStringPrintf("Bad shorty character: '%c'", shorty_char);
return false;
}
return true;
}
-bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size) {
- UniquePtr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size));
- return verifier->Verify();
-}
-
-bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) const {
+bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) {
uint32_t range_start = reinterpret_cast<uint32_t>(start);
uint32_t range_end = reinterpret_cast<uint32_t>(end);
uint32_t file_start = reinterpret_cast<uint32_t>(begin_);
uint32_t file_end = file_start + size_;
- if ((range_start < file_start) || (range_start > file_end) ||
- (range_end < file_start) || (range_end > file_end)) {
- LOG(ERROR) << StringPrintf("Bad range for %s: %x to %x", label,
- range_start - file_start, range_end - file_start);
+ if (UNLIKELY((range_start < file_start) || (range_start > file_end) ||
+ (range_end < file_start) || (range_end > file_end))) {
+ ErrorStringPrintf("Bad range for %s: %x to %x", label,
+ range_start - file_start, range_end - file_start);
return false;
}
return true;
}
bool DexFileVerifier::CheckListSize(const void* start, uint32_t count,
- uint32_t element_size, const char* label) const {
+ uint32_t element_size, const char* label) {
const byte* list_start = reinterpret_cast<const byte*>(start);
return CheckPointerRange(list_start, list_start + (count * element_size), label);
}
-bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) const {
- if (field >= limit) {
- LOG(ERROR) << StringPrintf("Bad index for %s: %x >= %x", label, field, limit);
+bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
+ if (UNLIKELY(field >= limit)) {
+ ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
return false;
}
return true;
}
-bool DexFileVerifier::CheckHeader() const {
+bool DexFileVerifier::CheckHeader() {
// Check file size from the header.
uint32_t expected_size = header_->file_size_;
if (size_ != expected_size) {
- LOG(ERROR) << "Bad file size (" << size_ << ", expected " << expected_size << ")";
+ ErrorStringPrintf("Bad file size (%zd, expected %ud)", size_, expected_size);
return false;
}
@@ -147,25 +153,25 @@
const byte* non_sum_ptr = reinterpret_cast<const byte*>(header_) + non_sum;
adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
if (adler_checksum != header_->checksum_) {
- LOG(ERROR) << StringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
+ ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
return false;
}
// Check the contents of the header.
if (header_->endian_tag_ != DexFile::kDexEndianConstant) {
- LOG(ERROR) << StringPrintf("Unexpected endian_tag: %x", header_->endian_tag_);
+ ErrorStringPrintf("Unexpected endian_tag: %x", header_->endian_tag_);
return false;
}
if (header_->header_size_ != sizeof(DexFile::Header)) {
- LOG(ERROR) << "Bad header size: " << header_->header_size_;
+ ErrorStringPrintf("Bad header size: %ud", header_->header_size_);
return false;
}
return true;
}
-bool DexFileVerifier::CheckMap() const {
+bool DexFileVerifier::CheckMap() {
const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
const DexFile::MapItem* item = map->list_;
@@ -182,19 +188,20 @@
// Check the items listed in the map.
for (uint32_t i = 0; i < count; i++) {
- if (last_offset >= item->offset_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out of order map item: %x then %x", last_offset, item->offset_);
+ if (UNLIKELY(last_offset >= item->offset_ && i != 0)) {
+ ErrorStringPrintf("Out of order map item: %x then %x", last_offset, item->offset_);
return false;
}
- if (item->offset_ >= header_->file_size_) {
- LOG(ERROR) << StringPrintf("Map item after end of file: %x, size %x", item->offset_, header_->file_size_);
+ if (UNLIKELY(item->offset_ >= header_->file_size_)) {
+ ErrorStringPrintf("Map item after end of file: %x, size %x",
+ item->offset_, header_->file_size_);
return false;
}
if (IsDataSectionType(item->type_)) {
uint32_t icount = item->size_;
- if (icount > data_items_left) {
- LOG(ERROR) << "Too many items in data section: " << data_item_count + icount;
+ if (UNLIKELY(icount > data_items_left)) {
+ ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount);
return false;
}
data_items_left -= icount;
@@ -203,13 +210,13 @@
uint32_t bit = MapTypeToBitMask(item->type_);
- if (bit == 0) {
- LOG(ERROR) << StringPrintf("Unknown map section type %x", item->type_);
+ if (UNLIKELY(bit == 0)) {
+ ErrorStringPrintf("Unknown map section type %x", item->type_);
return false;
}
- if ((used_bits & bit) != 0) {
- LOG(ERROR) << StringPrintf("Duplicate map section of type %x", item->type_);
+ if (UNLIKELY((used_bits & bit) != 0)) {
+ ErrorStringPrintf("Duplicate map section of type %x", item->type_);
return false;
}
@@ -219,63 +226,59 @@
}
// Check for missing sections in the map.
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0) {
- LOG(ERROR) << "Map is missing header entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0)) {
+ ErrorStringPrintf("Map is missing header entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0) {
- LOG(ERROR) << "Map is missing map_list entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0)) {
+ ErrorStringPrintf("Map is missing map_list entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 &&
- ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing string_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 &&
+ ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing string_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 &&
- ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing type_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 &&
+ ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing type_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 &&
- ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing proto_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 &&
+ ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing proto_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 &&
- ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing field_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 &&
+ ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing field_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 &&
- ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing method_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 &&
+ ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing method_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 &&
- ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0))) {
- LOG(ERROR) << "Map is missing class_defs entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 &&
+ ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing class_defs entry");
return false;
}
-
return true;
}
uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) {
uint32_t result = 0;
- if (!CheckPointerRange(ptr_, ptr_ + size, "encoded_value")) {
- return 0;
+ if (LIKELY(CheckPointerRange(ptr_, ptr_ + size, "encoded_value"))) {
+ for (uint32_t i = 0; i < size; i++) {
+ result |= ((uint32_t) *(ptr_++)) << (i * 8);
+ }
}
-
- for (uint32_t i = 0; i < size; i++) {
- result |= ((uint32_t) *(ptr_++)) << (i * 8);
- }
-
return result;
}
bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
- uint32_t* handler_offsets, uint32_t handlers_size) {
+ uint32_t* handler_offsets, uint32_t handlers_size) {
const byte* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0);
for (uint32_t i = 0; i < handlers_size; i++) {
@@ -283,8 +286,8 @@
uint32_t offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(handlers_base);
int32_t size = DecodeSignedLeb128(&ptr_);
- if ((size < -65536) || (size > 65536)) {
- LOG(ERROR) << "Invalid exception handler size: " << size;
+ if (UNLIKELY((size < -65536) || (size > 65536))) {
+ ErrorStringPrintf("Invalid exception handler size: %d", size);
return false;
}
@@ -304,16 +307,16 @@
}
uint32_t addr = DecodeUnsignedLeb128(&ptr_);
- if (addr >= code_item->insns_size_in_code_units_) {
- LOG(ERROR) << StringPrintf("Invalid handler addr: %x", addr);
+ if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) {
+ ErrorStringPrintf("Invalid handler addr: %x", addr);
return false;
}
}
if (catch_all) {
uint32_t addr = DecodeUnsignedLeb128(&ptr_);
- if (addr >= code_item->insns_size_in_code_units_) {
- LOG(ERROR) << StringPrintf("Invalid handler catch_all_addr: %x", addr);
+ if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) {
+ ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr);
return false;
}
}
@@ -323,21 +326,21 @@
}
bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flags,
- bool expect_static) const {
+ bool expect_static) {
if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
return false;
}
bool is_static = (access_flags & kAccStatic) != 0;
- if (is_static != expect_static) {
- LOG(ERROR) << "Static/instance field not in expected list";
+ if (UNLIKELY(is_static != expect_static)) {
+ ErrorStringPrintf("Static/instance field not in expected list");
return false;
}
uint32_t access_field_mask = kAccPublic | kAccPrivate | kAccProtected | kAccStatic |
kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum;
- if ((access_flags & ~access_field_mask) != 0) {
- LOG(ERROR) << StringPrintf("Bad class_data_item field access_flags %x", access_flags);
+ if (UNLIKELY((access_flags & ~access_field_mask) != 0)) {
+ ErrorStringPrintf("Bad class_data_item field access_flags %x", access_flags);
return false;
}
@@ -345,7 +348,7 @@
}
bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags,
- uint32_t code_offset, bool expect_direct) const {
+ uint32_t code_offset, bool expect_direct) {
if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
return false;
}
@@ -355,26 +358,27 @@
bool is_synchronized = (access_flags & kAccSynchronized) != 0;
bool allow_synchronized = (access_flags & kAccNative) != 0;
- if (is_direct != expect_direct) {
- LOG(ERROR) << "Direct/virtual method not in expected list";
+ if (UNLIKELY(is_direct != expect_direct)) {
+ ErrorStringPrintf("Direct/virtual method not in expected list");
return false;
}
uint32_t access_method_mask = kAccPublic | kAccPrivate | kAccProtected | kAccStatic |
kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | kAccAbstract |
kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized;
- if (((access_flags & ~access_method_mask) != 0) || (is_synchronized && !allow_synchronized)) {
- LOG(ERROR) << StringPrintf("Bad class_data_item method access_flags %x", access_flags);
+ if (UNLIKELY(((access_flags & ~access_method_mask) != 0) ||
+ (is_synchronized && !allow_synchronized))) {
+ ErrorStringPrintf("Bad class_data_item method access_flags %x", access_flags);
return false;
}
- if (expect_code && code_offset == 0) {
- LOG(ERROR)<< StringPrintf("Unexpected zero value for class_data_item method code_off"
- " with access flags %x", access_flags);
+ if (UNLIKELY(expect_code && (code_offset == 0))) {
+ ErrorStringPrintf("Unexpected zero value for class_data_item method code_off with access "
+ "flags %x", access_flags);
return false;
- } else if (!expect_code && code_offset != 0) {
- LOG(ERROR) << StringPrintf("Unexpected non-zero value %x for class_data_item method code_off"
- " with access flags %x", code_offset, access_flags);
+ } else if (UNLIKELY(!expect_code && (code_offset != 0))) {
+ ErrorStringPrintf("Unexpected non-zero value %x for class_data_item method code_off"
+ " with access flags %x", code_offset, access_flags);
return false;
}
@@ -387,8 +391,8 @@
return false;
}
while (offset < aligned_offset) {
- if (*ptr_ != '\0') {
- LOG(ERROR) << StringPrintf("Non-zero padding %x before section start at %x", *ptr_, offset);
+ if (UNLIKELY(*ptr_ != '\0')) {
+ ErrorStringPrintf("Non-zero padding %x before section start at %x", *ptr_, offset);
return false;
}
ptr_++;
@@ -409,24 +413,24 @@
switch (value_type) {
case DexFile::kDexAnnotationByte:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value byte size %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value byte size %x", value_arg);
return false;
}
ptr_++;
break;
case DexFile::kDexAnnotationShort:
case DexFile::kDexAnnotationChar:
- if (value_arg > 1) {
- LOG(ERROR) << StringPrintf("Bad encoded_value char/short size %x", value_arg);
+ if (UNLIKELY(value_arg > 1)) {
+ ErrorStringPrintf("Bad encoded_value char/short size %x", value_arg);
return false;
}
ptr_ += value_arg + 1;
break;
case DexFile::kDexAnnotationInt:
case DexFile::kDexAnnotationFloat:
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value int/float size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value int/float size %x", value_arg);
return false;
}
ptr_ += value_arg + 1;
@@ -436,8 +440,8 @@
ptr_ += value_arg + 1;
break;
case DexFile::kDexAnnotationString: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value string size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value string size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -447,8 +451,8 @@
break;
}
case DexFile::kDexAnnotationType: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value type size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value type size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -459,8 +463,8 @@
}
case DexFile::kDexAnnotationField:
case DexFile::kDexAnnotationEnum: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value field/enum size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value field/enum size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -470,8 +474,8 @@
break;
}
case DexFile::kDexAnnotationMethod: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value method size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -481,8 +485,8 @@
break;
}
case DexFile::kDexAnnotationArray:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value array value_arg %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value array value_arg %x", value_arg);
return false;
}
if (!CheckEncodedArray()) {
@@ -490,8 +494,8 @@
}
break;
case DexFile::kDexAnnotationAnnotation:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value annotation value_arg %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value annotation value_arg %x", value_arg);
return false;
}
if (!CheckEncodedAnnotation()) {
@@ -499,19 +503,19 @@
}
break;
case DexFile::kDexAnnotationNull:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value null value_arg %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value null value_arg %x", value_arg);
return false;
}
break;
case DexFile::kDexAnnotationBoolean:
- if (value_arg > 1) {
- LOG(ERROR) << StringPrintf("Bad encoded_value boolean size %x", value_arg);
+ if (UNLIKELY(value_arg > 1)) {
+ ErrorStringPrintf("Bad encoded_value boolean size %x", value_arg);
return false;
}
break;
default:
- LOG(ERROR) << StringPrintf("Bogus encoded_value value_type %x", value_type);
+ ErrorStringPrintf("Bogus encoded_value value_type %x", value_type);
return false;
}
@@ -523,7 +527,7 @@
while (size--) {
if (!CheckEncodedValue()) {
- LOG(ERROR) << "Bad encoded_array value";
+ failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str());
return false;
}
}
@@ -545,9 +549,9 @@
return false;
}
- if (last_idx >= idx && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order annotation_element name_idx: %x then %x",
- last_idx, idx);
+ if (UNLIKELY(last_idx >= idx && i != 0)) {
+ ErrorStringPrintf("Out-of-order annotation_element name_idx: %x then %x",
+ last_idx, idx);
return false;
}
@@ -596,21 +600,22 @@
return false;
}
- if (code_item->ins_size_ > code_item->registers_size_) {
- LOG(ERROR) << "ins_size (" << code_item->ins_size_ << ") > registers_size ("
- << code_item->registers_size_ << ")";
+ if (UNLIKELY(code_item->ins_size_ > code_item->registers_size_)) {
+ ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)",
+ code_item->ins_size_, code_item->registers_size_);
return false;
}
- if ((code_item->outs_size_ > 5) && (code_item->outs_size_ > code_item->registers_size_)) {
+ if (UNLIKELY((code_item->outs_size_ > 5) &&
+ (code_item->outs_size_ > code_item->registers_size_))) {
/*
* outs_size can be up to 5, even if registers_size is smaller, since the
* short forms of method invocation allow repetitions of a register multiple
* times within a single parameter list. However, longer parameter lists
* need to be represented in-order in the register file.
*/
- LOG(ERROR) << "outs_size (" << code_item->outs_size_ << ") > registers_size ("
- << code_item->registers_size_ << ")";
+ ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)",
+ code_item->outs_size_, code_item->registers_size_);
return false;
}
@@ -629,7 +634,7 @@
// try_items are 4-byte aligned. Verify the spacer is 0.
if ((((uint32_t) &insns[insns_size] & 3) != 0) && (insns[insns_size] != 0)) {
- LOG(ERROR) << StringPrintf("Non-zero padding: %x", insns[insns_size]);
+ ErrorStringPrintf("Non-zero padding: %x", insns[insns_size]);
return false;
}
@@ -641,8 +646,8 @@
return false;
}
- if ((handlers_size == 0) || (handlers_size >= 65536)) {
- LOG(ERROR) << "Invalid handlers_size: " << handlers_size;
+ if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) {
+ ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size);
return false;
}
@@ -653,14 +658,13 @@
uint32_t last_addr = 0;
while (try_items_size--) {
- if (try_items->start_addr_ < last_addr) {
- LOG(ERROR) << StringPrintf("Out-of_order try_item with start_addr: %x",
- try_items->start_addr_);
+ if (UNLIKELY(try_items->start_addr_ < last_addr)) {
+ ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_);
return false;
}
- if (try_items->start_addr_ >= insns_size) {
- LOG(ERROR) << StringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_);
+ if (UNLIKELY(try_items->start_addr_ >= insns_size)) {
+ ErrorStringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_);
return false;
}
@@ -671,14 +675,14 @@
}
}
- if (i == handlers_size) {
- LOG(ERROR) << StringPrintf("Bogus handler offset: %x", try_items->handler_off_);
+ if (UNLIKELY(i == handlers_size)) {
+ ErrorStringPrintf("Bogus handler offset: %x", try_items->handler_off_);
return false;
}
last_addr = try_items->start_addr_ + try_items->insn_count_;
- if (last_addr > insns_size) {
- LOG(ERROR) << StringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_);
+ if (UNLIKELY(last_addr > insns_size)) {
+ ErrorStringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_);
return false;
}
@@ -693,8 +697,8 @@
const byte* file_end = begin_ + size_;
for (uint32_t i = 0; i < size; i++) {
- if (ptr_ >= file_end) {
- LOG(ERROR) << "String data would go beyond end-of-file";
+ if (UNLIKELY(ptr_ >= file_end)) {
+ ErrorStringPrintf("String data would go beyond end-of-file");
return false;
}
@@ -704,8 +708,8 @@
switch (byte >> 4) {
case 0x00:
// Special case of bit pattern 0xxx.
- if (byte == 0) {
- LOG(ERROR) << StringPrintf("String data shorter than indicated utf16_size %x", size);
+ if (UNLIKELY(byte == 0)) {
+ ErrorStringPrintf("String data shorter than indicated utf16_size %x", size);
return false;
}
break;
@@ -725,19 +729,19 @@
case 0x0f:
// Illegal bit patterns 10xx or 1111.
// Note: 1111 is valid for normal UTF-8, but not here.
- LOG(ERROR) << StringPrintf("Illegal start byte %x in string data", byte);
+ ErrorStringPrintf("Illegal start byte %x in string data", byte);
return false;
case 0x0c:
case 0x0d: {
// Bit pattern 110x has an additional byte.
uint8_t byte2 = *(ptr_++);
- if ((byte2 & 0xc0) != 0x80) {
- LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte2);
+ if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
return false;
}
uint16_t value = ((byte & 0x1f) << 6) | (byte2 & 0x3f);
- if ((value != 0) && (value < 0x80)) {
- LOG(ERROR) << StringPrintf("Illegal representation for value %x in string data", value);
+ if (UNLIKELY((value != 0) && (value < 0x80))) {
+ ErrorStringPrintf("Illegal representation for value %x in string data", value);
return false;
}
break;
@@ -745,18 +749,18 @@
case 0x0e: {
// Bit pattern 1110 has 2 additional bytes.
uint8_t byte2 = *(ptr_++);
- if ((byte2 & 0xc0) != 0x80) {
- LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte2);
+ if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
return false;
}
uint8_t byte3 = *(ptr_++);
- if ((byte3 & 0xc0) != 0x80) {
- LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte3);
+ if (UNLIKELY((byte3 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte3);
return false;
}
uint16_t value = ((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f);
- if (value < 0x800) {
- LOG(ERROR) << StringPrintf("Illegal representation for value %x in string data", value);
+ if (UNLIKELY(value < 0x800)) {
+ ErrorStringPrintf("Illegal representation for value %x in string data", value);
return false;
}
break;
@@ -764,8 +768,8 @@
}
}
- if (*(ptr_++) != '\0') {
- LOG(ERROR) << StringPrintf("String longer than indicated size %x", size);
+ if (UNLIKELY(*(ptr_++) != '\0')) {
+ ErrorStringPrintf("String longer than indicated size %x", size);
return false;
}
@@ -775,8 +779,8 @@
bool DexFileVerifier::CheckIntraDebugInfoItem() {
DecodeUnsignedLeb128(&ptr_);
uint32_t parameters_size = DecodeUnsignedLeb128(&ptr_);
- if (parameters_size > 65536) {
- LOG(ERROR) << StringPrintf("Invalid parameters_size: %x", parameters_size);
+ if (UNLIKELY(parameters_size > 65536)) {
+ ErrorStringPrintf("Invalid parameters_size: %x", parameters_size);
return false;
}
@@ -806,8 +810,8 @@
}
case DexFile::DBG_START_LOCAL: {
uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
- if (reg_num >= 65536) {
- LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
@@ -829,16 +833,16 @@
case DexFile::DBG_END_LOCAL:
case DexFile::DBG_RESTART_LOCAL: {
uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
- if (reg_num >= 65536) {
- LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
break;
}
case DexFile::DBG_START_LOCAL_EXTENDED: {
uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
- if (reg_num >= 65536) {
- LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
@@ -890,7 +894,7 @@
case DexFile::kDexVisibilitySystem:
break;
default:
- LOG(ERROR) << StringPrintf("Bad annotation visibility: %x", *ptr_);
+ ErrorStringPrintf("Bad annotation visibility: %x", *ptr_);
return false;
}
@@ -918,8 +922,8 @@
uint32_t last_idx = 0;
for (uint32_t i = 0; i < field_count; i++) {
- if (last_idx >= field_item->field_idx_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_);
+ if (UNLIKELY(last_idx >= field_item->field_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_);
return false;
}
last_idx = field_item->field_idx_;
@@ -936,9 +940,9 @@
last_idx = 0;
for (uint32_t i = 0; i < method_count; i++) {
- if (last_idx >= method_item->method_idx_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x",
- last_idx, method_item->method_idx_);
+ if (UNLIKELY(last_idx >= method_item->method_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
+ last_idx, method_item->method_idx_);
return false;
}
last_idx = method_item->method_idx_;
@@ -950,15 +954,15 @@
reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
uint32_t parameter_count = item->parameters_size_;
if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem),
- "parameter_annotations list")) {
+ "parameter_annotations list")) {
return false;
}
last_idx = 0;
for (uint32_t i = 0; i < parameter_count; i++) {
- if (last_idx >= parameter_item->method_idx_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x",
- last_idx, parameter_item->method_idx_);
+ if (UNLIKELY(last_idx >= parameter_item->method_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
+ last_idx, parameter_item->method_idx_);
return false;
}
last_idx = parameter_item->method_idx_;
@@ -1059,7 +1063,7 @@
if (!CheckPointerRange(list, list + 1, "annotation_set_ref_list") ||
!CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem),
- "annotation_set_ref_list size")) {
+ "annotation_set_ref_list size")) {
return false;
}
ptr_ = reinterpret_cast<const byte*>(item + count);
@@ -1121,7 +1125,7 @@
break;
}
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1130,8 +1134,8 @@
}
aligned_offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
- if (aligned_offset > size_) {
- LOG(ERROR) << StringPrintf("Item %d at ends out of bounds", i);
+ if (UNLIKELY(aligned_offset > size_)) {
+ ErrorStringPrintf("Item %d at ends out of bounds", i);
return false;
}
@@ -1172,17 +1176,17 @@
expected_size = header_->class_defs_size_;
break;
default:
- LOG(ERROR) << StringPrintf("Bad type for id section: %x", type);
+ ErrorStringPrintf("Bad type for id section: %x", type);
return false;
}
// Check that the offset and size are what were expected from the header.
- if (offset != expected_offset) {
- LOG(ERROR) << StringPrintf("Bad offset for section: got %x, expected %x", offset, expected_offset);
+ if (UNLIKELY(offset != expected_offset)) {
+ ErrorStringPrintf("Bad offset for section: got %x, expected %x", offset, expected_offset);
return false;
}
- if (count != expected_size) {
- LOG(ERROR) << StringPrintf("Bad size for section: got %x, expected %x", count, expected_size);
+ if (UNLIKELY(count != expected_size)) {
+ ErrorStringPrintf("Bad size for section: got %x, expected %x", count, expected_size);
return false;
}
@@ -1194,8 +1198,8 @@
uint32_t data_end = data_start + header_->data_size_;
// Sanity check the offset of the section.
- if ((offset < data_start) || (offset > data_end)) {
- LOG(ERROR) << StringPrintf("Bad offset for data subsection: %x", offset);
+ if (UNLIKELY((offset < data_start) || (offset > data_end))) {
+ ErrorStringPrintf("Bad offset for data subsection: %x", offset);
return false;
}
@@ -1205,7 +1209,7 @@
uint32_t next_offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
if (next_offset > data_end) {
- LOG(ERROR) << StringPrintf("Out-of-bounds end of data subsection: %x", next_offset);
+ ErrorStringPrintf("Out-of-bounds end of data subsection: %x", next_offset);
return false;
}
@@ -1229,20 +1233,20 @@
// Check for padding and overlap between items.
if (!CheckPadding(offset, section_offset)) {
return false;
- } else if (offset > section_offset) {
- LOG(ERROR) << StringPrintf("Section overlap or out-of-order map: %x, %x", offset, section_offset);
+ } else if (UNLIKELY(offset > section_offset)) {
+ ErrorStringPrintf("Section overlap or out-of-order map: %x, %x", offset, section_offset);
return false;
}
// Check each item based on its type.
switch (type) {
case DexFile::kDexTypeHeaderItem:
- if (section_count != 1) {
- LOG(ERROR) << "Multiple header items";
+ if (UNLIKELY(section_count != 1)) {
+ ErrorStringPrintf("Multiple header items");
return false;
}
- if (section_offset != 0) {
- LOG(ERROR) << StringPrintf("Header at %x, not at start of file", section_offset);
+ if (UNLIKELY(section_offset != 0)) {
+ ErrorStringPrintf("Header at %x, not at start of file", section_offset);
return false;
}
ptr_ = begin_ + header_->header_size_;
@@ -1260,13 +1264,13 @@
offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
break;
case DexFile::kDexTypeMapList:
- if (section_count != 1) {
- LOG(ERROR) << "Multiple map list items";
+ if (UNLIKELY(section_count != 1)) {
+ ErrorStringPrintf("Multiple map list items");
return false;
}
- if (section_offset != header_->map_off_) {
- LOG(ERROR) << StringPrintf("Map not at header-defined offset: %x, expected %x",
- section_offset, header_->map_off_);
+ if (UNLIKELY(section_offset != header_->map_off_)) {
+ ErrorStringPrintf("Map not at header-defined offset: %x, expected %x",
+ section_offset, header_->map_off_);
return false;
}
ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
@@ -1288,7 +1292,7 @@
offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
break;
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1300,13 +1304,13 @@
bool DexFileVerifier::CheckOffsetToTypeMap(uint32_t offset, uint16_t type) {
auto it = offset_to_type_map_.find(offset);
- if (it == offset_to_type_map_.end()) {
- LOG(ERROR) << StringPrintf("No data map entry found @ %x; expected %x", offset, type);
+ if (UNLIKELY(it == offset_to_type_map_.end())) {
+ ErrorStringPrintf("No data map entry found @ %x; expected %x", offset, type);
return false;
}
- if (it->second != type) {
- LOG(ERROR) << StringPrintf("Unexpected data map entry @ %x; expected %x, found %x",
- offset, type, it->second);
+ if (UNLIKELY(it->second != type)) {
+ ErrorStringPrintf("Unexpected data map entry @ %x; expected %x, found %x",
+ offset, type, it->second);
return false;
}
return true;
@@ -1365,8 +1369,8 @@
const DexFile::StringId* prev_item = reinterpret_cast<const DexFile::StringId*>(previous_item_);
const char* prev_str = dex_file_->GetStringData(*prev_item);
const char* str = dex_file_->GetStringData(*item);
- if (CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0) {
- LOG(ERROR) << StringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str);
+ if (UNLIKELY(CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0)) {
+ ErrorStringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str);
return false;
}
}
@@ -1380,17 +1384,17 @@
const char* descriptor = dex_file_->StringDataByIdx(item->descriptor_idx_);
// Check that the descriptor is a valid type.
- if (!IsValidDescriptor(descriptor)) {
- LOG(ERROR) << StringPrintf("Invalid type descriptor: '%s'", descriptor);
+ if (UNLIKELY(!IsValidDescriptor(descriptor))) {
+ ErrorStringPrintf("Invalid type descriptor: '%s'", descriptor);
return false;
}
// Check ordering between items.
if (previous_item_ != NULL) {
const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_);
- if (prev_item->descriptor_idx_ >= item->descriptor_idx_) {
- LOG(ERROR) << StringPrintf("Out-of-order type_ids: %x then %x",
- prev_item->descriptor_idx_, item->descriptor_idx_);
+ if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) {
+ ErrorStringPrintf("Out-of-order type_ids: %x then %x",
+ prev_item->descriptor_idx_, item->descriptor_idx_);
return false;
}
}
@@ -1422,16 +1426,16 @@
it.Next();
shorty++;
}
- if (it.HasNext() || *shorty != '\0') {
- LOG(ERROR) << "Mismatched length for parameters and shorty";
+ if (UNLIKELY(it.HasNext() || *shorty != '\0')) {
+ ErrorStringPrintf("Mismatched length for parameters and shorty");
return false;
}
// Check ordering between items. This relies on type_ids being in order.
if (previous_item_ != NULL) {
const DexFile::ProtoId* prev = reinterpret_cast<const DexFile::ProtoId*>(previous_item_);
- if (prev->return_type_idx_ > item->return_type_idx_) {
- LOG(ERROR) << "Out-of-order proto_id return types";
+ if (UNLIKELY(prev->return_type_idx_ > item->return_type_idx_)) {
+ ErrorStringPrintf("Out-of-order proto_id return types");
return false;
} else if (prev->return_type_idx_ == item->return_type_idx_) {
DexFileParameterIterator curr_it(*dex_file_, *item);
@@ -1443,15 +1447,15 @@
if (prev_idx == DexFile::kDexNoIndex16) {
break;
}
- if (curr_idx == DexFile::kDexNoIndex16) {
- LOG(ERROR) << "Out-of-order proto_id arguments";
+ if (UNLIKELY(curr_idx == DexFile::kDexNoIndex16)) {
+ ErrorStringPrintf("Out-of-order proto_id arguments");
return false;
}
if (prev_idx < curr_idx) {
break;
- } else if (prev_idx > curr_idx) {
- LOG(ERROR) << "Out-of-order proto_id arguments";
+ } else if (UNLIKELY(prev_idx > curr_idx)) {
+ ErrorStringPrintf("Out-of-order proto_id arguments");
return false;
}
@@ -1470,38 +1474,38 @@
// Check that the class descriptor is valid.
const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid descriptor for class_idx: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor);
return false;
}
// Check that the type descriptor is a valid field name.
descriptor = dex_file_->StringByTypeIdx(item->type_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] == 'V') {
- LOG(ERROR) << "Invalid descriptor for type_idx: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] == 'V')) {
+ ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", descriptor);
return false;
}
// Check that the name is valid.
descriptor = dex_file_->StringDataByIdx(item->name_idx_);
- if (!IsValidMemberName(descriptor)) {
- LOG(ERROR) << "Invalid field name: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidMemberName(descriptor))) {
+ ErrorStringPrintf("Invalid field name: '%s'", descriptor);
return false;
}
// Check ordering between items. This relies on the other sections being in order.
if (previous_item_ != NULL) {
const DexFile::FieldId* prev_item = reinterpret_cast<const DexFile::FieldId*>(previous_item_);
- if (prev_item->class_idx_ > item->class_idx_) {
- LOG(ERROR) << "Out-of-order field_ids";
+ if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
return false;
} else if (prev_item->class_idx_ == item->class_idx_) {
- if (prev_item->name_idx_ > item->name_idx_) {
- LOG(ERROR) << "Out-of-order field_ids";
+ if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
return false;
} else if (prev_item->name_idx_ == item->name_idx_) {
- if (prev_item->type_idx_ >= item->type_idx_) {
- LOG(ERROR) << "Out-of-order field_ids";
+ if (UNLIKELY(prev_item->type_idx_ >= item->type_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
return false;
}
}
@@ -1517,31 +1521,31 @@
// Check that the class descriptor is a valid reference name.
const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_);
- if (!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '[')) {
- LOG(ERROR) << "Invalid descriptor for class_idx: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '['))) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor);
return false;
}
// Check that the name is valid.
descriptor = dex_file_->StringDataByIdx(item->name_idx_);
- if (!IsValidMemberName(descriptor)) {
- LOG(ERROR) << "Invalid method name: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidMemberName(descriptor))) {
+ ErrorStringPrintf("Invalid method name: '%s'", descriptor);
return false;
}
// Check ordering between items. This relies on the other sections being in order.
if (previous_item_ != NULL) {
const DexFile::MethodId* prev_item = reinterpret_cast<const DexFile::MethodId*>(previous_item_);
- if (prev_item->class_idx_ > item->class_idx_) {
- LOG(ERROR) << "Out-of-order method_ids";
+ if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
return false;
} else if (prev_item->class_idx_ == item->class_idx_) {
- if (prev_item->name_idx_ > item->name_idx_) {
- LOG(ERROR) << "Out-of-order method_ids";
+ if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
return false;
} else if (prev_item->name_idx_ == item->name_idx_) {
- if (prev_item->proto_idx_ >= item->proto_idx_) {
- LOG(ERROR) << "Out-of-order method_ids";
+ if (UNLIKELY(prev_item->proto_idx_ >= item->proto_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
return false;
}
}
@@ -1557,8 +1561,8 @@
uint32_t class_idx = item->class_idx_;
const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid class descriptor: '" << descriptor << "'";
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid class descriptor: '%s'", descriptor);
return false;
}
@@ -1581,8 +1585,8 @@
if (item->superclass_idx_ != DexFile::kDexNoIndex16) {
descriptor = dex_file_->StringByTypeIdx(item->superclass_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid superclass: '" << descriptor << "'";
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid superclass: '%s'", descriptor);
return false;
}
}
@@ -1594,8 +1598,8 @@
// Ensure that all interfaces refer to classes (not arrays or primitives).
for (uint32_t i = 0; i < size; i++) {
descriptor = dex_file_->StringByTypeIdx(interfaces->GetTypeItem(i).type_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid interface: '" << descriptor << "'";
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid interface: '%s'", descriptor);
return false;
}
}
@@ -1608,8 +1612,8 @@
uint32_t idx1 = interfaces->GetTypeItem(i).type_idx_;
for (uint32_t j =0; j < i; j++) {
uint32_t idx2 = interfaces->GetTypeItem(j).type_idx_;
- if (idx1 == idx2) {
- LOG(ERROR) << "Duplicate interface: '" << dex_file_->StringByTypeIdx(idx1) << "'";
+ if (UNLIKELY(idx1 == idx2)) {
+ ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1));
return false;
}
}
@@ -1620,8 +1624,8 @@
if (item->class_data_off_ != 0) {
const byte* data = begin_ + item->class_data_off_;
uint16_t data_definer = FindFirstClassDataDefiner(data);
- if ((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16)) {
- LOG(ERROR) << "Invalid class_data_item";
+ if (UNLIKELY((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16))) {
+ ErrorStringPrintf("Invalid class_data_item");
return false;
}
}
@@ -1630,8 +1634,9 @@
if (item->annotations_off_ != 0) {
const byte* data = begin_ + item->annotations_off_;
uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data);
- if ((annotations_definer != item->class_idx_) && (annotations_definer != DexFile::kDexNoIndex16)) {
- LOG(ERROR) << "Invalid annotations_directory_item";
+ if (UNLIKELY((annotations_definer != item->class_idx_) &&
+ (annotations_definer != DexFile::kDexNoIndex16))) {
+ ErrorStringPrintf("Invalid annotations_directory_item");
return false;
}
}
@@ -1675,8 +1680,8 @@
const uint8_t* data = annotation->annotation_;
uint32_t idx = DecodeUnsignedLeb128(&data);
- if (last_idx >= idx && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
+ if (UNLIKELY(last_idx >= idx && i != 0)) {
+ ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
return false;
}
@@ -1694,8 +1699,8 @@
for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) {
const DexFile::FieldId& field = dex_file_->GetFieldId(it.GetMemberIndex());
- if (field.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for class_data_item field";
+ if (UNLIKELY(field.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for class_data_item field");
return false;
}
}
@@ -1705,8 +1710,8 @@
return false;
}
const DexFile::MethodId& method = dex_file_->GetMethodId(it.GetMemberIndex());
- if (method.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for class_data_item method";
+ if (UNLIKELY(method.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for class_data_item method");
return false;
}
}
@@ -1731,8 +1736,8 @@
uint32_t field_count = item->fields_size_;
for (uint32_t i = 0; i < field_count; i++) {
const DexFile::FieldId& field = dex_file_->GetFieldId(field_item->field_idx_);
- if (field.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for field_annotation";
+ if (UNLIKELY(field.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for field_annotation");
return false;
}
if (!CheckOffsetToTypeMap(field_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
@@ -1747,8 +1752,8 @@
uint32_t method_count = item->methods_size_;
for (uint32_t i = 0; i < method_count; i++) {
const DexFile::MethodId& method = dex_file_->GetMethodId(method_item->method_idx_);
- if (method.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for method_annotation";
+ if (UNLIKELY(method.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for method_annotation");
return false;
}
if (!CheckOffsetToTypeMap(method_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
@@ -1763,8 +1768,8 @@
uint32_t parameter_count = item->parameters_size_;
for (uint32_t i = 0; i < parameter_count; i++) {
const DexFile::MethodId& parameter_method = dex_file_->GetMethodId(parameter_item->method_idx_);
- if (parameter_method.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for parameter_annotation";
+ if (UNLIKELY(parameter_method.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for parameter_annotation");
return false;
}
if (!CheckOffsetToTypeMap(parameter_item->annotations_off_,
@@ -1860,7 +1865,7 @@
break;
}
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1908,7 +1913,7 @@
break;
}
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1942,4 +1947,13 @@
return true;
}
+void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ DCHECK(failure_reason_.empty()) << failure_reason_;
+ failure_reason_ = StringPrintf("Failure to verify dex file '%s': ", location_);
+ StringAppendV(&failure_reason_, fmt, ap);
+ va_end(ap);
+}
+
} // namespace art
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 3797dc7..4b8b80a 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -24,29 +24,35 @@
class DexFileVerifier {
public:
- static bool Verify(const DexFile* dex_file, const byte* begin, size_t size);
+ static bool Verify(const DexFile* dex_file, const byte* begin, size_t size,
+ const char* location, std::string* error_msg);
+
+ const std::string& FailureReason() const {
+ return failure_reason_;
+ }
private:
- DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size)
- : dex_file_(dex_file), begin_(begin), size_(size),
+ DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size, const char* location)
+ : dex_file_(dex_file), begin_(begin), size_(size), location_(location),
header_(&dex_file->GetHeader()), ptr_(NULL), previous_item_(NULL) {
}
bool Verify();
- bool CheckPointerRange(const void* start, const void* end, const char* label) const;
- bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label) const;
- bool CheckIndex(uint32_t field, uint32_t limit, const char* label) const;
+ bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
+ bool CheckPointerRange(const void* start, const void* end, const char* label);
+ bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label);
+ bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
- bool CheckHeader() const;
- bool CheckMap() const;
+ bool CheckHeader();
+ bool CheckMap();
uint32_t ReadUnsignedLittleEndian(uint32_t size);
bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
- uint32_t* handler_offsets, uint32_t handlers_size);
- bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static) const;
+ uint32_t* handler_offsets, uint32_t handlers_size);
+ bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static);
bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset,
- bool expect_direct) const;
+ bool expect_direct);
bool CheckPadding(uint32_t offset, uint32_t aligned_offset);
bool CheckEncodedValue();
bool CheckEncodedArray();
@@ -82,14 +88,20 @@
bool CheckInterSectionIterate(uint32_t offset, uint32_t count, uint16_t type);
bool CheckInterSection();
- const DexFile* dex_file_;
- const byte* begin_;
- size_t size_;
- const DexFile::Header* header_;
+ void ErrorStringPrintf(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
+
+ const DexFile* const dex_file_;
+ const byte* const begin_;
+ const size_t size_;
+ const char* const location_;
+ const DexFile::Header* const header_;
SafeMap<uint32_t, uint16_t> offset_to_type_map_;
const byte* ptr_;
const void* previous_item_;
+
+ std::string failure_reason_;
};
} // namespace art
diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h
index 6e21273..207b0b6 100644
--- a/runtime/dex_instruction-inl.h
+++ b/runtime/dex_instruction-inl.h
@@ -24,29 +24,29 @@
//------------------------------------------------------------------------------
// VRegA
//------------------------------------------------------------------------------
-inline int8_t Instruction::VRegA_10t() const {
+inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k10t);
- return static_cast<int8_t>(InstAA());
+ return static_cast<int8_t>(InstAA(inst_data));
}
-inline uint8_t Instruction::VRegA_10x() const {
+inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k10x);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint4_t Instruction::VRegA_11n() const {
+inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k11n);
- return InstA();
+ return InstA(inst_data);
}
-inline uint8_t Instruction::VRegA_11x() const {
+inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k11x);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint4_t Instruction::VRegA_12x() const {
+inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k12x);
- return InstA();
+ return InstA(inst_data);
}
inline int16_t Instruction::VRegA_20t() const {
@@ -54,54 +54,54 @@
return static_cast<int16_t>(Fetch16(1));
}
-inline uint8_t Instruction::VRegA_21c() const {
+inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21c);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_21h() const {
+inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21h);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_21s() const {
+inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21s);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_21t() const {
+inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21t);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_22b() const {
+inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22b);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint4_t Instruction::VRegA_22c() const {
+inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22c);
- return InstA();
+ return InstA(inst_data);
}
-inline uint4_t Instruction::VRegA_22s() const {
+inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22s);
- return InstA();
+ return InstA(inst_data);
}
-inline uint4_t Instruction::VRegA_22t() const {
+inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22t);
- return InstA();
+ return InstA(inst_data);
}
-inline uint8_t Instruction::VRegA_22x() const {
+inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22x);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_23x() const {
+inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k23x);
- return InstAA();
+ return InstAA(inst_data);
}
inline int32_t Instruction::VRegA_30t() const {
@@ -109,19 +109,19 @@
return static_cast<int32_t>(Fetch32(1));
}
-inline uint8_t Instruction::VRegA_31c() const {
+inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k31c);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_31i() const {
+inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k31i);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_31t() const {
+inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k31t);
- return InstAA();
+ return InstAA(inst_data);
}
inline uint16_t Instruction::VRegA_32x() const {
@@ -129,32 +129,32 @@
return Fetch16(1);
}
-inline uint4_t Instruction::VRegA_35c() const {
+inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k35c);
- return InstB(); // This is labeled A in the spec.
+ return InstB(inst_data); // This is labeled A in the spec.
}
-inline uint8_t Instruction::VRegA_3rc() const {
+inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k3rc);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_51l() const {
+inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k51l);
- return InstAA();
+ return InstAA(inst_data);
}
//------------------------------------------------------------------------------
// VRegB
//------------------------------------------------------------------------------
-inline int4_t Instruction::VRegB_11n() const {
+inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k11n);
- return static_cast<int4_t>((InstB() << 28) >> 28);
+ return static_cast<int4_t>((InstB(inst_data) << 28) >> 28);
}
-inline uint4_t Instruction::VRegB_12x() const {
+inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k12x);
- return InstB();
+ return InstB(inst_data);
}
inline uint16_t Instruction::VRegB_21c() const {
@@ -182,19 +182,19 @@
return static_cast<uint8_t>(Fetch16(1) & 0xff);
}
-inline uint4_t Instruction::VRegB_22c() const {
+inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22c);
- return InstB();
+ return InstB(inst_data);
}
-inline uint4_t Instruction::VRegB_22s() const {
+inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22s);
- return InstB();
+ return InstB(inst_data);
}
-inline uint4_t Instruction::VRegB_22t() const {
+inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22t);
- return InstB();
+ return InstB(inst_data);
}
inline uint16_t Instruction::VRegB_22x() const {
@@ -281,7 +281,7 @@
return Fetch16(2);
}
-inline void Instruction::GetArgs(uint32_t arg[5]) const {
+inline void Instruction::GetArgs(uint32_t arg[5], uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k35c);
/*
@@ -295,7 +295,8 @@
* method constant (or equivalent) is always in vB.
*/
uint16_t regList = Fetch16(2);
- uint4_t count = InstB(); // This is labeled A in the spec.
+ uint4_t count = InstB(inst_data); // This is labeled A in the spec.
+ DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")";
/*
* Copy the argument registers into the arg[] array, and
@@ -305,15 +306,13 @@
* copies of those.) Note that cases 5..2 fall through.
*/
switch (count) {
- case 5: arg[4] = InstA();
+ case 5: arg[4] = InstA(inst_data);
case 4: arg[3] = (regList >> 12) & 0x0f;
case 3: arg[2] = (regList >> 8) & 0x0f;
case 2: arg[1] = (regList >> 4) & 0x0f;
case 1: arg[0] = regList & 0x0f; break;
- case 0: break; // Valid, but no need to do anything.
- default:
- LOG(ERROR) << "Invalid arg count in 35c (" << count << ")";
- return;
+ default: // case 0
+ break; // Valid, but no need to do anything.
}
}
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 13b0f1c..c434cdd 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -217,44 +217,122 @@
// VRegA
bool HasVRegA() const;
int32_t VRegA() const;
- int8_t VRegA_10t() const;
- uint8_t VRegA_10x() const;
- uint4_t VRegA_11n() const;
- uint8_t VRegA_11x() const;
- uint4_t VRegA_12x() const;
+
+ int8_t VRegA_10t() const {
+ return VRegA_10t(Fetch16(0));
+ }
+ uint8_t VRegA_10x() const {
+ return VRegA_10x(Fetch16(0));
+ }
+ uint4_t VRegA_11n() const {
+ return VRegA_11n(Fetch16(0));
+ }
+ uint8_t VRegA_11x() const {
+ return VRegA_11x(Fetch16(0));
+ }
+ uint4_t VRegA_12x() const {
+ return VRegA_12x(Fetch16(0));
+ }
int16_t VRegA_20t() const;
- uint8_t VRegA_21c() const;
- uint8_t VRegA_21h() const;
- uint8_t VRegA_21s() const;
- uint8_t VRegA_21t() const;
- uint8_t VRegA_22b() const;
- uint4_t VRegA_22c() const;
- uint4_t VRegA_22s() const;
- uint4_t VRegA_22t() const;
- uint8_t VRegA_22x() const;
- uint8_t VRegA_23x() const;
+ uint8_t VRegA_21c() const {
+ return VRegA_21c(Fetch16(0));
+ }
+ uint8_t VRegA_21h() const {
+ return VRegA_21h(Fetch16(0));
+ }
+ uint8_t VRegA_21s() const {
+ return VRegA_21s(Fetch16(0));
+ }
+ uint8_t VRegA_21t() const {
+ return VRegA_21t(Fetch16(0));
+ }
+ uint8_t VRegA_22b() const {
+ return VRegA_22b(Fetch16(0));
+ }
+ uint4_t VRegA_22c() const {
+ return VRegA_22c(Fetch16(0));
+ }
+ uint4_t VRegA_22s() const {
+ return VRegA_22s(Fetch16(0));
+ }
+ uint4_t VRegA_22t() const {
+ return VRegA_22t(Fetch16(0));
+ }
+ uint8_t VRegA_22x() const {
+ return VRegA_22x(Fetch16(0));
+ }
+ uint8_t VRegA_23x() const {
+ return VRegA_23x(Fetch16(0));
+ }
int32_t VRegA_30t() const;
- uint8_t VRegA_31c() const;
- uint8_t VRegA_31i() const;
- uint8_t VRegA_31t() const;
+ uint8_t VRegA_31c() const {
+ return VRegA_31c(Fetch16(0));
+ }
+ uint8_t VRegA_31i() const {
+ return VRegA_31i(Fetch16(0));
+ }
+ uint8_t VRegA_31t() const {
+ return VRegA_31t(Fetch16(0));
+ }
uint16_t VRegA_32x() const;
- uint4_t VRegA_35c() const;
- uint8_t VRegA_3rc() const;
- uint8_t VRegA_51l() const;
+ uint4_t VRegA_35c() const {
+ return VRegA_35c(Fetch16(0));
+ }
+ uint8_t VRegA_3rc() const {
+ return VRegA_3rc(Fetch16(0));
+ }
+ uint8_t VRegA_51l() const {
+ return VRegA_51l(Fetch16(0));
+ }
+
+ // The following methods return the vA operand for various instruction formats. The "inst_data"
+ // parameter holds the first 16 bits of instruction which the returned value is decoded from.
+ int8_t VRegA_10t(uint16_t inst_data) const;
+ uint8_t VRegA_10x(uint16_t inst_data) const;
+ uint4_t VRegA_11n(uint16_t inst_data) const;
+ uint8_t VRegA_11x(uint16_t inst_data) const;
+ uint4_t VRegA_12x(uint16_t inst_data) const;
+ uint8_t VRegA_21c(uint16_t inst_data) const;
+ uint8_t VRegA_21h(uint16_t inst_data) const;
+ uint8_t VRegA_21s(uint16_t inst_data) const;
+ uint8_t VRegA_21t(uint16_t inst_data) const;
+ uint8_t VRegA_22b(uint16_t inst_data) const;
+ uint4_t VRegA_22c(uint16_t inst_data) const;
+ uint4_t VRegA_22s(uint16_t inst_data) const;
+ uint4_t VRegA_22t(uint16_t inst_data) const;
+ uint8_t VRegA_22x(uint16_t inst_data) const;
+ uint8_t VRegA_23x(uint16_t inst_data) const;
+ uint8_t VRegA_31c(uint16_t inst_data) const;
+ uint8_t VRegA_31i(uint16_t inst_data) const;
+ uint8_t VRegA_31t(uint16_t inst_data) const;
+ uint4_t VRegA_35c(uint16_t inst_data) const;
+ uint8_t VRegA_3rc(uint16_t inst_data) const;
+ uint8_t VRegA_51l(uint16_t inst_data) const;
// VRegB
bool HasVRegB() const;
int32_t VRegB() const;
- int4_t VRegB_11n() const;
- uint4_t VRegB_12x() const;
+
+ int4_t VRegB_11n() const {
+ return VRegB_11n(Fetch16(0));
+ }
+ uint4_t VRegB_12x() const {
+ return VRegB_12x(Fetch16(0));
+ }
uint16_t VRegB_21c() const;
uint16_t VRegB_21h() const;
int16_t VRegB_21s() const;
int16_t VRegB_21t() const;
uint8_t VRegB_22b() const;
- uint4_t VRegB_22c() const;
- uint4_t VRegB_22s() const;
- uint4_t VRegB_22t() const;
+ uint4_t VRegB_22c() const {
+ return VRegB_22c(Fetch16(0));
+ }
+ uint4_t VRegB_22s() const {
+ return VRegB_22s(Fetch16(0));
+ }
+ uint4_t VRegB_22t() const {
+ return VRegB_22t(Fetch16(0));
+ }
uint16_t VRegB_22x() const;
uint8_t VRegB_23x() const;
uint32_t VRegB_31c() const;
@@ -265,9 +343,19 @@
uint16_t VRegB_3rc() const;
uint64_t VRegB_51l() const; // vB_wide
+ // The following methods return the vB operand for all instruction formats where it is encoded in
+ // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned
+ // value is decoded from it.
+ int4_t VRegB_11n(uint16_t inst_data) const;
+ uint4_t VRegB_12x(uint16_t inst_data) const;
+ uint4_t VRegB_22c(uint16_t inst_data) const;
+ uint4_t VRegB_22s(uint16_t inst_data) const;
+ uint4_t VRegB_22t(uint16_t inst_data) const;
+
// VRegC
bool HasVRegC() const;
int32_t VRegC() const;
+
int8_t VRegC_22b() const;
uint16_t VRegC_22c() const;
int16_t VRegC_22s() const;
@@ -277,11 +365,21 @@
uint16_t VRegC_3rc() const;
// Fills the given array with the 'arg' array of the instruction.
- void GetArgs(uint32_t args[5]) const;
+ void GetArgs(uint32_t args[5], uint16_t inst_data) const;
+ void GetArgs(uint32_t args[5]) const {
+ return GetArgs(args, Fetch16(0));
+ }
- // Returns the opcode field of the instruction.
+ // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first
+ // 16 bits of instruction.
+ Code Opcode(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<Code>(inst_data & 0xFF);
+ }
+
+ // Returns the opcode field of the instruction from the first 16 bits of instruction.
Code Opcode() const {
- return static_cast<Code>(Fetch16(0) & 0xFF);
+ return Opcode(Fetch16(0));
}
void SetOpcode(Code opcode) {
@@ -395,28 +493,43 @@
// Dump code_units worth of this instruction, padding to code_units for shorter instructions
std::string DumpHex(size_t code_units) const;
- private:
- size_t SizeInCodeUnitsComplexOpcode() const;
-
uint16_t Fetch16(size_t offset) const {
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
return insns[offset];
}
+ private:
+ size_t SizeInCodeUnitsComplexOpcode() const;
+
uint32_t Fetch32(size_t offset) const {
return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16));
}
uint4_t InstA() const {
- return static_cast<uint4_t>((Fetch16(0) >> 8) & 0x0f);
+ return InstA(Fetch16(0));
}
uint4_t InstB() const {
- return static_cast<uint4_t>(Fetch16(0) >> 12);
+ return InstB(Fetch16(0));
}
uint8_t InstAA() const {
- return static_cast<uint8_t>(Fetch16(0) >> 8);
+ return InstAA(Fetch16(0));
+ }
+
+ uint4_t InstA(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint4_t>((inst_data >> 8) & 0x0f);
+ }
+
+ uint4_t InstB(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint4_t>(inst_data >> 12);
+ }
+
+ uint8_t InstAA(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint8_t>(inst_data >> 8);
}
static const char* const kInstructionNames[];
diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc
index 64c645e..9961df9 100644
--- a/runtime/dex_method_iterator_test.cc
+++ b/runtime/dex_method_iterator_test.cc
@@ -20,16 +20,27 @@
namespace art {
-class DexMethodIteratorTest : public CommonTest {};
+class DexMethodIteratorTest : public CommonTest {
+ public:
+ const DexFile* OpenDexFile(const std::string& partial_filename) {
+ std::string dfn = GetDexFileName(partial_filename);
+ std::string error_msg;
+ const DexFile* dexfile = DexFile::Open(dfn.c_str(), dfn.c_str(), &error_msg);
+ if (dexfile == nullptr) {
+ LG << "Failed to open '" << dfn << "': " << error_msg;
+ }
+ return dexfile;
+ }
+};
TEST_F(DexMethodIteratorTest, Basic) {
ScopedObjectAccess soa(Thread::Current());
std::vector<const DexFile*> dex_files;
- dex_files.push_back(DexFile::Open(GetDexFileName("core"), GetDexFileName("core")));
- dex_files.push_back(DexFile::Open(GetDexFileName("conscrypt"), GetDexFileName("conscrypt")));
- dex_files.push_back(DexFile::Open(GetDexFileName("okhttp"), GetDexFileName("okhttp")));
- dex_files.push_back(DexFile::Open(GetDexFileName("core-junit"), GetDexFileName("core-junit")));
- dex_files.push_back(DexFile::Open(GetDexFileName("bouncycastle"), GetDexFileName("bouncycastle")));
+ dex_files.push_back(OpenDexFile("core"));
+ dex_files.push_back(OpenDexFile("conscrypt"));
+ dex_files.push_back(OpenDexFile("okhttp"));
+ dex_files.push_back(OpenDexFile("core-junit"));
+ dex_files.push_back(OpenDexFile("bouncycastle"));
DexMethodIterator it(dex_files);
while (it.HasNext()) {
const DexFile& dex_file = it.GetDexFile();
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index dfb6819..b3b24ba 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -40,15 +40,16 @@
symtab_symbol_table_(NULL),
dynsym_symbol_table_(NULL) {}
-ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only) {
+ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
+ std::string* error_msg) {
UniquePtr<ElfFile> elf_file(new ElfFile());
- if (!elf_file->Setup(file, writable, program_header_only)) {
- return NULL;
+ if (!elf_file->Setup(file, writable, program_header_only, error_msg)) {
+ return nullptr;
}
return elf_file.release();
}
-bool ElfFile::Setup(File* file, bool writable, bool program_header_only) {
+bool ElfFile::Setup(File* file, bool writable, bool program_header_only, std::string* error_msg) {
CHECK(file != NULL);
file_ = file;
writable_ = writable;
@@ -66,40 +67,45 @@
int64_t file_length = file_->GetLength();
if (file_length < 0) {
errno = -file_length;
- PLOG(WARNING) << "Failed to get length of file: " << file_->GetPath() << " fd=" << file_->Fd();
+ *error_msg = StringPrintf("Failed to get length of file: '%s' fd=%d: %s",
+ file_->GetPath().c_str(), file_->Fd(), strerror(errno));
return false;
}
if (file_length < sizeof(llvm::ELF::Elf32_Ehdr)) {
- if (writable) {
- LOG(WARNING) << "File size of " << file_length
- << " bytes not large enough to contain ELF header of "
- << sizeof(llvm::ELF::Elf32_Ehdr) << " bytes: " << file_->GetPath();
- }
+ *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF header of "
+ "%zd bytes: '%s'", file_length, sizeof(llvm::ELF::Elf32_Ehdr),
+ file_->GetPath().c_str());
return false;
}
if (program_header_only) {
// first just map ELF header to get program header size information
size_t elf_header_size = sizeof(llvm::ELF::Elf32_Ehdr);
- if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0))) {
+ if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0,
+ file_->GetPath().c_str(), error_msg),
+ error_msg)) {
return false;
}
// then remap to cover program header
size_t program_header_size = header_->e_phoff + (header_->e_phentsize * header_->e_phnum);
if (file_length < program_header_size) {
- LOG(WARNING) << "File size of " << file_length
- << " bytes not large enough to contain ELF program header of "
- << program_header_size << " bytes: " << file_->GetPath();
+ *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF program "
+ "header of %zd bytes: '%s'", file_length,
+ sizeof(llvm::ELF::Elf32_Ehdr), file_->GetPath().c_str());
return false;
}
- if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0))) {
- LOG(WARNING) << "Failed to map ELF program headers: " << file_->GetPath();
+ if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0,
+ file_->GetPath().c_str(), error_msg),
+ error_msg)) {
+ *error_msg = StringPrintf("Failed to map ELF program headers: %s", error_msg->c_str());
return false;
}
} else {
// otherwise map entire file
- if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0))) {
- LOG(WARNING) << "Failed to map ELF file: " << file_->GetPath();
+ if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0,
+ file_->GetPath().c_str(), error_msg),
+ error_msg)) {
+ *error_msg = StringPrintf("Failed to map ELF file: %s", error_msg->c_str());
return false;
}
}
@@ -114,7 +120,8 @@
// Find .dynamic section info from program header
dynamic_program_header_ = FindProgamHeaderByType(llvm::ELF::PT_DYNAMIC);
if (dynamic_program_header_ == NULL) {
- LOG(WARNING) << "Failed to find PT_DYNAMIC program header in ELF file: " << file_->GetPath();
+ *error_msg = StringPrintf("Failed to find PT_DYNAMIC program header in ELF file: '%s'",
+ file_->GetPath().c_str());
return false;
}
@@ -169,9 +176,10 @@
delete dynsym_symbol_table_;
}
-bool ElfFile::SetMap(MemMap* map) {
+bool ElfFile::SetMap(MemMap* map, std::string* error_msg) {
if (map == NULL) {
- // MemMap::Open should have already logged
+ // MemMap::Open should have already set an error.
+ DCHECK(!error_msg->empty());
return false;
}
map_.reset(map);
@@ -183,12 +191,12 @@
|| (llvm::ELF::ElfMagic[1] != header_->e_ident[llvm::ELF::EI_MAG1])
|| (llvm::ELF::ElfMagic[2] != header_->e_ident[llvm::ELF::EI_MAG2])
|| (llvm::ELF::ElfMagic[3] != header_->e_ident[llvm::ELF::EI_MAG3])) {
- LOG(WARNING) << "Failed to find ELF magic in " << file_->GetPath()
- << ": " << std::hex
- << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG0])
- << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG1])
- << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG2])
- << static_cast<uint8_t>(header_->e_ident[llvm::ELF::EI_MAG3]);
+ *error_msg = StringPrintf("Failed to find ELF magic in %s: %c%c%c%c",
+ file_->GetPath().c_str(),
+ header_->e_ident[llvm::ELF::EI_MAG0],
+ header_->e_ident[llvm::ELF::EI_MAG1],
+ header_->e_ident[llvm::ELF::EI_MAG2],
+ header_->e_ident[llvm::ELF::EI_MAG3]);
return false;
}
@@ -596,7 +604,7 @@
return loaded_size;
}
-bool ElfFile::Load(bool executable) {
+bool ElfFile::Load(bool executable, std::string* error_msg) {
// TODO: actually return false error
CHECK(program_header_only_) << file_->GetPath();
for (llvm::ELF::Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
@@ -628,9 +636,10 @@
if (program_header.p_vaddr == 0) {
std::string reservation_name("ElfFile reservation for ");
reservation_name += file_->GetPath();
+ std::string error_msg;
UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
- NULL, GetLoadedSize(), PROT_NONE));
- CHECK(reserve.get() != NULL) << file_->GetPath();
+ NULL, GetLoadedSize(), PROT_NONE, &error_msg));
+ CHECK(reserve.get() != NULL) << file_->GetPath() << ": " << error_msg;
base_address_ = reserve->Begin();
segments_.push_back(reserve.release());
}
@@ -657,18 +666,20 @@
flags |= MAP_PRIVATE;
}
if (file_length < (program_header.p_offset + program_header.p_memsz)) {
- LOG(WARNING) << "File size of " << file_length
- << " bytes not large enough to contain ELF segment " << i
- << " of " << (program_header.p_offset + program_header.p_memsz)
- << " bytes: " << file_->GetPath();
+ *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF segment "
+ "%d of %d bytes: '%s'", file_length, i,
+ program_header.p_offset + program_header.p_memsz,
+ file_->GetPath().c_str());
return false;
}
UniquePtr<MemMap> segment(MemMap::MapFileAtAddress(p_vaddr,
program_header.p_memsz,
prot, flags, file_->Fd(),
program_header.p_offset,
- true));
- CHECK(segment.get() != NULL) << file_->GetPath();
+ true,
+ file_->GetPath().c_str(),
+ error_msg));
+ CHECK(segment.get() != nullptr) << *error_msg;
CHECK_EQ(segment->Begin(), p_vaddr) << file_->GetPath();
segments_.push_back(segment.release());
}
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 33b5fc3..f8c235d 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -35,7 +35,7 @@
// ELFObjectFile.
class ElfFile {
public:
- static ElfFile* Open(File* file, bool writable, bool program_header_only);
+ static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
~ElfFile();
// Load segments into memory based on PT_LOAD program headers
@@ -115,14 +115,14 @@
// Load segments into memory based on PT_LOAD program headers.
// executable is true at run time, false at compile time.
- bool Load(bool executable);
+ bool Load(bool executable, std::string* error_msg);
private:
ElfFile();
- bool Setup(File* file, bool writable, bool program_header_only);
+ bool Setup(File* file, bool writable, bool program_header_only, std::string* error_msg);
- bool SetMap(MemMap* map);
+ bool SetMap(MemMap* map, std::string* error_msg);
byte* GetProgramHeadersStart();
byte* GetSectionHeadersStart();
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 52f8c81..24ab1ce 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -33,20 +33,20 @@
namespace art {
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer,
- int32_t component_count, Thread* self,
- bool access_check) {
+static inline bool CheckFilledNewArrayAlloc(uint32_t type_idx, mirror::ArtMethod* referrer,
+ int32_t component_count, Thread* self,
+ bool access_check, mirror::Class** klass_ptr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (UNLIKELY(component_count < 0)) {
ThrowNegativeArraySizeException(component_count);
- return NULL; // Failure
+ return false; // Failure
}
- mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->Get(type_idx);
+ mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer);
if (klass == NULL) { // Error
DCHECK(self->IsExceptionPending());
- return NULL; // Failure
+ return false; // Failure
}
}
if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
@@ -60,213 +60,40 @@
"Found type %s; filled-new-array not implemented for anything but \'int\'",
PrettyDescriptor(klass).c_str());
}
- return NULL; // Failure
- } else {
- if (access_check) {
- mirror::Class* referrer_klass = referrer->GetDeclaringClass();
- if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer_klass, klass);
- return NULL; // Failure
- }
- }
- DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
- return mirror::Array::Alloc(self, klass, component_count);
+ return false; // Failure
}
-}
-
-mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer,
- Thread* self, FindFieldType type, size_t expected_size,
- bool access_check) {
- bool is_primitive;
- bool is_set;
- bool is_static;
- switch (type) {
- case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
- case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
- case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
- case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
- case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
- case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
- case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
- case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through.
- default: is_primitive = true; is_set = true; is_static = true; break;
- }
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
- if (UNLIKELY(resolved_field == NULL)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return NULL; // Failure.
- }
- mirror::Class* fields_class = resolved_field->GetDeclaringClass();
if (access_check) {
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
- return NULL;
- }
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
- !referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- // The referring class can't access the resolved field, this may occur as a result of a
- // protected field being made public by a sub-class. Resort to the dex file to determine
- // the correct class for the access check.
- const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
- fields_class = class_linker->ResolveType(dex_file,
- dex_file.GetFieldId(field_idx).class_idx_,
- referring_class);
- if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
- ThrowIllegalAccessErrorClass(referring_class, fields_class);
- return NULL; // failure
- } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- ThrowIllegalAccessErrorField(referring_class, resolved_field);
- return NULL; // failure
- }
- }
- if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
- return NULL; // failure
- } else {
- FieldHelper fh(resolved_field);
- if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
- fh.FieldSize() != expected_size)) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- DCHECK(throw_location.GetMethod() == referrer);
- self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
- "Attempted read of %zd-bit %s on field '%s'",
- expected_size * (32 / sizeof(int32_t)),
- is_primitive ? "primitive" : "non-primitive",
- PrettyField(resolved_field, true).c_str());
- return NULL; // failure
- }
+ mirror::Class* referrer_klass = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referrer_klass, klass);
+ return false; // Failure
}
}
- if (!is_static) {
- // instance fields must be being accessed on an initialized class
- return resolved_field;
- } else {
- // If the class is initialized we're done.
- if (fields_class->IsInitialized()) {
- return resolved_field;
- } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true, true)) {
- // Otherwise let's ensure the class is initialized before resolving the field.
- return resolved_field;
- } else {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return NULL; // failure
- }
- }
+ DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
+ *klass_ptr = klass;
+ return true;
}
-// Slow path method resolution
-mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* self, bool access_check, InvokeType type) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- bool is_direct = type == kStatic || type == kDirect;
- mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type);
- if (UNLIKELY(resolved_method == NULL)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return NULL; // Failure.
- } else if (UNLIKELY(this_object == NULL && type != kStatic)) {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- DCHECK(referrer == throw_location.GetMethod());
- ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
- return NULL; // Failure.
- } else {
- if (!access_check) {
- if (is_direct) {
- return resolved_method;
- } else if (type == kInterface) {
- mirror::ArtMethod* interface_method =
- this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == NULL)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
- referrer);
- return NULL; // Failure.
- } else {
- return interface_method;
- }
- } else {
- mirror::ObjectArray<mirror::ArtMethod>* vtable;
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (type == kSuper) {
- vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
- } else {
- vtable = this_object->GetClass()->GetVTable();
- }
- // TODO: eliminate bounds check?
- return vtable->Get(vtable_index);
- }
- } else {
- // Incompatible class change should have been handled in resolve method.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
- referrer);
- return NULL; // Failure.
- }
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // The referring class can't access the resolved method, this may occur as a result of a
- // protected method being made public by implementing an interface that re-declares the
- // method public. Resort to the dex file to determine the correct class for the access check
- const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
- methods_class = class_linker->ResolveType(dex_file,
- dex_file.GetMethodId(method_idx).class_idx_,
- referring_class);
- if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
- ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
- referrer, resolved_method, type);
- return NULL; // Failure.
- } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
- return NULL; // Failure.
- }
- }
- if (is_direct) {
- return resolved_method;
- } else if (type == kInterface) {
- mirror::ArtMethod* interface_method =
- this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == NULL)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
- referrer);
- return NULL; // Failure.
- } else {
- return interface_method;
- }
- } else {
- mirror::ObjectArray<mirror::ArtMethod>* vtable;
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (type == kSuper) {
- mirror::Class* super_class = referring_class->GetSuperClass();
- if (LIKELY(super_class != NULL)) {
- vtable = referring_class->GetSuperClass()->GetVTable();
- } else {
- vtable = NULL;
- }
- } else {
- vtable = this_object->GetClass()->GetVTable();
- }
- if (LIKELY(vtable != NULL &&
- vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
- return vtable->GetWithoutChecks(vtable_index);
- } else {
- // Behavior to agree with that of the verifier.
- MethodHelper mh(resolved_method);
- ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
- mh.GetSignature());
- return NULL; // Failure.
- }
- }
- }
+// Helper function to allocate array for FILLED_NEW_ARRAY.
+mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer,
+ int32_t component_count, Thread* self,
+ bool access_check) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) {
+ return NULL;
}
+ return mirror::Array::AllocUninstrumented(self, klass, component_count);
+}
+
+// Helper function to allocate array for FILLED_NEW_ARRAY.
+mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* referrer,
+ int32_t component_count, Thread* self,
+ bool access_check) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) {
+ return NULL;
+ }
+ return mirror::Array::AllocInstrumented(self, klass, component_count);
}
void ThrowStackOverflowError(Thread* self) {
@@ -405,5 +232,4 @@
return zero;
}
}
-
} // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b58cb3..7ce50c5 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -16,7 +16,8 @@
#ifndef ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
#define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
-#include "object_utils.h"
+
+#include "base/macros.h"
#include "class_linker.h"
#include "common_throws.h"
#include "dex_file.h"
@@ -27,6 +28,7 @@
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/throwable.h"
+#include "object_utils.h"
#include "thread.h"
@@ -38,6 +40,42 @@
class Object;
} // namespace mirror
+static inline bool CheckObjectAlloc(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self,
+ bool access_check,
+ mirror::Class** klass_ptr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(klass == NULL)) {
+ klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
+ if (klass == NULL) {
+ DCHECK(self->IsExceptionPending());
+ return false; // Failure
+ }
+ }
+ if (access_check) {
+ if (UNLIKELY(!klass->IsInstantiable())) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
+ PrettyDescriptor(klass).c_str());
+ return false; // Failure
+ }
+ mirror::Class* referrer = method->GetDeclaringClass();
+ if (UNLIKELY(!referrer->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referrer, klass);
+ return false; // Failure
+ }
+ }
+ if (!klass->IsInitialized() &&
+ !runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) {
+ DCHECK(self->IsExceptionPending());
+ return false; // Failure
+ }
+ *klass_ptr = klass;
+ return true;
+}
+
// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
// cannot be resolved, throw an error. If it can, use it to create an instance.
// When verification/compiler hasn't been able to verify access, optionally perform an access
@@ -46,34 +84,50 @@
Thread* self,
bool access_check)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
- Runtime* runtime = Runtime::Current();
- if (UNLIKELY(klass == NULL)) {
- klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
- if (klass == NULL) {
- DCHECK(self->IsExceptionPending());
- return NULL; // Failure
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) {
+ return NULL;
+ }
+ return klass->AllocObjectUninstrumented(self);
+}
+
+static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self,
+ bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) {
+ return NULL;
+ }
+ return klass->AllocObjectInstrumented(self);
+}
+
+static inline bool CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ bool access_check, mirror::Class** klass_ptr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (UNLIKELY(component_count < 0)) {
+ ThrowNegativeArraySizeException(component_count);
+ return false; // Failure
+ }
+ mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
+ if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
+ klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
+ if (klass == NULL) { // Error
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return false; // Failure
}
+ CHECK(klass->IsArrayClass()) << PrettyClass(klass);
}
if (access_check) {
- if (UNLIKELY(!klass->IsInstantiable())) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
- PrettyDescriptor(klass).c_str());
- return NULL; // Failure
- }
mirror::Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
ThrowIllegalAccessErrorClass(referrer, klass);
- return NULL; // Failure
+ return false; // Failure
}
}
- if (!klass->IsInitialized() &&
- !runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) {
- DCHECK(self->IsExceptionPending());
- return NULL; // Failure
- }
- return klass->AllocObject(self);
+ *klass_ptr = klass;
+ return true;
}
// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
@@ -84,27 +138,22 @@
int32_t component_count,
Thread* self, bool access_check)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(component_count < 0)) {
- ThrowNegativeArraySizeException(component_count);
- return NULL; // Failure
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) {
+ return NULL;
}
- mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
- if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
- klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
- if (klass == NULL) { // Error
- DCHECK(Thread::Current()->IsExceptionPending());
- return NULL; // Failure
- }
- CHECK(klass->IsArrayClass()) << PrettyClass(klass);
+ return mirror::Array::AllocUninstrumented(self, klass, component_count);
+}
+
+static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self, bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) {
+ return NULL;
}
- if (access_check) {
- mirror::Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer, klass);
- return NULL; // Failure
- }
- }
- return mirror::Array::Alloc(self, klass, component_count);
+ return mirror::Array::AllocInstrumented(self, klass, component_count);
}
extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
@@ -112,6 +161,11 @@
Thread* self, bool access_check)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self, bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Type of find field operation for fast and slow case.
enum FindFieldType {
InstanceObjectRead,
@@ -124,11 +178,242 @@
StaticPrimitiveWrite,
};
-// Slow field find that can initialize classes and may throw exceptions.
-extern mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer,
- Thread* self, FindFieldType type, size_t expected_size,
- bool access_check)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+template<FindFieldType type, bool access_check>
+static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer,
+ Thread* self, size_t expected_size) {
+ bool is_primitive;
+ bool is_set;
+ bool is_static;
+ switch (type) {
+ case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
+ case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
+ case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
+ case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
+ case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
+ case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
+ case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
+ case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through.
+ default: is_primitive = true; is_set = true; is_static = true; break;
+ }
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
+ if (UNLIKELY(resolved_field == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ }
+ mirror::Class* fields_class = resolved_field->GetDeclaringClass();
+ if (access_check) {
+ if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+ ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
+ return nullptr;
+ }
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
+ !referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()))) {
+ // The referring class can't access the resolved field, this may occur as a result of a
+ // protected field being made public by a sub-class. Resort to the dex file to determine
+ // the correct class for the access check.
+ const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
+ fields_class = class_linker->ResolveType(dex_file,
+ dex_file.GetFieldId(field_idx).class_idx_,
+ referring_class);
+ if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
+ ThrowIllegalAccessErrorClass(referring_class, fields_class);
+ return nullptr; // failure
+ } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()))) {
+ ThrowIllegalAccessErrorField(referring_class, resolved_field);
+ return nullptr; // failure
+ }
+ }
+ if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
+ ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
+ return nullptr; // failure
+ } else {
+ FieldHelper fh(resolved_field);
+ if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
+ fh.FieldSize() != expected_size)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ DCHECK(throw_location.GetMethod() == referrer);
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+ "Attempted read of %zd-bit %s on field '%s'",
+ expected_size * (32 / sizeof(int32_t)),
+ is_primitive ? "primitive" : "non-primitive",
+ PrettyField(resolved_field, true).c_str());
+ return nullptr; // failure
+ }
+ }
+ }
+ if (!is_static) {
+ // instance fields must be being accessed on an initialized class
+ return resolved_field;
+ } else {
+ // If the class is initialized we're done.
+ if (LIKELY(fields_class->IsInitialized())) {
+ return resolved_field;
+ } else if (LIKELY(class_linker->EnsureInitialized(fields_class, true, true))) {
+ // Otherwise let's ensure the class is initialized before resolving the field.
+ return resolved_field;
+ } else {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind
+ return nullptr; // failure
+ }
+ }
+}
+
+// Explicit template declarations of FindFieldFromCode for all field access types.
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+static mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
+ const mirror::ArtMethod* referrer, \
+ Thread* self, size_t expected_size) \
+
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
+
+#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+
+template<InvokeType type, bool access_check>
+static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* referrer, Thread* self) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type);
+ if (UNLIKELY(resolved_method == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ } else if (UNLIKELY(this_object == nullptr && type != kStatic)) {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ DCHECK(referrer == throw_location.GetMethod());
+ ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
+ return nullptr; // Failure.
+ } else if (access_check) {
+ // Incompatible class change should have been handled in resolve method.
+ if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+ ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
+ referrer);
+ return nullptr; // Failure.
+ }
+ mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+ !referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ // The referring class can't access the resolved method, this may occur as a result of a
+ // protected method being made public by implementing an interface that re-declares the
+ // method public. Resort to the dex file to determine the correct class for the access check
+ const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ methods_class = class_linker->ResolveType(dex_file,
+ dex_file.GetMethodId(method_idx).class_idx_,
+ referring_class);
+ if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
+ ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
+ referrer, resolved_method, type);
+ return nullptr; // Failure.
+ } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
+ return nullptr; // Failure.
+ }
+ }
+ }
+ switch (type) {
+ case kStatic:
+ case kDirect:
+ return resolved_method;
+ case kVirtual: {
+ mirror::ObjectArray<mirror::ArtMethod>* vtable = this_object->GetClass()->GetVTable();
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ if (access_check &&
+ (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) {
+ // Behavior to agree with that of the verifier.
+ MethodHelper mh(resolved_method);
+ ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
+ mh.GetSignature());
+ return nullptr; // Failure.
+ }
+ DCHECK(vtable != nullptr);
+ return vtable->GetWithoutChecks(vtable_index);
+ }
+ case kSuper: {
+ mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass();
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ mirror::ObjectArray<mirror::ArtMethod>* vtable;
+ if (access_check) {
+ // Check existence of super class.
+ vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr;
+ if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) {
+ // Behavior to agree with that of the verifier.
+ MethodHelper mh(resolved_method);
+ ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
+ mh.GetSignature());
+ return nullptr; // Failure.
+ }
+ } else {
+ // Super class must exist.
+ DCHECK(super_class != nullptr);
+ vtable = super_class->GetVTable();
+ }
+ DCHECK(vtable != nullptr);
+ return vtable->GetWithoutChecks(vtable_index);
+ }
+ case kInterface: {
+ uint32_t imt_index = resolved_method->GetDexMethodIndex() % ClassLinker::kImtSize;
+ mirror::ObjectArray<mirror::ArtMethod>* imt_table = this_object->GetClass()->GetImTable();
+ mirror::ArtMethod* imt_method = imt_table->Get(imt_index);
+ if (!imt_method->IsImtConflictMethod()) {
+ return imt_method;
+ } else {
+ mirror::ArtMethod* interface_method =
+ this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ if (UNLIKELY(interface_method == nullptr)) {
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
+ referrer);
+ return nullptr; // Failure.
+ } else {
+ return interface_method;
+ }
+ }
+ }
+ default:
+ LOG(FATAL) << "Unknown invoke type " << type;
+ return nullptr; // Failure.
+ }
+}
+
+// Explicit template declarations of FindMethodFromCode for all invoke types.
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* referrer, \
+ Thread* self)
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
// Fast path field resolution that can't initialize classes or throw exceptions.
static inline mirror::ArtField* FindFieldFast(uint32_t field_idx,
@@ -228,11 +513,6 @@
}
}
-extern mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* self, bool access_check, InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx,
const mirror::ArtMethod* referrer,
Thread* self, bool can_run_clinit,
@@ -258,7 +538,7 @@
//
// Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
// running.
- if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
+ if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) {
return klass;
}
if (!class_linker->EnsureInitialized(klass, true, true)) {
@@ -324,7 +604,6 @@
for (;;) {
if (thread->ReadFlag(kCheckpointRequest)) {
thread->RunCheckpointFunction();
- thread->AtomicClearFlag(kCheckpointRequest);
} else if (thread->ReadFlag(kSuspendRequest)) {
thread->FullSuspendCheck();
} else {
@@ -393,6 +672,23 @@
#endif
}
+static inline const void* GetPortableImtConflictTrampoline(ClassLinker* class_linker) {
+ return class_linker->GetPortableImtConflictTrampoline();
+}
+
+static inline const void* GetQuickImtConflictTrampoline(ClassLinker* class_linker) {
+ return class_linker->GetQuickImtConflictTrampoline();
+}
+
+// Return address of imt conflict trampoline stub for defined compiler.
+static inline const void* GetImtConflictTrampoline(ClassLinker* class_linker) {
+#if defined(ART_USE_PORTABLE_COMPILER)
+ return GetPortableImtConflictTrampoline(class_linker);
+#else
+ return GetQuickImtConflictTrampoline(class_linker);
+#endif
+}
+
extern "C" void art_portable_proxy_invoke_handler();
static inline const void* GetPortableProxyInvokeHandler() {
return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
@@ -416,6 +712,23 @@
return reinterpret_cast<void*>(art_jni_dlsym_lookup_stub);
}
+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());
+ const INT_TYPE kMinInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::min());
+ const FLOAT_TYPE kMaxIntAsFloat = static_cast<FLOAT_TYPE>(kMaxInt);
+ const FLOAT_TYPE kMinIntAsFloat = static_cast<FLOAT_TYPE>(kMinInt);
+ if (LIKELY(f > kMinIntAsFloat)) {
+ if (LIKELY(f < kMaxIntAsFloat)) {
+ return static_cast<INT_TYPE>(f);
+ } else {
+ return kMaxInt;
+ }
+ } else {
+ return (f != f) ? 0 : kMinInt; // f != f implies NaN
+ }
+}
+
} // namespace art
#endif // ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index ecf98bc..05c02f2 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -31,7 +31,15 @@
mirror::ArtMethod* method = shadow_frame->GetMethod();
// Ensure static methods are initialized.
if (method->IsStatic()) {
- Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), true, true);
+ mirror::Class* declaringClass = method->GetDeclaringClass();
+ if (UNLIKELY(!declaringClass->IsInitializing())) {
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass,
+ true, true))) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return;
+ }
+ CHECK(declaringClass->IsInitializing());
+ }
}
uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
#if defined(ART_USE_PORTABLE_COMPILER)
@@ -40,7 +48,7 @@
method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()[0]);
#else
method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
- (shadow_frame->NumberOfVRegs() - arg_offset) * 4,
+ (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
result, mh.GetShorty()[0]);
#endif
}
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index 83d3a58..16364fc 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -41,7 +41,7 @@
return NULL;
} else {
// Register so that future calls don't come here
- method->RegisterNative(self, native_code);
+ method->RegisterNative(self, native_code, false);
return native_code;
}
}
@@ -115,7 +115,7 @@
const void* code = reinterpret_cast<const void*>(jni_method->GetNativeGcMap());
if (UNLIKELY(code == NULL)) {
code = GetJniDlsymLookupStub();
- jni_method->RegisterNative(self, code);
+ jni_method->RegisterNative(self, code, false);
}
return code;
}
diff --git a/runtime/entrypoints/math_entrypoints.cc b/runtime/entrypoints/math_entrypoints.cc
index 31d13c8..b839b63 100644
--- a/runtime/entrypoints/math_entrypoints.cc
+++ b/runtime/entrypoints/math_entrypoints.cc
@@ -16,6 +16,8 @@
#include "math_entrypoints.h"
+#include "entrypoint_utils.h"
+
namespace art {
extern "C" double art_l2d(int64_t l) {
@@ -31,59 +33,19 @@
* target doesn't support this normally, use these.
*/
extern "C" int64_t art_d2l(double d) {
- static const double kMaxLong = static_cast<double>(static_cast<int64_t>(0x7fffffffffffffffULL));
- static const double kMinLong = static_cast<double>(static_cast<int64_t>(0x8000000000000000ULL));
- if (d >= kMaxLong) {
- return static_cast<int64_t>(0x7fffffffffffffffULL);
- } else if (d <= kMinLong) {
- return static_cast<int64_t>(0x8000000000000000ULL);
- } else if (d != d) { // NaN case
- return 0;
- } else {
- return static_cast<int64_t>(d);
- }
+ return art_float_to_integral<int64_t, double>(d);
}
extern "C" int64_t art_f2l(float f) {
- static const float kMaxLong = static_cast<float>(static_cast<int64_t>(0x7fffffffffffffffULL));
- static const float kMinLong = static_cast<float>(static_cast<int64_t>(0x8000000000000000ULL));
- if (f >= kMaxLong) {
- return static_cast<int64_t>(0x7fffffffffffffffULL);
- } else if (f <= kMinLong) {
- return static_cast<int64_t>(0x8000000000000000ULL);
- } else if (f != f) { // NaN case
- return 0;
- } else {
- return static_cast<int64_t>(f);
- }
+ return art_float_to_integral<int64_t, float>(f);
}
extern "C" int32_t art_d2i(double d) {
- static const double kMaxInt = static_cast<double>(static_cast<int32_t>(0x7fffffffUL));
- static const double kMinInt = static_cast<double>(static_cast<int32_t>(0x80000000UL));
- if (d >= kMaxInt) {
- return static_cast<int32_t>(0x7fffffffUL);
- } else if (d <= kMinInt) {
- return static_cast<int32_t>(0x80000000UL);
- } else if (d != d) { // NaN case
- return 0;
- } else {
- return static_cast<int32_t>(d);
- }
+ return art_float_to_integral<int32_t, double>(d);
}
extern "C" int32_t art_f2i(float f) {
- static const float kMaxInt = static_cast<float>(static_cast<int32_t>(0x7fffffffUL));
- static const float kMinInt = static_cast<float>(static_cast<int32_t>(0x80000000UL));
- if (f >= kMaxInt) {
- return static_cast<int32_t>(0x7fffffffUL);
- } else if (f <= kMinInt) {
- return static_cast<int32_t>(0x80000000UL);
- } else if (f != f) { // NaN case
- return 0;
- } else {
- return static_cast<int32_t>(f);
- }
+ return art_float_to_integral<int32_t, float>(f);
}
} // namespace art
diff --git a/runtime/entrypoints/portable/portable_entrypoints.h b/runtime/entrypoints/portable/portable_entrypoints.h
index d456447..dbea707 100644
--- a/runtime/entrypoints/portable/portable_entrypoints.h
+++ b/runtime/entrypoints/portable/portable_entrypoints.h
@@ -35,6 +35,7 @@
// compiler ABI.
struct PACKED(4) PortableEntryPoints {
// Invocation
+ void (*pPortableImtConflictTrampoline)(mirror::ArtMethod*);
void (*pPortableResolutionTrampoline)(mirror::ArtMethod*);
void (*pPortableToInterpreterBridge)(mirror::ArtMethod*);
};
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
index bd6f795..095e99e 100644
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc
@@ -33,12 +33,8 @@
field->Set32(field->GetDeclaringClass(), new_value);
return 0;
}
- field = FindFieldFromCode(field_idx,
- referrer,
- Thread::Current(),
- StaticPrimitiveWrite,
- sizeof(uint32_t),
- true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
field->Set32(field->GetDeclaringClass(), new_value);
return 0;
@@ -55,12 +51,8 @@
field->Set64(field->GetDeclaringClass(), new_value);
return 0;
}
- field = FindFieldFromCode(field_idx,
- referrer,
- Thread::Current(),
- StaticPrimitiveWrite,
- sizeof(uint64_t),
- true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
field->Set64(field->GetDeclaringClass(), new_value);
return 0;
@@ -78,8 +70,8 @@
field->SetObj(field->GetDeclaringClass(), new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticObjectWrite, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
field->SetObj(field->GetDeclaringClass(), new_value);
return 0;
@@ -94,8 +86,8 @@
if (LIKELY(field != NULL)) {
return field->Get32(field->GetDeclaringClass());
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticPrimitiveRead, sizeof(uint32_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
return field->Get32(field->GetDeclaringClass());
}
@@ -109,8 +101,8 @@
if (LIKELY(field != NULL)) {
return field->Get64(field->GetDeclaringClass());
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticPrimitiveRead, sizeof(uint64_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
return field->Get64(field->GetDeclaringClass());
}
@@ -125,8 +117,8 @@
if (LIKELY(field != NULL)) {
return field->GetObj(field->GetDeclaringClass());
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticObjectRead, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(field->GetDeclaringClass());
}
@@ -142,8 +134,8 @@
field->Set32(obj, new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveWrite, sizeof(uint32_t), true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
field->Set32(obj, new_value);
return 0;
@@ -160,8 +152,8 @@
field->Set64(obj, new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveWrite, sizeof(uint64_t), true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
field->Set64(obj, new_value);
return 0;
@@ -180,8 +172,8 @@
field->SetObj(obj, new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstanceObjectWrite, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
field->SetObj(obj, new_value);
return 0;
@@ -197,8 +189,8 @@
if (LIKELY(field != NULL)) {
return field->Get32(obj);
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveRead, sizeof(uint32_t), true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
return field->Get32(obj);
}
@@ -213,8 +205,8 @@
if (LIKELY(field != NULL)) {
return field->Get64(obj);
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveRead, sizeof(uint64_t), true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
return field->Get64(obj);
}
@@ -230,8 +222,8 @@
if (LIKELY(field != NULL)) {
return field->GetObj(obj);
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstanceObjectRead, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(obj);
}
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
index 14cbd84..e2a0cc2 100644
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
@@ -21,21 +21,13 @@
namespace art {
-static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- bool access_check,
- InvokeType type,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* method = FindMethodFast(method_idx,
- this_object,
- caller_method,
- access_check,
- type);
+template<InvokeType type, bool access_check>
+static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* thread) {
+ mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method,
+ access_check, type);
if (UNLIKELY(method == NULL)) {
- method = FindMethodFromCode(method_idx, this_object, caller_method,
- thread, access_check, type);
+ method = FindMethodFromCode<type, access_check>(method_idx, this_object, caller_method, thread);
if (UNLIKELY(method == NULL)) {
CHECK(thread->IsExceptionPending());
return 0; // failure
@@ -53,12 +45,32 @@
return method;
}
+// Explicit template declarations of FindMethodHelper for all invoke types.
+#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ static mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* caller_method, \
+ Thread* thread)
+#define EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL
+
extern "C" mirror::Object* art_portable_find_static_method_from_code_with_access_check(uint32_t method_idx,
mirror::Object* this_object,
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kStatic, thread);
+ return FindMethodHelper<kStatic, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access_check(uint32_t method_idx,
@@ -66,7 +78,7 @@
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kDirect, thread);
+ return FindMethodHelper<kDirect, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_access_check(uint32_t method_idx,
@@ -74,7 +86,7 @@
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kVirtual, thread);
+ return FindMethodHelper<kVirtual, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_check(uint32_t method_idx,
@@ -82,7 +94,7 @@
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kSuper, thread);
+ return FindMethodHelper<kSuper, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_access_check(uint32_t method_idx,
@@ -90,7 +102,7 @@
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kInterface, thread);
+ return FindMethodHelper<kInterface, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t method_idx,
@@ -98,7 +110,7 @@
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, false, kInterface, thread);
+ return FindMethodHelper<kInterface, false>(method_idx, this_object, referrer, thread);
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 420e63a..6f7b1ab 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -76,4 +76,57 @@
return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true);
}
+extern "C" mirror::Object* artAllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocObjectFromCodeInstrumented(type_idx, method, self, false);
+}
+
+extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheckInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocObjectFromCodeInstrumented(type_idx, method, self, true);
+}
+
+extern "C" mirror::Array* artAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count, Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false);
+}
+
+extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true);
+}
+
+extern "C" mirror::Array* artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count, Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false);
+}
+
+extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true);
+}
+
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_cast_entrypoints.cc b/runtime/entrypoints/quick/quick_cast_entrypoints.cc
index 9ffa736..ae53d6c 100644
--- a/runtime/entrypoints/quick/quick_cast_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_cast_entrypoints.cc
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
namespace art {
@@ -31,38 +28,4 @@
return klass->IsAssignableFrom(ref_class) ? 1 : 0;
}
-// Check whether it is safe to cast one class to the other, throw exception and return -1 on failure
-extern "C" int artCheckCastFromCode(mirror::Class* src_type, mirror::Class* dest_type,
- Thread* self, mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(src_type->IsClass()) << PrettyClass(src_type);
- DCHECK(dest_type->IsClass()) << PrettyClass(dest_type);
- if (LIKELY(dest_type->IsAssignableFrom(src_type))) {
- return 0; // Success
- } else {
- FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- ThrowClassCastException(dest_type, src_type);
- return -1; // Failure
- }
-}
-
-// Tests whether 'element' can be assigned into an array of type 'array_class'.
-// Returns 0 on success and -1 if an exception is pending.
-extern "C" int artCanPutArrayElementFromCode(const mirror::Object* element,
- const mirror::Class* array_class,
- Thread* self, mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(array_class != NULL);
- // element can't be NULL as we catch this is screened in runtime_support
- mirror::Class* element_class = element->GetClass();
- mirror::Class* component_type = array_class->GetComponentType();
- if (LIKELY(component_type->IsAssignableFrom(element_class))) {
- return 0; // Success
- } else {
- FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- ThrowArrayStoreException(element_class, array_class);
- return -1; // Failure
- }
-}
-
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 9d3b8ef..1ba2066 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -48,7 +48,6 @@
// Cast
uint32_t (*pInstanceofNonTrivial)(const mirror::Class*, const mirror::Class*);
- void (*pCanPutArrayElement)(void*, void*);
void (*pCheckCast)(void*, void*);
// DexCache
@@ -71,7 +70,10 @@
void* (*pGetObjInstance)(uint32_t, void*);
void* (*pGetObjStatic)(uint32_t);
- // FillArray
+ // Array
+ void (*pAputObjectWithNullAndBoundCheck)(void*, uint32_t, void*); // array, index, src
+ void (*pAputObjectWithBoundCheck)(void*, uint32_t, void*); // array, index, src
+ void (*pAputObject)(void*, uint32_t, void*); // array, index, src
void (*pHandleFillArrayData)(void*, void*);
// JNI
@@ -103,7 +105,7 @@
int64_t (*pD2l)(double);
int64_t (*pF2l)(float);
int64_t (*pLdiv)(int64_t, int64_t);
- int64_t (*pLdivmod)(int64_t, int64_t);
+ int64_t (*pLmod)(int64_t, int64_t);
int64_t (*pLmul)(int64_t, int64_t);
uint64_t (*pShlLong)(uint64_t, uint32_t);
uint64_t (*pShrLong)(uint64_t, uint32_t);
@@ -116,10 +118,10 @@
void* (*pMemcpy)(void*, const void*, size_t);
// Invocation
+ void (*pQuickImtConflictTrampoline)(mirror::ArtMethod*);
void (*pQuickResolutionTrampoline)(mirror::ArtMethod*);
void (*pQuickToInterpreterBridge)(mirror::ArtMethod*);
void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*);
- void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*);
void (*pInvokeStaticTrampolineWithAccessCheck)(uint32_t, void*);
void (*pInvokeSuperTrampolineWithAccessCheck)(uint32_t, void*);
@@ -140,22 +142,23 @@
// JNI entrypoints.
-extern uint32_t JniMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
+extern uint32_t JniMethodStart(Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self)
- UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie,
Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result,
uint32_t saved_local_ref_cookie,
jobject locked, Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 0ec1eb7..0a533bd 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -35,7 +35,7 @@
return field->Get32(field->GetDeclaringClass());
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int32_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t));
if (LIKELY(field != NULL)) {
return field->Get32(field->GetDeclaringClass());
}
@@ -52,7 +52,7 @@
return field->Get64(field->GetDeclaringClass());
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int64_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int64_t));
if (LIKELY(field != NULL)) {
return field->Get64(field->GetDeclaringClass());
}
@@ -69,8 +69,8 @@
return field->GetObj(field->GetDeclaringClass());
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticObjectRead, sizeof(mirror::Object*),
- true);
+ field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(field->GetDeclaringClass());
}
@@ -87,8 +87,8 @@
return field->Get32(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -110,8 +110,8 @@
return field->Get64(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -134,8 +134,8 @@
return field->GetObj(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*),
- true);
+ field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -158,7 +158,7 @@
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int32_t), true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t));
if (LIKELY(field != NULL)) {
field->Set32(field->GetDeclaringClass(), new_value);
return 0; // success
@@ -176,7 +176,7 @@
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int64_t), true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t));
if (LIKELY(field != NULL)) {
field->Set64(field->GetDeclaringClass(), new_value);
return 0; // success
@@ -197,7 +197,8 @@
}
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticObjectWrite, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
field->SetObj(field->GetDeclaringClass(), new_value);
return 0; // success
@@ -216,8 +217,8 @@
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+ sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -244,8 +245,8 @@
}
*sp = callee_save;
self->SetTopOfStack(sp, 0);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+ sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -270,8 +271,8 @@
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectWrite,
- sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
diff --git a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc
index 07c1c01..b852a32 100644
--- a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc
@@ -118,8 +118,7 @@
DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
dex_method_idx = instr->VRegB_3rc();
}
- method = FindMethodFromCode(dex_method_idx, this_object, caller_method, self,
- false, kInterface);
+ method = FindMethodFromCode<kInterface, false>(dex_method_idx, this_object, caller_method, self);
if (UNLIKELY(method == NULL)) {
CHECK(self->IsExceptionPending());
return 0; // Failure.
@@ -142,17 +141,15 @@
return result;
}
-
+template<InvokeType type, bool access_check>
static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
mirror::ArtMethod* caller_method,
- Thread* self, mirror::ArtMethod** sp, bool access_check,
- InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Thread* self, mirror::ArtMethod** sp) {
mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method,
access_check, type);
if (UNLIKELY(method == NULL)) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
- method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check, type);
+ method = FindMethodFromCode<type, access_check>(method_idx, this_object, caller_method, self);
if (UNLIKELY(method == NULL)) {
CHECK(self->IsExceptionPending());
return 0; // failure
@@ -176,6 +173,27 @@
return result;
}
+// Explicit template declarations of artInvokeCommon for all invoke types.
+#define EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ static uint64_t artInvokeCommon<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* caller_method, \
+ Thread* self, mirror::ArtMethod** sp)
+
+#define EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL
+
// See comments in runtime_support_asm.S
extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
mirror::Object* this_object,
@@ -183,7 +201,7 @@
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kInterface);
+ return artInvokeCommon<kInterface, true>(method_idx, this_object, caller_method, self, sp);
}
@@ -193,7 +211,7 @@
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kDirect);
+ return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method, self, sp);
}
extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
@@ -202,7 +220,7 @@
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kStatic);
+ return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method, self, sp);
}
extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
@@ -211,7 +229,7 @@
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kSuper);
+ return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method, self, sp);
}
extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
@@ -220,7 +238,7 @@
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kVirtual);
+ return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method, self, sp);
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 27ae59b..59da7a0 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -30,10 +30,14 @@
// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_.
extern uint32_t JniMethodStart(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
- DCHECK(env != NULL);
+ DCHECK(env != nullptr);
uint32_t saved_local_ref_cookie = env->local_ref_cookie;
env->local_ref_cookie = env->locals.GetSegmentState();
- self->TransitionFromRunnableToSuspended(kNative);
+ mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+ if (!native_method->IsFastNative()) {
+ // When not fast JNI we transition out of runnable.
+ self->TransitionFromRunnableToSuspended(kNative);
+ }
return saved_local_ref_cookie;
}
@@ -42,6 +46,20 @@
return JniMethodStart(self);
}
+// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
+static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+ mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+ bool is_fast = native_method->IsFastNative();
+ if (!is_fast) {
+ self->TransitionFromSuspendedToRunnable();
+ } else if (UNLIKELY(self->TestAllFlags())) {
+ // 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);
+ }
+}
+
static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
env->locals.SetSegmentState(env->local_ref_cookie);
@@ -50,21 +68,21 @@
}
extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
PopLocalReferences(saved_local_ref_cookie, self);
}
extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
UnlockJniSynchronizedMethod(locked, self); // Must decode before pop.
PopLocalReferences(saved_local_ref_cookie, self);
}
extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie,
Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
mirror::Object* o = self->DecodeJObject(result); // Must decode before pop.
PopLocalReferences(saved_local_ref_cookie, self);
// Process result.
@@ -80,7 +98,7 @@
extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result,
uint32_t saved_local_ref_cookie,
jobject locked, Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
UnlockJniSynchronizedMethod(locked, self); // Must decode before pop.
mirror::Object* o = self->DecodeJObject(result);
PopLocalReferences(saved_local_ref_cookie, self);
diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
index 36ca604..2102ab1 100644
--- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
@@ -15,28 +15,40 @@
*/
#include "callee_save_frame.h"
+#include "common_throws.h"
#include "mirror/object-inl.h"
namespace art {
-extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self,
- mirror::ArtMethod** sp)
- UNLOCK_FUNCTION(monitor_lock_) {
+extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, mirror::ArtMethod** sp)
+ EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- DCHECK(obj != NULL); // Assumed to have been checked before entry
- // MonitorExit may throw exception
- return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */;
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location(self->GetCurrentLocationForThrow());
+ ThrowNullPointerException(&throw_location,
+ "Null reference used for synchronization (monitor-enter)");
+ return -1; // Failure.
+ } else {
+ obj->MonitorEnter(self); // May block
+ DCHECK(self->HoldsLock(obj));
+ DCHECK(!self->IsExceptionPending());
+ return 0; // Success.
+ // Only possible exception is NPE and is handled before entry
+ }
}
-extern "C" void artLockObjectFromCode(mirror::Object* obj, Thread* thread,
- mirror::ArtMethod** sp)
- EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) {
- FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly);
- DCHECK(obj != NULL); // Assumed to have been checked before entry
- obj->MonitorEnter(thread); // May block
- DCHECK(thread->HoldsLock(obj));
- // Only possible exception is NPE and is handled before entry
- DCHECK(!thread->IsExceptionPending());
+extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self, mirror::ArtMethod** sp)
+ UNLOCK_FUNCTION(monitor_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location(self->GetCurrentLocationForThrow());
+ ThrowNullPointerException(&throw_location,
+ "Null reference used for synchronization (monitor-exit)");
+ return -1; // Failure.
+ } else {
+ // MonitorExit may throw exception.
+ return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */;
+ }
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_math_entrypoints.cc b/runtime/entrypoints/quick/quick_math_entrypoints.cc
index 0bfe59d..014aad3 100644
--- a/runtime/entrypoints/quick/quick_math_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_math_entrypoints.cc
@@ -62,15 +62,15 @@
return -1;
}
-extern "C" int64_t artLmulFromCode(int64_t a, int64_t b) {
+extern "C" int64_t artLmul(int64_t a, int64_t b) {
return a * b;
}
-extern "C" int64_t artLdivFromCode(int64_t a, int64_t b) {
+extern "C" int64_t artLdiv(int64_t a, int64_t b) {
return a / b;
}
-extern "C" int64_t artLdivmodFromCode(int64_t a, int64_t b) {
+extern "C" int64_t artLmod(int64_t a, int64_t b) {
return a % b;
}
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index f67b2fc..31eacac 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -15,8 +15,9 @@
*/
#include "callee_save_frame.h"
+#include "common_throws.h"
#include "entrypoints/entrypoint_utils.h"
-#include "mirror/object.h"
+#include "mirror/object-inl.h"
#include "object_utils.h"
#include "thread.h"
#include "well_known_classes.h"
@@ -95,4 +96,21 @@
self->QuickDeliverException();
}
+extern "C" void artThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+ CHECK(!dest_type->IsAssignableFrom(src_type));
+ ThrowClassCastException(dest_type, src_type);
+ self->QuickDeliverException();
+}
+
+extern "C" void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+ ThrowArrayStoreException(value->GetClass(), array->GetClass());
+ self->QuickDeliverException();
+}
+
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index cb486d5..01d3549 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "entrypoints/entrypoint_utils.h"
+#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
#include "invoke_arg_array_builder.h"
#include "mirror/art_method-inl.h"
@@ -423,13 +424,23 @@
virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (IsParamAReference()) {
- soa_->AddLocalReference<jobject>(*reinterpret_cast<mirror::Object**>(GetParamAddress()));
+ mirror::Object** param_address = reinterpret_cast<mirror::Object**>(GetParamAddress());
+ jobject reference =
+ soa_->AddLocalReference<jobject>(*param_address);
+ references_.push_back(std::make_pair(reference, param_address));
+ }
+ }
+
+ void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Fixup any references which may have changed.
+ for (std::pair<jobject, mirror::Object**>& it : references_) {
+ *it.second = soa_->Decode<mirror::Object*>(it.first);
}
}
private:
ScopedObjectAccessUnchecked* soa_;
-
+ std::vector<std::pair<jobject, mirror::Object**> > references_;
DISALLOW_COPY_AND_ASSIGN(RememberFoGcArgumentVisitor);
};
@@ -537,6 +548,21 @@
} else if (invoke_type == kInterface) {
called = receiver->GetClass()->FindVirtualMethodForInterface(called);
}
+ if ((invoke_type == kVirtual) || (invoke_type == kInterface)) {
+ // We came here because of sharpening. Ensure the dex cache is up-to-date on the method index
+ // of the sharpened method.
+ if (called->GetDexCacheResolvedMethods() == caller->GetDexCacheResolvedMethods()) {
+ caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called);
+ } else {
+ // Calling from one dex file to another, need to compute the method index appropriate to
+ // the caller's dex file.
+ uint32_t method_index =
+ MethodHelper(called).FindDexMethodIndexInOtherDexFile(MethodHelper(caller).GetDexFile());
+ if (method_index != DexFile::kDexNoIndex) {
+ caller->GetDexCacheResolvedMethods()->Set(method_index, called);
+ }
+ }
+ }
// Ensure that the called method's class is initialized.
mirror::Class* called_class = called->GetDeclaringClass();
linker->EnsureInitialized(called_class, true, true);
@@ -556,11 +582,8 @@
}
}
CHECK_EQ(code == NULL, thread->IsExceptionPending());
-#ifdef MOVING_GARBAGE_COLLECTOR
- // TODO: locally saved objects may have moved during a GC during resolution. Need to update the
- // registers so that the stale objects aren't passed to the method we've resolved.
- UNIMPLEMENTED(WARNING);
-#endif
+ // Fixup any locally saved objects may have moved during a GC.
+ visitor.FixupReferences();
// Place called method in callee-save frame to be placed as first argument to quick method.
*sp = called;
return code;
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 2e6b0a8..a5f9997 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -146,7 +146,7 @@
ScopedObjectAccess soa(env);
std::vector<uintptr_t> fake_stack;
- ASSERT_EQ(kStackAlignment, 16);
+ ASSERT_EQ(kStackAlignment, 16U);
ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t));
#if !defined(ART_USE_PORTABLE_COMPILER)
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 997d725..8fa5b86 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -163,8 +163,10 @@
// Size in number of elements.
void Init() {
- mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T), PROT_READ | PROT_WRITE));
- CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack";
+ std::string error_msg;
+ mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T),
+ PROT_READ | PROT_WRITE, &error_msg));
+ CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg;
byte* addr = mem_map_->Begin();
CHECK(addr != NULL);
debug_is_sorted_ = true;
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index 85034a0..7818bc8 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -54,9 +54,11 @@
/* Set up the card table */
size_t capacity = heap_capacity / kCardSize;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
+ std::string error_msg;
UniquePtr<MemMap> mem_map(MemMap::MapAnonymous("card table", NULL,
- capacity + 256, PROT_READ | PROT_WRITE));
- CHECK(mem_map.get() != NULL) << "couldn't allocate card table";
+ capacity + 256, PROT_READ | PROT_WRITE,
+ &error_msg));
+ CHECK(mem_map.get() != NULL) << "couldn't allocate card table: " << error_msg;
// All zeros is the correct initial value; all clean. Anonymous mmaps are initialized to zero, we
// don't clear the card table to avoid unnecessary pages being allocated
COMPILE_ASSERT(kCardClean == 0, card_clean_must_be_0);
diff --git a/runtime/gc/accounting/gc_allocator.cc b/runtime/gc/accounting/gc_allocator.cc
index 11d0e67..49d84fa 100644
--- a/runtime/gc/accounting/gc_allocator.cc
+++ b/runtime/gc/accounting/gc_allocator.cc
@@ -22,15 +22,17 @@
namespace art {
namespace gc {
namespace accounting {
- void* RegisterGCAllocation(size_t bytes) {
- Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes);
- return malloc(bytes);
- }
- void RegisterGCDeAllocation(void* p, size_t bytes) {
- Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes);
- free(p);
- }
+void* RegisterGcAllocation(size_t bytes) {
+ Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes);
+ return malloc(bytes);
+}
+
+void RegisterGcDeallocation(void* p, size_t bytes) {
+ Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes);
+ free(p);
+}
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/gc_allocator.h b/runtime/gc/accounting/gc_allocator.h
index 1fba858..4fe9367 100644
--- a/runtime/gc/accounting/gc_allocator.h
+++ b/runtime/gc/accounting/gc_allocator.h
@@ -26,55 +26,56 @@
namespace art {
namespace gc {
namespace accounting {
- void* RegisterGCAllocation(size_t bytes);
- void RegisterGCDeAllocation(void* p, size_t bytes);
- static const bool kMeasureGCMemoryOverhead = false;
+void* RegisterGcAllocation(size_t bytes);
+void RegisterGcDeallocation(void* p, size_t bytes);
- template <typename T>
- class GCAllocatorImpl : public std::allocator<T> {
- public:
- typedef typename std::allocator<T>::value_type value_type;
- typedef typename std::allocator<T>::size_type size_type;
- typedef typename std::allocator<T>::difference_type difference_type;
- typedef typename std::allocator<T>::pointer pointer;
- typedef typename std::allocator<T>::const_pointer const_pointer;
- typedef typename std::allocator<T>::reference reference;
- typedef typename std::allocator<T>::const_reference const_reference;
+static const bool kMeasureGcMemoryOverhead = false;
- // Used internally by STL data structures.
- template <class U>
- GCAllocatorImpl(const GCAllocatorImpl<U>& alloc) throw() {
- }
+template <typename T>
+class GcAllocatorImpl : public std::allocator<T> {
+ public:
+ typedef typename std::allocator<T>::value_type value_type;
+ typedef typename std::allocator<T>::size_type size_type;
+ typedef typename std::allocator<T>::difference_type difference_type;
+ typedef typename std::allocator<T>::pointer pointer;
+ typedef typename std::allocator<T>::const_pointer const_pointer;
+ typedef typename std::allocator<T>::reference reference;
+ typedef typename std::allocator<T>::const_reference const_reference;
- // Used internally by STL data structures.
- GCAllocatorImpl() throw() {
- }
+ // Used internally by STL data structures.
+ template <class U>
+ GcAllocatorImpl(const GcAllocatorImpl<U>& alloc) throw() {
+ }
- // Enables an allocator for objects of one type to allocate storage for objects of another type.
- // Used internally by STL data structures.
- template <class U>
- struct rebind {
- typedef GCAllocatorImpl<U> other;
- };
+ // Used internally by STL data structures.
+ GcAllocatorImpl() throw() {
+ }
- pointer allocate(size_type n, const_pointer hint = 0) {
- return reinterpret_cast<pointer>(RegisterGCAllocation(n * sizeof(T)));
- }
-
- template <typename PT>
- void deallocate(PT p, size_type n) {
- RegisterGCDeAllocation(p, n * sizeof(T));
- }
+ // Enables an allocator for objects of one type to allocate storage for objects of another type.
+ // Used internally by STL data structures.
+ template <class U>
+ struct rebind {
+ typedef GcAllocatorImpl<U> other;
};
- // C++ doesn't allow template typedefs. This is a workaround template typedef which is
- // GCAllocatorImpl<T> if kMeasureGCMemoryOverhead is true, std::allocator<T> otherwise.
- template <typename T>
- class GCAllocator : public TypeStaticIf<kMeasureGCMemoryOverhead,
- GCAllocatorImpl<T>,
- std::allocator<T> >::value {
- };
+ pointer allocate(size_type n, const_pointer hint = 0) {
+ return reinterpret_cast<pointer>(RegisterGcAllocation(n * sizeof(T)));
+ }
+
+ template <typename PT>
+ void deallocate(PT p, size_type n) {
+ RegisterGcDeallocation(p, n * sizeof(T));
+ }
+};
+
+// C++ doesn't allow template typedefs. This is a workaround template typedef which is
+// GCAllocatorImpl<T> if kMeasureGCMemoryOverhead is true, std::allocator<T> otherwise.
+template <typename T>
+class GcAllocator : public TypeStaticIf<kMeasureGcMemoryOverhead, GcAllocatorImpl<T>,
+ std::allocator<T> >::value {
+};
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h
index 2ca8c4a..24ebbaa 100644
--- a/runtime/gc/accounting/heap_bitmap.h
+++ b/runtime/gc/accounting/heap_bitmap.h
@@ -31,8 +31,8 @@
class HeapBitmap {
public:
- typedef std::vector<SpaceBitmap*, GCAllocator<SpaceBitmap*> > SpaceBitmapVector;
- typedef std::vector<SpaceSetMap*, GCAllocator<SpaceSetMap*> > SpaceSetMapVector;
+ typedef std::vector<SpaceBitmap*, GcAllocator<SpaceBitmap*> > SpaceBitmapVector;
+ typedef std::vector<SpaceSetMap*, GcAllocator<SpaceSetMap*> > SpaceSetMapVector;
bool Test(const mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
SpaceBitmap* bitmap = GetContinuousSpaceBitmap(obj);
diff --git a/runtime/gc/accounting/mod_union_table-inl.h b/runtime/gc/accounting/mod_union_table-inl.h
index 29450c1..fb425df 100644
--- a/runtime/gc/accounting/mod_union_table-inl.h
+++ b/runtime/gc/accounting/mod_union_table-inl.h
@@ -28,9 +28,11 @@
// A mod-union table to record image references to the Zygote and alloc space.
class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache {
public:
- explicit ModUnionTableToZygoteAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) {}
+ explicit ModUnionTableToZygoteAllocspace(const std::string& name, Heap* heap,
+ space::ContinuousSpace* space)
+ : ModUnionTableReferenceCache(name, heap, space) {}
- bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) {
+ bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) ALWAYS_INLINE {
const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces();
typedef std::vector<space::ContinuousSpace*>::const_iterator It;
for (It it = spaces.begin(); it != spaces.end(); ++it) {
@@ -47,16 +49,18 @@
// A mod-union table to record Zygote references to the alloc space.
class ModUnionTableToAllocspace : public ModUnionTableReferenceCache {
public:
- explicit ModUnionTableToAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) {}
+ explicit ModUnionTableToAllocspace(const std::string& name, Heap* heap,
+ space::ContinuousSpace* space)
+ : ModUnionTableReferenceCache(name, heap, space) {}
- bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) {
+ bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) ALWAYS_INLINE {
const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces();
typedef std::vector<space::ContinuousSpace*>::const_iterator It;
for (It it = spaces.begin(); it != spaces.end(); ++it) {
space::ContinuousSpace* space = *it;
if (space->Contains(ref)) {
// The allocation space is always considered for collection whereas the Zygote space is
- //
+ // only considered for full GC.
return space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect;
}
}
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 4865219..7cbe94d 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -19,6 +19,7 @@
#include "base/stl_util.h"
#include "card_table-inl.h"
#include "heap_bitmap.h"
+#include "gc/collector/mark_sweep.h"
#include "gc/collector/mark_sweep-inl.h"
#include "gc/heap.h"
#include "gc/space/space.h"
@@ -67,60 +68,87 @@
std::vector<byte*>* const cleared_cards_;
};
-class ModUnionScanImageRootVisitor {
+class ModUnionUpdateObjectReferencesVisitor {
public:
- explicit ModUnionScanImageRootVisitor(collector::MarkSweep* const mark_sweep)
- : mark_sweep_(mark_sweep) {}
+ ModUnionUpdateObjectReferencesVisitor(RootVisitor visitor, void* arg)
+ : visitor_(visitor),
+ arg_(arg) {
+ }
- void operator()(const Object* root) const
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(root != NULL);
- mark_sweep_->ScanRoot(root);
+ // Extra parameters are required since we use this same visitor signature for checking objects.
+ void operator()(Object* obj, Object* ref, const MemberOffset& offset,
+ bool /* is_static */) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Only add the reference if it is non null and fits our criteria.
+ if (ref != nullptr) {
+ Object* new_ref = visitor_(ref, arg_);
+ if (new_ref != ref) {
+ obj->SetFieldObject(offset, ref, false, true);
+ }
+ }
}
private:
- collector::MarkSweep* const mark_sweep_;
+ RootVisitor* visitor_;
+ void* arg_;
};
-void ModUnionTableReferenceCache::ClearCards(space::ContinuousSpace* space) {
+class ModUnionScanImageRootVisitor {
+ public:
+ ModUnionScanImageRootVisitor(RootVisitor visitor, void* arg)
+ : visitor_(visitor), arg_(arg) {}
+
+ void operator()(Object* root) const
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(root != NULL);
+ ModUnionUpdateObjectReferencesVisitor ref_visitor(visitor_, arg_);
+ collector::MarkSweep::VisitObjectReferences(root, ref_visitor, true);
+ }
+
+ private:
+ RootVisitor* visitor_;
+ void* arg_;
+};
+
+void ModUnionTableReferenceCache::ClearCards() {
CardTable* card_table = GetHeap()->GetCardTable();
ModUnionClearCardSetVisitor visitor(&cleared_cards_);
// Clear dirty cards in the this space and update the corresponding mod-union bits.
- card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor);
+ card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
}
class AddToReferenceArrayVisitor {
public:
explicit AddToReferenceArrayVisitor(ModUnionTableReferenceCache* mod_union_table,
- std::vector<const Object*>* references)
+ std::vector<Object**>* references)
: mod_union_table_(mod_union_table),
references_(references) {
}
// Extra parameters are required since we use this same visitor signature for checking objects.
- void operator()(const Object* obj, const Object* ref, const MemberOffset& /* offset */,
+ void operator()(Object* obj, Object* ref, const MemberOffset& offset,
bool /* is_static */) const {
// Only add the reference if it is non null and fits our criteria.
- if (ref != NULL && mod_union_table_->AddReference(obj, ref)) {
- references_->push_back(ref);
+ if (ref != nullptr && mod_union_table_->AddReference(obj, ref)) {
+ // Push the adddress of the reference.
+ references_->push_back(obj->GetFieldObjectAddr(offset));
}
}
private:
ModUnionTableReferenceCache* const mod_union_table_;
- std::vector<const Object*>* const references_;
+ std::vector<Object**>* const references_;
};
class ModUnionReferenceVisitor {
public:
explicit ModUnionReferenceVisitor(ModUnionTableReferenceCache* const mod_union_table,
- std::vector<const Object*>* references)
+ std::vector<Object**>* references)
: mod_union_table_(mod_union_table),
references_(references) {
}
- void operator()(const Object* obj) const
+ void operator()(Object* obj) const
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
DCHECK(obj != NULL);
// We don't have an early exit since we use the visitor pattern, an early
@@ -130,7 +158,7 @@
}
private:
ModUnionTableReferenceCache* const mod_union_table_;
- std::vector<const Object*>* const references_;
+ std::vector<Object**>* const references_;
};
class CheckReferenceVisitor {
@@ -143,8 +171,8 @@
// Extra parameters are required since we use this same visitor signature for checking objects.
// TODO: Fixme when anotatalysis works with visitors.
- void operator()(const Object* obj, const Object* ref, const MemberOffset& /* offset */,
- bool /* is_static */) const
+ void operator()(const Object* obj, const Object* ref,
+ const MemberOffset& /* offset */, bool /* is_static */) const
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
Heap* heap = mod_union_table_->GetHeap();
if (ref != NULL && mod_union_table_->AddReference(obj, ref) &&
@@ -174,7 +202,7 @@
: mod_union_table_(mod_union_table), references_(references) {
}
- void operator()(const Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
+ void operator()(Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current());
DCHECK(obj != NULL);
CheckReferenceVisitor visitor(mod_union_table_, references_);
@@ -188,26 +216,25 @@
void ModUnionTableReferenceCache::Verify() {
// Start by checking that everything in the mod union table is marked.
- Heap* heap = GetHeap();
- for (const std::pair<const byte*, std::vector<const Object*> >& it : references_) {
- for (const Object* ref : it.second) {
- CHECK(heap->IsLiveObjectLocked(ref));
+ for (const auto& ref_pair : references_) {
+ for (Object** ref : ref_pair.second) {
+ CHECK(heap_->IsLiveObjectLocked(*ref));
}
}
// Check the references of each clean card which is also in the mod union table.
- CardTable* card_table = heap->GetCardTable();
- for (const std::pair<const byte*, std::vector<const Object*> > & it : references_) {
- const byte* card = it.first;
+ CardTable* card_table = heap_->GetCardTable();
+ SpaceBitmap* live_bitmap = space_->GetLiveBitmap();
+ for (const auto& ref_pair : references_) {
+ const byte* card = ref_pair.first;
if (*card == CardTable::kCardClean) {
- std::set<const Object*> reference_set(it.second.begin(), it.second.end());
+ std::set<const Object*> reference_set;
+ for (Object** obj_ptr : ref_pair.second) {
+ reference_set.insert(*obj_ptr);
+ }
ModUnionCheckReferences visitor(this, reference_set);
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
- uintptr_t end = start + CardTable::kCardSize;
- auto* space = heap->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false);
- DCHECK(space != nullptr);
- SpaceBitmap* live_bitmap = space->GetLiveBitmap();
- live_bitmap->VisitMarkedRange(start, end, visitor);
+ live_bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, visitor);
}
}
}
@@ -221,24 +248,24 @@
os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ",";
}
os << "]\nModUnionTable references: [";
- for (const std::pair<const byte*, std::vector<const Object*> >& it : references_) {
- const byte* card_addr = it.first;
+ for (const auto& ref_pair : references_) {
+ const byte* card_addr = ref_pair.first;
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
uintptr_t end = start + CardTable::kCardSize;
os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "->{";
- for (const mirror::Object* ref : it.second) {
- os << reinterpret_cast<const void*>(ref) << ",";
+ for (Object** ref : ref_pair.second) {
+ os << reinterpret_cast<const void*>(*ref) << ",";
}
os << "},";
}
}
-void ModUnionTableReferenceCache::Update() {
+void ModUnionTableReferenceCache::UpdateAndMarkReferences(RootVisitor visitor, void* arg) {
Heap* heap = GetHeap();
CardTable* card_table = heap->GetCardTable();
- std::vector<const Object*> cards_references;
- ModUnionReferenceVisitor visitor(this, &cards_references);
+ std::vector<Object**> cards_references;
+ ModUnionReferenceVisitor add_visitor(this, &cards_references);
for (const auto& card : cleared_cards_) {
// Clear and re-compute alloc space references associated with this card.
@@ -248,7 +275,7 @@
auto* space = heap->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false);
DCHECK(space != nullptr);
SpaceBitmap* live_bitmap = space->GetLiveBitmap();
- live_bitmap->VisitMarkedRange(start, end, visitor);
+ live_bitmap->VisitMarkedRange(start, end, add_visitor);
// Update the corresponding references for the card.
auto found = references_.find(card);
@@ -263,46 +290,41 @@
}
}
cleared_cards_.clear();
-}
-
-void ModUnionTableReferenceCache::MarkReferences(collector::MarkSweep* mark_sweep) {
size_t count = 0;
-
for (const auto& ref : references_) {
- for (const auto& obj : ref.second) {
- mark_sweep->MarkRoot(obj);
- ++count;
+ for (const auto& obj_ptr : ref.second) {
+ Object* obj = *obj_ptr;
+ if (obj != nullptr) {
+ Object* new_obj = visitor(obj, arg);
+ // Avoid dirtying pages in the image unless necessary.
+ if (new_obj != obj) {
+ *obj_ptr = new_obj;
+ }
+ }
}
+ count += ref.second.size();
}
if (VLOG_IS_ON(heap)) {
VLOG(gc) << "Marked " << count << " references in mod union table";
}
}
-void ModUnionTableCardCache::ClearCards(space::ContinuousSpace* space) {
+void ModUnionTableCardCache::ClearCards() {
CardTable* card_table = GetHeap()->GetCardTable();
ModUnionClearCardSetVisitor visitor(&cleared_cards_);
// Clear dirty cards in the this space and update the corresponding mod-union bits.
- card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor);
+ card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
}
// Mark all references to the alloc space(s).
-void ModUnionTableCardCache::MarkReferences(collector::MarkSweep* mark_sweep) {
+void ModUnionTableCardCache::UpdateAndMarkReferences(RootVisitor visitor, void* arg) {
CardTable* card_table = heap_->GetCardTable();
- ModUnionScanImageRootVisitor visitor(mark_sweep);
- space::ContinuousSpace* space = nullptr;
- SpaceBitmap* bitmap = nullptr;
+ ModUnionScanImageRootVisitor scan_visitor(visitor, arg);
+ SpaceBitmap* bitmap = space_->GetLiveBitmap();
for (const byte* card_addr : cleared_cards_) {
- auto start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
- auto end = start + CardTable::kCardSize;
- auto obj_start = reinterpret_cast<Object*>(start);
- if (UNLIKELY(space == nullptr || !space->Contains(obj_start))) {
- space = heap_->FindContinuousSpaceFromObject(obj_start, false);
- DCHECK(space != nullptr);
- bitmap = space->GetLiveBitmap();
- DCHECK(bitmap != nullptr);
- }
- bitmap->VisitMarkedRange(start, end, visitor);
+ uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
+ DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
+ bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
}
}
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index eb7a754..5a99f1b 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -19,6 +19,7 @@
#include "gc_allocator.h"
#include "globals.h"
+#include "root_visitor.h"
#include "safe_map.h"
#include <set>
@@ -50,23 +51,25 @@
// cleared between GC phases, reducing the number of dirty cards that need to be scanned.
class ModUnionTable {
public:
- typedef std::set<byte*, std::less<byte*>, GCAllocator<byte*> > CardSet;
+ typedef std::set<byte*, std::less<byte*>, GcAllocator<byte*> > CardSet;
- explicit ModUnionTable(Heap* heap) : heap_(heap) {}
+ explicit ModUnionTable(const std::string& name, Heap* heap, space::ContinuousSpace* space)
+ : name_(name),
+ heap_(heap),
+ space_(space) {
+ }
virtual ~ModUnionTable() {}
// Clear cards which map to a memory range of a space. This doesn't immediately update the
// mod-union table, as updating the mod-union table may have an associated cost, such as
// determining references to track.
- virtual void ClearCards(space::ContinuousSpace* space) = 0;
+ virtual void ClearCards() = 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.
- virtual void Update() = 0;
-
- // Mark the bitmaps for all references which are stored in the mod-union table.
- virtual void MarkReferences(collector::MarkSweep* mark_sweep) = 0;
+ // 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.
+ virtual void UpdateAndMarkReferences(RootVisitor visitor, void* arg) = 0;
// Verification, sanity checks that we don't have clean cards which conflict with out cached data
// for said cards. Exclusive lock is required since verify sometimes uses
@@ -75,31 +78,35 @@
virtual void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) = 0;
virtual void Dump(std::ostream& os) = 0;
-
+ space::ContinuousSpace* GetSpace() {
+ return space_;
+ }
Heap* GetHeap() const {
return heap_;
}
+ const std::string& GetName() const {
+ return name_;
+ }
protected:
+ const std::string name_;
Heap* const heap_;
+ space::ContinuousSpace* const space_;
};
// Reference caching implementation. Caches references pointing to alloc space(s) for each card.
class ModUnionTableReferenceCache : public ModUnionTable {
public:
- explicit ModUnionTableReferenceCache(Heap* heap) : ModUnionTable(heap) {}
+ explicit ModUnionTableReferenceCache(const std::string& name, Heap* heap,
+ space::ContinuousSpace* space)
+ : ModUnionTable(name, heap, space) {}
virtual ~ModUnionTableReferenceCache() {}
// Clear and store cards for a space.
- void ClearCards(space::ContinuousSpace* space);
+ void ClearCards();
- // Update table based on cleared cards.
- void Update()
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // Mark all references to the alloc space(s).
- void MarkReferences(collector::MarkSweep* mark_sweep)
+ // Update table based on cleared cards and mark all references to the other spaces.
+ void UpdateAndMarkReferences(RootVisitor visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -117,24 +124,22 @@
ModUnionTable::CardSet cleared_cards_;
// Maps from dirty cards to their corresponding alloc space references.
- SafeMap<const byte*, std::vector<const mirror::Object*>, std::less<const byte*>,
- GCAllocator<std::pair<const byte*, std::vector<const mirror::Object*> > > > references_;
+ SafeMap<const byte*, std::vector<mirror::Object**>, std::less<const byte*>,
+ GcAllocator<std::pair<const byte*, std::vector<mirror::Object**> > > > references_;
};
// Card caching implementation. Keeps track of which cards we cleared and only this information.
class ModUnionTableCardCache : public ModUnionTable {
public:
- explicit ModUnionTableCardCache(Heap* heap) : ModUnionTable(heap) {}
+ explicit ModUnionTableCardCache(const std::string& name, Heap* heap, space::ContinuousSpace* space)
+ : ModUnionTable(name, heap, space) {}
virtual ~ModUnionTableCardCache() {}
// Clear and store cards for a space.
- void ClearCards(space::ContinuousSpace* space);
-
- // Nothing to update as all dirty cards were placed into cleared cards during clearing.
- void Update() {}
+ void ClearCards();
// Mark all references to the alloc space(s).
- void MarkReferences(collector::MarkSweep* mark_sweep)
+ void UpdateAndMarkReferences(RootVisitor visitor, void* arg)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 63b24ff..52c02f7 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -62,9 +62,11 @@
CHECK(heap_begin != NULL);
// Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord.
size_t bitmap_size = OffsetToIndex(RoundUp(heap_capacity, kAlignment * kBitsPerWord)) * kWordSize;
- UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), NULL, bitmap_size, PROT_READ | PROT_WRITE));
- if (mem_map.get() == NULL) {
- LOG(ERROR) << "Failed to allocate bitmap " << name;
+ std::string error_msg;
+ UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), NULL, bitmap_size,
+ PROT_READ | PROT_WRITE, &error_msg));
+ if (UNLIKELY(mem_map.get() == nullptr)) {
+ LOG(ERROR) << "Failed to allocate bitmap " << name << ": " << error_msg;
return NULL;
}
return CreateFromMemMap(name, mem_map.release(), heap_begin, heap_capacity);
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index f975692..21709ad 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -212,7 +212,7 @@
public:
typedef std::set<
const mirror::Object*, std::less<const mirror::Object*>,
- GCAllocator<const mirror::Object*> > Objects;
+ GcAllocator<const mirror::Object*> > Objects;
bool IsEmpty() const {
return contained_.empty();
@@ -247,8 +247,8 @@
template <typename Visitor>
void Visit(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS {
- for (Objects::iterator it = contained_.begin(); it != contained_.end(); ++it) {
- visitor(*it);
+ for (const mirror::Object* obj : contained_) {
+ visitor(const_cast<mirror::Object*>(obj));
}
}
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
index 3cc64e9..a6a3ee7 100644
--- a/runtime/gc/allocator/dlmalloc.cc
+++ b/runtime/gc/allocator/dlmalloc.cc
@@ -69,3 +69,19 @@
*reclaimed += length;
}
}
+
+extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* bytes_allocated = reinterpret_cast<size_t*>(arg);
+ *bytes_allocated += used_bytes + sizeof(size_t);
+}
+
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* objects_allocated = reinterpret_cast<size_t*>(arg);
+ ++(*objects_allocated);
+}
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
index 07ebd1c..c820b19 100644
--- a/runtime/gc/allocator/dlmalloc.h
+++ b/runtime/gc/allocator/dlmalloc.h
@@ -18,6 +18,8 @@
#define ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
// Configure dlmalloc for mspaces.
+// Avoid a collision with one used in llvm.
+#undef HAVE_MMAP
#define HAVE_MMAP 0
#define HAVE_MREMAP 0
#define HAVE_MORECORE 1
@@ -37,4 +39,10 @@
// pages back to the kernel.
extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
+// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will
+// count the number of bytes allocated and objects allocated,
+// respectively.
+extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+
#endif // ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index edfea94..6691cad 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -26,7 +26,7 @@
#include "gc/accounting/heap_bitmap.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "thread_list.h"
namespace art {
diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h
index d0b0b5c..270c9ef 100644
--- a/runtime/gc/collector/mark_sweep-inl.h
+++ b/runtime/gc/collector/mark_sweep-inl.h
@@ -29,7 +29,7 @@
namespace collector {
template <typename MarkVisitor>
-inline void MarkSweep::ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) {
+inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) {
DCHECK(obj != NULL);
if (kIsDebugBuild && !IsMarked(obj)) {
heap_->DumpSpaces();
@@ -62,7 +62,8 @@
}
template <typename Visitor>
-inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Visitor& visitor)
+inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& visitor,
+ bool visit_class)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_) {
DCHECK(obj != NULL);
@@ -70,6 +71,9 @@
mirror::Class* klass = obj->GetClass();
DCHECK(klass != NULL);
+ if (visit_class) {
+ visitor(obj, klass, MemberOffset(0), false);
+ }
if (klass == mirror::Class::GetJavaLangClass()) {
DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass());
VisitClassReferences(klass, obj, visitor);
@@ -86,8 +90,8 @@
}
template <typename Visitor>
-inline void MarkSweep::VisitInstanceFieldsReferences(const mirror::Class* klass,
- const mirror::Object* obj,
+inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass,
+ mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
DCHECK(obj != NULL);
@@ -96,7 +100,7 @@
}
template <typename Visitor>
-inline void MarkSweep::VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj,
+inline void MarkSweep::VisitClassReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
VisitInstanceFieldsReferences(klass, obj, visitor);
@@ -104,15 +108,14 @@
}
template <typename Visitor>
-inline void MarkSweep::VisitStaticFieldsReferences(const mirror::Class* klass,
- const Visitor& visitor)
+inline void MarkSweep::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
DCHECK(klass != NULL);
VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets(), true, visitor);
}
template <typename Visitor>
-inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets,
+inline void MarkSweep::VisitFieldsReferences(mirror::Object* obj, uint32_t ref_offsets,
bool is_static, const Visitor& visitor) {
if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
// Found a reference offset bitmap. Mark the specified offsets.
@@ -124,7 +127,7 @@
while (ref_offsets != 0) {
size_t right_shift = CLZ(ref_offsets);
MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
- const mirror::Object* ref = obj->GetFieldObject<const mirror::Object*>(field_offset, false);
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object*>(field_offset, false);
visitor(obj, ref, field_offset, is_static);
ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
}
@@ -143,7 +146,7 @@
mirror::ArtField* field = (is_static ? klass->GetStaticField(i)
: klass->GetInstanceField(i));
MemberOffset field_offset = field->GetOffset();
- const mirror::Object* ref = obj->GetFieldObject<const mirror::Object*>(field_offset, false);
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object*>(field_offset, false);
visitor(obj, ref, field_offset, is_static);
}
}
@@ -151,11 +154,11 @@
}
template <typename Visitor>
-inline void MarkSweep::VisitObjectArrayReferences(const mirror::ObjectArray<mirror::Object>* array,
+inline void MarkSweep::VisitObjectArrayReferences(mirror::ObjectArray<mirror::Object>* array,
const Visitor& visitor) {
const size_t length = static_cast<size_t>(array->GetLength());
for (size_t i = 0; i < length; ++i) {
- const mirror::Object* element = array->GetWithoutChecks(static_cast<int32_t>(i));
+ mirror::Object* element = array->GetWithoutChecks(static_cast<int32_t>(i));
const size_t width = sizeof(mirror::Object*);
MemberOffset offset(i * width + mirror::Array::DataOffset(width).Int32Value());
visitor(array, element, offset, false);
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6790144..2c69c77 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -28,6 +28,7 @@
#include "base/timing_logger.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc/accounting/mod_union_table.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
@@ -99,7 +100,7 @@
} else {
const space::ContinuousSpace* prev_space = nullptr;
// Find out if the previous space is immune.
- for (space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) {
+ for (const space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) {
if (cur_space == space) {
break;
}
@@ -107,15 +108,19 @@
}
// If previous space was immune, then extend the immune region. Relies on continuous spaces
// being sorted by Heap::AddContinuousSpace.
- if (prev_space != NULL &&
- immune_begin_ <= reinterpret_cast<Object*>(prev_space->Begin()) &&
- immune_end_ >= reinterpret_cast<Object*>(prev_space->End())) {
+ if (prev_space != NULL && IsImmuneSpace(prev_space)) {
immune_begin_ = std::min(reinterpret_cast<Object*>(space->Begin()), immune_begin_);
immune_end_ = std::max(reinterpret_cast<Object*>(space->End()), immune_end_);
}
}
}
+bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) {
+ return
+ immune_begin_ <= reinterpret_cast<Object*>(space->Begin()) &&
+ immune_end_ >= reinterpret_cast<Object*>(space->End());
+}
+
void MarkSweep::BindBitmaps() {
timings_.StartSplit("BindBitmaps");
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
@@ -263,11 +268,23 @@
}
live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
MarkConcurrentRoots();
-
- heap_->UpdateAndMarkModUnion(this, timings_, GetGcType());
+ UpdateAndMarkModUnion();
MarkReachableObjects();
}
+void MarkSweep::UpdateAndMarkModUnion() {
+ for (const auto& space : heap_->GetContinuousSpaces()) {
+ if (IsImmuneSpace(space)) {
+ const char* name = space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" :
+ "UpdateAndMarkImageModUnionTable";
+ base::TimingLogger::ScopedSplit split(name, &timings_);
+ accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space);
+ CHECK(mod_union_table != nullptr);
+ mod_union_table->UpdateAndMarkReferences(MarkRootCallback, this);
+ }
+ }
+}
+
void MarkSweep::MarkThreadRoots(Thread* self) {
MarkRootsCheckpoint(self);
}
@@ -519,24 +536,18 @@
}
}
-void MarkSweep::MarkRootParallelCallback(const Object* root, void* arg) {
+Object* MarkSweep::MarkRootParallelCallback(Object* root, void* arg) {
DCHECK(root != NULL);
DCHECK(arg != NULL);
reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(root);
+ return root;
}
-void MarkSweep::MarkObjectCallback(const Object* root, void* arg) {
- DCHECK(root != NULL);
- DCHECK(arg != NULL);
- MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg);
- mark_sweep->MarkObjectNonNull(root);
-}
-
-void MarkSweep::ReMarkObjectVisitor(const Object* root, void* arg) {
- DCHECK(root != NULL);
- DCHECK(arg != NULL);
- MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg);
- mark_sweep->MarkObjectNonNull(root);
+Object* MarkSweep::MarkRootCallback(Object* root, void* arg) {
+ DCHECK(root != nullptr);
+ DCHECK(arg != nullptr);
+ reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(root);
+ return root;
}
void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
@@ -564,30 +575,30 @@
// Marks all objects in the root set.
void MarkSweep::MarkRoots() {
timings_.StartSplit("MarkRoots");
- Runtime::Current()->VisitNonConcurrentRoots(MarkObjectCallback, this);
+ Runtime::Current()->VisitNonConcurrentRoots(MarkRootCallback, this);
timings_.EndSplit();
}
void MarkSweep::MarkNonThreadRoots() {
timings_.StartSplit("MarkNonThreadRoots");
- Runtime::Current()->VisitNonThreadRoots(MarkObjectCallback, this);
+ Runtime::Current()->VisitNonThreadRoots(MarkRootCallback, this);
timings_.EndSplit();
}
void MarkSweep::MarkConcurrentRoots() {
timings_.StartSplit("MarkConcurrentRoots");
// Visit all runtime roots and clear dirty flags.
- Runtime::Current()->VisitConcurrentRoots(MarkObjectCallback, this, false, true);
+ Runtime::Current()->VisitConcurrentRoots(MarkRootCallback, this, false, true);
timings_.EndSplit();
}
void MarkSweep::CheckObject(const Object* obj) {
DCHECK(obj != NULL);
- VisitObjectReferences(obj, [this](const Object* obj, const Object* ref, MemberOffset offset,
- bool is_static) NO_THREAD_SAFETY_ANALYSIS {
+ VisitObjectReferences(const_cast<Object*>(obj), [this](const Object* obj, const Object* ref,
+ MemberOffset offset, bool is_static) NO_THREAD_SAFETY_ANALYSIS {
Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current());
CheckReference(obj, ref, offset, is_static);
- });
+ }, true);
}
void MarkSweep::VerifyImageRootVisitor(Object* root, void* arg) {
@@ -653,11 +664,11 @@
explicit ScanObjectParallelVisitor(MarkStackTask<kUseFinger>* chunk_task) ALWAYS_INLINE
: chunk_task_(chunk_task) {}
- void operator()(const Object* obj) const {
+ void operator()(Object* obj) const {
MarkSweep* mark_sweep = chunk_task_->mark_sweep_;
mark_sweep->ScanObjectVisit(obj,
- [mark_sweep, this](const Object* /* obj */, const Object* ref,
- const MemberOffset& /* offset */, bool /* is_static */) ALWAYS_INLINE {
+ [mark_sweep, this](Object* /* obj */, Object* ref, const MemberOffset& /* offset */,
+ bool /* is_static */) ALWAYS_INLINE {
if (ref != nullptr && mark_sweep->MarkObjectParallel(ref)) {
if (kUseFinger) {
android_memory_barrier();
@@ -714,11 +725,11 @@
static const size_t kFifoSize = 4;
BoundedFifoPowerOfTwo<const Object*, kFifoSize> prefetch_fifo;
for (;;) {
- const Object* obj = NULL;
+ const Object* obj = nullptr;
if (kUseMarkStackPrefetch) {
while (mark_stack_pos_ != 0 && prefetch_fifo.size() < kFifoSize) {
const Object* obj = mark_stack_[--mark_stack_pos_];
- DCHECK(obj != NULL);
+ DCHECK(obj != nullptr);
__builtin_prefetch(obj);
prefetch_fifo.push_back(obj);
}
@@ -733,8 +744,8 @@
}
obj = mark_stack_[--mark_stack_pos_];
}
- DCHECK(obj != NULL);
- visitor(obj);
+ DCHECK(obj != nullptr);
+ visitor(const_cast<mirror::Object*>(obj));
}
}
};
@@ -765,7 +776,6 @@
ScanObjectParallelVisitor visitor(this);
accounting::CardTable* card_table = mark_sweep_->GetHeap()->GetCardTable();
size_t cards_scanned = card_table->Scan(bitmap_, begin_, end_, visitor, minimum_age_);
- mark_sweep_->cards_scanned_.fetch_add(cards_scanned);
VLOG(heap) << "Parallel scanning cards " << reinterpret_cast<void*>(begin_) << " - "
<< reinterpret_cast<void*>(end_) << " = " << cards_scanned;
// Finish by emptying our local mark stack.
@@ -803,11 +813,14 @@
DCHECK_NE(mark_stack_tasks, 0U);
const size_t mark_stack_delta = std::min(CardScanTask::kMaxSize / 2,
mark_stack_size / mark_stack_tasks + 1);
- size_t ref_card_count = 0;
- cards_scanned_ = 0;
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
byte* card_begin = space->Begin();
byte* card_end = space->End();
+ // Align up the end address. For example, the image space's end
+ // may not be card-size-aligned.
+ card_end = AlignUp(card_end, accounting::CardTable::kCardSize);
+ DCHECK(IsAligned<accounting::CardTable::kCardSize>(card_begin));
+ DCHECK(IsAligned<accounting::CardTable::kCardSize>(card_end));
// Calculate how many bytes of heap we will scan,
const size_t address_range = card_end - card_begin;
// Calculate how much address range each task gets.
@@ -831,24 +844,15 @@
thread_pool->AddTask(self, task);
card_begin += card_increment;
}
-
- if (paused && kIsDebugBuild) {
- // Make sure we don't miss scanning any cards.
- size_t scanned_cards = card_table->Scan(space->GetMarkBitmap(), space->Begin(),
- space->End(), VoidFunctor(), minimum_age);
- VLOG(heap) << "Scanning space cards " << reinterpret_cast<void*>(space->Begin()) << " - "
- << reinterpret_cast<void*>(space->End()) << " = " << scanned_cards;
- ref_card_count += scanned_cards;
- }
}
+ // Note: the card scan below may dirty new cards (and scan them)
+ // as a side effect when a Reference object is encountered and
+ // queued during the marking. See b/11465268.
thread_pool->SetMaxActiveWorkers(thread_count - 1);
thread_pool->StartWorkers(self);
thread_pool->Wait(self, true, true);
thread_pool->StopWorkers(self);
- if (paused) {
- DCHECK_EQ(ref_card_count, static_cast<size_t>(cards_scanned_.load()));
- }
timings_.EndSplit();
} else {
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
@@ -990,8 +994,11 @@
ProcessMarkStack(false);
}
-bool MarkSweep::IsMarkedCallback(const Object* object, void* arg) {
- return reinterpret_cast<MarkSweep*>(arg)->IsMarked(object);
+mirror::Object* MarkSweep::SystemWeakIsMarkedCallback(Object* object, void* arg) {
+ if (reinterpret_cast<MarkSweep*>(arg)->IsMarked(object)) {
+ return object;
+ }
+ return nullptr;
}
void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) {
@@ -1001,45 +1008,21 @@
void MarkSweep::ReMarkRoots() {
timings_.StartSplit("ReMarkRoots");
- Runtime::Current()->VisitRoots(ReMarkObjectVisitor, this, true, true);
+ Runtime::Current()->VisitRoots(MarkRootCallback, this, true, true);
timings_.EndSplit();
}
-void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) {
- Runtime::Current()->GetJavaVM()->SweepWeakGlobals(is_marked, arg);
-}
-
-struct ArrayMarkedCheck {
- accounting::ObjectStack* live_stack;
- MarkSweep* mark_sweep;
-};
-
-// Either marked or not live.
-bool MarkSweep::IsMarkedArrayCallback(const Object* object, void* arg) {
- ArrayMarkedCheck* array_check = reinterpret_cast<ArrayMarkedCheck*>(arg);
- if (array_check->mark_sweep->IsMarked(object)) {
- return true;
- }
- accounting::ObjectStack* live_stack = array_check->live_stack;
- if (std::find(live_stack->Begin(), live_stack->End(), object) == live_stack->End()) {
- return true;
- }
- return false;
-}
-
void MarkSweep::SweepSystemWeaks() {
Runtime* runtime = Runtime::Current();
timings_.StartSplit("SweepSystemWeaks");
- runtime->GetInternTable()->SweepInternTableWeaks(IsMarkedCallback, this);
- runtime->GetMonitorList()->SweepMonitorList(IsMarkedCallback, this);
- SweepJniWeakGlobals(IsMarkedCallback, this);
+ runtime->SweepSystemWeaks(SystemWeakIsMarkedCallback, this);
timings_.EndSplit();
}
-bool MarkSweep::VerifyIsLiveCallback(const Object* obj, void* arg) {
+mirror::Object* MarkSweep::VerifySystemWeakIsLiveCallback(Object* obj, void* arg) {
reinterpret_cast<MarkSweep*>(arg)->VerifyIsLive(obj);
// We don't actually want to sweep the object, so lets return "marked"
- return true;
+ return obj;
}
void MarkSweep::VerifyIsLive(const Object* obj) {
@@ -1058,11 +1041,8 @@
}
void MarkSweep::VerifySystemWeaks() {
- Runtime* runtime = Runtime::Current();
- // Verify system weaks, uses a special IsMarked callback which always returns true.
- runtime->GetInternTable()->SweepInternTableWeaks(VerifyIsLiveCallback, this);
- runtime->GetMonitorList()->SweepMonitorList(VerifyIsLiveCallback, this);
- runtime->GetJavaVM()->SweepWeakGlobals(VerifyIsLiveCallback, this);
+ // Verify system weaks, uses a special object visitor which returns the input object.
+ Runtime::Current()->SweepSystemWeaks(VerifySystemWeakIsLiveCallback, this);
}
struct SweepCallbackContext {
@@ -1369,10 +1349,6 @@
}
}
-void MarkSweep::ScanRoot(const Object* obj) {
- ScanObject(obj);
-}
-
class MarkObjectVisitor {
public:
explicit MarkObjectVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) {}
@@ -1396,7 +1372,7 @@
// and dispatches to a specialized scanning routine.
void MarkSweep::ScanObject(const Object* obj) {
MarkObjectVisitor visitor(this);
- ScanObjectVisit(obj, visitor);
+ ScanObjectVisit(const_cast<Object*>(obj), visitor);
}
void MarkSweep::ProcessMarkStackParallel(size_t thread_count) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index feef992..3bc014a 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -69,7 +69,7 @@
virtual bool HandleDirtyObjectsPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
virtual void MarkingPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
virtual void ReclaimPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- virtual void FinishPhase();
+ virtual void FinishPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
virtual void MarkReachableObjects()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -114,6 +114,9 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsImmuneSpace(const space::ContinuousSpace* space)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie
// the image. Mark that portion of the heap as immune.
virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -137,6 +140,9 @@
void ProcessReferences(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ virtual void UpdateAndMarkModUnion()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Sweeps unmarked objects to complete the garbage collection.
virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -151,11 +157,6 @@
return cleared_reference_list_;
}
- // Proxy for external access to ScanObject.
- void ScanRoot(const mirror::Object* obj)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Blackens an object.
void ScanObject(const mirror::Object* obj)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
@@ -163,7 +164,7 @@
// TODO: enable thread safety analysis when in use by multiple worker threads.
template <typename MarkVisitor>
- void ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor)
+ void ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor)
NO_THREAD_SAFETY_ANALYSIS;
size_t GetFreedBytes() const {
@@ -202,28 +203,29 @@
void SetImmuneRange(mirror::Object* begin, mirror::Object* end);
void SweepSystemWeaks()
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
- static bool VerifyIsLiveCallback(const mirror::Object* obj, void* arg)
+ static mirror::Object* VerifySystemWeakIsLiveCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
void VerifySystemWeaks()
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
// Verify that an object is live, either in a live bitmap or in the allocation stack.
void VerifyIsLive(const mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
template <typename Visitor>
- static void VisitObjectReferences(const mirror::Object* obj, const Visitor& visitor)
+ static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor,
+ bool visit_class = false)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_);
- static void MarkObjectCallback(const mirror::Object* root, void* arg)
+ static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void MarkRootParallelCallback(const mirror::Object* root, void* arg);
+ static mirror::Object* MarkRootParallelCallback(mirror::Object* root, void* arg);
// Marks an object.
void MarkObject(const mirror::Object* obj)
@@ -242,16 +244,12 @@
// Returns true if the object has its bit set in the mark bitmap.
bool IsMarked(const mirror::Object* object) const;
- static bool IsMarkedCallback(const mirror::Object* object, void* arg)
+ static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static bool IsMarkedArrayCallback(const mirror::Object* object, void* arg)
+ static mirror::Object* SystemWeakIsMarkedArrayCallback(mirror::Object* object, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void ReMarkObjectVisitor(const mirror::Object* root, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-
static void VerifyImageRootVisitor(mirror::Object* root, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_);
@@ -310,7 +308,7 @@
size_t GetThreadCount(bool paused) const;
// Returns true if an object is inside of the immune region (assumed to be marked).
- bool IsImmune(const mirror::Object* obj) const {
+ bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE {
return obj >= immune_begin_ && obj < immune_end_;
}
@@ -321,34 +319,34 @@
NO_THREAD_SAFETY_ANALYSIS;
template <typename Visitor>
- static void VisitInstanceFieldsReferences(const mirror::Class* klass, const mirror::Object* obj,
+ static void VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Visit the header, static field references, and interface pointers of a class object.
template <typename Visitor>
- static void VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj,
+ static void VisitClassReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
template <typename Visitor>
- static void VisitStaticFieldsReferences(const mirror::Class* klass, const Visitor& visitor)
+ static void VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
template <typename Visitor>
- static void VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets, bool is_static,
+ static void VisitFieldsReferences(mirror::Object* obj, uint32_t ref_offsets, bool is_static,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Visit all of the references in an object array.
template <typename Visitor>
- static void VisitObjectArrayReferences(const mirror::ObjectArray<mirror::Object>* array,
+ static void VisitObjectArrayReferences(mirror::ObjectArray<mirror::Object>* array,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Visits the header and field references of a data object.
template <typename Visitor>
- static void VisitOtherReferences(const mirror::Class* klass, const mirror::Object* obj,
+ static void VisitOtherReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
return VisitInstanceFieldsReferences(klass, obj, visitor);
@@ -390,9 +388,6 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-
// Whether or not we count how many of each type of object were scanned.
static const bool kCountScannedTypes = false;
@@ -438,7 +433,6 @@
AtomicInteger work_chunks_created_;
AtomicInteger work_chunks_deleted_;
AtomicInteger reference_count_;
- AtomicInteger cards_scanned_;
// Verification.
size_t live_stack_freeze_size_;
diff --git a/runtime/gc/collector/partial_mark_sweep.cc b/runtime/gc/collector/partial_mark_sweep.cc
index cc3cfe5..29367ce 100644
--- a/runtime/gc/collector/partial_mark_sweep.cc
+++ b/runtime/gc/collector/partial_mark_sweep.cc
@@ -19,7 +19,7 @@
#include "gc/heap.h"
#include "gc/space/space.h"
#include "partial_mark_sweep.h"
-#include "thread.h"
+#include "thread-inl.h"
namespace art {
namespace gc {
diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc
index 19cbe6b..9f0bf33 100644
--- a/runtime/gc/collector/sticky_mark_sweep.cc
+++ b/runtime/gc/collector/sticky_mark_sweep.cc
@@ -18,7 +18,7 @@
#include "gc/space/large_object_space.h"
#include "gc/space/space.h"
#include "sticky_mark_sweep.h"
-#include "thread.h"
+#include "thread-inl.h"
namespace art {
namespace gc {
diff --git a/runtime/gc/collector/sticky_mark_sweep.h b/runtime/gc/collector/sticky_mark_sweep.h
index 79c4359..8bee00f 100644
--- a/runtime/gc/collector/sticky_mark_sweep.h
+++ b/runtime/gc/collector/sticky_mark_sweep.h
@@ -31,6 +31,10 @@
return kGcTypeSticky;
}
+ // Don't need to do anything special here since we scan all the cards which may have references
+ // to the newly allocated objects.
+ virtual void UpdateAndMarkModUnion() { }
+
explicit StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = "");
~StickyMarkSweep() {}
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
new file mode 100644
index 0000000..873eadc
--- /dev/null
+++ b/runtime/gc/heap-inl.h
@@ -0,0 +1,188 @@
+/*
+ * 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_GC_HEAP_INL_H_
+#define ART_RUNTIME_GC_HEAP_INL_H_
+
+#include "heap.h"
+
+#include "debugger.h"
+#include "gc/space/dlmalloc_space-inl.h"
+#include "gc/space/large_object_space.h"
+#include "object_utils.h"
+#include "runtime.h"
+#include "thread.h"
+#include "thread-inl.h"
+
+namespace art {
+namespace gc {
+
+inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count) {
+ DebugCheckPreconditionsForAllobObject(c, byte_count);
+ mirror::Object* obj;
+ size_t bytes_allocated;
+ AllocationTimer alloc_timer(this, &obj);
+ bool large_object_allocation = TryAllocLargeObjectUninstrumented(self, c, byte_count,
+ &obj, &bytes_allocated);
+ if (LIKELY(!large_object_allocation)) {
+ // Non-large object allocation.
+ obj = AllocateUninstrumented(self, alloc_space_, byte_count, &bytes_allocated);
+ // Ensure that we did not allocate into a zygote space.
+ DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace());
+ }
+ if (LIKELY(obj != NULL)) {
+ obj->SetClass(c);
+ // Record allocation after since we want to use the atomic add for the atomic fence to guard
+ // the SetClass since we do not want the class to appear NULL in another thread.
+ size_t new_num_bytes_allocated = RecordAllocationUninstrumented(bytes_allocated, obj);
+ DCHECK(!Dbg::IsAllocTrackingEnabled());
+ CheckConcurrentGC(self, new_num_bytes_allocated, obj);
+ if (kDesiredHeapVerification > kNoHeapVerification) {
+ VerifyObject(obj);
+ }
+ return obj;
+ }
+ ThrowOutOfMemoryError(self, byte_count, large_object_allocation);
+ return NULL;
+}
+
+inline size_t Heap::RecordAllocationUninstrumented(size_t size, mirror::Object* obj) {
+ DCHECK(obj != NULL);
+ DCHECK_GT(size, 0u);
+ size_t old_num_bytes_allocated = static_cast<size_t>(num_bytes_allocated_.fetch_add(size));
+
+ DCHECK(!Runtime::Current()->HasStatsEnabled());
+
+ // This is safe to do since the GC will never free objects which are neither in the allocation
+ // stack or the live bitmap.
+ while (!allocation_stack_->AtomicPushBack(obj)) {
+ CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
+ }
+
+ return old_num_bytes_allocated + size;
+}
+
+inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
+ if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
+ return NULL;
+ }
+ DCHECK(!running_on_valgrind_);
+ return space->Alloc(self, alloc_size, bytes_allocated);
+}
+
+// DlMallocSpace-specific version.
+inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
+ if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
+ return NULL;
+ }
+ DCHECK(!running_on_valgrind_);
+ return space->AllocNonvirtual(self, alloc_size, bytes_allocated);
+}
+
+template <class T>
+inline mirror::Object* Heap::AllocateUninstrumented(Thread* self, T* space, size_t alloc_size,
+ size_t* bytes_allocated) {
+ // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
+ // done in the runnable state where suspension is expected.
+ DCHECK_EQ(self->GetState(), kRunnable);
+ self->AssertThreadSuspensionIsAllowable();
+
+ mirror::Object* ptr = TryToAllocateUninstrumented(self, space, alloc_size, false, bytes_allocated);
+ if (LIKELY(ptr != NULL)) {
+ return ptr;
+ }
+ return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated);
+}
+
+inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated) {
+ bool large_object_allocation = ShouldAllocLargeObject(c, byte_count);
+ if (UNLIKELY(large_object_allocation)) {
+ mirror::Object* obj = AllocateUninstrumented(self, large_object_space_, byte_count, bytes_allocated);
+ // Make sure that our large object didn't get placed anywhere within the space interval or else
+ // it breaks the immune range.
+ DCHECK(obj == NULL ||
+ reinterpret_cast<byte*>(obj) < continuous_spaces_.front()->Begin() ||
+ reinterpret_cast<byte*>(obj) >= continuous_spaces_.back()->End());
+ *obj_ptr = obj;
+ }
+ return large_object_allocation;
+}
+
+inline void Heap::DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) {
+ DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
+ (c->IsVariableSize() || c->GetObjectSize() == byte_count) ||
+ strlen(ClassHelper(c).GetDescriptor()) == 0);
+ DCHECK_GE(byte_count, sizeof(mirror::Object));
+}
+
+inline Heap::AllocationTimer::AllocationTimer(Heap* heap, mirror::Object** allocated_obj_ptr)
+ : heap_(heap), allocated_obj_ptr_(allocated_obj_ptr) {
+ if (kMeasureAllocationTime) {
+ allocation_start_time_ = NanoTime() / kTimeAdjust;
+ }
+}
+
+inline Heap::AllocationTimer::~AllocationTimer() {
+ if (kMeasureAllocationTime) {
+ mirror::Object* allocated_obj = *allocated_obj_ptr_;
+ // Only if the allocation succeeded, record the time.
+ if (allocated_obj != NULL) {
+ uint64_t allocation_end_time = NanoTime() / kTimeAdjust;
+ heap_->total_allocation_time_.fetch_add(allocation_end_time - allocation_start_time_);
+ }
+ }
+};
+
+inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) {
+ // We need to have a zygote space or else our newly allocated large object can end up in the
+ // Zygote resulting in it being prematurely freed.
+ // We can only do this for primitive objects since large objects will not be within the card table
+ // range. This also means that we rely on SetClass not dirtying the object's card.
+ return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray();
+}
+
+inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) {
+ size_t new_footprint = num_bytes_allocated_ + alloc_size;
+ if (UNLIKELY(new_footprint > max_allowed_footprint_)) {
+ if (UNLIKELY(new_footprint > growth_limit_)) {
+ return true;
+ }
+ if (!concurrent_gc_) {
+ if (!grow) {
+ return true;
+ } else {
+ max_allowed_footprint_ = new_footprint;
+ }
+ }
+ }
+ return false;
+}
+
+inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj) {
+ if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) {
+ // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint.
+ SirtRef<mirror::Object> ref(self, obj);
+ RequestConcurrentGC(self);
+ }
+}
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_HEAP_INL_H_
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 1b46257..804c669 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -39,6 +39,7 @@
#include "gc/space/image_space.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
+#include "heap-inl.h"
#include "image.h"
#include "invoke_arg_array_builder.h"
#include "mirror/art_field-inl.h"
@@ -63,11 +64,9 @@
static constexpr bool kDumpGcPerformanceOnShutdown = false;
// Minimum amount of remaining bytes before a concurrent GC is triggered.
static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
-// If true, measure the total allocation time.
-static constexpr bool kMeasureAllocationTime = false;
Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
- double target_utilization, size_t capacity, const std::string& original_image_file_name,
+ double target_utilization, size_t capacity, const std::string& image_file_name,
bool concurrent_gc, 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)
@@ -105,7 +104,6 @@
: std::numeric_limits<size_t>::max()),
total_bytes_freed_ever_(0),
total_objects_freed_ever_(0),
- large_object_threshold_(3 * kPageSize),
num_bytes_allocated_(0),
native_bytes_allocated_(0),
gc_memory_overhead_(0),
@@ -146,9 +144,8 @@
// Requested begin for the alloc space, to follow the mapped image and oat files
byte* requested_alloc_space_begin = NULL;
- std::string image_file_name(original_image_file_name);
if (!image_file_name.empty()) {
- space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name);
+ space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str());
CHECK(image_space != NULL) << "Failed to create space for " << image_file_name;
AddContinuousSpace(image_space);
// Oat files referenced by image files immediately follow them in memory, ensure alloc space
@@ -191,11 +188,11 @@
card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
CHECK(card_table_.get() != NULL) << "Failed to create card table";
- image_mod_union_table_.reset(new accounting::ModUnionTableToZygoteAllocspace(this));
- CHECK(image_mod_union_table_.get() != NULL) << "Failed to create image mod-union table";
-
- zygote_mod_union_table_.reset(new accounting::ModUnionTableCardCache(this));
- CHECK(zygote_mod_union_table_.get() != NULL) << "Failed to create Zygote mod-union table";
+ accounting::ModUnionTable* mod_union_table =
+ new accounting::ModUnionTableToZygoteAllocspace("Image mod-union table", this,
+ GetImageSpace());
+ CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
+ AddModUnionTable(mod_union_table);
// TODO: Count objects in the image space here.
num_bytes_allocated_ = 0;
@@ -238,6 +235,11 @@
}
CHECK_NE(max_allowed_footprint_, 0U);
+
+ if (running_on_valgrind_) {
+ Runtime::Current()->InstrumentQuickAllocEntryPoints();
+ }
+
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() exiting";
}
@@ -489,10 +491,7 @@
live_stack_->Reset();
VLOG(heap) << "~Heap()";
- // We can't take the heap lock here because there might be a daemon thread suspended with the
- // heap lock held. We know though that no non-daemon threads are executing, and we know that
- // all daemon threads are suspended, and we also know that the threads list have been deleted, so
- // those threads can't resume. We're the only running thread, and we can do whatever we like...
+ STLDeleteValues(&mod_union_tables_);
STLDeleteElements(&continuous_spaces_);
STLDeleteElements(&discontinuous_spaces_);
delete gc_complete_lock_;
@@ -554,81 +553,69 @@
}
}
-mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_count) {
- DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
- (c->IsVariableSize() || c->GetObjectSize() == byte_count) ||
- strlen(ClassHelper(c).GetDescriptor()) == 0);
- DCHECK_GE(byte_count, sizeof(mirror::Object));
-
- mirror::Object* obj = NULL;
- size_t bytes_allocated = 0;
- uint64_t allocation_start = 0;
- if (UNLIKELY(kMeasureAllocationTime)) {
- allocation_start = NanoTime() / kTimeAdjust;
+void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) {
+ std::ostringstream oss;
+ int64_t total_bytes_free = GetFreeMemory();
+ oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
+ << " free bytes";
+ // If the allocation failed due to fragmentation, print out the largest continuous allocation.
+ if (!large_object_allocation && total_bytes_free >= byte_count) {
+ size_t max_contiguous_allocation = 0;
+ for (const auto& space : continuous_spaces_) {
+ if (space->IsDlMallocSpace()) {
+ space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation);
+ }
+ }
+ oss << "; failed due to fragmentation (largest possible contiguous allocation "
+ << max_contiguous_allocation << " bytes)";
}
+ self->ThrowOutOfMemoryError(oss.str().c_str());
+}
- // We need to have a zygote space or else our newly allocated large object can end up in the
- // Zygote resulting in it being prematurely freed.
- // We can only do this for primitive objects since large objects will not be within the card table
- // range. This also means that we rely on SetClass not dirtying the object's card.
- bool large_object_allocation =
- byte_count >= large_object_threshold_ && have_zygote_space_ && c->IsPrimitiveArray();
+inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated) {
+ bool large_object_allocation = ShouldAllocLargeObject(c, byte_count);
if (UNLIKELY(large_object_allocation)) {
- obj = Allocate(self, large_object_space_, byte_count, &bytes_allocated);
+ mirror::Object* obj = AllocateInstrumented(self, large_object_space_, byte_count, bytes_allocated);
// Make sure that our large object didn't get placed anywhere within the space interval or else
// it breaks the immune range.
DCHECK(obj == NULL ||
reinterpret_cast<byte*>(obj) < continuous_spaces_.front()->Begin() ||
reinterpret_cast<byte*>(obj) >= continuous_spaces_.back()->End());
- } else {
- obj = Allocate(self, alloc_space_, byte_count, &bytes_allocated);
+ *obj_ptr = obj;
+ }
+ return large_object_allocation;
+}
+
+mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count) {
+ DebugCheckPreconditionsForAllobObject(c, byte_count);
+ mirror::Object* obj;
+ size_t bytes_allocated;
+ AllocationTimer alloc_timer(this, &obj);
+ bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count,
+ &obj, &bytes_allocated);
+ if (LIKELY(!large_object_allocation)) {
+ // Non-large object allocation.
+ obj = AllocateInstrumented(self, alloc_space_, byte_count, &bytes_allocated);
// Ensure that we did not allocate into a zygote space.
DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace());
}
-
if (LIKELY(obj != NULL)) {
obj->SetClass(c);
-
// Record allocation after since we want to use the atomic add for the atomic fence to guard
// the SetClass since we do not want the class to appear NULL in another thread.
- RecordAllocation(bytes_allocated, obj);
-
+ size_t new_num_bytes_allocated = RecordAllocationInstrumented(bytes_allocated, obj);
if (Dbg::IsAllocTrackingEnabled()) {
Dbg::RecordAllocation(c, byte_count);
}
- if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_) >= concurrent_start_bytes_)) {
- // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint.
- SirtRef<mirror::Object> ref(self, obj);
- RequestConcurrentGC(self);
- }
+ CheckConcurrentGC(self, new_num_bytes_allocated, obj);
if (kDesiredHeapVerification > kNoHeapVerification) {
VerifyObject(obj);
}
-
- if (UNLIKELY(kMeasureAllocationTime)) {
- total_allocation_time_.fetch_add(NanoTime() / kTimeAdjust - allocation_start);
- }
-
return obj;
- } else {
- std::ostringstream oss;
- int64_t total_bytes_free = GetFreeMemory();
- oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
- << " free bytes";
- // If the allocation failed due to fragmentation, print out the largest continuous allocation.
- if (!large_object_allocation && total_bytes_free >= byte_count) {
- size_t max_contiguous_allocation = 0;
- for (const auto& space : continuous_spaces_) {
- if (space->IsDlMallocSpace()) {
- space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation);
- }
- }
- oss << "; failed due to fragmentation (largest possible contiguous allocation "
- << max_contiguous_allocation << " bytes)";
- }
- self->ThrowOutOfMemoryError(oss.str().c_str());
- return NULL;
}
+ ThrowOutOfMemoryError(self, byte_count, large_object_allocation);
+ return NULL;
}
bool Heap::IsHeapAddress(const mirror::Object* obj) {
@@ -771,10 +758,10 @@
GetLiveBitmap()->Walk(Heap::VerificationCallback, this);
}
-inline void Heap::RecordAllocation(size_t size, mirror::Object* obj) {
+inline size_t Heap::RecordAllocationInstrumented(size_t size, mirror::Object* obj) {
DCHECK(obj != NULL);
DCHECK_GT(size, 0u);
- num_bytes_allocated_.fetch_add(size);
+ size_t old_num_bytes_allocated = static_cast<size_t>(num_bytes_allocated_.fetch_add(size));
if (Runtime::Current()->HasStatsEnabled()) {
RuntimeStats* thread_stats = Thread::Current()->GetStats();
@@ -792,6 +779,8 @@
while (!allocation_stack_->AtomicPushBack(obj)) {
CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
}
+
+ return old_num_bytes_allocated + size;
}
void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) {
@@ -810,25 +799,8 @@
}
}
-inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) {
- size_t new_footprint = num_bytes_allocated_ + alloc_size;
- if (UNLIKELY(new_footprint > max_allowed_footprint_)) {
- if (UNLIKELY(new_footprint > growth_limit_)) {
- return true;
- }
- if (!concurrent_gc_) {
- if (!grow) {
- return true;
- } else {
- max_allowed_footprint_ = new_footprint;
- }
- }
- }
- return false;
-}
-
-inline mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size,
- bool grow, size_t* bytes_allocated) {
+inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
return NULL;
}
@@ -836,8 +808,8 @@
}
// DlMallocSpace-specific version.
-inline mirror::Object* Heap::TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
- bool grow, size_t* bytes_allocated) {
+inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
return NULL;
}
@@ -849,15 +821,15 @@
}
template <class T>
-inline mirror::Object* Heap::Allocate(Thread* self, T* space, size_t alloc_size,
- size_t* bytes_allocated) {
+inline mirror::Object* Heap::AllocateInstrumented(Thread* self, T* space, size_t alloc_size,
+ size_t* bytes_allocated) {
// Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
// done in the runnable state where suspension is expected.
DCHECK_EQ(self->GetState(), kRunnable);
self->AssertThreadSuspensionIsAllowable();
- mirror::Object* ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
- if (ptr != NULL) {
+ mirror::Object* ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated);
+ if (LIKELY(ptr != NULL)) {
return ptr;
}
return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated);
@@ -872,7 +844,7 @@
collector::GcType last_gc = WaitForConcurrentGcToComplete(self);
if (last_gc != collector::kGcTypeNone) {
// A GC was in progress and we blocked, retry allocation now that memory has been freed.
- ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
+ ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
@@ -907,7 +879,7 @@
i = static_cast<size_t>(gc_type_ran);
// Did we free sufficient memory for the allocation to succeed?
- ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
+ ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
@@ -916,7 +888,7 @@
// Allocations have failed after GCs; this is an exceptional state.
// Try harder, growing the heap if necessary.
- ptr = TryToAllocate(self, space, alloc_size, true, bytes_allocated);
+ ptr = TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
@@ -931,7 +903,7 @@
// We don't need a WaitForConcurrentGcToComplete here either.
CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true);
- return TryToAllocate(self, space, alloc_size, true, bytes_allocated);
+ return TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated);
}
void Heap::SetTargetHeapUtilization(float target) {
@@ -1084,15 +1056,15 @@
// For bitmap Visit.
// TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
// annotalysis on visitors.
- void operator()(const mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS {
- collector::MarkSweep::VisitObjectReferences(o, *this);
+ void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
+ collector::MarkSweep::VisitObjectReferences(obj, *this, true);
}
// For MarkSweep::VisitObjectReferences.
- void operator()(const mirror::Object* referrer, const mirror::Object* object,
+ void operator()(mirror::Object* referrer, mirror::Object* object,
const MemberOffset&, bool) const {
if (object == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
- referring_objects_.push_back(const_cast<mirror::Object*>(referrer));
+ referring_objects_.push_back(referrer);
}
}
@@ -1157,6 +1129,12 @@
AddContinuousSpace(alloc_space_);
have_zygote_space_ = true;
+ // Create the zygote space mod union table.
+ accounting::ModUnionTable* mod_union_table =
+ new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space);
+ CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
+ AddModUnionTable(mod_union_table);
+
// Reset the cumulative loggers since we now have a few additional timing phases.
for (const auto& collector : mark_sweep_collectors_) {
collector->ResetCumulativeStatistics();
@@ -1313,38 +1291,12 @@
return gc_type;
}
-void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings,
- collector::GcType gc_type) {
- if (gc_type == collector::kGcTypeSticky) {
- // Don't need to do anything for mod union table in this case since we are only scanning dirty
- // cards.
- return;
- }
-
- base::TimingLogger::ScopedSplit split("UpdateModUnionTable", &timings);
- // Update zygote mod union table.
- if (gc_type == collector::kGcTypePartial) {
- base::TimingLogger::ScopedSplit split("UpdateZygoteModUnionTable", &timings);
- zygote_mod_union_table_->Update();
-
- timings.NewSplit("ZygoteMarkReferences");
- zygote_mod_union_table_->MarkReferences(mark_sweep);
- }
-
- // Processes the cards we cleared earlier and adds their objects into the mod-union table.
- timings.NewSplit("UpdateModUnionTable");
- image_mod_union_table_->Update();
-
- // Scans all objects in the mod-union table.
- timings.NewSplit("MarkImageToAllocSpaceReferences");
- image_mod_union_table_->MarkReferences(mark_sweep);
-}
-
-static void RootMatchesObjectVisitor(const mirror::Object* root, void* arg) {
+static mirror::Object* RootMatchesObjectVisitor(mirror::Object* root, void* arg) {
mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
if (root == obj) {
LOG(INFO) << "Object " << obj << " is a root";
}
+ return root;
}
class ScanVisitor {
@@ -1459,9 +1411,10 @@
return heap_->IsLiveObjectLocked(obj, true, false, true);
}
- static void VerifyRoots(const mirror::Object* root, void* arg) {
+ static mirror::Object* VerifyRoots(mirror::Object* root, void* arg) {
VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
- (*visitor)(NULL, root, MemberOffset(0), true);
+ (*visitor)(nullptr, root, MemberOffset(0), true);
+ return root;
}
private:
@@ -1481,7 +1434,7 @@
VerifyReferenceVisitor visitor(heap_);
// The class doesn't count as a reference but we should verify it anyways.
visitor(obj, obj->GetClass(), MemberOffset(0), false);
- collector::MarkSweep::VisitObjectReferences(obj, visitor);
+ collector::MarkSweep::VisitObjectReferences(const_cast<mirror::Object*>(obj), visitor, true);
failed_ = failed_ || visitor.Failed();
}
@@ -1514,8 +1467,10 @@
// pointing to dead objects if they are not reachable.
if (visitor.Failed()) {
// Dump mod-union tables.
- image_mod_union_table_->Dump(LOG(ERROR) << "Image mod-union table: ");
- zygote_mod_union_table_->Dump(LOG(ERROR) << "Zygote mod-union table: ");
+ for (const auto& table_pair : mod_union_tables_) {
+ accounting::ModUnionTable* mod_union_table = table_pair.second;
+ mod_union_table->Dump(LOG(ERROR) << mod_union_table->GetName() << ": ");
+ }
DumpSpaces();
return false;
}
@@ -1599,10 +1554,10 @@
: heap_(heap),
failed_(false) {}
- void operator()(const mirror::Object* obj) const
+ void operator()(mirror::Object* obj) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
VerifyReferenceCardVisitor visitor(heap_, const_cast<bool*>(&failed_));
- collector::MarkSweep::VisitObjectReferences(obj, visitor);
+ collector::MarkSweep::VisitObjectReferences(obj, visitor, true);
}
bool Failed() const {
@@ -1638,15 +1593,23 @@
allocation_stack_.swap(live_stack_);
}
+accounting::ModUnionTable* Heap::FindModUnionTableFromSpace(space::Space* space) {
+ auto it = mod_union_tables_.find(space);
+ if (it == mod_union_tables_.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
void Heap::ProcessCards(base::TimingLogger& timings) {
// Clear cards and keep track of cards cleared in the mod-union table.
for (const auto& space : continuous_spaces_) {
- if (space->IsImageSpace()) {
- base::TimingLogger::ScopedSplit split("ImageModUnionClearCards", &timings);
- image_mod_union_table_->ClearCards(space);
- } else if (space->IsZygoteSpace()) {
- base::TimingLogger::ScopedSplit split("ZygoteModUnionClearCards", &timings);
- zygote_mod_union_table_->ClearCards(space);
+ accounting::ModUnionTable* table = FindModUnionTableFromSpace(space);
+ if (table != nullptr) {
+ const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" :
+ "ImageModUnionClearCards";
+ base::TimingLogger::ScopedSplit split(name, &timings);
+ table->ClearCards();
} else {
base::TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings);
// No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards
@@ -1656,6 +1619,10 @@
}
}
+static mirror::Object* IdentityCallback(mirror::Object* obj, void*) {
+ return obj;
+}
+
void Heap::PreGcVerification(collector::GarbageCollector* gc) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* self = Thread::Current();
@@ -1689,10 +1656,11 @@
if (verify_mod_union_table_) {
thread_list->SuspendAll();
ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_);
- zygote_mod_union_table_->Update();
- zygote_mod_union_table_->Verify();
- image_mod_union_table_->Update();
- image_mod_union_table_->Verify();
+ for (const auto& table_pair : mod_union_tables_) {
+ accounting::ModUnionTable* mod_union_table = table_pair.second;
+ mod_union_table->UpdateAndMarkReferences(IdentityCallback, nullptr);
+ mod_union_table->Verify();
+ }
thread_list->ResumeAll();
}
}
@@ -2030,8 +1998,10 @@
// We could try mincore(2) but that's only a measure of how many pages we haven't given away,
// not how much use we're making of those pages.
uint64_t ms_time = MilliTime();
- float utilization =
- static_cast<float>(alloc_space_->GetBytesAllocated()) / alloc_space_->Size();
+ // Note the large object space's bytes allocated is equal to its capacity.
+ uint64_t los_bytes_allocated = large_object_space_->GetBytesAllocated();
+ float utilization = static_cast<float>(GetBytesAllocated() - los_bytes_allocated) /
+ (GetTotalMemory() - los_bytes_allocated);
if ((utilization > 0.75f && !IsLowMemoryMode()) || ((ms_time - last_trim_time_ms_) < 2 * 1000)) {
// Don't bother trimming the alloc space if it's more than 75% utilized and low memory mode is
// not enabled, or if a heap trim occurred in the last two seconds.
@@ -2073,24 +2043,22 @@
return concurrent_start_bytes_ != std::numeric_limits<size_t>::max();
}
-void Heap::RegisterNativeAllocation(int bytes) {
+void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) {
// Total number of native bytes allocated.
native_bytes_allocated_.fetch_add(bytes);
- Thread* self = Thread::Current();
if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_gc_watermark_) {
// The second watermark is higher than the gc watermark. If you hit this it means you are
// allocating native objects faster than the GC can keep up with.
if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) {
- JNIEnv* env = self->GetJniEnv();
// Can't do this in WellKnownClasses::Init since System is not properly set up at that
// point.
- if (WellKnownClasses::java_lang_System_runFinalization == NULL) {
+ if (UNLIKELY(WellKnownClasses::java_lang_System_runFinalization == NULL)) {
DCHECK(WellKnownClasses::java_lang_System != NULL);
WellKnownClasses::java_lang_System_runFinalization =
CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V");
- assert(WellKnownClasses::java_lang_System_runFinalization != NULL);
+ CHECK(WellKnownClasses::java_lang_System_runFinalization != NULL);
}
- if (WaitForConcurrentGcToComplete(self) != collector::kGcTypeNone) {
+ if (WaitForConcurrentGcToComplete(ThreadForEnv(env)) != collector::kGcTypeNone) {
// Just finished a GC, attempt to run finalizers.
env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
WellKnownClasses::java_lang_System_runFinalization);
@@ -2109,20 +2077,22 @@
UpdateMaxNativeFootprint();
} else {
if (!IsGCRequestPending()) {
- RequestConcurrentGC(self);
+ RequestConcurrentGC(ThreadForEnv(env));
}
}
}
}
-void Heap::RegisterNativeFree(int bytes) {
+void Heap::RegisterNativeFree(JNIEnv* env, int bytes) {
int expected_size, new_size;
do {
expected_size = native_bytes_allocated_.load();
new_size = expected_size - bytes;
- if (new_size < 0) {
- ThrowRuntimeException("attempted to free %d native bytes with only %d native bytes registered as allocated",
- bytes, expected_size);
+ if (UNLIKELY(new_size < 0)) {
+ ScopedObjectAccess soa(env);
+ env->ThrowNew(WellKnownClasses::java_lang_RuntimeException,
+ StringPrintf("Attempted to free %d native bytes with only %d native bytes "
+ "registered as allocated", bytes, expected_size).c_str());
break;
}
} while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size));
@@ -2146,5 +2116,10 @@
return ret;
}
+void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) {
+ DCHECK(mod_union_table != nullptr);
+ mod_union_tables_.Put(mod_union_table->GetSpace(), mod_union_table);
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 0b64261..7d2441b 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -101,6 +101,11 @@
};
static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification;
+// 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 kLargeObjectThreshold = 3 * kPageSize;
+
class Heap {
public:
static constexpr size_t kDefaultInitialSize = 2 * MB;
@@ -129,11 +134,20 @@
// Allocates and initializes storage for an object instance.
mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocObjectInstrumented(self, klass, num_bytes);
+ }
+ mirror::Object* AllocObjectInstrumented(Thread* self, mirror::Class* klass, size_t num_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* AllocObjectUninstrumented(Thread* self, mirror::Class* klass, size_t num_bytes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RegisterNativeAllocation(int bytes)
+ void DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RegisterNativeFree(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation);
+
+ void RegisterNativeAllocation(JNIEnv* env, int bytes);
+ void RegisterNativeFree(JNIEnv* env, int bytes);
// The given reference is believed to be to an object in the Java heap, check the soundness of it.
void VerifyObjectImpl(const mirror::Object* o);
@@ -368,11 +382,6 @@
accounting::ObjectStack* stack)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- // Update and mark mod union table based on gc type.
- void UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings,
- collector::GcType gc_type)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-
// Gets called when we get notified by ActivityThread that the process state has changed.
void ListenForProcessStateChange();
@@ -426,11 +435,28 @@
size_t GetConcGCThreadCount() const {
return conc_gc_threads_;
}
+ accounting::ModUnionTable* FindModUnionTableFromSpace(space::Space* space);
+ void AddModUnionTable(accounting::ModUnionTable* mod_union_table);
private:
+ bool TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count);
+ void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj);
+
// Allocates uninitialized storage. Passing in a null space tries to place the object in the
// large object space.
- template <class T> mirror::Object* Allocate(Thread* self, T* space, size_t num_bytes, size_t* bytes_allocated)
+ template <class T> mirror::Object* AllocateInstrumented(Thread* self, T* space, size_t num_bytes,
+ size_t* bytes_allocated)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <class T> mirror::Object* AllocateUninstrumented(Thread* self, T* space, size_t num_bytes,
+ size_t* bytes_allocated)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -442,17 +468,29 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Try to allocate a number of bytes, this function never does any GCs.
- mirror::Object* TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow,
- size_t* bytes_allocated)
+ mirror::Object* TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Try to allocate a number of bytes, this function never does any GCs. DlMallocSpace-specialized version.
- mirror::Object* TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size, bool grow,
- size_t* bytes_allocated)
+ mirror::Object* TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Object* TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow);
// Pushes a list of cleared references out to the managed heap.
@@ -462,7 +500,11 @@
void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
bool IsGCRequestPending() const;
- void RecordAllocation(size_t size, mirror::Object* object)
+ size_t RecordAllocationInstrumented(size_t size, mirror::Object* object)
+ LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ size_t RecordAllocationUninstrumented(size_t size, mirror::Object* object)
LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -476,8 +518,9 @@
void PreGcVerification(collector::GarbageCollector* gc);
void PreSweepingGcVerification(collector::GarbageCollector* gc)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PostGcVerification(collector::GarbageCollector* gc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void PostGcVerification(collector::GarbageCollector* gc);
// Update the watermark for the native allocated bytes based on the current number of native
// bytes allocated and the target utilization ratio.
@@ -522,12 +565,8 @@
// The card table, dirtied by the write barrier.
UniquePtr<accounting::CardTable> card_table_;
- // The mod-union table remembers all of the references from the image space to the alloc /
- // zygote spaces to allow the card table to be cleared.
- UniquePtr<accounting::ModUnionTable> image_mod_union_table_;
-
- // This table holds all of the references from the zygote space to the alloc space.
- UniquePtr<accounting::ModUnionTable> zygote_mod_union_table_;
+ // A mod-union table remembers all of the references from the it's space to other spaces.
+ SafeMap<space::Space*, accounting::ModUnionTable*> mod_union_tables_;
// What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC,
// false for stop-the-world mark sweep.
@@ -615,9 +654,6 @@
// Since the heap was created, how many objects have been freed.
size_t total_objects_freed_ever_;
- // Primitive objects larger than this size are put in the large object space.
- const size_t large_object_threshold_;
-
// Number of bytes allocated. Adjusted after each allocation and free.
AtomicInteger num_bytes_allocated_;
@@ -719,6 +755,16 @@
friend class ScopedHeapLock;
friend class space::SpaceTest;
+ class AllocationTimer {
+ private:
+ Heap* heap_;
+ mirror::Object** allocated_obj_ptr_;
+ uint64_t allocation_start_time_;
+ public:
+ AllocationTimer(Heap* heap, mirror::Object** allocated_obj_ptr);
+ ~AllocationTimer();
+ };
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Heap);
};
diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h
index 5481141..fb2c66b 100644
--- a/runtime/gc/space/dlmalloc_space-inl.h
+++ b/runtime/gc/space/dlmalloc_space-inl.h
@@ -30,7 +30,7 @@
MutexLock mu(self, lock_);
obj = AllocWithoutGrowthLocked(num_bytes, bytes_allocated);
}
- if (obj != NULL) {
+ if (LIKELY(obj != NULL)) {
// Zero freshly allocated memory, done while not holding the space's lock.
memset(obj, 0, num_bytes);
}
@@ -39,7 +39,7 @@
inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) {
mirror::Object* result = reinterpret_cast<mirror::Object*>(mspace_malloc(mspace_, num_bytes));
- if (result != NULL) {
+ if (LIKELY(result != NULL)) {
if (kDebugSpaces) {
CHECK(Contains(result)) << "Allocation (" << reinterpret_cast<void*>(result)
<< ") not in bounds of allocation space " << *this;
@@ -47,10 +47,6 @@
size_t allocation_size = AllocationSizeNonvirtual(result);
DCHECK(bytes_allocated != NULL);
*bytes_allocated = allocation_size;
- num_bytes_allocated_ += allocation_size;
- total_bytes_allocated_ += allocation_size;
- ++total_objects_allocated_;
- ++num_objects_allocated_;
}
return result;
}
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index a9440d3..9ebc16a 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -23,7 +23,7 @@
#include "utils.h"
#include <valgrind.h>
-#include <../memcheck/memcheck.h>
+#include <memcheck/memcheck.h>
namespace art {
namespace gc {
@@ -119,8 +119,7 @@
DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
byte* end, size_t growth_limit)
: MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect),
- recent_free_pos_(0), num_bytes_allocated_(0), num_objects_allocated_(0),
- total_bytes_allocated_(0), total_objects_allocated_(0),
+ recent_free_pos_(0), total_bytes_freed_(0), total_objects_freed_(0),
lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace),
growth_limit_(growth_limit) {
CHECK(mspace != NULL);
@@ -184,11 +183,12 @@
growth_limit = RoundUp(growth_limit, kPageSize);
capacity = RoundUp(capacity, kPageSize);
+ std::string error_msg;
UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
- PROT_READ | PROT_WRITE));
+ PROT_READ | PROT_WRITE, &error_msg));
if (mem_map.get() == NULL) {
LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
- << PrettySize(capacity);
+ << PrettySize(capacity) << ": " << error_msg;
return NULL;
}
@@ -287,8 +287,6 @@
size_t size = RoundUp(Size(), kPageSize);
// Trim the heap so that we minimize the size of the Zygote space.
Trim();
- // Trim our mem-map to free unused pages.
- GetMemMap()->UnMapAtEnd(end_);
// TODO: Not hardcode these in?
const size_t starting_size = kPageSize;
const size_t initial_size = 2 * MB;
@@ -308,7 +306,11 @@
VLOG(heap) << "Size " << GetMemMap()->Size();
VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit);
VLOG(heap) << "Capacity " << PrettySize(capacity);
- UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, PROT_READ | PROT_WRITE));
+ // Remap the tail.
+ std::string error_msg;
+ UniquePtr<MemMap> mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name,
+ PROT_READ | PROT_WRITE, &error_msg));
+ CHECK(mem_map.get() != nullptr) << error_msg;
void* mspace = CreateMallocSpace(end_, starting_size, initial_size);
// Protect memory beyond the initial size.
byte* end = mem_map->Begin() + starting_size;
@@ -353,8 +355,8 @@
CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this;
}
const size_t bytes_freed = InternalAllocationSize(ptr);
- num_bytes_allocated_ -= bytes_freed;
- --num_objects_allocated_;
+ total_bytes_freed_ += bytes_freed;
+ ++total_objects_freed_;
if (kRecentFreeCount > 0) {
RegisterRecentFree(ptr);
}
@@ -400,8 +402,8 @@
{
MutexLock mu(self, lock_);
- num_bytes_allocated_ -= bytes_freed;
- num_objects_allocated_ -= num_ptrs;
+ total_bytes_freed_ += bytes_freed;
+ total_objects_freed_ += num_ptrs;
mspace_bulk_free(mspace_, reinterpret_cast<void**>(ptrs), num_ptrs);
return bytes_freed;
}
@@ -501,6 +503,20 @@
<< ",name=\"" << GetName() << "\"]";
}
+uint64_t DlMallocSpace::GetBytesAllocated() {
+ MutexLock mu(Thread::Current(), lock_);
+ size_t bytes_allocated = 0;
+ mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated);
+ return bytes_allocated;
+}
+
+uint64_t DlMallocSpace::GetObjectsAllocated() {
+ MutexLock mu(Thread::Current(), lock_);
+ size_t objects_allocated = 0;
+ mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated);
+ return objects_allocated;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index b08da01..522535e 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -127,20 +127,13 @@
// Turn ourself into a zygote space and return a new alloc space which has our unused memory.
DlMallocSpace* CreateZygoteSpace(const char* alloc_space_name);
- uint64_t GetBytesAllocated() const {
- return num_bytes_allocated_;
+ uint64_t GetBytesAllocated();
+ uint64_t GetObjectsAllocated();
+ uint64_t GetTotalBytesAllocated() {
+ return GetBytesAllocated() + total_bytes_freed_;
}
-
- uint64_t GetObjectsAllocated() const {
- return num_objects_allocated_;
- }
-
- uint64_t GetTotalBytesAllocated() const {
- return total_bytes_allocated_;
- }
-
- uint64_t GetTotalObjectsAllocated() const {
- return total_objects_allocated_;
+ uint64_t GetTotalObjectsAllocated() {
+ return GetObjectsAllocated() + total_objects_freed_;
}
// Returns the class of a recently freed object.
@@ -168,11 +161,9 @@
std::pair<const mirror::Object*, mirror::Class*> recent_freed_objects_[kRecentFreeCount];
size_t recent_free_pos_;
- // Approximate number of bytes which have been allocated into the space.
- size_t num_bytes_allocated_;
- size_t num_objects_allocated_;
- size_t total_bytes_allocated_;
- size_t total_objects_allocated_;
+ // Approximate number of bytes and objects which have been deallocated in the space.
+ size_t total_bytes_freed_;
+ size_t total_objects_freed_;
static size_t bitmap_index_;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1cd33ee..e12ee06 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -44,12 +44,13 @@
live_bitmap_.reset(live_bitmap);
}
-static bool GenerateImage(const std::string& image_file_name) {
+static bool GenerateImage(const std::string& image_file_name, std::string* error_msg) {
const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
std::vector<std::string> boot_class_path;
Split(boot_class_path_string, ':', boot_class_path);
if (boot_class_path.empty()) {
- LOG(FATAL) << "Failed to generate image because no boot class path specified";
+ *error_msg = "Failed to generate image because no boot class path specified";
+ return false;
}
std::vector<std::string> arg_vector;
@@ -112,41 +113,57 @@
return false;
} else {
if (pid == -1) {
- PLOG(ERROR) << "fork failed";
+ *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s",
+ image_file_name.c_str(), strerror(errno));
+ return false;
}
// wait for dex2oat to finish
int status;
pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (got_pid != pid) {
- PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+ *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: "
+ "wanted %d, got %d: %s",
+ image_file_name.c_str(), pid, got_pid, strerror(errno));
return false;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << dex2oat << " failed: " << command_line;
+ *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s",
+ image_file_name.c_str(), command_line.c_str());
return false;
}
}
return true;
}
-ImageSpace* ImageSpace::Create(const std::string& original_image_file_name) {
- if (OS::FileExists(original_image_file_name.c_str())) {
+ImageSpace* ImageSpace::Create(const char* original_image_file_name) {
+ if (OS::FileExists(original_image_file_name)) {
// If the /system file exists, it should be up-to-date, don't try to generate
- return space::ImageSpace::Init(original_image_file_name, false);
+ std::string error_msg;
+ ImageSpace* space = ImageSpace::Init(original_image_file_name, false, &error_msg);
+ if (space == nullptr) {
+ LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg;
+ }
+ return space;
}
// If the /system file didn't exist, we need to use one from the dalvik-cache.
// If the cache file exists, try to open, but if it fails, regenerate.
// If it does not exist, generate.
std::string image_file_name(GetDalvikCacheFilenameOrDie(original_image_file_name));
+ std::string error_msg;
if (OS::FileExists(image_file_name.c_str())) {
- space::ImageSpace* image_space = space::ImageSpace::Init(image_file_name, true);
- if (image_space != NULL) {
+ space::ImageSpace* image_space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg);
+ if (image_space != nullptr) {
return image_space;
}
}
- CHECK(GenerateImage(image_file_name)) << "Failed to generate image: " << image_file_name;
- return space::ImageSpace::Init(image_file_name, true);
+ CHECK(GenerateImage(image_file_name, &error_msg))
+ << "Failed to generate image '" << image_file_name << "': " << error_msg;
+ ImageSpace* space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg);
+ if (space == nullptr) {
+ LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg;
+ }
+ return space;
}
void ImageSpace::VerifyImageAllocations() {
@@ -160,8 +177,9 @@
}
}
-ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_oat_file) {
- CHECK(!image_file_name.empty());
+ImageSpace* ImageSpace::Init(const char* image_file_name, bool validate_oat_file,
+ std::string* error_msg) {
+ CHECK(image_file_name != nullptr);
uint64_t start_time = 0;
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -169,16 +187,16 @@
LOG(INFO) << "ImageSpace::Init entering image_file_name=" << image_file_name;
}
- UniquePtr<File> file(OS::OpenFileForReading(image_file_name.c_str()));
+ UniquePtr<File> file(OS::OpenFileForReading(image_file_name));
if (file.get() == NULL) {
- LOG(ERROR) << "Failed to open " << image_file_name;
- return NULL;
+ *error_msg = StringPrintf("Failed to open '%s'", image_file_name);
+ return nullptr;
}
ImageHeader image_header;
bool success = file->ReadFully(&image_header, sizeof(image_header));
if (!success || !image_header.IsValid()) {
- LOG(ERROR) << "Invalid image header " << image_file_name;
- return NULL;
+ *error_msg = StringPrintf("Invalid image header in '%s'", image_file_name);
+ return nullptr;
}
// Note: The image header is part of the image due to mmap page alignment required of offset.
@@ -188,10 +206,12 @@
MAP_PRIVATE | MAP_FIXED,
file->Fd(),
0,
- false));
+ false,
+ image_file_name,
+ error_msg));
if (map.get() == NULL) {
- LOG(ERROR) << "Failed to map " << image_file_name;
- return NULL;
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
CHECK_EQ(image_header.GetImageBegin(), map->Begin());
DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader)));
@@ -199,20 +219,32 @@
UniquePtr<MemMap> image_map(MemMap::MapFileAtAddress(nullptr, image_header.GetImageBitmapSize(),
PROT_READ, MAP_PRIVATE,
file->Fd(), image_header.GetBitmapOffset(),
- false));
- CHECK(image_map.get() != nullptr) << "failed to map image bitmap";
+ false,
+ image_file_name,
+ error_msg));
+ if (image_map.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
+ return nullptr;
+ }
size_t bitmap_index = bitmap_index_.fetch_add(1);
- std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name.c_str(),
+ std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name,
bitmap_index));
UniquePtr<accounting::SpaceBitmap> bitmap(
accounting::SpaceBitmap::CreateFromMemMap(bitmap_name, image_map.release(),
reinterpret_cast<byte*>(map->Begin()),
map->Size()));
- CHECK(bitmap.get() != nullptr) << "could not create " << bitmap_name;
+ if (bitmap.get() == nullptr) {
+ *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
+ return nullptr;
+ }
Runtime* runtime = Runtime::Current();
mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod);
runtime->SetResolutionMethod(down_cast<mirror::ArtMethod*>(resolution_method));
+ mirror::Object* imt_conflict_method = image_header.GetImageRoot(ImageHeader::kImtConflictMethod);
+ runtime->SetImtConflictMethod(down_cast<mirror::ArtMethod*>(imt_conflict_method));
+ mirror::Object* default_imt = image_header.GetImageRoot(ImageHeader::kDefaultImt);
+ runtime->SetDefaultImt(down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(default_imt));
mirror::Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
runtime->SetCalleeSaveMethod(down_cast<mirror::ArtMethod*>(callee_save_method), Runtime::kSaveAll);
@@ -226,15 +258,15 @@
space->VerifyImageAllocations();
}
- space->oat_file_.reset(space->OpenOatFile());
- if (space->oat_file_.get() == NULL) {
- LOG(ERROR) << "Failed to open oat file for image: " << image_file_name;
- return NULL;
+ space->oat_file_.reset(space->OpenOatFile(error_msg));
+ if (space->oat_file_.get() == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
- if (validate_oat_file && !space->ValidateOatFile()) {
- LOG(WARNING) << "Failed to validate oat file for image: " << image_file_name;
- return NULL;
+ if (validate_oat_file && !space->ValidateOatFile(error_msg)) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -244,7 +276,7 @@
return space.release();
}
-OatFile* ImageSpace::OpenOatFile() const {
+OatFile* ImageSpace::OpenOatFile(std::string* error_msg) const {
const Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = GetImageHeader();
// Grab location but don't use Object::AsString as we haven't yet initialized the roots to
@@ -255,45 +287,47 @@
oat_filename += runtime->GetHostPrefix();
oat_filename += oat_location->ToModifiedUtf8();
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
- !Runtime::Current()->IsCompiler());
+ !Runtime::Current()->IsCompiler(), error_msg);
if (oat_file == NULL) {
- LOG(ERROR) << "Failed to open oat file " << oat_filename << " referenced from image.";
- return NULL;
+ *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
+ oat_filename.c_str(), GetName(), error_msg->c_str());
+ return nullptr;
}
uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
uint32_t image_oat_checksum = image_header.GetOatChecksum();
if (oat_checksum != image_oat_checksum) {
- LOG(ERROR) << "Failed to match oat file checksum " << std::hex << oat_checksum
- << " to expected oat checksum " << std::hex << image_oat_checksum
- << " in image";
- return NULL;
+ *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x"
+ " in image %s", oat_checksum, image_oat_checksum, GetName());
+ return nullptr;
}
return oat_file;
}
-bool ImageSpace::ValidateOatFile() const {
+bool ImageSpace::ValidateOatFile(std::string* error_msg) const {
CHECK(oat_file_.get() != NULL);
for (const OatFile::OatDexFile* oat_dex_file : oat_file_->GetOatDexFiles()) {
const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
uint32_t dex_file_location_checksum;
- if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum)) {
- LOG(WARNING) << "ValidateOatFile could not find checksum for " << dex_file_location;
+ if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
+ *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
+ "%s", dex_file_location.c_str(), GetName(), error_msg->c_str());
return false;
}
if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- LOG(WARNING) << "ValidateOatFile found checksum mismatch between oat file "
- << oat_file_->GetLocation() << " and dex file " << dex_file_location
- << " (" << oat_dex_file->GetDexFileLocationChecksum() << " != "
- << dex_file_location_checksum << ")";
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
+ "dex file '%s' (0x%x != 0x%x)",
+ oat_file_->GetLocation().c_str(), dex_file_location.c_str(),
+ oat_dex_file->GetDexFileLocationChecksum(),
+ dex_file_location_checksum);
return false;
}
}
return true;
}
-OatFile& ImageSpace::ReleaseOatFile() {
+OatFile* ImageSpace::ReleaseOatFile() {
CHECK(oat_file_.get() != NULL);
- return *oat_file_.release();
+ return oat_file_.release();
}
void ImageSpace::Dump(std::ostream& os) const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 381a98e..78a83c9 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -45,12 +45,11 @@
// creation of the alloc space. The ReleaseOatFile will later be
// used to transfer ownership of the OatFile to the ClassLinker when
// it is initialized.
- static ImageSpace* Create(const std::string& image)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static ImageSpace* Create(const char* image) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Releases the OatFile from the ImageSpace so it can be transfer to
// the caller, presumably the ClassLinker.
- OatFile& ReleaseOatFile()
+ OatFile* ReleaseOatFile()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VerifyImageAllocations()
@@ -84,13 +83,13 @@
// image's OatFile is up-to-date relative to its DexFile
// inputs. Otherwise (for /data), validate the inputs and generate
// the OatFile in /data/dalvik-cache if necessary.
- static ImageSpace* Init(const std::string& image, bool validate_oat_file)
+ static ImageSpace* Init(const char* image, bool validate_oat_file, std::string* error_msg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- OatFile* OpenOatFile() const
+ OatFile* OpenOatFile(std::string* error_msg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool ValidateOatFile() const
+ bool ValidateOatFile(std::string* error_msg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
friend class Space;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index a174c0a..ab6e42b 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -17,11 +17,12 @@
#include "large_object_space.h"
#include "base/logging.h"
+#include "base/mutex-inl.h"
#include "base/stl_util.h"
#include "UniquePtr.h"
#include "image.h"
#include "os.h"
-#include "thread.h"
+#include "thread-inl.h"
#include "utils.h"
namespace art {
@@ -55,10 +56,13 @@
return new LargeObjectMapSpace(name);
}
-mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) {
+mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes,
+ size_t* bytes_allocated) {
+ std::string error_msg;
MemMap* mem_map = MemMap::MapAnonymous("large object space allocation", NULL, num_bytes,
- PROT_READ | PROT_WRITE);
- if (mem_map == NULL) {
+ PROT_READ | PROT_WRITE, &error_msg);
+ if (UNLIKELY(mem_map == NULL)) {
+ LOG(WARNING) << "Large object allocation failed: " << error_msg;
return NULL;
}
MutexLock mu(self, lock_);
@@ -128,9 +132,10 @@
FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) {
CHECK_EQ(size % kAlignment, 0U);
+ std::string error_msg;
MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size,
- PROT_READ | PROT_WRITE);
- CHECK(mem_map != NULL) << "Failed to allocate large object space mem map";
+ PROT_READ | PROT_WRITE, &error_msg);
+ CHECK(mem_map != NULL) << "Failed to allocate large object space mem map: " << error_msg;
return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End());
}
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index a703e86..ef889d4 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -41,19 +41,19 @@
virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0;
virtual ~LargeObjectSpace() {}
- uint64_t GetBytesAllocated() const {
+ uint64_t GetBytesAllocated() {
return num_bytes_allocated_;
}
- uint64_t GetObjectsAllocated() const {
+ uint64_t GetObjectsAllocated() {
return num_objects_allocated_;
}
- uint64_t GetTotalBytesAllocated() const {
+ uint64_t GetTotalBytesAllocated() {
return total_bytes_allocated_;
}
- uint64_t GetTotalObjectsAllocated() const {
+ uint64_t GetTotalObjectsAllocated() {
return total_objects_allocated_;
}
@@ -96,9 +96,9 @@
// Used to ensure mutual exclusion when the allocation spaces data structures are being modified.
mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<mirror::Object*,
- accounting::GCAllocator<mirror::Object*> > large_objects_ GUARDED_BY(lock_);
+ accounting::GcAllocator<mirror::Object*> > large_objects_ GUARDED_BY(lock_);
typedef SafeMap<mirror::Object*, MemMap*, std::less<mirror::Object*>,
- accounting::GCAllocator<std::pair<const mirror::Object*, MemMap*> > > MemMaps;
+ accounting::GcAllocator<std::pair<const mirror::Object*, MemMap*> > > MemMaps;
MemMaps mem_maps_ GUARDED_BY(lock_);
};
@@ -217,7 +217,7 @@
AllocationHeader* GetAllocationHeader(const mirror::Object* obj);
typedef std::set<AllocationHeader*, AllocationHeader::SortByPrevFree,
- accounting::GCAllocator<AllocationHeader*> > FreeBlocks;
+ accounting::GcAllocator<AllocationHeader*> > FreeBlocks;
byte* const begin_;
byte* const end_;
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 68df563..6dd7952 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -146,13 +146,13 @@
class AllocSpace {
public:
// Number of bytes currently allocated.
- virtual uint64_t GetBytesAllocated() const = 0;
+ virtual uint64_t GetBytesAllocated() = 0;
// Number of objects currently allocated.
- virtual uint64_t GetObjectsAllocated() const = 0;
+ virtual uint64_t GetObjectsAllocated() = 0;
// Number of bytes allocated since the space was created.
- virtual uint64_t GetTotalBytesAllocated() const = 0;
+ virtual uint64_t GetTotalBytesAllocated() = 0;
// Number of objects allocated since the space was created.
- virtual uint64_t GetTotalObjectsAllocated() const = 0;
+ virtual uint64_t GetTotalObjectsAllocated() = 0;
// Allocate num_bytes without allowing growth. If the allocation
// succeeds, the output parameter bytes_allocated will be set to the
diff --git a/runtime/globals.h b/runtime/globals.h
index c397494..31574ff 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -30,30 +30,30 @@
const size_t MB = KB * KB;
const size_t GB = KB * KB * KB;
-const int kWordSize = sizeof(word);
-const int kPointerSize = sizeof(void*);
+const size_t kWordSize = sizeof(word);
+const size_t kPointerSize = sizeof(void*);
-const int kBitsPerByte = 8;
-const int kBitsPerByteLog2 = 3;
+const size_t kBitsPerByte = 8;
+const size_t kBitsPerByteLog2 = 3;
const int kBitsPerWord = kWordSize * kBitsPerByte;
-const int kWordHighBitMask = 1 << (kBitsPerWord - 1);
+const size_t kWordHighBitMask = 1 << (kBitsPerWord - 1);
// Required stack alignment
-const int kStackAlignment = 16;
+const size_t kStackAlignment = 16;
// Required object alignment
-const int kObjectAlignment = 8;
+const size_t kObjectAlignment = 8;
// ARM instruction alignment. ARM processors require code to be 4-byte aligned,
// but ARM ELF requires 8..
-const int kArmAlignment = 8;
+const size_t kArmAlignment = 8;
// MIPS instruction alignment. MIPS processors require code to be 4-byte aligned.
// TODO: Can this be 4?
-const int kMipsAlignment = 8;
+const size_t kMipsAlignment = 8;
// X86 instruction alignment. This is the recommended alignment for maximum performance.
-const int kX86Alignment = 16;
+const size_t kX86Alignment = 16;
// System page size. We check this against sysconf(_SC_PAGE_SIZE) at runtime, but use a simple
// compile-time constant so the compiler can generate better code.
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 0b2e741..67620a0 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -484,11 +484,11 @@
}
private:
- static void RootVisitor(const mirror::Object* obj, void* arg)
+ static mirror::Object* RootVisitor(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK(arg != NULL);
- Hprof* hprof = reinterpret_cast<Hprof*>(arg);
- hprof->VisitRoot(obj);
+ DCHECK(arg != NULL);
+ reinterpret_cast<Hprof*>(arg)->VisitRoot(obj);
+ return obj;
}
static void HeapBitmapCallback(mirror::Object* obj, void* arg)
diff --git a/runtime/image.h b/runtime/image.h
index 2cb468f..246f106 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -90,6 +90,8 @@
enum ImageRoot {
kResolutionMethod,
+ kImtConflictMethod,
+ kDefaultImt,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 8af4d7e..2bd8353 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -40,7 +40,7 @@
CHECK_LE(initialCount, maxCount);
CHECK_NE(desiredKind, kSirtOrInvalid);
- table_ = reinterpret_cast<const mirror::Object**>(malloc(initialCount * sizeof(const mirror::Object*)));
+ table_ = reinterpret_cast<mirror::Object**>(malloc(initialCount * sizeof(const mirror::Object*)));
CHECK(table_ != NULL);
memset(table_, 0xd1, initialCount * sizeof(const mirror::Object*));
@@ -75,7 +75,7 @@
return true;
}
-IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* obj) {
+IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) {
IRTSegmentState prevState;
prevState.all = cookie;
size_t topIndex = segment_state_.parts.topIndex;
@@ -101,7 +101,7 @@
}
DCHECK_GT(newSize, alloc_entries_);
- table_ = reinterpret_cast<const mirror::Object**>(realloc(table_, newSize * sizeof(const mirror::Object*)));
+ table_ = reinterpret_cast<mirror::Object**>(realloc(table_, newSize * sizeof(mirror::Object*)));
slot_data_ = reinterpret_cast<IndirectRefSlot*>(realloc(slot_data_,
newSize * sizeof(IndirectRefSlot)));
if (table_ == NULL || slot_data_ == NULL) {
@@ -126,7 +126,7 @@
if (numHoles > 0) {
DCHECK_GT(topIndex, 1U);
// Find the first hole; likely to be near the end of the list.
- const mirror::Object** pScan = &table_[topIndex - 1];
+ mirror::Object** pScan = &table_[topIndex - 1];
DCHECK(*pScan != NULL);
while (*--pScan != NULL) {
DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
@@ -194,7 +194,8 @@
return true;
}
-static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex, const mirror::Object** table) {
+static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex,
+ mirror::Object** table) {
for (int i = bottomIndex; i < topIndex; ++i) {
if (table[i] == direct_pointer) {
return i;
@@ -310,13 +311,14 @@
void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) {
for (auto ref : *this) {
- visitor(*ref, arg);
+ *ref = visitor(const_cast<mirror::Object*>(*ref), arg);
+ DCHECK(*ref != nullptr);
}
}
void IndirectReferenceTable::Dump(std::ostream& os) const {
os << kind_ << " table dump:\n";
- std::vector<const mirror::Object*> entries(table_, table_ + Capacity());
+ ReferenceTable::Table entries(table_, table_ + Capacity());
// Remove NULLs.
for (int i = entries.size() - 1; i >= 0; --i) {
if (entries[i] == NULL) {
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 26f53db..51b238c 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -206,7 +206,7 @@
class IrtIterator {
public:
- explicit IrtIterator(const mirror::Object** table, size_t i, size_t capacity)
+ explicit IrtIterator(mirror::Object** table, size_t i, size_t capacity)
: table_(table), i_(i), capacity_(capacity) {
SkipNullsAndTombstones();
}
@@ -217,7 +217,7 @@
return *this;
}
- const mirror::Object** operator*() {
+ mirror::Object** operator*() {
return &table_[i_];
}
@@ -233,7 +233,7 @@
}
}
- const mirror::Object** table_;
+ mirror::Object** table_;
size_t i_;
size_t capacity_;
};
@@ -258,7 +258,7 @@
* Returns NULL if the table is full (max entries reached, or alloc
* failed during expansion).
*/
- IndirectRef Add(uint32_t cookie, const mirror::Object* obj)
+ IndirectRef Add(uint32_t cookie, mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -266,7 +266,7 @@
*
* Returns kInvalidIndirectRefObject if iref is invalid.
*/
- const mirror::Object* Get(IndirectRef iref) const {
+ mirror::Object* Get(IndirectRef iref) const {
if (!GetChecked(iref)) {
return kInvalidIndirectRefObject;
}
@@ -363,7 +363,7 @@
IRTSegmentState segment_state_;
/* bottom of the stack */
- const mirror::Object** table_;
+ mirror::Object** table_;
/* bit mask, ORed into all irefs */
IndirectRefKind kind_;
/* extended debugging info */
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index bd2890c..b6c6cb4 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -17,6 +17,7 @@
#include "common_test.h"
#include "indirect_reference_table.h"
+#include "mirror/object-inl.h"
namespace art {
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index 2217f7f..aee7447 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -18,6 +18,9 @@
#define ART_RUNTIME_INSTRUCTION_SET_H_
#include <iosfwd>
+#include <string>
+
+#include "base/macros.h"
namespace art {
@@ -29,6 +32,53 @@
kMips
};
+enum InstructionFeatures {
+ kHwDiv = 1 // Supports hardware divide.
+};
+
+// This is a bitmask of supported features per architecture.
+class PACKED(4) InstructionSetFeatures {
+ public:
+ InstructionSetFeatures() : mask_(0) {}
+ explicit InstructionSetFeatures(uint32_t mask) : mask_(mask) {}
+
+ bool HasDivideInstruction() const {
+ return (mask_ & kHwDiv) != 0;
+ }
+
+ void SetHasDivideInstruction(bool v) {
+ mask_ = (mask_ & ~kHwDiv) | (v ? kHwDiv : 0);
+ }
+
+ std::string GetFeatureString() const {
+ std::string result;
+ if ((mask_ & kHwDiv) != 0) {
+ result += "div";
+ }
+ if (result.size() == 0) {
+ result = "none";
+ }
+ return result;
+ }
+
+ uint32_t get_mask() const {
+ return mask_;
+ }
+
+ // Other features in here.
+
+ bool operator==(const InstructionSetFeatures &peer) const {
+ return mask_ == peer.mask_;
+ }
+
+ bool operator!=(const InstructionSetFeatures &peer) const {
+ return mask_ != peer.mask_;
+ }
+
+ private:
+ uint32_t mask_;
+};
+
std::ostream& operator<<(std::ostream& os, const InstructionSet& rhs);
} // namespace art
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 6caad01..8cf486f 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -41,6 +41,11 @@
namespace art {
namespace instrumentation {
+// Do we want to deoptimize for method entry and exit listeners or just try to intercept
+// invocations? Deoptimization forces all code to run in the interpreter and considerably hurts the
+// application's performance.
+static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = false;
+
static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
@@ -56,10 +61,10 @@
bool is_initialized = klass->IsInitialized();
for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
mirror::ArtMethod* method = klass->GetDirectMethod(i);
- if (!method->IsAbstract()) {
+ if (!method->IsAbstract() && !method->IsProxyMethod()) {
const void* new_code;
if (uninstall) {
- if (forced_interpret_only_ && !method->IsNative() && !method->IsProxyMethod()) {
+ if (forced_interpret_only_ && !method->IsNative()) {
new_code = GetCompiledCodeToInterpreterBridge();
} else if (is_initialized || !method->IsStatic() || method->IsConstructor()) {
new_code = class_linker->GetOatCodeFor(method);
@@ -78,10 +83,10 @@
}
for (size_t i = 0; i < klass->NumVirtualMethods(); i++) {
mirror::ArtMethod* method = klass->GetVirtualMethod(i);
- if (!method->IsAbstract()) {
+ if (!method->IsAbstract() && !method->IsProxyMethod()) {
const void* new_code;
if (uninstall) {
- if (forced_interpret_only_ && !method->IsNative() && !method->IsProxyMethod()) {
+ if (forced_interpret_only_ && !method->IsNative()) {
new_code = GetCompiledCodeToInterpreterBridge();
} else {
new_code = class_linker->GetOatCodeFor(method);
@@ -264,12 +269,14 @@
bool require_interpreter = false;
if ((events & kMethodEntered) != 0) {
method_entry_listeners_.push_back(listener);
- require_entry_exit_stubs = true;
+ require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners;
have_method_entry_listeners_ = true;
}
if ((events & kMethodExited) != 0) {
method_exit_listeners_.push_back(listener);
- require_entry_exit_stubs = true;
+ require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners;
have_method_exit_listeners_ = true;
}
if ((events & kMethodUnwind) != 0) {
@@ -286,6 +293,7 @@
have_exception_caught_listeners_ = true;
}
ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+ UpdateInterpreterHandlerTable();
}
void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
@@ -300,7 +308,10 @@
method_entry_listeners_.remove(listener);
}
have_method_entry_listeners_ = method_entry_listeners_.size() > 0;
- require_entry_exit_stubs |= have_method_entry_listeners_;
+ require_entry_exit_stubs |= have_method_entry_listeners_ &&
+ !kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_interpreter = have_method_entry_listeners_ &&
+ kDeoptimizeForAccurateMethodEntryExitListeners;
}
if ((events & kMethodExited) != 0) {
bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(),
@@ -309,7 +320,10 @@
method_exit_listeners_.remove(listener);
}
have_method_exit_listeners_ = method_exit_listeners_.size() > 0;
- require_entry_exit_stubs |= have_method_exit_listeners_;
+ require_entry_exit_stubs |= have_method_exit_listeners_ &&
+ !kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_interpreter = have_method_exit_listeners_ &&
+ kDeoptimizeForAccurateMethodEntryExitListeners;
}
if ((events & kMethodUnwind) != 0) {
method_unwind_listeners_.remove(listener);
@@ -328,6 +342,7 @@
have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0;
}
ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+ UpdateInterpreterHandlerTable();
}
void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) {
@@ -455,7 +470,7 @@
void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
mirror::ArtMethod* catch_method,
uint32_t catch_dex_pc,
- mirror::Throwable* exception_object) {
+ mirror::Throwable* exception_object) const {
if (have_exception_caught_listeners_) {
DCHECK_EQ(thread->GetException(NULL), exception_object);
thread->ClearException();
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 6c80b41..7a0aaf7 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -38,6 +38,14 @@
const bool kVerboseInstrumentation = false;
+// Interpreter handler tables.
+enum InterpreterHandlerTable {
+ kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation.
+ kAlternativeHandlerTable = 1, // Alternative handler table: suspend check and/or instrumentation
+ // enabled.
+ kNumHandlerTables
+};
+
// Instrumentation event listener API. Registered listeners will get the appropriate call back for
// the events they are listening for. The call backs supply the thread, method and dex_pc the event
// occurred upon. The thread may or may not be Thread::Current().
@@ -95,7 +103,8 @@
interpret_only_(false), forced_interpret_only_(false),
have_method_entry_listeners_(false), have_method_exit_listeners_(false),
have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
- have_exception_caught_listeners_(false) {}
+ have_exception_caught_listeners_(false),
+ interpreter_handler_table_(kMainHandlerTable) {}
// Add a listener to be notified of the masked together sent of instrumentation events. This
// suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy
@@ -110,6 +119,10 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
+ InterpreterHandlerTable GetInterpreterHandlerTable() const {
+ return interpreter_handler_table_;
+ }
+
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const;
@@ -149,6 +162,11 @@
return have_dex_pc_listeners_;
}
+ bool IsActive() const {
+ return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
+ have_exception_caught_listeners_ || have_method_unwind_listeners_;
+ }
+
// Inform listeners that a method has been entered. A dex PC is provided as we may install
// listeners into executing code and get method enter events for methods already on the stack.
void MethodEnterEvent(Thread* thread, mirror::Object* this_object,
@@ -186,7 +204,7 @@
// Inform listeners that an exception was caught.
void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
mirror::ArtMethod* catch_method, uint32_t catch_dex_pc,
- mirror::Throwable* exception_object)
+ mirror::Throwable* exception_object) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Called when an instrumented method is entered. The intended link register (lr) is saved so
@@ -215,6 +233,10 @@
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
+ void UpdateInterpreterHandlerTable() {
+ interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable;
+ }
+
void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
const mirror::ArtMethod* method, uint32_t dex_pc) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -267,6 +289,9 @@
std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ // Current interpreter handler table. This is updated each time the thread state flags are modified.
+ InterpreterHandlerTable interpreter_handler_table_;
+
DISALLOW_COPY_AND_ASSIGN(Instrumentation);
};
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 2072979..8f9e072 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -47,15 +47,16 @@
bool only_dirty, bool clean_dirty) {
MutexLock mu(Thread::Current(), intern_table_lock_);
if (!only_dirty || is_dirty_) {
- for (const auto& strong_intern : strong_interns_) {
- visitor(strong_intern.second, arg);
+ for (auto& strong_intern : strong_interns_) {
+ strong_intern.second = reinterpret_cast<mirror::String*>(visitor(strong_intern.second, arg));
+ DCHECK(strong_intern.second != nullptr);
}
+
if (clean_dirty) {
is_dirty_ = false;
}
}
- // Note: we deliberately don't visit the weak_interns_ table and the immutable
- // image roots.
+ // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
}
mirror::String* InternTable::Lookup(Table& table, mirror::String* s,
@@ -216,14 +217,16 @@
return found == s;
}
-void InternTable::SweepInternTableWeaks(IsMarkedTester is_marked, void* arg) {
+void InternTable::SweepInternTableWeaks(RootVisitor visitor, void* arg) {
MutexLock mu(Thread::Current(), intern_table_lock_);
- // TODO: std::remove_if + lambda.
for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
mirror::Object* object = it->second;
- if (!is_marked(object, arg)) {
+ mirror::Object* new_object = visitor(object, arg);
+ if (new_object == nullptr) {
+ // TODO: use it = weak_interns_.erase(it) when we get a c++11 stl.
weak_interns_.erase(it++);
} else {
+ it->second = down_cast<mirror::String*>(new_object);
++it;
}
}
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index e68af90..eec63c8 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -55,8 +55,7 @@
// Interns a potentially new string in the 'weak' table. (See above.)
mirror::String* InternWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepInternTableWeaks(IsMarkedTester is_marked, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ void SweepInternTableWeaks(RootVisitor visitor, void* arg);
bool ContainsWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index d79d2c4..aa2502d 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -81,8 +81,11 @@
mutable std::vector<const mirror::String*> expected_;
};
-bool IsMarked(const mirror::Object* object, void* arg) {
- return reinterpret_cast<TestPredicate*>(arg)->IsMarked(object);
+mirror::Object* IsMarkedSweepingVisitor(mirror::Object* object, void* arg) {
+ if (reinterpret_cast<TestPredicate*>(arg)->IsMarked(object)) {
+ return object;
+ }
+ return nullptr;
}
TEST_F(InternTableTest, SweepInternTableWeaks) {
@@ -105,7 +108,7 @@
p.Expect(s1.get());
{
ReaderMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
- t.SweepInternTableWeaks(IsMarked, &p);
+ t.SweepInternTableWeaks(IsMarkedSweepingVisitor, &p);
}
EXPECT_EQ(2U, t.Size());
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 8cd2ac8..d7555dd 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -14,153 +14,11 @@
* limitations under the License.
*/
-#include "interpreter.h"
-
-#include <math.h>
-
-#include "base/logging.h"
-#include "class_linker-inl.h"
-#include "common_throws.h"
-#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
-#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "gc/accounting/card_table-inl.h"
-#include "invoke_arg_array_builder.h"
-#include "nth_caller_visitor.h"
-#include "mirror/art_field-inl.h"
-#include "mirror/art_method.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class.h"
-#include "mirror/class-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "object_utils.h"
-#include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
-#include "well_known_classes.h"
-
-using ::art::mirror::ArtField;
-using ::art::mirror::ArtMethod;
-using ::art::mirror::Array;
-using ::art::mirror::BooleanArray;
-using ::art::mirror::ByteArray;
-using ::art::mirror::CharArray;
-using ::art::mirror::Class;
-using ::art::mirror::ClassLoader;
-using ::art::mirror::IntArray;
-using ::art::mirror::LongArray;
-using ::art::mirror::Object;
-using ::art::mirror::ObjectArray;
-using ::art::mirror::ShortArray;
-using ::art::mirror::String;
-using ::art::mirror::Throwable;
+#include "interpreter_common.h"
namespace art {
-
namespace interpreter {
-static const int32_t kMaxInt = std::numeric_limits<int32_t>::max();
-static const int32_t kMinInt = std::numeric_limits<int32_t>::min();
-static const int64_t kMaxLong = std::numeric_limits<int64_t>::max();
-static const int64_t kMinLong = std::numeric_limits<int64_t>::min();
-
-static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
- JValue* result, size_t arg_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // In a runtime that's not started we intercept certain methods to avoid complicated dependency
- // problems in core libraries.
- std::string name(PrettyMethod(shadow_frame->GetMethod()));
- if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
- std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
- ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
- Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
- class_loader);
- CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
- << PrettyDescriptor(descriptor);
- result->SetL(found);
- } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
- Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
- ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
- CHECK(c != NULL);
- SirtRef<Object> obj(self, klass->AllocObject(self));
- CHECK(obj.get() != NULL);
- EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL);
- result->SetL(obj.get());
- } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
- // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
- // going the reflective Dex way.
- Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
- String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
- ArtField* found = NULL;
- FieldHelper fh;
- ObjectArray<ArtField>* fields = klass->GetIFields();
- for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
- ArtField* f = fields->Get(i);
- fh.ChangeField(f);
- if (name->Equals(fh.GetName())) {
- found = f;
- }
- }
- if (found == NULL) {
- fields = klass->GetSFields();
- for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
- ArtField* f = fields->Get(i);
- fh.ChangeField(f);
- if (name->Equals(fh.GetName())) {
- found = f;
- }
- }
- }
- CHECK(found != NULL)
- << "Failed to find field in Class.getDeclaredField in un-started runtime. name="
- << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
- // TODO: getDeclaredField calls GetType once the field is found to ensure a
- // NoClassDefFoundError is thrown if the field's type cannot be resolved.
- Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass();
- SirtRef<Object> field(self, jlr_Field->AllocObject(self));
- CHECK(field.get() != NULL);
- ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("<init>", "(Ljava/lang/reflect/ArtField;)V");
- uint32_t args[1];
- args[0] = reinterpret_cast<uint32_t>(found);
- EnterInterpreterFromInvoke(self, c, field.get(), args, NULL);
- result->SetL(field.get());
- } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" ||
- name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") {
- // Special case array copying without initializing System.
- Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
- jint srcPos = shadow_frame->GetVReg(arg_offset + 1);
- jint dstPos = shadow_frame->GetVReg(arg_offset + 3);
- jint length = shadow_frame->GetVReg(arg_offset + 4);
- if (!ctype->IsPrimitive()) {
- ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>();
- ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>();
- for (jint i = 0; i < length; ++i) {
- dst->Set(dstPos + i, src->Get(srcPos + i));
- }
- } else if (ctype->IsPrimitiveChar()) {
- CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray();
- CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray();
- for (jint i = 0; i < length; ++i) {
- dst->Set(dstPos + i, src->Get(srcPos + i));
- }
- } else if (ctype->IsPrimitiveInt()) {
- IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray();
- IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray();
- for (jint i = 0; i < length; ++i) {
- dst->Set(dstPos + i, src->Get(srcPos + i));
- }
- } else {
- UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype);
- }
- } else {
- // Not special, continue with regular interpreter execution.
- artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result);
- }
-}
-
// Hand select a number of methods to be run in a not yet started runtime without using JNI.
static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
Object* receiver, uint32_t* args, JValue* result)
@@ -224,7 +82,7 @@
}
}
-static void InterpreterJni(Thread* self, ArtMethod* method, StringPiece shorty,
+static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& shorty,
Object* receiver, uint32_t* args, JValue* result)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// TODO: The following enters JNI code using a typedef-ed function rather than the JNI compiler,
@@ -406,2703 +264,12 @@
}
}
-static void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
- ref->MonitorEnter(self);
-}
+enum InterpreterImplKind {
+ kSwitchImpl, // switch-based interpreter implementation.
+ kComputedGotoImplKind // computed-goto-based interpreter implementation.
+};
-static void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
- ref->MonitorExit(self);
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<InvokeType type, bool is_range, bool do_access_check>
-static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result) NO_THREAD_SAFETY_ANALYSIS;
-
-template<InvokeType type, bool is_range, bool do_access_check>
-static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result) {
- bool do_assignability_check = do_access_check;
- uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC);
- ArtMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self,
- do_access_check, type);
- if (UNLIKELY(method == NULL)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- } else if (UNLIKELY(method->IsAbstract())) {
- ThrowAbstractMethodError(method);
- result->SetJ(0);
- return false;
- }
-
- MethodHelper mh(method);
- const DexFile::CodeItem* code_item = mh.GetCodeItem();
- uint16_t num_regs;
- uint16_t num_ins;
- if (LIKELY(code_item != NULL)) {
- num_regs = code_item->registers_size_;
- num_ins = code_item->ins_size_;
- } else {
- DCHECK(method->IsNative() || method->IsProxyMethod());
- num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty());
- if (!method->IsStatic()) {
- num_regs++;
- num_ins++;
- }
- }
-
- void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
- ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory));
- size_t cur_reg = num_regs - num_ins;
- if (receiver != NULL) {
- new_shadow_frame->SetVRegReference(cur_reg, receiver);
- ++cur_reg;
- }
-
- const DexFile::TypeList* params;
- if (do_assignability_check) {
- params = mh.GetParameterTypeList();
- }
- size_t arg_offset = (receiver == NULL) ? 0 : 1;
- const char* shorty = mh.GetShorty();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetArgs(arg);
- }
- for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
- DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
- size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
- switch (shorty[shorty_pos + 1]) {
- case 'L': {
- Object* o = shadow_frame.GetVRegReference(arg_pos);
- if (do_assignability_check && o != NULL) {
- Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_);
- if (arg_type == NULL) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- if (!o->VerifierInstanceOf(arg_type)) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
- mh.GetName(), shorty_pos,
- ClassHelper(o->GetClass()).GetDescriptor(),
- ClassHelper(arg_type).GetDescriptor());
- return false;
- }
- }
- new_shadow_frame->SetVRegReference(cur_reg, o);
- break;
- }
- case 'J': case 'D': {
- uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
- static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
- new_shadow_frame->SetVRegLong(cur_reg, wide_value);
- cur_reg++;
- arg_offset++;
- break;
- }
- default:
- new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
- break;
- }
- }
-
- if (LIKELY(Runtime::Current()->IsStarted())) {
- (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
- } else {
- UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
- }
- return !self->IsExceptionPending();
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<bool is_range>
-static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result)
- NO_THREAD_SAFETY_ANALYSIS;
-
-template<bool is_range>
-static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result) {
- uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- Object* receiver = shadow_frame.GetVRegReference(vregC);
- if (UNLIKELY(receiver == NULL)) {
- // We lost the reference to the method index so we cannot get a more
- // precised exception message.
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- return false;
- }
- uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- // TODO: use ObjectArray<T>::GetWithoutChecks ?
- ArtMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx);
- if (UNLIKELY(method == NULL)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- } else if (UNLIKELY(method->IsAbstract())) {
- ThrowAbstractMethodError(method);
- result->SetJ(0);
- return false;
- }
-
- MethodHelper mh(method);
- const DexFile::CodeItem* code_item = mh.GetCodeItem();
- uint16_t num_regs;
- uint16_t num_ins;
- if (code_item != NULL) {
- num_regs = code_item->registers_size_;
- num_ins = code_item->ins_size_;
- } else {
- DCHECK(method->IsNative() || method->IsProxyMethod());
- num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty());
- if (!method->IsStatic()) {
- num_regs++;
- num_ins++;
- }
- }
-
- void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
- ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
- method, 0, memory));
- size_t cur_reg = num_regs - num_ins;
- if (receiver != NULL) {
- new_shadow_frame->SetVRegReference(cur_reg, receiver);
- ++cur_reg;
- }
-
- size_t arg_offset = (receiver == NULL) ? 0 : 1;
- const char* shorty = mh.GetShorty();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetArgs(arg);
- }
- for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
- DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
- size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
- switch (shorty[shorty_pos + 1]) {
- case 'L': {
- Object* o = shadow_frame.GetVRegReference(arg_pos);
- new_shadow_frame->SetVRegReference(cur_reg, o);
- break;
- }
- case 'J': case 'D': {
- uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
- static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
- new_shadow_frame->SetVRegLong(cur_reg, wide_value);
- cur_reg++;
- arg_offset++;
- break;
- }
- default:
- new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
- break;
- }
- }
-
- if (LIKELY(Runtime::Current()->IsStarted())) {
- (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
- } else {
- UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
- }
- return !self->IsExceptionPending();
-}
-
-// We use template functions to optimize compiler inlining process. Otherwise,
-// some parts of the code (like a switch statement) which depend on a constant
-// parameter would not be inlined while it should be. These constant parameters
-// are now part of the template arguments.
-// Note these template functions are static and inlined so they should not be
-// part of the final object file.
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst) {
- bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self,
- find_type, Primitive::FieldSize(field_type),
- do_access_check);
- if (UNLIKELY(f == NULL)) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- Object* obj;
- if (is_static) {
- obj = f->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true);
- return false;
- }
- }
- uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimBoolean:
- shadow_frame.SetVReg(vregA, f->GetBoolean(obj));
- break;
- case Primitive::kPrimByte:
- shadow_frame.SetVReg(vregA, f->GetByte(obj));
- break;
- case Primitive::kPrimChar:
- shadow_frame.SetVReg(vregA, f->GetChar(obj));
- break;
- case Primitive::kPrimShort:
- shadow_frame.SetVReg(vregA, f->GetShort(obj));
- break;
- case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, f->GetInt(obj));
- break;
- case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, f->GetLong(obj));
- break;
- case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, f->GetObject(obj));
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<Primitive::Type field_type>
-static bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<Primitive::Type field_type>
-static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst) {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- // We lost the reference to the field index so we cannot get a more
- // precised exception message.
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- return false;
- }
- MemberOffset field_offset(inst->VRegC_22c());
- const bool is_volatile = false; // iget-x-quick only on non volatile fields.
- const uint32_t vregA = inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
- break;
- case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
- break;
- case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
- const Instruction* inst) {
- bool do_assignability_check = do_access_check;
- bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self,
- find_type, Primitive::FieldSize(field_type),
- do_access_check);
- if (UNLIKELY(f == NULL)) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- Object* obj;
- if (is_static) {
- obj = f->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(),
- f, false);
- return false;
- }
- }
- uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimBoolean:
- f->SetBoolean(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimByte:
- f->SetByte(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimChar:
- f->SetChar(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimShort:
- f->SetShort(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimInt:
- f->SetInt(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimLong:
- f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
- break;
- case Primitive::kPrimNot: {
- Object* reg = shadow_frame.GetVRegReference(vregA);
- if (do_assignability_check && reg != NULL) {
- Class* field_class = FieldHelper(f).GetType();
- if (!reg->VerifierInstanceOf(field_class)) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Put '%s' that is not instance of field '%s' in '%s'",
- ClassHelper(reg->GetClass()).GetDescriptor(),
- ClassHelper(field_class).GetDescriptor(),
- ClassHelper(f->GetDeclaringClass()).GetDescriptor());
- return false;
- }
- }
- f->SetObj(obj, reg);
- break;
- }
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<Primitive::Type field_type>
-static bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<Primitive::Type field_type>
-static inline bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst) {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- // We lost the reference to the field index so we cannot get a more
- // precised exception message.
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- return false;
- }
- MemberOffset field_offset(inst->VRegC_22c());
- const bool is_volatile = false; // iput-x-quick only on non volatile fields.
- const uint32_t vregA = inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimInt:
- obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
- break;
- case Primitive::kPrimLong:
- obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
- break;
- case Primitive::kPrimNot:
- obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Class* java_lang_string_class = String::GetJavaLangString();
- if (UNLIKELY(!java_lang_string_class->IsInitialized())) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class,
- true, true))) {
- DCHECK(self->IsExceptionPending());
- return NULL;
- }
- }
- return mh.ResolveString(string_idx);
-}
-
-static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg,
- int32_t dividend, int32_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
- shadow_frame.SetVReg(result_reg, kMinInt);
- } else {
- shadow_frame.SetVReg(result_reg, dividend / divisor);
- }
- return true;
-}
-
-static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg,
- int32_t dividend, int32_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
- shadow_frame.SetVReg(result_reg, 0);
- } else {
- shadow_frame.SetVReg(result_reg, dividend % divisor);
- }
- return true;
-}
-
-static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg,
- int64_t dividend, int64_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
- shadow_frame.SetVRegLong(result_reg, kMinLong);
- } else {
- shadow_frame.SetVRegLong(result_reg, dividend / divisor);
- }
- return true;
-}
-
-static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg,
- int64_t dividend, int64_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
- shadow_frame.SetVRegLong(result_reg, 0);
- } else {
- shadow_frame.SetVRegLong(result_reg, dividend % divisor);
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-// Returns true on success, otherwise throws an exception and returns false.
-template <bool is_range, bool do_access_check>
-static bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
- Thread* self, JValue* result)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template <bool is_range, bool do_access_check>
-static inline bool DoFilledNewArray(const Instruction* inst,
- const ShadowFrame& shadow_frame,
- Thread* self, JValue* result) {
- DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
- inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE);
- const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
- if (!is_range) {
- // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments.
- CHECK_LE(length, 5);
- }
- if (UNLIKELY(length < 0)) {
- ThrowNegativeArraySizeException(length);
- return false;
- }
- uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
- Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(arrayClass == NULL)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
- CHECK(arrayClass->IsArrayClass());
- Class* componentClass = arrayClass->GetComponentType();
- if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) {
- if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) {
- ThrowRuntimeException("Bad filled array request for type %s",
- PrettyDescriptor(componentClass).c_str());
- } else {
- self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
- "Ljava/lang/InternalError;",
- "Found type %s; filled-new-array not implemented for anything but \'int\'",
- PrettyDescriptor(componentClass).c_str());
- }
- return false;
- }
- Object* newArray = Array::Alloc(self, arrayClass, length);
- if (UNLIKELY(newArray == NULL)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
- if (is_range) {
- uint32_t vregC = inst->VRegC_3rc();
- const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
- for (int32_t i = 0; i < length; ++i) {
- if (is_primitive_int_component) {
- newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i));
- } else {
- newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i));
- }
- }
- } else {
- uint32_t arg[5];
- inst->GetArgs(arg);
- const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
- for (int32_t i = 0; i < length; ++i) {
- if (is_primitive_int_component) {
- newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i]));
- } else {
- newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i]));
- }
- }
- }
-
- result->SetL(newArray);
- return true;
-}
-
-static inline const Instruction* DoSparseSwitch(const Instruction* inst,
- const ShadowFrame& shadow_frame)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH);
- const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t());
- DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
- uint16_t size = switch_data[1];
- DCHECK_GT(size, 0);
- const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
- DCHECK(IsAligned<4>(keys));
- const int32_t* entries = keys + size;
- DCHECK(IsAligned<4>(entries));
- int lo = 0;
- int hi = size - 1;
- while (lo <= hi) {
- int mid = (lo + hi) / 2;
- int32_t foundVal = keys[mid];
- if (test_val < foundVal) {
- hi = mid - 1;
- } else if (test_val > foundVal) {
- lo = mid + 1;
- } else {
- return inst->RelativeAt(entries[mid]);
- }
- }
- return inst->Next_3xx();
-}
-
-static inline const Instruction* FindNextInstructionFollowingException(Thread* self,
- ShadowFrame& shadow_frame,
- uint32_t dex_pc,
- const uint16_t* insns,
- SirtRef<Object>& this_object_ref,
- instrumentation::Instrumentation* instrumentation)
- ALWAYS_INLINE;
-
-static inline const Instruction* FindNextInstructionFollowingException(Thread* self,
- ShadowFrame& shadow_frame,
- uint32_t dex_pc,
- const uint16_t* insns,
- SirtRef<Object>& this_object_ref,
- instrumentation::Instrumentation* instrumentation)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- self->VerifyStack();
- ThrowLocation throw_location;
- mirror::Throwable* exception = self->GetException(&throw_location);
- bool clear_exception;
- uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc,
- &clear_exception);
- if (found_dex_pc == DexFile::kDexNoIndex) {
- instrumentation->MethodUnwindEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), dex_pc);
- return NULL;
- } else {
- instrumentation->ExceptionCaughtEvent(self, throw_location,
- shadow_frame.GetMethod(),
- found_dex_pc, exception);
- if (clear_exception) {
- self->ClearException();
- }
- return Instruction::At(insns + found_dex_pc);
- }
-}
-
-#define HANDLE_PENDING_EXCEPTION() \
- CHECK(self->IsExceptionPending()); \
- inst = FindNextInstructionFollowingException(self, shadow_frame, inst->GetDexPc(insns), insns, \
- this_object_ref, instrumentation); \
- if (inst == NULL) { \
- return JValue(); /* Handled in caller. */ \
- }
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION(is_exception_pending, next_function) \
- if (UNLIKELY(is_exception_pending)) { \
- HANDLE_PENDING_EXCEPTION(); \
- } else { \
- inst = inst->next_function(); \
- }
-
-static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
- __attribute__((cold, noreturn, noinline));
-
-static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile());
- exit(0); // Unreachable, keep GCC happy.
-}
-
-// Code to run before each dex instruction.
-#define PREAMBLE()
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<bool do_access_check>
-static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register)
- NO_THREAD_SAFETY_ANALYSIS __attribute__((hot));
-
-template<bool do_access_check>
-static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register) {
- bool do_assignability_check = do_access_check;
- if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
- LOG(FATAL) << "Invalid shadow frame for interpreter use";
- return JValue();
- }
- self->VerifyStack();
- instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
-
- // As the 'this' object won't change during the execution of current code, we
- // want to cache it in local variables. Nevertheless, in order to let the
- // garbage collector access it, we store it into sirt references.
- SirtRef<Object> this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_));
-
- uint32_t dex_pc = shadow_frame.GetDexPC();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
- if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
- instrumentation->MethodEnterEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), 0);
- }
- }
- const uint16_t* const insns = code_item->insns_;
- const Instruction* inst = Instruction::At(insns + dex_pc);
- while (true) {
- dex_pc = inst->GetDexPc(insns);
- shadow_frame.SetDexPC(dex_pc);
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
- if (UNLIKELY(instrumentation->HasDexPcListeners())) {
- instrumentation->DexPcMovedEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), dex_pc);
- }
- const bool kTracing = false;
- if (kTracing) {
-#define TRACE_LOG std::cerr
- TRACE_LOG << PrettyMethod(shadow_frame.GetMethod())
- << StringPrintf("\n0x%x: ", dex_pc)
- << inst->DumpString(&mh.GetDexFile()) << "\n";
- for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
- uint32_t raw_value = shadow_frame.GetVReg(i);
- Object* ref_value = shadow_frame.GetVRegReference(i);
- TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value);
- if (ref_value != NULL) {
- if (ref_value->GetClass()->IsStringClass() &&
- ref_value->AsString()->GetCharArray() != NULL) {
- TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";
- } else {
- TRACE_LOG << "/" << PrettyTypeOf(ref_value);
- }
- }
- }
- TRACE_LOG << "\n";
-#undef TRACE_LOG
- }
- switch (inst->Opcode()) {
- case Instruction::NOP:
- PREAMBLE();
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_FROM16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22x(),
- shadow_frame.GetVReg(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_32x(),
- shadow_frame.GetVReg(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(),
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_WIDE_FROM16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_22x(),
- shadow_frame.GetVRegLong(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_WIDE_16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_32x(),
- shadow_frame.GetVRegLong(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_OBJECT:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_12x(),
- shadow_frame.GetVRegReference(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_OBJECT_FROM16:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_22x(),
- shadow_frame.GetVRegReference(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_OBJECT_16:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_32x(),
- shadow_frame.GetVRegReference(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_RESULT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_11x(), result_register.GetI());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_RESULT_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_11x(), result_register.GetJ());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_RESULT_OBJECT:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_EXCEPTION: {
- PREAMBLE();
- Throwable* exception = self->GetException(NULL);
- self->ClearException();
- shadow_frame.SetVRegReference(inst->VRegA_11x(), exception);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::RETURN_VOID: {
- PREAMBLE();
- JValue result;
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN_VOID_BARRIER: {
- PREAMBLE();
- ANDROID_MEMBAR_STORE();
- JValue result;
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN: {
- PREAMBLE();
- JValue result;
- result.SetJ(0);
- result.SetI(shadow_frame.GetVReg(inst->VRegA_11x()));
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN_WIDE: {
- PREAMBLE();
- JValue result;
- result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x()));
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN_OBJECT: {
- PREAMBLE();
- JValue result;
- Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x());
- result.SetJ(0);
- result.SetL(obj_result);
- if (do_assignability_check && obj_result != NULL) {
- Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
- if (return_type == NULL) {
- // Return the pending exception.
- HANDLE_PENDING_EXCEPTION();
- }
- if (!obj_result->VerifierInstanceOf(return_type)) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Returning '%s' that is not instance of return type '%s'",
- ClassHelper(obj_result->GetClass()).GetDescriptor(),
- ClassHelper(return_type).GetDescriptor());
- HANDLE_PENDING_EXCEPTION();
- }
- }
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::CONST_4: {
- PREAMBLE();
- uint4_t dst = inst->VRegA_11n();
- int4_t val = inst->VRegB_11n();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::CONST_16: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_21s();
- int16_t val = inst->VRegB_21s();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CONST: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_31i();
- int32_t val = inst->VRegB_31i();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_3xx();
- break;
- }
- case Instruction::CONST_HIGH16: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_21h();
- int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CONST_WIDE_16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s());
- inst = inst->Next_2xx();
- break;
- case Instruction::CONST_WIDE_32:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i());
- inst = inst->Next_3xx();
- break;
- case Instruction::CONST_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_51l(), inst->VRegB_51l());
- inst = inst->Next_51l();
- break;
- case Instruction::CONST_WIDE_HIGH16:
- shadow_frame.SetVRegLong(inst->VRegA_21h(),
- static_cast<uint64_t>(inst->VRegB_21h()) << 48);
- inst = inst->Next_2xx();
- break;
- case Instruction::CONST_STRING: {
- PREAMBLE();
- String* s = ResolveString(self, mh, inst->VRegB_21c());
- if (UNLIKELY(s == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(), s);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::CONST_STRING_JUMBO: {
- PREAMBLE();
- String* s = ResolveString(self, mh, inst->VRegB_31c());
- if (UNLIKELY(s == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_31c(), s);
- inst = inst->Next_3xx();
- }
- break;
- }
- case Instruction::CONST_CLASS: {
- PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(c == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(), c);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::MONITOR_ENTER: {
- PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- } else {
- DoMonitorEnter(self, obj);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- }
- break;
- }
- case Instruction::MONITOR_EXIT: {
- PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- } else {
- DoMonitorExit(self, obj);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- }
- break;
- }
- case Instruction::CHECK_CAST: {
- PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(c == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c());
- if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
- ThrowClassCastException(c, obj->GetClass());
- HANDLE_PENDING_EXCEPTION();
- } else {
- inst = inst->Next_2xx();
- }
- }
- break;
- }
- case Instruction::INSTANCE_OF: {
- PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(c == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::ARRAY_LENGTH: {
- PREAMBLE();
- Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x());
- if (UNLIKELY(array == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength());
- inst = inst->Next_1xx();
- }
- break;
- }
- case Instruction::NEW_INSTANCE: {
- PREAMBLE();
- Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, do_access_check);
- if (UNLIKELY(obj == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(), obj);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::NEW_ARRAY: {
- PREAMBLE();
- int32_t length = shadow_frame.GetVReg(inst->VRegB_22c());
- Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(),
- length, self, do_access_check);
- if (UNLIKELY(obj == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_22c(), obj);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::FILLED_NEW_ARRAY: {
- PREAMBLE();
- bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
- self, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::FILLED_NEW_ARRAY_RANGE: {
- PREAMBLE();
- bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
- self, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::FILL_ARRAY_DATA: {
- PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- Array* array = obj->AsArray();
- DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
- const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- const Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
- if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
- self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
- "Ljava/lang/ArrayIndexOutOfBoundsException;",
- "failed FILL_ARRAY_DATA; length=%d, index=%d",
- array->GetLength(), payload->element_count);
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint32_t size_in_bytes = payload->element_count * payload->element_width;
- memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes);
- inst = inst->Next_3xx();
- break;
- }
- case Instruction::THROW: {
- PREAMBLE();
- Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x());
- if (UNLIKELY(exception == NULL)) {
- ThrowNullPointerException(NULL, "throw with null exception");
- } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Throwing '%s' that is not instance of Throwable",
- ClassHelper(exception->GetClass()).GetDescriptor());
- } else {
- self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
- }
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- case Instruction::GOTO: {
- PREAMBLE();
- inst = inst->RelativeAt(inst->VRegA_10t());
- break;
- }
- case Instruction::GOTO_16: {
- PREAMBLE();
- inst = inst->RelativeAt(inst->VRegA_20t());
- break;
- }
- case Instruction::GOTO_32: {
- PREAMBLE();
- inst = inst->RelativeAt(inst->VRegA_30t());
- break;
- }
- case Instruction::PACKED_SWITCH: {
- PREAMBLE();
- const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t());
- DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
- uint16_t size = switch_data[1];
- DCHECK_GT(size, 0);
- const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
- DCHECK(IsAligned<4>(keys));
- int32_t first_key = keys[0];
- const int32_t* targets = reinterpret_cast<const int32_t*>(&switch_data[4]);
- DCHECK(IsAligned<4>(targets));
- int32_t index = test_val - first_key;
- if (index >= 0 && index < size) {
- inst = inst->RelativeAt(targets[index]);
- } else {
- inst = inst->Next_3xx();
- }
- break;
- }
- case Instruction::SPARSE_SWITCH: {
- PREAMBLE();
- inst = DoSparseSwitch(inst, shadow_frame);
- break;
- }
- case Instruction::CMPL_FLOAT: {
- PREAMBLE();
- float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
- float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMPG_FLOAT: {
- PREAMBLE();
- float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
- float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
- int32_t result;
- if (val1 < val2) {
- result = -1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = 1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMPL_DOUBLE: {
- PREAMBLE();
- double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
- double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
-
- case Instruction::CMPG_DOUBLE: {
- PREAMBLE();
- double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
- double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
- int32_t result;
- if (val1 < val2) {
- result = -1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = 1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMP_LONG: {
- PREAMBLE();
- int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
- int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::IF_EQ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_NE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LT: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GT: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_EQZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_NEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LTZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GTZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::AGET_BOOLEAN: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- BooleanArray* array = a->AsBooleanArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_BYTE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ByteArray* array = a->AsByteArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_CHAR: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- CharArray* array = a->AsCharArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_SHORT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ShortArray* array = a->AsShortArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- IntArray* array = a->AsIntArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_WIDE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- LongArray* array = a->AsLongArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_OBJECT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_BOOLEAN: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- BooleanArray* array = a->AsBooleanArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_BYTE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int8_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ByteArray* array = a->AsByteArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_CHAR: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- CharArray* array = a->AsCharArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_SHORT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int16_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ShortArray* array = a->AsShortArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- IntArray* array = a->AsIntArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_WIDE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- LongArray* array = a->AsLongArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_OBJECT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x());
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
- if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) {
- array->SetWithoutChecks(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::IGET_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_BYTE: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_CHAR: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_SHORT: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_WIDE: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_OBJECT: {
- PREAMBLE();
- bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_WIDE_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_OBJECT_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_BYTE: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_CHAR: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_SHORT: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_WIDE: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_OBJECT: {
- PREAMBLE();
- bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BYTE: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_CHAR: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_SHORT: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_WIDE: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_OBJECT: {
- PREAMBLE();
- bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_WIDE_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_OBJECT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_BYTE: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_CHAR: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_SHORT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_WIDE: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_OBJECT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_SUPER: {
- PREAMBLE();
- bool success = DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_SUPER_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_DIRECT: {
- PREAMBLE();
- bool success = DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_DIRECT_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_INTERFACE: {
- PREAMBLE();
- bool success = DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_INTERFACE_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_STATIC: {
- PREAMBLE();
- bool success = DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_STATIC_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_QUICK: {
- PREAMBLE();
- bool success = DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
- PREAMBLE();
- bool success = DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::NEG_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NOT_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(), ~shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(), -shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NOT_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(), ~shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), -shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), -shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::FLOAT_TO_INT: {
- PREAMBLE();
- float val = shadow_frame.GetVRegFloat(inst->VRegB_12x());
- int32_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<float>(kMaxInt)) {
- result = kMaxInt;
- } else if (val < static_cast<float>(kMinInt)) {
- result = kMinInt;
- } else {
- result = val;
- }
- shadow_frame.SetVReg(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::FLOAT_TO_LONG: {
- PREAMBLE();
- float val = shadow_frame.GetVRegFloat(inst->VRegB_12x());
- int64_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<float>(kMaxLong)) {
- result = kMaxLong;
- } else if (val < static_cast<float>(kMinLong)) {
- result = kMinLong;
- } else {
- result = val;
- }
- shadow_frame.SetVRegLong(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::FLOAT_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::DOUBLE_TO_INT: {
- PREAMBLE();
- double val = shadow_frame.GetVRegDouble(inst->VRegB_12x());
- int32_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<double>(kMaxInt)) {
- result = kMaxInt;
- } else if (val < static_cast<double>(kMinInt)) {
- result = kMinInt;
- } else {
- result = val;
- }
- shadow_frame.SetVReg(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DOUBLE_TO_LONG: {
- PREAMBLE();
- double val = shadow_frame.GetVRegDouble(inst->VRegB_12x());
- int64_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<double>(kMaxLong)) {
- result = kMaxLong;
- } else if (val < static_cast<double>(kMinLong)) {
- result = kMinLong;
- } else {
- result = val;
- }
- shadow_frame.SetVRegLong(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DOUBLE_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_BYTE:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_CHAR:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_SHORT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- case Instruction::ADD_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) +
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) -
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) *
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SHL_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) <<
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::AND_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) &
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) |
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) ^
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) +
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) -
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) *
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_LONG:
- PREAMBLE();
- DoLongDivide(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
- break;
- case Instruction::REM_LONG:
- PREAMBLE();
- DoLongRemainder(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
- break;
- case Instruction::AND_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) &
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) |
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHL_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::REM_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
- shadow_frame.GetVRegFloat(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::REM_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
- shadow_frame.GetVRegDouble(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) +
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) -
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) *
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
- break;
- }
- case Instruction::REM_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
- break;
- }
- case Instruction::SHL_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) <<
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::USHR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::AND_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) &
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::OR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) |
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::XOR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) ^
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) +
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) -
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) *
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- break;
- }
- case Instruction::REM_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- break;
- }
- case Instruction::AND_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) &
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::OR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) |
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::XOR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) ^
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHL_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) <<
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::USHR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) +
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) -
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) *
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) /
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::REM_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- fmodf(shadow_frame.GetVRegFloat(vregA),
- shadow_frame.GetVRegFloat(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) +
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) -
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) *
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) /
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::REM_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- fmod(shadow_frame.GetVRegDouble(vregA),
- shadow_frame.GetVRegDouble(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) +
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::RSUB_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- inst->VRegC_22s() -
- shadow_frame.GetVReg(inst->VRegB_22s()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) *
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT_LIT16: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT_LIT16: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::AND_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) &
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) |
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) ^
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) +
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::RSUB_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- inst->VRegC_22b() -
- shadow_frame.GetVReg(inst->VRegB_22b()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) *
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT_LIT8: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT_LIT8: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::AND_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) &
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) |
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) ^
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::SHL_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) <<
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) >>
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
- case Instruction::UNUSED_79:
- case Instruction::UNUSED_7A:
- UnexpectedOpcode(inst, mh);
- }
- }
-} // NOLINT(readability/fn_size)
+static const InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKind;
static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register)
@@ -3114,12 +281,23 @@
shadow_frame.GetMethod()->GetDeclaringClass()->IsProxyClass());
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
- if (shadow_frame.GetMethod()->IsPreverified()) {
+
+ if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) {
// Enter the "without access check" interpreter.
- return ExecuteImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ if (kInterpreterImplKind == kSwitchImpl) {
+ return ExecuteSwitchImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
+ return ExecuteGotoImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ }
} else {
// Enter the "with access check" interpreter.
- return ExecuteImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ if (kInterpreterImplKind == kSwitchImpl) {
+ return ExecuteSwitchImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
+ return ExecuteGotoImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ }
}
}
@@ -3131,6 +309,7 @@
return;
}
+ const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
MethodHelper mh(method);
const DexFile::CodeItem* code_item = mh.GetCodeItem();
uint16_t num_regs;
@@ -3139,6 +318,7 @@
num_regs = code_item->registers_size_;
num_ins = code_item->ins_size_;
} else if (method->IsAbstract()) {
+ self->EndAssertNoThreadSuspension(old_cause);
ThrowAbstractMethodError(method);
return;
} else {
@@ -3154,6 +334,8 @@
void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, last_shadow_frame, method, 0, memory));
self->PushShadowFrame(shadow_frame);
+ self->EndAssertNoThreadSuspension(old_cause);
+
size_t cur_reg = num_regs - num_ins;
if (!method->IsStatic()) {
CHECK(receiver != NULL);
@@ -3161,8 +343,7 @@
++cur_reg;
} else if (UNLIKELY(!method->GetDeclaringClass()->IsInitializing())) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(),
- true, true))) {
+ if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(), true, true))) {
CHECK(self->IsExceptionPending());
self->PopShadowFrame();
return;
@@ -3243,17 +424,21 @@
return;
}
- ArtMethod* method = shadow_frame->GetMethod();
- if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) {
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(),
- true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return;
- }
- CHECK(method->GetDeclaringClass()->IsInitializing());
- }
-
self->PushShadowFrame(shadow_frame);
+ ArtMethod* method = shadow_frame->GetMethod();
+ // Ensure static methods are initialized.
+ if (method->IsStatic()) {
+ Class* declaringClass = method->GetDeclaringClass();
+ if (UNLIKELY(!declaringClass->IsInitializing())) {
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass,
+ true, true))) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ self->PopShadowFrame();
+ return;
+ }
+ CHECK(declaringClass->IsInitializing());
+ }
+ }
if (LIKELY(!method->IsNative())) {
result->SetJ(Execute(self, mh, code_item, *shadow_frame, JValue()).GetJ());
@@ -3261,13 +446,12 @@
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
CHECK(!Runtime::Current()->IsStarted());
- Object* receiver = method->IsStatic() ? NULL : shadow_frame->GetVRegReference(0);
+ Object* receiver = method->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
UnstartedRuntimeJni(self, method, receiver, args, result);
}
self->PopShadowFrame();
- return;
}
} // namespace interpreter
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
new file mode 100644
index 0000000..19f55d2
--- /dev/null
+++ b/runtime/interpreter/interpreter_common.cc
@@ -0,0 +1,348 @@
+/*
+ * 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.
+ */
+
+#include "interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
+ JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame.
+static inline void AssignRegister(ShadowFrame& new_shadow_frame, const ShadowFrame& shadow_frame,
+ size_t dest_reg, size_t src_reg) {
+ // If both register locations contains the same value, the register probably holds a reference.
+ int32_t src_value = shadow_frame.GetVReg(src_reg);
+ mirror::Object* o = shadow_frame.GetVRegReference(src_reg);
+ if (src_value == reinterpret_cast<int32_t>(o)) {
+ new_shadow_frame.SetVRegReference(dest_reg, o);
+ } else {
+ new_shadow_frame.SetVReg(dest_reg, src_value);
+ }
+}
+
+template<bool is_range, bool do_assignability_check>
+bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data, JValue* result) {
+ // Compute method information.
+ MethodHelper mh(method);
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ const uint16_t num_ins = (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
+ uint16_t num_regs;
+ if (LIKELY(code_item != NULL)) {
+ num_regs = code_item->registers_size_;
+ DCHECK_EQ(num_ins, code_item->ins_size_);
+ } else {
+ DCHECK(method->IsNative() || method->IsProxyMethod());
+ num_regs = num_ins;
+ }
+
+ // Allocate shadow frame on the stack.
+ const char* old_cause = self->StartAssertNoThreadSuspension("DoCall");
+ void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
+ ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory));
+
+ // Initialize new shadow frame.
+ const size_t first_dest_reg = num_regs - num_ins;
+ if (do_assignability_check) {
+ // Slow path: we need to do runtime check on reference assignment. We need to load the shorty
+ // to get the exact type of each reference argument.
+ const DexFile::TypeList* params = mh.GetParameterTypeList();
+ const char* shorty = mh.GetShorty();
+
+ // Handle receiver apart since it's not part of the shorty.
+ size_t dest_reg = first_dest_reg;
+ size_t arg_offset = 0;
+ if (receiver != NULL) {
+ DCHECK(!method->IsStatic());
+ new_shadow_frame->SetVRegReference(dest_reg, receiver);
+ ++dest_reg;
+ ++arg_offset;
+ } else {
+ DCHECK(method->IsStatic());
+ }
+ // TODO: find a cleaner way to separate non-range and range information without duplicating code.
+ uint32_t arg[5]; // only used in invoke-XXX.
+ uint32_t vregC; // only used in invoke-XXX-range.
+ if (is_range) {
+ vregC = inst->VRegC_3rc();
+ } else {
+ inst->GetArgs(arg, inst_data);
+ }
+ for (size_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) {
+ DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
+ const size_t src_reg = (is_range) ? vregC + arg_offset : arg[arg_offset];
+ switch (shorty[shorty_pos + 1]) {
+ case 'L': {
+ Object* o = shadow_frame.GetVRegReference(src_reg);
+ if (do_assignability_check && o != NULL) {
+ Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_);
+ if (arg_type == NULL) {
+ CHECK(self->IsExceptionPending());
+ self->EndAssertNoThreadSuspension(old_cause);
+ return false;
+ }
+ if (!o->VerifierInstanceOf(arg_type)) {
+ self->EndAssertNoThreadSuspension(old_cause);
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
+ mh.GetName(), shorty_pos,
+ ClassHelper(o->GetClass()).GetDescriptor(),
+ ClassHelper(arg_type).GetDescriptor());
+ return false;
+ }
+ }
+ new_shadow_frame->SetVRegReference(dest_reg, o);
+ break;
+ }
+ case 'J': case 'D': {
+ uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(src_reg + 1)) << 32) |
+ static_cast<uint32_t>(shadow_frame.GetVReg(src_reg));
+ new_shadow_frame->SetVRegLong(dest_reg, wide_value);
+ ++dest_reg;
+ ++arg_offset;
+ break;
+ }
+ default:
+ new_shadow_frame->SetVReg(dest_reg, shadow_frame.GetVReg(src_reg));
+ break;
+ }
+ }
+ } else {
+ // Fast path: no extra checks.
+ if (is_range) {
+ const uint16_t first_src_reg = inst->VRegC_3rc();
+ for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < num_regs;
+ ++dest_reg, ++src_reg) {
+ AssignRegister(*new_shadow_frame, shadow_frame, dest_reg, src_reg);
+ }
+ } else {
+ DCHECK_LE(num_ins, 5U);
+ uint16_t regList = inst->Fetch16(2);
+ uint16_t count = num_ins;
+ if (count == 5) {
+ AssignRegister(*new_shadow_frame, shadow_frame, first_dest_reg + 4U, (inst_data >> 8) & 0x0f);
+ --count;
+ }
+ for (size_t arg_index = 0; arg_index < count; ++arg_index, regList >>= 4) {
+ AssignRegister(*new_shadow_frame, shadow_frame, first_dest_reg + arg_index, regList & 0x0f);
+ }
+ }
+ }
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ // Do the call now.
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
+ } else {
+ UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, first_dest_reg);
+ }
+ return !self->IsExceptionPending();
+}
+
+template <bool is_range, bool do_access_check>
+bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
+ Thread* self, JValue* result) {
+ DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
+ inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE);
+ const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
+ if (!is_range) {
+ // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments.
+ CHECK_LE(length, 5);
+ }
+ if (UNLIKELY(length < 0)) {
+ ThrowNegativeArraySizeException(length);
+ return false;
+ }
+ uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(arrayClass == NULL)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ CHECK(arrayClass->IsArrayClass());
+ Class* componentClass = arrayClass->GetComponentType();
+ if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) {
+ if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) {
+ ThrowRuntimeException("Bad filled array request for type %s",
+ PrettyDescriptor(componentClass).c_str());
+ } else {
+ self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+ "Ljava/lang/InternalError;",
+ "Found type %s; filled-new-array not implemented for anything but \'int\'",
+ PrettyDescriptor(componentClass).c_str());
+ }
+ return false;
+ }
+ Object* newArray = Array::Alloc(self, arrayClass, length);
+ if (UNLIKELY(newArray == NULL)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ if (is_range) {
+ uint32_t vregC = inst->VRegC_3rc();
+ const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
+ for (int32_t i = 0; i < length; ++i) {
+ if (is_primitive_int_component) {
+ newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i));
+ } else {
+ newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i));
+ }
+ }
+ } else {
+ uint32_t arg[5];
+ inst->GetArgs(arg);
+ const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
+ for (int32_t i = 0; i < length; ++i) {
+ if (is_primitive_int_component) {
+ newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i]));
+ } else {
+ newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i]));
+ }
+ }
+ }
+
+ result->SetL(newArray);
+ return true;
+}
+
+static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
+ JValue* result, size_t arg_offset) {
+ // In a runtime that's not started we intercept certain methods to avoid complicated dependency
+ // problems in core libraries.
+ std::string name(PrettyMethod(shadow_frame->GetMethod()));
+ if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
+ std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
+ ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
+ Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+ class_loader);
+ CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
+ << PrettyDescriptor(descriptor);
+ result->SetL(found);
+ } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
+ Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+ ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
+ CHECK(c != NULL);
+ SirtRef<Object> obj(self, klass->AllocObject(self));
+ CHECK(obj.get() != NULL);
+ EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL);
+ result->SetL(obj.get());
+ } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
+ // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
+ // going the reflective Dex way.
+ Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+ String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
+ ArtField* found = NULL;
+ FieldHelper fh;
+ ObjectArray<ArtField>* fields = klass->GetIFields();
+ for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
+ ArtField* f = fields->Get(i);
+ fh.ChangeField(f);
+ if (name->Equals(fh.GetName())) {
+ found = f;
+ }
+ }
+ if (found == NULL) {
+ fields = klass->GetSFields();
+ for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
+ ArtField* f = fields->Get(i);
+ fh.ChangeField(f);
+ if (name->Equals(fh.GetName())) {
+ found = f;
+ }
+ }
+ }
+ CHECK(found != NULL)
+ << "Failed to find field in Class.getDeclaredField in un-started runtime. name="
+ << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
+ // TODO: getDeclaredField calls GetType once the field is found to ensure a
+ // NoClassDefFoundError is thrown if the field's type cannot be resolved.
+ Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass();
+ SirtRef<Object> field(self, jlr_Field->AllocObject(self));
+ CHECK(field.get() != NULL);
+ ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("<init>", "(Ljava/lang/reflect/ArtField;)V");
+ uint32_t args[1];
+ args[0] = reinterpret_cast<uint32_t>(found);
+ EnterInterpreterFromInvoke(self, c, field.get(), args, NULL);
+ result->SetL(field.get());
+ } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" ||
+ name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") {
+ // Special case array copying without initializing System.
+ Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
+ jint srcPos = shadow_frame->GetVReg(arg_offset + 1);
+ jint dstPos = shadow_frame->GetVReg(arg_offset + 3);
+ jint length = shadow_frame->GetVReg(arg_offset + 4);
+ if (!ctype->IsPrimitive()) {
+ ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>();
+ ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else if (ctype->IsPrimitiveChar()) {
+ CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray();
+ CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else if (ctype->IsPrimitiveInt()) {
+ IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray();
+ IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else {
+ UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype);
+ }
+ } else {
+ // Not special, continue with regular interpreter execution.
+ artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result);
+ }
+}
+
+// Explicit DoCall template function declarations.
+#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Object* receiver, \
+ Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
+#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
+
+// Explicit DoFilledNewArray template function declarations.
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \
+ const ShadowFrame& shadow_frame, \
+ Thread* self, JValue* result)
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true);
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false);
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true);
+#undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
new file mode 100644
index 0000000..0bc834c
--- /dev/null
+++ b/runtime/interpreter/interpreter_common.h
@@ -0,0 +1,677 @@
+/*
+ * 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_INTERPRETER_INTERPRETER_COMMON_H_
+#define ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_
+
+#include "interpreter.h"
+
+#include <math.h>
+
+#include "base/logging.h"
+#include "class_linker-inl.h"
+#include "common_throws.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "dex_instruction.h"
+#include "entrypoints/entrypoint_utils.h"
+#include "gc/accounting/card_table-inl.h"
+#include "invoke_arg_array_builder.h"
+#include "nth_caller_visitor.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "object_utils.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+#include "well_known_classes.h"
+
+using ::art::mirror::ArtField;
+using ::art::mirror::ArtMethod;
+using ::art::mirror::Array;
+using ::art::mirror::BooleanArray;
+using ::art::mirror::ByteArray;
+using ::art::mirror::CharArray;
+using ::art::mirror::Class;
+using ::art::mirror::ClassLoader;
+using ::art::mirror::IntArray;
+using ::art::mirror::LongArray;
+using ::art::mirror::Object;
+using ::art::mirror::ObjectArray;
+using ::art::mirror::ShortArray;
+using ::art::mirror::String;
+using ::art::mirror::Throwable;
+
+namespace art {
+namespace interpreter {
+
+// External references to both interpreter implementations.
+
+template<bool do_access_check>
+extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+template<bool do_access_check>
+extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+static inline void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+ ref->MonitorEnter(self);
+}
+
+static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+ ref->MonitorExit(self);
+}
+
+// Invokes the given method. This is part of the invocation support and is used by DoInvoke and
+// DoInvokeVirtualQuick functions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<bool is_range, bool do_assignability_check>
+bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data, JValue* result);
+
+// Handles invoke-XXX/range instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<InvokeType type, bool is_range, bool do_access_check>
+static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
+ uint16_t inst_data, JValue* result) {
+ const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ Object* receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
+ ArtMethod* const method = FindMethodFromCode<type, do_access_check>(method_idx, receiver,
+ shadow_frame.GetMethod(),
+ self);
+ if (type != kStatic) {
+ // Reload the vreg since the GC may have moved the object.
+ receiver = shadow_frame.GetVRegReference(vregC);
+ }
+ if (UNLIKELY(method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ } else if (UNLIKELY(method->IsAbstract())) {
+ ThrowAbstractMethodError(method);
+ result->SetJ(0);
+ return false;
+ } else {
+ return DoCall<is_range, do_access_check>(method, receiver, self, shadow_frame, inst,
+ inst_data, result);
+ }
+}
+
+// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<bool is_range>
+static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data,
+ JValue* result) {
+ const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ Object* const receiver = shadow_frame.GetVRegReference(vregC);
+ if (UNLIKELY(receiver == nullptr)) {
+ // We lost the reference to the method index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return false;
+ }
+ const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ ArtMethod* const method = receiver->GetClass()->GetVTable()->GetWithoutChecks(vtable_idx);
+ if (UNLIKELY(method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ } else if (UNLIKELY(method->IsAbstract())) {
+ ThrowAbstractMethodError(method);
+ result->SetJ(0);
+ return false;
+ } else {
+ // No need to check since we've been quickened.
+ return DoCall<is_range, false>(method, receiver, self, shadow_frame, inst, inst_data, result);
+ }
+}
+
+// Handles iget-XXX and sget-XXX instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data) {
+ 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));
+ if (UNLIKELY(f == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ Object* obj;
+ if (is_static) {
+ obj = f->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true);
+ return false;
+ }
+ }
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ shadow_frame.SetVReg(vregA, f->GetBoolean(obj));
+ break;
+ case Primitive::kPrimByte:
+ shadow_frame.SetVReg(vregA, f->GetByte(obj));
+ break;
+ case Primitive::kPrimChar:
+ shadow_frame.SetVReg(vregA, f->GetChar(obj));
+ break;
+ case Primitive::kPrimShort:
+ shadow_frame.SetVReg(vregA, f->GetShort(obj));
+ break;
+ case Primitive::kPrimInt:
+ shadow_frame.SetVReg(vregA, f->GetInt(obj));
+ break;
+ case Primitive::kPrimLong:
+ shadow_frame.SetVRegLong(vregA, f->GetLong(obj));
+ break;
+ case Primitive::kPrimNot:
+ shadow_frame.SetVRegReference(vregA, f->GetObject(obj));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles iget-quick, iget-wide-quick and iget-object-quick instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<Primitive::Type field_type>
+static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return false;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iget-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimLong:
+ shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimNot:
+ shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles iput-XXX and sput-XXX instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data) {
+ bool do_assignability_check = do_access_check;
+ 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));
+ if (UNLIKELY(f == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ Object* obj;
+ if (is_static) {
+ obj = f->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(),
+ f, false);
+ return false;
+ }
+ }
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ f->SetBoolean(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimByte:
+ f->SetByte(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimChar:
+ f->SetChar(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimShort:
+ f->SetShort(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimInt:
+ f->SetInt(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimLong:
+ f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
+ break;
+ case Primitive::kPrimNot: {
+ Object* reg = shadow_frame.GetVRegReference(vregA);
+ if (do_assignability_check && reg != nullptr) {
+ Class* field_class = FieldHelper(f).GetType();
+ if (!reg->VerifierInstanceOf(field_class)) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Put '%s' that is not instance of field '%s' in '%s'",
+ ClassHelper(reg->GetClass()).GetDescriptor(),
+ ClassHelper(field_class).GetDescriptor(),
+ ClassHelper(f->GetDeclaringClass()).GetDescriptor());
+ return false;
+ }
+ }
+ f->SetObj(obj, reg);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles iput-quick, iput-wide-quick and iput-object-quick instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<Primitive::Type field_type>
+static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return false;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iput-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+ break;
+ case Primitive::kPrimLong:
+ obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+ break;
+ case Primitive::kPrimNot:
+ obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
+// java.lang.String class is initialized.
+static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Class* java_lang_string_class = String::GetJavaLangString();
+ if (UNLIKELY(!java_lang_string_class->IsInitialized())) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class,
+ true, true))) {
+ DCHECK(self->IsExceptionPending());
+ return NULL;
+ }
+ }
+ return mh.ResolveString(string_idx);
+}
+
+// Handles div-int, div-int/2addr, div-int/li16 and div-int/lit8 instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg,
+ int32_t dividend, int32_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int32_t kMinInt = std::numeric_limits<int32_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
+ shadow_frame.SetVReg(result_reg, kMinInt);
+ } else {
+ shadow_frame.SetVReg(result_reg, dividend / divisor);
+ }
+ return true;
+}
+
+// Handles rem-int, rem-int/2addr, rem-int/li16 and rem-int/lit8 instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg,
+ int32_t dividend, int32_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int32_t kMinInt = std::numeric_limits<int32_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
+ shadow_frame.SetVReg(result_reg, 0);
+ } else {
+ shadow_frame.SetVReg(result_reg, dividend % divisor);
+ }
+ return true;
+}
+
+// Handles div-long and div-long-2addr instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg,
+ int64_t dividend, int64_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int64_t kMinLong = std::numeric_limits<int64_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
+ shadow_frame.SetVRegLong(result_reg, kMinLong);
+ } else {
+ shadow_frame.SetVRegLong(result_reg, dividend / divisor);
+ }
+ return true;
+}
+
+// Handles rem-long and rem-long-2addr instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg,
+ int64_t dividend, int64_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int64_t kMinLong = std::numeric_limits<int64_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
+ shadow_frame.SetVRegLong(result_reg, 0);
+ } else {
+ shadow_frame.SetVRegLong(result_reg, dividend % divisor);
+ }
+ return true;
+}
+
+// Handles filled-new-array and filled-new-array-range instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template <bool is_range, bool do_access_check>
+bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
+ Thread* self, JValue* result);
+
+// Handles packed-switch instruction.
+// Returns the branch offset to the next instruction to execute.
+static inline int32_t DoPackedSwitch(const Instruction* inst, const ShadowFrame& shadow_frame,
+ uint16_t inst_data)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(inst->Opcode() == Instruction::PACKED_SWITCH);
+ const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t(inst_data));
+ DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
+ uint16_t size = switch_data[1];
+ DCHECK_GT(size, 0);
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
+ DCHECK(IsAligned<4>(keys));
+ int32_t first_key = keys[0];
+ const int32_t* targets = reinterpret_cast<const int32_t*>(&switch_data[4]);
+ DCHECK(IsAligned<4>(targets));
+ int32_t index = test_val - first_key;
+ if (index >= 0 && index < size) {
+ return targets[index];
+ } else {
+ // No corresponding value: move forward by 3 (size of PACKED_SWITCH).
+ return 3;
+ }
+}
+
+// Handles sparse-switch instruction.
+// Returns the branch offset to the next instruction to execute.
+static inline int32_t DoSparseSwitch(const Instruction* inst, const ShadowFrame& shadow_frame,
+ uint16_t inst_data)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH);
+ const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t(inst_data));
+ DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
+ uint16_t size = switch_data[1];
+ DCHECK_GT(size, 0);
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
+ DCHECK(IsAligned<4>(keys));
+ const int32_t* entries = keys + size;
+ DCHECK(IsAligned<4>(entries));
+ int lo = 0;
+ int hi = size - 1;
+ while (lo <= hi) {
+ int mid = (lo + hi) / 2;
+ int32_t foundVal = keys[mid];
+ if (test_val < foundVal) {
+ hi = mid - 1;
+ } else if (test_val > foundVal) {
+ lo = mid + 1;
+ } else {
+ return entries[mid];
+ }
+ }
+ // No corresponding value: move forward by 3 (size of SPARSE_SWITCH).
+ return 3;
+}
+
+static inline uint32_t FindNextInstructionFollowingException(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ mirror::Object* this_object,
+ const instrumentation::Instrumentation* instrumentation)
+ ALWAYS_INLINE;
+
+static inline uint32_t FindNextInstructionFollowingException(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ mirror::Object* this_object,
+ const instrumentation::Instrumentation* instrumentation)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ self->VerifyStack();
+ ThrowLocation throw_location;
+ mirror::Throwable* exception = self->GetException(&throw_location);
+ bool clear_exception = false;
+ uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc,
+ &clear_exception);
+ if (found_dex_pc == DexFile::kDexNoIndex) {
+ instrumentation->MethodUnwindEvent(self, this_object,
+ shadow_frame.GetMethod(), dex_pc);
+ } else {
+ instrumentation->ExceptionCaughtEvent(self, throw_location,
+ shadow_frame.GetMethod(),
+ found_dex_pc, exception);
+ if (clear_exception) {
+ self->ClearException();
+ }
+ }
+ return found_dex_pc;
+}
+
+static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
+ __attribute__((cold, noreturn, noinline));
+
+static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile());
+ exit(0); // Unreachable, keep GCC happy.
+}
+
+static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst,
+ const uint32_t dex_pc, MethodHelper& mh)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ constexpr bool kTracing = false;
+ if (kTracing) {
+#define TRACE_LOG std::cerr
+ std::ostringstream oss;
+ oss << PrettyMethod(shadow_frame.GetMethod())
+ << StringPrintf("\n0x%x: ", dex_pc)
+ << inst->DumpString(&mh.GetDexFile()) << "\n";
+ for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
+ uint32_t raw_value = shadow_frame.GetVReg(i);
+ Object* ref_value = shadow_frame.GetVRegReference(i);
+ oss << StringPrintf(" vreg%d=0x%08X", i, raw_value);
+ if (ref_value != NULL) {
+ if (ref_value->GetClass()->IsStringClass() &&
+ ref_value->AsString()->GetCharArray() != NULL) {
+ oss << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";
+ } else {
+ oss << "/" << PrettyTypeOf(ref_value);
+ }
+ }
+ }
+ TRACE_LOG << oss.str() << "\n";
+#undef TRACE_LOG
+ }
+}
+
+static inline bool IsBackwardBranch(int32_t branch_offset) {
+ return branch_offset <= 0;
+}
+
+// Explicitly instantiate all DoInvoke functions.
+#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoInvoke<_type, _is_range, _do_check>(Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+
+#define EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(_type) \
+ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \
+ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true); \
+ 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.
+#undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL
+#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL
+
+// Explicitly instantiate all DoFieldGet functions.
+#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoFieldGet<_find_type, _field_type, _do_check>(Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data)
+
+#define EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(_find_type, _field_type) \
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false); \
+ 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);
+
+// 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);
+
+#undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL
+#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
+
+// Explicitly instantiate all DoFieldPut functions.
+#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data)
+
+#define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type) \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false); \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, 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);
+
+// 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);
+
+#undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL
+#undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL
+
+// Explicitly instantiate all DoInvokeVirtualQuick functions.
+#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+
+EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); // invoke-virtual-quick.
+EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); // invoke-virtual-quick-range.
+#undef EXPLICIT_INSTANTIATION_DO_INVOKE_VIRTUAL_QUICK
+
+// Explicitly instantiate all DoIGetQuick functions.
+#define EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(_field_type) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data)
+
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+#undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
+
+// Explicitly instantiate all DoIPutQuick functions.
+#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data)
+
+EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
+EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
+EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+#undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
+
+} // namespace interpreter
+} // namespace art
+
+#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
new file mode 100644
index 0000000..aa6bcd6
--- /dev/null
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -0,0 +1,2400 @@
+/*
+ * 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.
+ */
+
+#include "interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+// In the following macros, we expect the following local variables exist:
+// - "self": the current Thread*.
+// - "inst" : the current Instruction*.
+// - "inst_data" : the current instruction's first 16 bits.
+// - "dex_pc": the current pc.
+// - "shadow_frame": the current shadow frame.
+// - "mh": the current MethodHelper.
+// - "currentHandlersTable": the current table of pointer to each instruction handler.
+
+// Advance to the next instruction and updates interpreter state.
+#define ADVANCE(_offset) \
+ do { \
+ int32_t disp = static_cast<int32_t>(_offset); \
+ inst = inst->RelativeAt(disp); \
+ dex_pc = static_cast<uint32_t>(static_cast<int32_t>(dex_pc) + disp); \
+ shadow_frame.SetDexPC(dex_pc); \
+ TraceExecution(shadow_frame, inst, dex_pc, mh); \
+ inst_data = inst->Fetch16(0); \
+ goto *currentHandlersTable[inst->Opcode(inst_data)]; \
+ } while (false)
+
+#define HANDLE_PENDING_EXCEPTION() goto exception_pending_label
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _offset) \
+ do { \
+ if (UNLIKELY(_is_exception_pending)) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ } else { \
+ ADVANCE(_offset); \
+ } \
+ } while (false)
+
+#define UPDATE_HANDLER_TABLE() \
+ currentHandlersTable = handlersTable[Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()]
+
+#define UNREACHABLE_CODE_CHECK() \
+ do { \
+ if (kIsDebugBuild) { \
+ LOG(FATAL) << "We should not be here !"; \
+ } \
+ } while (false)
+
+#define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels)
+#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()
+
+/**
+ * Interpreter based on computed goto tables.
+ *
+ * Each instruction is associated to a handler. This handler is responsible for executing the
+ * instruction and jump to the next instruction's handler.
+ * In order to limit the cost of instrumentation, we have two handler tables:
+ * - the "main" handler table: it contains handlers for normal execution of each instruction without
+ * handling of instrumentation.
+ * - the "alternative" handler table: it contains alternative handlers which first handle
+ * instrumentation before jumping to the corresponding "normal" instruction's handler.
+ *
+ * When instrumentation is active, the interpreter uses the "alternative" handler table. Otherwise
+ * it uses the "main" handler table.
+ *
+ * The current handler table is the handler table being used by the interpreter. It is updated:
+ * - on backward branch (goto, if and switch instructions)
+ * - after invoke
+ * - when an exception is thrown.
+ * This allows to support an attaching debugger to an already running application for instance.
+ *
+ * For a fast handler table update, handler tables are stored in an array of handler tables. Each
+ * handler table is represented by the InterpreterHandlerTable enum which allows to associate it
+ * to an index in this array of handler tables ((see Instrumentation::GetInterpreterHandlerTable).
+ *
+ * Here's the current layout of this array of handler tables:
+ *
+ * ---------------------+---------------+
+ * | NOP | (handler for NOP instruction)
+ * +---------------+
+ * "main" | MOVE | (handler for MOVE instruction)
+ * handler table +---------------+
+ * | ... |
+ * +---------------+
+ * | UNUSED_FF | (handler for UNUSED_FF instruction)
+ * ---------------------+---------------+
+ * | NOP | (alternative handler for NOP instruction)
+ * +---------------+
+ * "alternative" | MOVE | (alternative handler for MOVE instruction)
+ * handler table +---------------+
+ * | ... |
+ * +---------------+
+ * | UNUSED_FF | (alternative handler for UNUSED_FF instruction)
+ * ---------------------+---------------+
+ *
+ */
+template<bool do_access_check>
+JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register) {
+ // Define handler tables:
+ // - The main handler table contains execution handlers for each instruction.
+ // - The alternative handler table contains prelude handlers which check for thread suspend and
+ // manage instrumentation before jumping to the execution handler.
+ static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
+ {
+ // Main handler table.
+#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_HANDLER
+ }, {
+ // Alternative handler table.
+#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code,
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_HANDLER
+ }
+ };
+
+ const bool do_assignability_check = do_access_check;
+ if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
+ LOG(FATAL) << "Invalid shadow frame for interpreter use";
+ return JValue();
+ }
+ self->VerifyStack();
+
+ uint32_t dex_pc = shadow_frame.GetDexPC();
+ const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
+ uint16_t inst_data;
+ const void* const* currentHandlersTable;
+ UPDATE_HANDLER_TABLE();
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
+ instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), 0);
+ }
+ }
+
+ // Jump to first instruction.
+ ADVANCE(0);
+ UNREACHABLE_CODE_CHECK();
+
+ HANDLE_INSTRUCTION_START(NOP)
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_FROM16)
+ shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_16)
+ shadow_frame.SetVReg(inst->VRegA_32x(),
+ shadow_frame.GetVReg(inst->VRegB_32x()));
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_WIDE)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_WIDE_FROM16)
+ shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_22x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_WIDE_16)
+ shadow_frame.SetVRegLong(inst->VRegA_32x(),
+ shadow_frame.GetVRegLong(inst->VRegB_32x()));
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_OBJECT)
+ shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_OBJECT_FROM16)
+ shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_22x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_OBJECT_16)
+ shadow_frame.SetVRegReference(inst->VRegA_32x(),
+ shadow_frame.GetVRegReference(inst->VRegB_32x()));
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_RESULT)
+ shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_RESULT_WIDE)
+ shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_RESULT_OBJECT)
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) {
+ Throwable* exception = self->GetException(NULL);
+ self->ClearException();
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_VOID) {
+ JValue result;
+ if (do_access_check) {
+ // If access checks are required then the dex-to-dex compiler and analysis of
+ // whether the class has final fields hasn't been performed. Conservatively
+ // perform the memory barrier now.
+ ANDROID_MEMBAR_STORE();
+ }
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) {
+ ANDROID_MEMBAR_STORE();
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN) {
+ JValue result;
+ result.SetJ(0);
+ result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_WIDE) {
+ JValue result;
+ result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_OBJECT) {
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ result.SetJ(0);
+ result.SetL(obj_result);
+ if (do_assignability_check && obj_result != NULL) {
+ Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
+ if (return_type == NULL) {
+ // Return the pending exception.
+ HANDLE_PENDING_EXCEPTION();
+ }
+ if (!obj_result->VerifierInstanceOf(return_type)) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Returning '%s' that is not instance of return type '%s'",
+ ClassHelper(obj_result->GetClass()).GetDescriptor(),
+ ClassHelper(return_type).GetDescriptor());
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_4) {
+ uint32_t dst = inst->VRegA_11n(inst_data);
+ int32_t val = inst->VRegB_11n(inst_data);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_16) {
+ uint32_t dst = inst->VRegA_21s(inst_data);
+ int32_t val = inst->VRegB_21s();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST) {
+ uint32_t dst = inst->VRegA_31i(inst_data);
+ int32_t val = inst->VRegB_31i();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_HIGH16) {
+ uint32_t dst = inst->VRegA_21h(inst_data);
+ int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE_16)
+ shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE_32)
+ shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE)
+ shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
+ ADVANCE(5);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE_HIGH16)
+ shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
+ static_cast<uint64_t>(inst->VRegB_21h()) << 48);
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_STRING) {
+ String* s = ResolveString(self, mh, inst->VRegB_21c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_STRING_JUMBO) {
+ String* s = ResolveString(self, mh, inst->VRegB_31c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+ ADVANCE(3);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_CLASS) {
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MONITOR_ENTER) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorEnter(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MONITOR_EXIT) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorExit(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CHECK_CAST) {
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+ if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
+ ThrowClassCastException(c, obj->GetClass());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ ADVANCE(2);
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INSTANCE_OF) {
+ Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ARRAY_LENGTH) {
+ Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+ if (UNLIKELY(array == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
+ ADVANCE(1);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEW_INSTANCE) {
+ Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEW_ARRAY) {
+ int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
+ Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ length, self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) {
+ bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) {
+ bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FILL_ARRAY_DATA) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Array* array = obj->AsArray();
+ DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
+ const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+ if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
+ self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+ "Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "failed FILL_ARRAY_DATA; length=%d, index=%d",
+ array->GetLength(), payload->element_count);
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ uint32_t size_in_bytes = payload->element_count * payload->element_width;
+ memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes);
+ ADVANCE(3);
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(THROW) {
+ Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(exception == NULL)) {
+ ThrowNullPointerException(NULL, "throw with null exception");
+ } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Throwing '%s' that is not instance of Throwable",
+ ClassHelper(exception->GetClass()).GetDescriptor());
+ } else {
+ self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
+ }
+ HANDLE_PENDING_EXCEPTION();
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(GOTO) {
+ int8_t offset = inst->VRegA_10t(inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(GOTO_16) {
+ int16_t offset = inst->VRegA_20t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(GOTO_32) {
+ int32_t offset = inst->VRegA_30t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(PACKED_SWITCH) {
+ int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPARSE_SWITCH) {
+ int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPL_FLOAT) {
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPG_FLOAT) {
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPL_DOUBLE) {
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPG_DOUBLE) {
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMP_LONG) {
+ int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
+ int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_EQ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_NE) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LT) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GE) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GT) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LE) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_EQZ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_NEZ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LTZ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GEZ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GTZ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LEZ) {
+ 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);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_BOOLEAN) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_BYTE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_CHAR) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_SHORT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_WIDE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_OBJECT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_BOOLEAN) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_BYTE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_CHAR) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_SHORT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_WIDE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_OBJECT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) {
+ array->SetWithoutChecks(index, val);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_BOOLEAN) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_BYTE) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_CHAR) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_SHORT) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_WIDE) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_OBJECT) {
+ bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_WIDE_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_OBJECT_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_BOOLEAN) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_BYTE) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_CHAR) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_SHORT) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_WIDE) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_OBJECT) {
+ bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_BYTE) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_CHAR) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_SHORT) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_WIDE) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_OBJECT) {
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimInt>(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>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_BYTE) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_CHAR) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_SHORT) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_WIDE) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_OBJECT) {
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL) {
+ bool success = DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE) {
+ bool success = DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_SUPER) {
+ bool success = DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_SUPER_RANGE) {
+ bool success = DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_DIRECT) {
+ bool success = DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_DIRECT_RANGE) {
+ bool success = DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_INTERFACE) {
+ bool success = DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_INTERFACE_RANGE) {
+ bool success = DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_STATIC) {
+ bool success = DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_STATIC_RANGE) {
+ bool success = DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_QUICK) {
+ bool success = DoInvokeVirtualQuick<false>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE_QUICK) {
+ bool success = DoInvokeVirtualQuick<true>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_INT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NOT_INT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NOT_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(LONG_TO_INT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(LONG_TO_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(LONG_TO_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FLOAT_TO_INT) {
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, float>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FLOAT_TO_LONG) {
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, float>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FLOAT_TO_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DOUBLE_TO_INT) {
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, double>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DOUBLE_TO_LONG) {
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, double>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DOUBLE_TO_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_BYTE)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_CHAR)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_SHORT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) +
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) -
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) *
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) &
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) |
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) ^
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) +
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) -
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) *
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_LONG) {
+ bool success = DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_LONG) {
+ bool success = DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) &
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) |
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
+ shadow_frame.GetVRegFloat(inst->VRegC_23x())));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
+ shadow_frame.GetVRegDouble(inst->VRegC_23x())));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) +
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) -
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) *
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) &
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) |
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) ^
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) +
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) -
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) *
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) &
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) |
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) ^
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) +
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) -
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) *
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) /
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ fmodf(shadow_frame.GetVRegFloat(vregA),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) +
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) -
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) *
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) /
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ fmod(shadow_frame.GetVRegDouble(vregA),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) +
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RSUB_INT)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ inst->VRegC_22s() -
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) *
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT_LIT16) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT_LIT16) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) +
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RSUB_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ inst->VRegC_22b() -
+ shadow_frame.GetVReg(inst->VRegB_22b()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) *
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT_LIT8) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT_LIT8) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) &
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) |
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) ^
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) <<
+ (inst->VRegC_22b() & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) >>
+ (inst->VRegC_22b() & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
+ (inst->VRegC_22b() & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_3E)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_3F)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_40)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_41)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_42)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_43)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_79)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_7A)
+ 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();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F0)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F1)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F2)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F3)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F4)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F5)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F6)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F7)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F8)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F9)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FA)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FB)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FC)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FD)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FE)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FF)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ exception_pending_label: {
+ CHECK(self->IsExceptionPending());
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc,
+ this_object,
+ instrumentation);
+ if (found_dex_pc == DexFile::kDexNoIndex) {
+ return JValue(); /* Handled in caller. */
+ } else {
+ int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc);
+ ADVANCE(displacement);
+ }
+ }
+
+ // Create alternative instruction handlers dedicated to instrumentation.
+#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \
+ alt_op_##code: { \
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
+ instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \
+ shadow_frame.GetMethod(), dex_pc); \
+ } \
+ UPDATE_HANDLER_TABLE(); \
+ goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \
+ }
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUMENTATION_INSTRUCTION_HANDLER
+} // NOLINT(readability/fn_size)
+
+// Explicit definitions of ExecuteGotoImpl.
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteGotoImpl<true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteGotoImpl<false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
new file mode 100644
index 0000000..bd0d87e
--- /dev/null
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -0,0 +1,2147 @@
+/*
+ * 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.
+ */
+
+#include "interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+#define HANDLE_PENDING_EXCEPTION() \
+ do { \
+ CHECK(self->IsExceptionPending()); \
+ if (UNLIKELY(self->TestAllFlags())) { \
+ CheckSuspend(self); \
+ } \
+ Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \
+ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \
+ inst->GetDexPc(insns), \
+ this_object, \
+ instrumentation); \
+ if (found_dex_pc == DexFile::kDexNoIndex) { \
+ return JValue(); /* Handled in caller. */ \
+ } else { \
+ int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc); \
+ inst = inst->RelativeAt(displacement); \
+ } \
+ } while (false)
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _next_function) \
+ do { \
+ if (UNLIKELY(_is_exception_pending)) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ } else { \
+ inst = inst->_next_function(); \
+ } \
+ } while (false)
+
+// Code to run before each dex instruction.
+#define PREAMBLE()
+
+template<bool do_access_check>
+JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register) {
+ bool do_assignability_check = do_access_check;
+ if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
+ LOG(FATAL) << "Invalid shadow frame for interpreter use";
+ return JValue();
+ }
+ self->VerifyStack();
+
+ uint32_t dex_pc = shadow_frame.GetDexPC();
+ const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
+ instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), 0);
+ }
+ }
+ const uint16_t* const insns = code_item->insns_;
+ const Instruction* inst = Instruction::At(insns + dex_pc);
+ uint16_t inst_data;
+ while (true) {
+ dex_pc = inst->GetDexPc(insns);
+ shadow_frame.SetDexPC(dex_pc);
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) {
+ instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc);
+ }
+ TraceExecution(shadow_frame, inst, dex_pc, mh);
+ inst_data = inst->Fetch16(0);
+ switch (inst->Opcode(inst_data)) {
+ case Instruction::NOP:
+ PREAMBLE();
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_FROM16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MOVE_16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_32x(),
+ shadow_frame.GetVReg(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::MOVE_WIDE:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_WIDE_FROM16:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MOVE_WIDE_16:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_32x(),
+ shadow_frame.GetVRegLong(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::MOVE_OBJECT:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_OBJECT_FROM16:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MOVE_OBJECT_16:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_32x(),
+ shadow_frame.GetVRegReference(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::MOVE_RESULT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_RESULT_WIDE:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_RESULT_OBJECT:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_EXCEPTION: {
+ PREAMBLE();
+ Throwable* exception = self->GetException(NULL);
+ self->ClearException();
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::RETURN_VOID: {
+ PREAMBLE();
+ JValue result;
+ if (do_access_check) {
+ // If access checks are required then the dex-to-dex compiler and analysis of
+ // whether the class has final fields hasn't been performed. Conservatively
+ // perform the memory barrier now.
+ ANDROID_MEMBAR_STORE();
+ }
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN_VOID_BARRIER: {
+ PREAMBLE();
+ ANDROID_MEMBAR_STORE();
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN: {
+ PREAMBLE();
+ JValue result;
+ result.SetJ(0);
+ result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN_WIDE: {
+ PREAMBLE();
+ JValue result;
+ result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN_OBJECT: {
+ PREAMBLE();
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ result.SetJ(0);
+ result.SetL(obj_result);
+ if (do_assignability_check && obj_result != NULL) {
+ Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
+ if (return_type == NULL) {
+ // Return the pending exception.
+ HANDLE_PENDING_EXCEPTION();
+ }
+ if (!obj_result->VerifierInstanceOf(return_type)) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Returning '%s' that is not instance of return type '%s'",
+ ClassHelper(obj_result->GetClass()).GetDescriptor(),
+ ClassHelper(return_type).GetDescriptor());
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::CONST_4: {
+ PREAMBLE();
+ uint4_t dst = inst->VRegA_11n(inst_data);
+ int4_t val = inst->VRegB_11n(inst_data);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::CONST_16: {
+ PREAMBLE();
+ uint8_t dst = inst->VRegA_21s(inst_data);
+ int16_t val = inst->VRegB_21s();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CONST: {
+ PREAMBLE();
+ uint8_t dst = inst->VRegA_31i(inst_data);
+ int32_t val = inst->VRegB_31i();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_3xx();
+ break;
+ }
+ case Instruction::CONST_HIGH16: {
+ PREAMBLE();
+ uint8_t dst = inst->VRegA_21h(inst_data);
+ int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CONST_WIDE_16:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::CONST_WIDE_32:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::CONST_WIDE:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
+ inst = inst->Next_51l();
+ break;
+ case Instruction::CONST_WIDE_HIGH16:
+ shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
+ static_cast<uint64_t>(inst->VRegB_21h()) << 48);
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::CONST_STRING: {
+ PREAMBLE();
+ String* s = ResolveString(self, mh, inst->VRegB_21c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::CONST_STRING_JUMBO: {
+ PREAMBLE();
+ String* s = ResolveString(self, mh, inst->VRegB_31c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+ inst = inst->Next_3xx();
+ }
+ break;
+ }
+ case Instruction::CONST_CLASS: {
+ PREAMBLE();
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::MONITOR_ENTER: {
+ PREAMBLE();
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorEnter(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+ break;
+ }
+ case Instruction::MONITOR_EXIT: {
+ PREAMBLE();
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorExit(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+ break;
+ }
+ case Instruction::CHECK_CAST: {
+ PREAMBLE();
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+ if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
+ ThrowClassCastException(c, obj->GetClass());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ inst = inst->Next_2xx();
+ }
+ }
+ break;
+ }
+ case Instruction::INSTANCE_OF: {
+ PREAMBLE();
+ Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::ARRAY_LENGTH: {
+ PREAMBLE();
+ Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+ if (UNLIKELY(array == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
+ inst = inst->Next_1xx();
+ }
+ break;
+ }
+ case Instruction::NEW_INSTANCE: {
+ PREAMBLE();
+ Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::NEW_ARRAY: {
+ PREAMBLE();
+ int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
+ Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ length, self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::FILLED_NEW_ARRAY: {
+ PREAMBLE();
+ bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ PREAMBLE();
+ bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::FILL_ARRAY_DATA: {
+ PREAMBLE();
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ Array* array = obj->AsArray();
+ DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
+ const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+ if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
+ self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+ "Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "failed FILL_ARRAY_DATA; length=%d, index=%d",
+ array->GetLength(), payload->element_count);
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ uint32_t size_in_bytes = payload->element_count * payload->element_width;
+ memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes);
+ inst = inst->Next_3xx();
+ break;
+ }
+ case Instruction::THROW: {
+ PREAMBLE();
+ Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(exception == NULL)) {
+ ThrowNullPointerException(NULL, "throw with null exception");
+ } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Throwing '%s' that is not instance of Throwable",
+ ClassHelper(exception->GetClass()).GetDescriptor());
+ } else {
+ self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
+ }
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ case Instruction::GOTO: {
+ PREAMBLE();
+ int8_t offset = inst->VRegA_10t(inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::GOTO_16: {
+ PREAMBLE();
+ int16_t offset = inst->VRegA_20t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::GOTO_32: {
+ PREAMBLE();
+ int32_t offset = inst->VRegA_30t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::PACKED_SWITCH: {
+ PREAMBLE();
+ int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::SPARSE_SWITCH: {
+ PREAMBLE();
+ int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::CMPL_FLOAT: {
+ PREAMBLE();
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CMPG_FLOAT: {
+ PREAMBLE();
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CMPL_DOUBLE: {
+ PREAMBLE();
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+
+ case Instruction::CMPG_DOUBLE: {
+ PREAMBLE();
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CMP_LONG: {
+ PREAMBLE();
+ int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
+ int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::IF_EQ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_NE: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LT: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GE: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GT: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LE: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_EQZ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_NEZ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LTZ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GEZ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GTZ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LEZ: {
+ PREAMBLE();
+ 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);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::AGET_BOOLEAN: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_BYTE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_CHAR: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_SHORT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_WIDE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_OBJECT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_BOOLEAN: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_BYTE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_CHAR: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_SHORT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_WIDE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_OBJECT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) {
+ array->SetWithoutChecks(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::IGET_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_WIDE_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_OBJECT_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_WIDE_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_OBJECT_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL: {
+ PREAMBLE();
+ bool success = DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_SUPER: {
+ PREAMBLE();
+ bool success = DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_SUPER_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_DIRECT: {
+ PREAMBLE();
+ bool success = DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_DIRECT_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_INTERFACE: {
+ PREAMBLE();
+ bool success = DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_INTERFACE_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_STATIC: {
+ PREAMBLE();
+ bool success = DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_STATIC_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL_QUICK: {
+ PREAMBLE();
+ bool success = DoInvokeVirtualQuick<false>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ PREAMBLE();
+ bool success = DoInvokeVirtualQuick<true>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::NEG_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NOT_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NEG_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NOT_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NEG_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NEG_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::LONG_TO_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::LONG_TO_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::LONG_TO_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::FLOAT_TO_INT: {
+ PREAMBLE();
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, float>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::FLOAT_TO_LONG: {
+ PREAMBLE();
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, float>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::FLOAT_TO_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::DOUBLE_TO_INT: {
+ PREAMBLE();
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, double>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DOUBLE_TO_LONG: {
+ PREAMBLE();
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, double>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DOUBLE_TO_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_BYTE:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_CHAR:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_SHORT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::ADD_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) +
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) -
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) *
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_INT: {
+ PREAMBLE();
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::REM_INT: {
+ PREAMBLE();
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SHL_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::USHR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::AND_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) &
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) |
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) ^
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) +
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) -
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) *
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_LONG:
+ PREAMBLE();
+ DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+ break;
+ case Instruction::REM_LONG:
+ PREAMBLE();
+ DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+ break;
+ case Instruction::AND_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) &
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) |
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHL_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::USHR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::REM_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
+ shadow_frame.GetVRegFloat(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::REM_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
+ shadow_frame.GetVRegDouble(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) +
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) -
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) *
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+ break;
+ }
+ case Instruction::REM_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+ break;
+ }
+ case Instruction::SHL_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SHR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::USHR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::AND_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) &
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::OR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) |
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::XOR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) ^
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) +
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) -
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) *
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ break;
+ }
+ case Instruction::REM_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ break;
+ }
+ case Instruction::AND_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) &
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::OR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) |
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::XOR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) ^
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SHL_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SHR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::USHR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) +
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) -
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) *
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) /
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::REM_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ fmodf(shadow_frame.GetVRegFloat(vregA),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) +
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) -
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) *
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) /
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::REM_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ fmod(shadow_frame.GetVRegDouble(vregA),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) +
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::RSUB_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ inst->VRegC_22s() -
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) *
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_INT_LIT16: {
+ PREAMBLE();
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::REM_INT_LIT16: {
+ PREAMBLE();
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::AND_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) +
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::RSUB_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ inst->VRegC_22b() -
+ shadow_frame.GetVReg(inst->VRegB_22b()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) *
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_INT_LIT8: {
+ PREAMBLE();
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::REM_INT_LIT8: {
+ PREAMBLE();
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::AND_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) &
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) |
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) ^
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHL_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) <<
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) >>
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::USHR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
+ case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_79:
+ case Instruction::UNUSED_7A:
+ UnexpectedOpcode(inst, mh);
+ }
+ }
+} // NOLINT(readability/fn_size)
+
+// Explicit definitions of ExecuteSwitchImpl.
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteSwitchImpl<true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteSwitchImpl<false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index a2efc48..523d892 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -47,7 +47,7 @@
std::string DescribeRefTypeId(const RefTypeId& ref_type_id) {
std::string signature("unknown");
- Dbg::GetSignature(ref_type_id, signature);
+ Dbg::GetSignature(ref_type_id, &signature);
return StringPrintf("%#llx (%s)", ref_type_id, signature.c_str());
}
@@ -547,7 +547,7 @@
RefTypeId refTypeId = request.ReadRefTypeId();
std::string signature;
- JdwpError status = Dbg::GetSignature(refTypeId, signature);
+ JdwpError status = Dbg::GetSignature(refTypeId, &signature);
if (status != ERR_NONE) {
return status;
}
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 6a0990e..ec717c1 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -228,6 +228,7 @@
const char* name, const char* sig, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Class* c = soa.Decode<Class*>(jni_class);
+ DCHECK(c != nullptr);
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
return NULL;
}
@@ -324,14 +325,14 @@
return soa.EncodeField(field);
}
-static void PinPrimitiveArray(const ScopedObjectAccess& soa, const Array* array)
+static void PinPrimitiveArray(const ScopedObjectAccess& soa, Array* array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
JavaVMExt* vm = soa.Vm();
MutexLock mu(soa.Self(), vm->pins_lock);
vm->pin_table.Add(array);
}
-static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, const Array* array)
+static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, Array* array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
JavaVMExt* vm = soa.Vm();
MutexLock mu(soa.Self(), vm->pins_lock);
@@ -450,7 +451,7 @@
class_loader_(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()->GetThinLockId()),
+ jni_on_load_thread_id_(Thread::Current()->GetThreadId()),
jni_on_load_result_(kPending) {
}
@@ -475,7 +476,7 @@
{
MutexLock mu(self, jni_on_load_lock_);
- if (jni_on_load_thread_id_ == self->GetThinLockId()) {
+ 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_ << "\"";
@@ -1999,7 +2000,7 @@
CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, java_string);
ScopedObjectAccess soa(env);
String* s = soa.Decode<String*>(java_string);
- const CharArray* chars = s->GetCharArray();
+ CharArray* chars = s->GetCharArray();
PinPrimitiveArray(soa, chars);
if (is_copy != NULL) {
*is_copy = JNI_FALSE;
@@ -2356,9 +2357,9 @@
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
-
+ bool is_fast = false;
if (*sig == '!') {
- // TODO: fast jni. it's too noisy to log all these.
+ is_fast = true;
++sig;
}
@@ -2381,7 +2382,7 @@
VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
- m->RegisterNative(soa.Self(), methods[i].fnPtr);
+ m->RegisterNative(soa.Self(), methods[i].fnPtr, is_fast);
}
return JNI_OK;
}
@@ -3081,15 +3082,6 @@
weak_globals_add_condition_.Broadcast(self);
}
-void JavaVMExt::SweepWeakGlobals(IsMarkedTester is_marked, void* arg) {
- MutexLock mu(Thread::Current(), weak_globals_lock_);
- for (const Object** entry : weak_globals_) {
- if (!is_marked(*entry, arg)) {
- *entry = kClearedJniWeakGlobal;
- }
- }
-}
-
mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
MutexLock mu(self, weak_globals_lock_);
while (UNLIKELY(!allow_new_weak_globals_)) {
@@ -3115,8 +3107,8 @@
}
bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader,
- std::string& detail) {
- detail.clear();
+ 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.
@@ -3134,7 +3126,7 @@
// 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 "
+ StringAppendF(detail, "Shared library \"%s\" already opened by "
"ClassLoader %p; can't open in ClassLoader %p",
path.c_str(), library->GetClassLoader(), class_loader);
LOG(WARNING) << detail;
@@ -3143,7 +3135,7 @@
VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
<< "ClassLoader " << class_loader << "]";
if (!library->CheckOnLoadResult()) {
- StringAppendF(&detail, "JNI_OnLoad failed on a previous attempt "
+ StringAppendF(detail, "JNI_OnLoad failed on a previous attempt "
"to load \"%s\"", path.c_str());
return false;
}
@@ -3170,7 +3162,7 @@
VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
if (handle == NULL) {
- detail = dlerror();
+ *detail = dlerror();
LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << detail;
return false;
}
@@ -3220,9 +3212,9 @@
self->SetClassLoaderOverride(old_class_loader);
if (version == JNI_ERR) {
- StringAppendF(&detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
+ 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",
+ 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.
@@ -3271,6 +3263,18 @@
return native_method;
}
+void JavaVMExt::SweepJniWeakGlobals(RootVisitor visitor, void* arg) {
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ for (mirror::Object** entry : weak_globals_) {
+ mirror::Object* obj = *entry;
+ mirror::Object* new_obj = visitor(obj, arg);
+ if (new_obj == nullptr) {
+ new_obj = kClearedJniWeakGlobal;
+ }
+ *entry = new_obj;
+ }
+}
+
void JavaVMExt::VisitRoots(RootVisitor* visitor, void* arg) {
Thread* self = Thread::Current();
{
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 32d0bfc..888d5e5 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -73,7 +73,7 @@
* human-readable description of the error.
*/
bool LoadNativeLibrary(const std::string& path, mirror::ClassLoader* class_loader,
- std::string& detail)
+ std::string* detail)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/**
@@ -98,7 +98,7 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DeleteWeakGlobalRef(Thread* self, jweak obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepWeakGlobals(IsMarkedTester is_marked, void* arg);
+ void SweepJniWeakGlobals(RootVisitor visitor, void* arg);
mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref);
Runtime* runtime;
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 79d156d..c389580 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -1012,31 +1012,50 @@
scalar_type, \
expected_class_descriptor) \
jsize size = 4; \
+ \
/* Allocate an array and check it has the right type and length. */ \
scalar_type ## Array a = env_->new_fn(size); \
EXPECT_TRUE(a != NULL); \
EXPECT_TRUE(env_->IsInstanceOf(a, env_->FindClass(expected_class_descriptor))); \
EXPECT_EQ(size, env_->GetArrayLength(a)); \
+ \
+ /* GetPrimitiveArrayRegion/SetPrimitiveArrayRegion */ \
/* AIOOBE for negative start offset. */ \
env_->get_region_fn(a, -1, 1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
env_->set_region_fn(a, -1, 1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
+ \
/* AIOOBE for negative length. */ \
env_->get_region_fn(a, 0, -1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
env_->set_region_fn(a, 0, -1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
+ \
/* AIOOBE for buffer overrun. */ \
env_->get_region_fn(a, size - 1, size, NULL); \
EXPECT_EXCEPTION(aioobe_); \
env_->set_region_fn(a, size - 1, size, NULL); \
EXPECT_EXCEPTION(aioobe_); \
+ \
+ /* It's okay for the buffer to be NULL as long as the length is 0. */ \
+ env_->get_region_fn(a, 2, 0, NULL); \
+ /* Even if the offset is invalid... */ \
+ env_->get_region_fn(a, 123, 0, NULL); \
+ EXPECT_EXCEPTION(aioobe_); \
+ \
+ /* It's okay for the buffer to be NULL as long as the length is 0. */ \
+ env_->set_region_fn(a, 2, 0, NULL); \
+ /* Even if the offset is invalid... */ \
+ env_->set_region_fn(a, 123, 0, NULL); \
+ EXPECT_EXCEPTION(aioobe_); \
+ \
/* Prepare a couple of buffers. */ \
UniquePtr<scalar_type[]> src_buf(new scalar_type[size]); \
UniquePtr<scalar_type[]> dst_buf(new scalar_type[size]); \
for (jsize i = 0; i < size; ++i) { src_buf[i] = scalar_type(i); } \
for (jsize i = 0; i < size; ++i) { dst_buf[i] = scalar_type(-1); } \
+ \
/* Copy all of src_buf onto the heap. */ \
env_->set_region_fn(a, 0, size, &src_buf[0]); \
/* Copy back only part. */ \
@@ -1252,6 +1271,12 @@
EXPECT_EQ('l', chars[2]);
EXPECT_EQ('x', chars[3]);
+ // It's okay for the buffer to be NULL as long as the length is 0.
+ env_->GetStringRegion(s, 2, 0, NULL);
+ // Even if the offset is invalid...
+ env_->GetStringRegion(s, 123, 0, NULL);
+ EXPECT_EXCEPTION(sioobe_);
+
env_->GetStringUTFRegion(s, -1, 0, NULL);
EXPECT_EXCEPTION(sioobe_);
env_->GetStringUTFRegion(s, 0, -1, NULL);
@@ -1267,6 +1292,12 @@
EXPECT_EQ('e', bytes[1]);
EXPECT_EQ('l', bytes[2]);
EXPECT_EQ('x', bytes[3]);
+
+ // It's okay for the buffer to be NULL as long as the length is 0.
+ env_->GetStringUTFRegion(s, 2, 0, NULL);
+ // Even if the offset is invalid...
+ env_->GetStringUTFRegion(s, 123, 0, NULL);
+ EXPECT_EXCEPTION(sioobe_);
}
TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h
new file mode 100644
index 0000000..efd3d9d
--- /dev/null
+++ b/runtime/lock_word-inl.h
@@ -0,0 +1,55 @@
+/*
+ * 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_LOCK_WORD_INL_H_
+#define ART_RUNTIME_LOCK_WORD_INL_H_
+
+#include "lock_word.h"
+
+namespace art {
+
+inline uint32_t LockWord::ThinLockOwner() const {
+ DCHECK_EQ(GetState(), kThinLocked);
+ return (value_ >> kThinLockOwnerShift) & kThinLockOwnerMask;
+}
+
+inline uint32_t LockWord::ThinLockCount() const {
+ DCHECK_EQ(GetState(), kThinLocked);
+ return (value_ >> kThinLockCountShift) & kThinLockCountMask;
+}
+
+inline Monitor* LockWord::FatLockMonitor() const {
+ DCHECK_EQ(GetState(), kFatLocked);
+ return reinterpret_cast<Monitor*>(value_ << kStateSize);
+}
+
+inline LockWord::LockWord() : value_(0) {
+ DCHECK_EQ(GetState(), kUnlocked);
+}
+
+inline LockWord::LockWord(Monitor* mon)
+ : value_((reinterpret_cast<uint32_t>(mon) >> kStateSize) | (kStateFat << kStateShift)) {
+ DCHECK_EQ(FatLockMonitor(), mon);
+}
+
+inline int32_t LockWord::GetHashCode() const {
+ DCHECK_EQ(GetState(), kHashCode);
+ return (value_ >> kHashShift) & kHashMask;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_LOCK_WORD_INL_H_
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
new file mode 100644
index 0000000..1882ae6
--- /dev/null
+++ b/runtime/lock_word.h
@@ -0,0 +1,155 @@
+/*
+ * 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_LOCK_WORD_H_
+#define ART_RUNTIME_LOCK_WORD_H_
+
+#include <iosfwd>
+#include <stdint.h>
+
+#include "base/logging.h"
+
+namespace art {
+namespace mirror {
+ class Object;
+} // namespace mirror
+
+class Monitor;
+
+/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of
+ * the state. The three possible states are fat locked, thin/unlocked, and hash code.
+ * When the lock word is in the "thin" state and its bits are formatted as follows:
+ *
+ * |33|22222222221111|1111110000000000|
+ * |10|98765432109876|5432109876543210|
+ * |00| lock count |thread id owner |
+ *
+ * When the lock word is in the "fat" state and its bits are formatted as follows:
+ *
+ * |33|222222222211111111110000000000|
+ * |10|987654321098765432109876543210|
+ * |01| Monitor* >> kStateSize |
+ *
+ * When the lock word is in hash state and its bits are formatted as follows:
+ *
+ * |33|222222222211111111110000000000|
+ * |10|987654321098765432109876543210|
+ * |10| HashCode |
+ */
+class LockWord {
+ public:
+ enum {
+ // Number of bits to encode the state, currently just fat or thin/unlocked or hash code.
+ kStateSize = 2,
+ // Number of bits to encode the thin lock owner.
+ kThinLockOwnerSize = 16,
+ // Remaining bits are the recursive lock count.
+ kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize,
+ // Thin lock bits. Owner in lowest bits.
+
+ kThinLockOwnerShift = 0,
+ kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1,
+ // Count in higher bits.
+ kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift,
+ kThinLockCountMask = (1 << kThinLockCountShift) - 1,
+ kThinLockMaxCount = kThinLockCountMask,
+
+ // State in the highest bits.
+ kStateShift = kThinLockCountSize + kThinLockCountShift,
+ kStateMask = (1 << kStateSize) - 1,
+ kStateThinOrUnlocked = 0,
+ kStateFat = 1,
+ kStateHash = 2,
+
+ // When the state is kHashCode, the non-state bits hold the hashcode.
+ kHashShift = 0,
+ kHashSize = 32 - kStateSize,
+ kHashMask = (1 << kHashSize) - 1,
+ };
+
+ static LockWord FromThinLockId(uint32_t thread_id, uint32_t count) {
+ CHECK_LE(thread_id, static_cast<uint32_t>(kThinLockOwnerMask));
+ return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift) |
+ (kStateThinOrUnlocked << kStateShift));
+ }
+
+ static LockWord FromHashCode(uint32_t hash_code) {
+ CHECK_LE(hash_code, static_cast<uint32_t>(kHashMask));
+ return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift));
+ }
+
+ enum LockState {
+ kUnlocked, // No lock owners.
+ kThinLocked, // Single uncontended owner.
+ kFatLocked, // See associated monitor.
+ kHashCode, // Lock word contains an identity hash.
+ };
+
+ LockState GetState() const {
+ uint32_t internal_state = (value_ >> kStateShift) & kStateMask;
+ if (value_ == 0) {
+ return kUnlocked;
+ } else if (internal_state == kStateThinOrUnlocked) {
+ return kThinLocked;
+ } else if (internal_state == kStateHash) {
+ return kHashCode;
+ } else {
+ DCHECK_EQ(internal_state, static_cast<uint32_t>(kStateFat));
+ return kFatLocked;
+ }
+ }
+
+ // Return the owner thin lock thread id.
+ uint32_t ThinLockOwner() const;
+
+ // Return the number of times a lock value has been locked.
+ uint32_t ThinLockCount() const;
+
+ // Return the Monitor encoded in a fat lock.
+ Monitor* FatLockMonitor() const;
+
+ // Default constructor with no lock ownership.
+ LockWord();
+
+ // Constructor a lock word for inflation to use a Monitor.
+ explicit LockWord(Monitor* mon);
+
+ bool operator==(const LockWord& rhs) const {
+ return GetValue() == rhs.GetValue();
+ }
+
+ // Return the hash code stored in the lock word, must be kHashCode state.
+ int32_t GetHashCode() const;
+
+ uint32_t GetValue() const {
+ return value_;
+ }
+
+ private:
+ explicit LockWord(uint32_t val) : value_(val) {}
+
+ // Only Object should be converting LockWords to/from uints.
+ friend class mirror::Object;
+
+ // The encoded value holding all the state.
+ uint32_t value_;
+};
+std::ostream& operator<<(std::ostream& os, const LockWord::LockState& code);
+
+} // namespace art
+
+
+#endif // ART_RUNTIME_LOCK_WORD_H_
diff --git a/runtime/locks.h b/runtime/locks.h
index f63e2b1..2262218 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -53,8 +53,8 @@
kJdwpAttachLock,
kJdwpStartLock,
kRuntimeShutdownLock,
- kHeapBitmapLock,
kMonitorLock,
+ kHeapBitmapLock,
kMutatorLock,
kZygoteCreationLock,
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 6451d5c..3afb606 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -16,7 +16,7 @@
#include "mem_map.h"
-#include <corkscrew/map_info.h>
+#include <backtrace/backtrace.h>
#include "base/stringprintf.h"
#include "ScopedFd.h"
@@ -32,8 +32,8 @@
#if !defined(NDEBUG)
-static std::ostream& operator<<(std::ostream& os, map_info_t* rhs) {
- for (map_info_t* m = rhs; m != NULL; m = m->next) {
+static std::ostream& operator<<(std::ostream& os, backtrace_map_info_t* rhs) {
+ for (backtrace_map_info_t* m = rhs; m != NULL; m = m->next) {
os << StringPrintf("0x%08x-0x%08x %c%c %s\n",
static_cast<uint32_t>(m->start),
static_cast<uint32_t>(m->end),
@@ -50,8 +50,8 @@
uint32_t base = reinterpret_cast<size_t>(addr);
uint32_t limit = base + byte_count;
- map_info_t* map_info_list = load_map_info_list(getpid());
- for (map_info_t* m = map_info_list; m != NULL; m = m->next) {
+ backtrace_map_info_t* map_info_list = backtrace_create_map_info_list(getpid());
+ for (backtrace_map_info_t* m = map_info_list; m != NULL; m = m->next) {
CHECK(!(base >= m->start && base < m->end) // start of new within old
&& !(limit > m->start && limit < m->end) // end of new within old
&& !(base <= m->start && limit > m->end)) // start/end of new includes all of old
@@ -60,14 +60,15 @@
static_cast<uint32_t>(m->start), static_cast<uint32_t>(m->end), m->name)
<< map_info_list;
}
- free_map_info_list(map_info_list);
+ backtrace_destroy_map_info_list(map_info_list);
}
#else
static void CheckMapRequest(byte*, size_t) { }
#endif
-MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot) {
+MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot,
+ std::string* error_msg) {
if (byte_count == 0) {
return new MemMap(name, NULL, 0, NULL, 0, prot);
}
@@ -82,8 +83,8 @@
ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count));
int flags = MAP_PRIVATE;
if (fd.get() == -1) {
- PLOG(ERROR) << "ashmem_create_region failed (" << name << ")";
- return NULL;
+ *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno));
+ return nullptr;
}
#else
ScopedFd fd(-1);
@@ -94,16 +95,17 @@
if (actual == MAP_FAILED) {
std::string maps;
ReadFileToString("/proc/self/maps", &maps);
- PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_byte_count
- << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name
- << "\n" << maps;
- return NULL;
+ *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s",
+ addr, page_aligned_byte_count, prot, flags, fd.get(),
+ maps.c_str());
+ return nullptr;
}
return new MemMap(name, actual, byte_count, actual, page_aligned_byte_count, prot);
}
-MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count,
- int prot, int flags, int fd, off_t start, bool reuse) {
+MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd,
+ off_t start, bool reuse, const char* filename,
+ std::string* error_msg) {
CHECK_NE(0, prot);
CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
if (byte_count == 0) {
@@ -133,10 +135,10 @@
if (actual == MAP_FAILED) {
std::string maps;
ReadFileToString("/proc/self/maps", &maps);
- PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(page_aligned_addr)
- << ", " << page_aligned_byte_count
- << ", " << prot << ", " << flags << ", " << fd << ", " << page_aligned_offset
- << ") failed\n" << maps;
+ *error_msg = StringPrintf("mmap(%p, %zd, %x, %x, %d, %lld) of file '%s' failed\n%s",
+ page_aligned_addr, page_aligned_byte_count, prot, flags, fd,
+ static_cast<int64_t>(page_aligned_offset),
+ filename, maps.c_str());
return NULL;
}
return new MemMap("file", actual + page_offset, byte_count, actual, page_aligned_byte_count,
@@ -168,12 +170,73 @@
}
};
-void MemMap::UnMapAtEnd(byte* new_end) {
+MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
+ std::string* error_msg) {
DCHECK_GE(new_end, Begin());
DCHECK_LE(new_end, End());
- size_t unmap_size = End() - new_end;
- munmap(new_end, unmap_size);
- size_ -= unmap_size;
+ DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
+ DCHECK(IsAligned<kPageSize>(begin_));
+ DCHECK(IsAligned<kPageSize>(base_begin_));
+ DCHECK(IsAligned<kPageSize>(reinterpret_cast<byte*>(base_begin_) + base_size_));
+ DCHECK(IsAligned<kPageSize>(new_end));
+ byte* old_end = begin_ + size_;
+ byte* old_base_end = reinterpret_cast<byte*>(base_begin_) + base_size_;
+ byte* new_base_end = new_end;
+ DCHECK_LE(new_base_end, old_base_end);
+ if (new_base_end == old_base_end) {
+ return new MemMap(tail_name, NULL, 0, NULL, 0, tail_prot);
+ }
+ size_ = new_end - reinterpret_cast<byte*>(begin_);
+ base_size_ = new_base_end - reinterpret_cast<byte*>(base_begin_);
+ DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
+ size_t tail_size = old_end - new_end;
+ byte* tail_base_begin = new_base_end;
+ size_t tail_base_size = old_base_end - new_base_end;
+ DCHECK_EQ(tail_base_begin + tail_base_size, old_base_end);
+ DCHECK(IsAligned<kPageSize>(tail_base_size));
+
+#ifdef USE_ASHMEM
+ // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
+ // prefixed "dalvik-".
+ std::string debug_friendly_name("dalvik-");
+ debug_friendly_name += tail_name;
+ ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
+ int flags = MAP_PRIVATE;
+ if (fd.get() == -1) {
+ *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
+ tail_name, strerror(errno));
+ return nullptr;
+ }
+#else
+ ScopedFd fd(-1);
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#endif
+
+ // Unmap/map the tail region.
+ int result = munmap(tail_base_begin, tail_base_size);
+ if (result == -1) {
+ std::string maps;
+ ReadFileToString("/proc/self/maps", &maps);
+ *error_msg = StringPrintf("munmap(%p, %zd) failed for '%s'\n%s",
+ tail_base_begin, tail_base_size, name_.c_str(),
+ maps.c_str());
+ return nullptr;
+ }
+ // Don't cause memory allocation between the munmap and the mmap
+ // calls. Otherwise, libc (or something else) might take this memory
+ // region. Note this isn't perfect as there's no way to prevent
+ // other threads to try to take this memory region here.
+ byte* actual = reinterpret_cast<byte*>(mmap(tail_base_begin, tail_base_size, tail_prot,
+ flags, fd.get(), 0));
+ if (actual == MAP_FAILED) {
+ std::string maps;
+ ReadFileToString("/proc/self/maps", &maps);
+ *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s",
+ tail_base_begin, tail_base_size, tail_prot, flags, fd.get(),
+ maps.c_str());
+ return nullptr;
+ }
+ return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot);
}
bool MemMap::Protect(int prot) {
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index e294824..2c65833 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -38,14 +38,16 @@
// a name.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot);
+ static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot,
+ std::string* error_msg);
// Map part of a file, taking care of non-page aligned offsets. The
// "start" offset is absolute, not relative.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start) {
- return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false);
+ static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start,
+ const char* filename, std::string* error_msg) {
+ return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false, filename, error_msg);
}
// Map part of a file, taking care of non-page aligned offsets. The
@@ -53,8 +55,9 @@
// requesting a specific address for the base of the mapping.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* MapFileAtAddress(
- byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start, bool reuse);
+ static MemMap* MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd,
+ off_t start, bool reuse, const char* filename,
+ std::string* error_msg);
// Releases the memory mapping
~MemMap();
@@ -81,8 +84,9 @@
return Begin() <= addr && addr < End();
}
- // Trim by unmapping pages at the end of the map.
- void UnMapAtEnd(byte* new_end);
+ // Unmap the pages at end and remap them to create another memory map.
+ MemMap* RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
+ std::string* error_msg);
private:
MemMap(const std::string& name, byte* begin, size_t size, void* base_begin, size_t base_size,
@@ -93,8 +97,10 @@
size_t size_; // Length of data.
void* const base_begin_; // Page-aligned base address.
- const size_t base_size_; // Length of mapping.
+ size_t base_size_; // Length of mapping. May be changed by RemapAtEnd (ie Zygote).
int prot_; // Protection of the map.
+
+ friend class MemMapTest; // To allow access to base_begin_ and base_size_.
};
} // namespace art
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index dade01b..cf2c9d0 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -21,14 +21,78 @@
namespace art {
-class MemMapTest : public testing::Test {};
+class MemMapTest : public testing::Test {
+ public:
+ byte* BaseBegin(MemMap* mem_map) {
+ return reinterpret_cast<byte*>(mem_map->base_begin_);
+ }
+ size_t BaseSize(MemMap* mem_map) {
+ return mem_map->base_size_;
+ }
+};
TEST_F(MemMapTest, MapAnonymousEmpty) {
+ std::string error_msg;
UniquePtr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty",
NULL,
0,
- PROT_READ));
- ASSERT_TRUE(map.get() != NULL);
+ PROT_READ,
+ &error_msg));
+ ASSERT_TRUE(map.get() != NULL) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+}
+
+TEST_F(MemMapTest, RemapAtEnd) {
+ std::string error_msg;
+ // Cast the page size to size_t.
+ const size_t page_size = static_cast<size_t>(kPageSize);
+ // Map a two-page memory region.
+ MemMap* m0 = MemMap::MapAnonymous("MemMapTest_RemapAtEndTest_map0",
+ NULL,
+ 2 * page_size,
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ // Check its state and write to it.
+ byte* base0 = m0->Begin();
+ ASSERT_TRUE(base0 != NULL) << error_msg;
+ size_t size0 = m0->Size();
+ EXPECT_EQ(m0->Size(), 2 * page_size);
+ EXPECT_EQ(BaseBegin(m0), base0);
+ EXPECT_EQ(BaseSize(m0), size0);
+ memset(base0, 42, 2 * page_size);
+ // Remap the latter half into a second MemMap.
+ MemMap* m1 = m0->RemapAtEnd(base0 + page_size,
+ "MemMapTest_RemapAtEndTest_map1",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ // Check the states of the two maps.
+ EXPECT_EQ(m0->Begin(), base0) << error_msg;
+ EXPECT_EQ(m0->Size(), page_size);
+ EXPECT_EQ(BaseBegin(m0), base0);
+ EXPECT_EQ(BaseSize(m0), page_size);
+ byte* base1 = m1->Begin();
+ size_t size1 = m1->Size();
+ EXPECT_EQ(base1, base0 + page_size);
+ EXPECT_EQ(size1, page_size);
+ EXPECT_EQ(BaseBegin(m1), base1);
+ EXPECT_EQ(BaseSize(m1), size1);
+ // Write to the second region.
+ memset(base1, 43, page_size);
+ // Check the contents of the two regions.
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(base0[i], 42);
+ }
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(base1[i], 43);
+ }
+ // Unmap the first region.
+ delete m0;
+ // Make sure the second region is still accessible after the first
+ // region is unmapped.
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(base1[i], 43);
+ }
+ delete m1;
}
} // namespace art
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index eb73c7d..c60e714 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -20,6 +20,9 @@
#include "array.h"
#include "class.h"
+#include "gc/heap-inl.h"
+#include "thread.h"
+#include "utils.h"
namespace art {
namespace mirror {
@@ -33,6 +36,68 @@
return header_size + data_size;
}
+static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(array_class != NULL);
+ DCHECK_GE(component_count, 0);
+ DCHECK(array_class->IsArrayClass());
+
+ size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
+ size_t data_size = component_count * component_size;
+ 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)) {
+ self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
+ PrettyDescriptor(array_class).c_str(),
+ component_count).c_str());
+ return 0; // failure
+ }
+ return size;
+}
+
+static inline Array* SetArrayLength(Array* array, size_t length) {
+ if (LIKELY(array != NULL)) {
+ DCHECK(array->IsArrayInstance());
+ array->SetLength(length);
+ }
+ return array;
+}
+
+inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size) {
+ size_t size = ComputeArraySize(self, array_class, component_count, component_size);
+ if (UNLIKELY(size == 0)) {
+ return NULL;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ Array* array = down_cast<Array*>(heap->AllocObjectInstrumented(self, array_class, size));
+ return SetArrayLength(array, component_count);
+}
+
+inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size) {
+ size_t size = ComputeArraySize(self, array_class, component_count, component_size);
+ if (UNLIKELY(size == 0)) {
+ return NULL;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ Array* array = down_cast<Array*>(heap->AllocObjectUninstrumented(self, array_class, size));
+ return SetArrayLength(array, component_count);
+}
+
+inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) {
+ DCHECK(array_class->IsArrayClass());
+ return AllocInstrumented(self, array_class, component_count, array_class->GetComponentSize());
+}
+
+inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) {
+ DCHECK(array_class->IsArrayClass());
+ return AllocUninstrumented(self, array_class, component_count, array_class->GetComponentSize());
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 88cd309..020085d 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -32,39 +32,6 @@
namespace art {
namespace mirror {
-Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size) {
- DCHECK(array_class != NULL);
- DCHECK_GE(component_count, 0);
- DCHECK(array_class->IsArrayClass());
-
- size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
- size_t data_size = component_count * component_size;
- 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)) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- PrettyDescriptor(array_class).c_str(),
- component_count).c_str());
- return NULL;
- }
-
- gc::Heap* heap = Runtime::Current()->GetHeap();
- Array* array = down_cast<Array*>(heap->AllocObject(self, array_class, size));
- if (array != NULL) {
- DCHECK(array->IsArrayInstance());
- array->SetLength(component_count);
- }
- return array;
-}
-
-Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) {
- DCHECK(array_class->IsArrayClass());
- return Alloc(self, array_class, component_count, array_class->GetComponentSize());
-}
-
// Create a multi-dimensional array of Objects or primitive types.
//
// We have to generate the names for X[], X[][], X[][][], and so on. The
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index db6132d..570dcaa 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -27,10 +27,24 @@
// A convenience for code that doesn't know the component size,
// and doesn't want to have to work it out itself.
static Array* Alloc(Thread* self, Class* array_class, int32_t component_count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocInstrumented(self, array_class, component_count);
+ }
+ static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
size_t component_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocInstrumented(self, array_class, component_count, component_size);
+ }
+ static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions)
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index ccf3e59..c9bf160 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -202,6 +202,13 @@
DCHECK(!result || IsRuntimeMethod());
return result;
}
+
+inline bool ArtMethod::IsImtConflictMethod() const {
+ bool result = this == Runtime::Current()->GetImtConflictMethod();
+ // Check that if we do think it is phony it looks like the imt conflict method.
+ DCHECK(!result || IsRuntimeMethod());
+ return result;
+}
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index cd05f41..f5c0e9f 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -306,11 +306,15 @@
}
extern "C" void art_work_around_app_jni_bugs(JNIEnv*, jobject);
-void ArtMethod::RegisterNative(Thread* self, const void* native_method) {
+void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) {
DCHECK(Thread::Current() == self);
CHECK(IsNative()) << PrettyMethod(this);
+ CHECK(!IsFastNative()) << PrettyMethod(this);
CHECK(native_method != NULL) << PrettyMethod(this);
if (!self->GetJniEnv()->vm->work_around_app_jni_bugs) {
+ if (is_fast) {
+ SetAccessFlags(GetAccessFlags() | kAccFastNative);
+ }
SetNativeMethod(native_method);
} else {
// We've been asked to associate this method with the given native method but are working
@@ -328,9 +332,9 @@
}
void ArtMethod::UnregisterNative(Thread* self) {
- CHECK(IsNative()) << PrettyMethod(this);
+ CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
// restore stub to lookup native pointer via dlsym
- RegisterNative(self, GetJniDlsymLookupStub());
+ RegisterNative(self, GetJniDlsymLookupStub(), false);
}
void ArtMethod::SetNativeMethod(const void* native_method) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 5d4a6ea..f396fbe 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -112,6 +112,10 @@
return (GetAccessFlags() & kAccNative) != 0;
}
+ bool IsFastNative() const {
+ return (GetAccessFlags() & kAccFastNative) != 0;
+ }
+
bool IsAbstract() const {
return (GetAccessFlags() & kAccAbstract) != 0;
}
@@ -307,7 +311,7 @@
bool IsRegistered() const;
- void RegisterNative(Thread* self, const void* native_method)
+ void RegisterNative(Thread* self, const void* native_method, bool is_fast)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -353,6 +357,8 @@
bool IsResolutionMethod() const;
+ bool IsImtConflictMethod() const;
+
uintptr_t NativePcOffset(const uintptr_t pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Converts a native PC to a dex PC.
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 1e11387..7f3a302 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -23,6 +23,7 @@
#include "art_method.h"
#include "class_loader.h"
#include "dex_cache.h"
+#include "gc/heap-inl.h"
#include "iftable.h"
#include "object_array-inl.h"
#include "runtime.h"
@@ -34,9 +35,7 @@
inline size_t Class::GetObjectSize() const {
DCHECK(!IsVariableSize()) << " class=" << PrettyTypeOf(this);
DCHECK_EQ(sizeof(size_t), sizeof(int32_t));
- size_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), false);
- DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(this);
- return result;
+ return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), false);
}
inline Class* Class::GetSuperClass() const {
@@ -140,6 +139,14 @@
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false);
}
+inline ObjectArray<ArtMethod>* Class::GetImTable() const {
+ return GetFieldObject<ObjectArray<ArtMethod>*>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), false);
+}
+
+inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) {
+ SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false);
+}
+
inline bool Class::Implements(const Class* klass) const {
DCHECK(klass != NULL);
DCHECK(klass->IsInterface()) << PrettyClass(this);
@@ -342,6 +349,24 @@
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
}
+inline void Class::CheckObjectAlloc() {
+ DCHECK(!IsArrayClass()) << PrettyClass(this);
+ DCHECK(IsInstantiable()) << PrettyClass(this);
+ // TODO: decide whether we want this check. It currently fails during bootstrap.
+ // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this);
+ DCHECK_GE(this->object_size_, sizeof(Object));
+}
+
+inline Object* Class::AllocObjectInstrumented(Thread* self) {
+ CheckObjectAlloc();
+ return Runtime::Current()->GetHeap()->AllocObjectInstrumented(self, this, this->object_size_);
+}
+
+inline Object* Class::AllocObjectUninstrumented(Thread* self) {
+ CheckObjectAlloc();
+ return Runtime::Current()->GetHeap()->AllocObjectUninstrumented(self, this, this->object_size_);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index add7e1b..f3cb54a 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -60,7 +60,7 @@
}
if (new_status >= kStatusResolved || old_status >= kStatusResolved) {
// When classes are being resolved the resolution code should hold the lock.
- CHECK_EQ(GetThinLockId(), self->GetThinLockId())
+ CHECK_EQ(GetLockOwnerThreadId(), self->GetThreadId())
<< "Attempt to change status of class while not holding its lock: "
<< PrettyClass(this) << " " << old_status << " -> " << new_status;
}
@@ -118,15 +118,6 @@
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false);
}
-Object* Class::AllocObject(Thread* self) {
- DCHECK(!IsArrayClass()) << PrettyClass(this);
- DCHECK(IsInstantiable()) << PrettyClass(this);
- // TODO: decide whether we want this check. It currently fails during bootstrap.
- // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this);
- DCHECK_GE(this->object_size_, sizeof(Object));
- return Runtime::Current()->GetHeap()->AllocObject(self, this, this->object_size_);
-}
-
void Class::SetClassSize(size_t new_class_size) {
if (kIsDebugBuild && (new_class_size < GetClassSize())) {
DumpClass(LOG(ERROR), kDumpClassFullDetail);
@@ -334,7 +325,7 @@
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
}
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const StringPiece& signature) const {
+ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature) const {
// Check the current class before checking the interfaces.
ArtMethod* method = FindDeclaredVirtualMethod(name, signature);
if (method != NULL) {
@@ -370,12 +361,23 @@
return NULL;
}
-
ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const {
MethodHelper mh;
for (size_t i = 0; i < NumDirectMethods(); ++i) {
ArtMethod* method = GetDirectMethod(i);
mh.ChangeMethod(method);
+ if (name == mh.GetName() && mh.GetSignature() == signature) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
+ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const Signature& signature) const {
+ MethodHelper mh;
+ for (size_t i = 0; i < NumDirectMethods(); ++i) {
+ ArtMethod* method = GetDirectMethod(i);
+ mh.ChangeMethod(method);
if (name == mh.GetName() && signature == mh.GetSignature()) {
return method;
}
@@ -405,6 +407,16 @@
return NULL;
}
+ArtMethod* Class::FindDirectMethod(const StringPiece& name, const Signature& signature) const {
+ for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+ ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtMethod* Class::FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const {
for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
ArtMethod* method = klass->FindDeclaredDirectMethod(dex_cache, dex_method_idx);
@@ -415,8 +427,20 @@
return NULL;
}
+ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const {
+ MethodHelper mh;
+ for (size_t i = 0; i < NumVirtualMethods(); ++i) {
+ ArtMethod* method = GetVirtualMethod(i);
+ mh.ChangeMethod(method);
+ if (name == mh.GetName() && mh.GetSignature() == signature) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature) const {
+ const Signature& signature) const {
MethodHelper mh;
for (size_t i = 0; i < NumVirtualMethods(); ++i) {
ArtMethod* method = GetVirtualMethod(i);
@@ -450,6 +474,16 @@
return NULL;
}
+ArtMethod* Class::FindVirtualMethod(const StringPiece& name, const Signature& signature) const {
+ for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+ ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtMethod* Class::FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const {
for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
ArtMethod* method = klass->FindDeclaredVirtualMethod(dex_cache, dex_method_idx);
@@ -460,6 +494,22 @@
return NULL;
}
+ArtMethod* Class::FindClassInitializer() const {
+ for (size_t i = 0; i < NumDirectMethods(); ++i) {
+ ArtMethod* method = GetDirectMethod(i);
+ if (method->IsConstructor() && method->IsStatic()) {
+ if (kIsDebugBuild) {
+ MethodHelper mh(method);
+ CHECK(mh.IsClassInitializer());
+ CHECK_STREQ(mh.GetName(), "<clinit>");
+ CHECK_STREQ(mh.GetSignature().ToString().c_str(), "()V");
+ }
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type) {
// Is the field in this class?
// Interfaces are not relevant because they can't contain instance fields.
@@ -611,7 +661,9 @@
for (int32_t index = 0, end = methods->GetLength(); index < end; ++index) {
mirror::ArtMethod* method = methods->GetWithoutChecks(index);
DCHECK(method != NULL);
- method->SetPreverified();
+ if (!method->IsNative() && !method->IsAbstract()) {
+ method->SetPreverified();
+ }
}
}
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d97b603..ed1aad3 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -59,6 +59,7 @@
struct ClassClassOffsets;
struct ClassOffsets;
+class Signature;
class StringPiece;
namespace mirror {
@@ -121,6 +122,7 @@
kStatusVerified = 7, // Logically part of linking; done pre-init.
kStatusInitializing = 8, // Class init in progress.
kStatusInitialized = 9, // Ready to go.
+ kStatusMax = 10,
};
Status GetStatus() const {
@@ -246,7 +248,7 @@
} else {
Class* component = GetComponentType();
if (component->IsPrimitive()) {
- return false;
+ return true;
} else {
return component->CannotBeAssignedFromOtherTypes();
}
@@ -345,14 +347,18 @@
bool IsArtMethodClass() const;
+ static MemberOffset ComponentTypeOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(Class, component_type_);
+ }
+
Class* GetComponentType() const {
- return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), false);
+ return GetFieldObject<Class*>(ComponentTypeOffset(), false);
}
void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(GetComponentType() == NULL);
DCHECK(new_component_type != NULL);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), new_component_type, false);
+ SetFieldObject(ComponentTypeOffset(), new_component_type, false);
}
size_t GetComponentSize() const {
@@ -371,7 +377,12 @@
}
// Creates a raw object instance but does not invoke the default constructor.
- Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocObjectInstrumented(self);
+ }
+
+ Object* AllocObjectUninstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Object* AllocObjectInstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsVariableSize() const {
// Classes and arrays vary in size, and so the object_size_ field cannot
@@ -544,6 +555,15 @@
return OFFSET_OF_OBJECT_MEMBER(Class, vtable_);
}
+ ObjectArray<ArtMethod>* GetImTable() const;
+
+ void SetImTable(ObjectArray<ArtMethod>* new_imtable)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static MemberOffset ImTableOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(Class, imtable_);
+ }
+
// Given a method implemented by this class but potentially from a super class, return the
// specific implementation method for this class.
ArtMethod* FindVirtualMethodForVirtual(ArtMethod* method) const
@@ -560,39 +580,53 @@
ArtMethod* FindVirtualMethodForInterface(ArtMethod* method) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE;
- ArtMethod* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const
+ ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindInterfaceMethod(const StringPiece& name, const Signature& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ArtMethod* FindInterfaceMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- ArtMethod* FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& descriptor) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- ArtMethod* FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const Signature& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
ArtMethod* FindDeclaredDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ArtMethod* FindDirectMethod(const StringPiece& name, const StringPiece& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ArtMethod* FindDirectMethod(const StringPiece& name, const Signature& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
ArtMethod* FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const Signature& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindVirtualMethod(const StringPiece& name, const Signature& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindClassInitializer() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
int32_t GetIfTableCount() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
IfTable* GetIfTable() const;
@@ -764,6 +798,8 @@
bool IsAssignableFromArray(const Class* klass) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// defining class loader, or NULL for the "bootstrap" system loader
ClassLoader* class_loader_;
@@ -803,6 +839,9 @@
// methods for the methods in the interface.
IfTable* iftable_;
+ // Interface method table (imt), for quick "invoke-interface".
+ ObjectArray<ArtMethod>* imtable_;
+
// descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
String* name_;
@@ -885,6 +924,7 @@
class MANAGED ClassClass : public Class {
private:
+ int32_t pad_;
int64_t serialVersionUID_;
friend struct art::ClassClassOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassClass);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 5ed3db3..7ac2c8c 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -24,6 +24,7 @@
#include "atomic.h"
#include "array-inl.h"
#include "class.h"
+#include "lock_word-inl.h"
#include "monitor.h"
#include "runtime.h"
#include "throwable.h"
@@ -43,8 +44,21 @@
SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false);
}
-inline uint32_t Object::GetThinLockId() {
- return Monitor::GetThinLockId(monitor_);
+inline LockWord Object::GetLockWord() const {
+ return LockWord(GetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), true));
+}
+
+inline void Object::SetLockWord(LockWord new_val) {
+ SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
+}
+
+inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) {
+ return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
+ new_val.GetValue());
+}
+
+inline uint32_t Object::GetLockOwnerThreadId() {
+ return Monitor::GetLockOwnerThreadId(this);
}
inline void Object::MonitorEnter(Thread* self) {
@@ -233,11 +247,19 @@
} else {
result = GetClass()->GetObjectSize();
}
+ DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass());
DCHECK(!IsArtField() || result == sizeof(ArtField));
DCHECK(!IsArtMethod() || result == sizeof(ArtMethod));
return result;
}
+inline bool Object::CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value) {
+ VerifyObject(this);
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ int32_t* addr = reinterpret_cast<int32_t*>(raw_addr);
+ return android_atomic_release_cas(old_value, new_value, addr) == 0;
+}
+
inline uint64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) const {
VerifyObject(this);
const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 92c05b2..bd187c1 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <ctime>
+
#include "object.h"
#include "art_field.h"
@@ -82,6 +84,52 @@
return copy.get();
}
+int32_t Object::GenerateIdentityHashCode() {
+ static AtomicInteger seed(987654321 + std::time(nullptr));
+ int32_t expected_value, new_value;
+ do {
+ expected_value = static_cast<uint32_t>(seed.load());
+ new_value = expected_value * 1103515245 + 12345;
+ } while ((expected_value & LockWord::kHashMask) == 0 ||
+ !seed.compare_and_swap(expected_value, new_value));
+ return expected_value & LockWord::kHashMask;
+}
+
+int32_t Object::IdentityHashCode() const {
+ while (true) {
+ LockWord lw = GetLockWord();
+ switch (lw.GetState()) {
+ case LockWord::kUnlocked: {
+ // Try to compare and swap in a new hash, if we succeed we will return the hash on the next
+ // loop iteration.
+ LockWord hash_word(LockWord::FromHashCode(GenerateIdentityHashCode()));
+ DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode);
+ if (const_cast<Object*>(this)->CasLockWord(lw, hash_word)) {
+ return hash_word.GetHashCode();
+ }
+ break;
+ }
+ case LockWord::kThinLocked: {
+ // Inflate the thin lock to a monitor and stick the hash code inside of the monitor.
+ Thread* self = Thread::Current();
+ Monitor::InflateThinLocked(self, const_cast<Object*>(this), lw, GenerateIdentityHashCode());
+ break;
+ }
+ case LockWord::kFatLocked: {
+ // Already inflated, return the has stored in the monitor.
+ Monitor* monitor = lw.FatLockMonitor();
+ DCHECK(monitor != nullptr);
+ return monitor->GetHashCode();
+ }
+ case LockWord::kHashCode: {
+ return lw.GetHashCode();
+ }
+ }
+ }
+ LOG(FATAL) << "Unreachable";
+ return 0;
+}
+
void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, const Object* new_value) {
const Class* c = GetClass();
if (Runtime::Current()->GetClassLinker() == NULL ||
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index e105525..e8ea3f2 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -26,6 +26,8 @@
namespace art {
class ImageWriter;
+class LockWord;
+class Monitor;
struct ObjectOffsets;
class Thread;
@@ -83,26 +85,16 @@
Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- int32_t IdentityHashCode() const {
-#ifdef MOVING_GARBAGE_COLLECTOR
- // TODO: we'll need to use the Object's internal concept of identity
- UNIMPLEMENTED(FATAL);
-#endif
- return reinterpret_cast<int32_t>(this);
- }
+ int32_t IdentityHashCode() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static MemberOffset MonitorOffset() {
return OFFSET_OF_OBJECT_MEMBER(Object, monitor_);
}
- volatile int32_t* GetRawLockWordAddress() {
- byte* raw_addr = reinterpret_cast<byte*>(this) +
- OFFSET_OF_OBJECT_MEMBER(Object, monitor_).Int32Value();
- int32_t* word_addr = reinterpret_cast<int32_t*>(raw_addr);
- return const_cast<volatile int32_t*>(word_addr);
- }
-
- uint32_t GetThinLockId();
+ LockWord GetLockWord() const;
+ void SetLockWord(LockWord new_val);
+ bool CasLockWord(LockWord old_val, LockWord new_val);
+ uint32_t GetLockOwnerThreadId();
void MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCK_FUNCTION(monitor_lock_);
@@ -189,6 +181,11 @@
}
}
+ Object** GetFieldObjectAddr(MemberOffset field_offset) ALWAYS_INLINE {
+ VerifyObject(this);
+ return reinterpret_cast<Object**>(reinterpret_cast<byte*>(this) + field_offset.Int32Value());
+ }
+
uint32_t GetField32(MemberOffset field_offset, bool is_volatile) const {
VerifyObject(this);
const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
@@ -221,6 +218,8 @@
}
}
+ bool CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value);
+
uint64_t GetField64(MemberOffset field_offset, bool is_volatile) const;
void SetField64(MemberOffset field_offset, uint64_t new_value, bool is_volatile);
@@ -239,7 +238,6 @@
private:
static void VerifyObject(const Object* obj) ALWAYS_INLINE;
-
// Verify the type correctness of stores to fields.
void CheckFieldAssignmentImpl(MemberOffset field_offset, const Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -250,6 +248,9 @@
}
}
+ // Generate an identity hash code.
+ static int32_t GenerateIdentityHashCode();
+
// Write barrier called post update to a reference bearing field.
static void WriteBarrierField(const Object* dst, MemberOffset offset, const Object* new_value);
@@ -258,6 +259,7 @@
uint32_t monitor_;
friend class art::ImageWriter;
+ friend class art::Monitor;
friend struct art::ObjectOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
};
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index b8765af..d0d1ee4 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -71,12 +71,21 @@
// Keep the assembly code in sync
TEST_F(ObjectTest, AsmConstants) {
- ASSERT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value());
- ASSERT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value());
- ASSERT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value());
- ASSERT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
+ EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value());
+ EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value());
- ASSERT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value());
+ EXPECT_EQ(CLASS_COMPONENT_TYPE_OFFSET, Class::ComponentTypeOffset().Int32Value());
+
+ EXPECT_EQ(ARRAY_LENGTH_OFFSET, Array::LengthOffset().Int32Value());
+ EXPECT_EQ(OBJECT_ARRAY_DATA_OFFSET, Array::DataOffset(sizeof(Object*)).Int32Value());
+
+ EXPECT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value());
+ EXPECT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value());
+ EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value());
+ EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
+
+ EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ EXPECT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value());
}
TEST_F(ObjectTest, IsInSamePackage) {
@@ -262,7 +271,7 @@
Class* klass =
class_linker_->FindClass("LStaticsFromCode;", soa.Decode<ClassLoader*>(class_loader));
- ArtMethod* clinit = klass->FindDirectMethod("<clinit>", "()V");
+ ArtMethod* clinit = klass->FindClassInitializer();
const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;");
ASSERT_TRUE(klass_string_id != NULL);
const DexFile::TypeId* klass_type_id = dex_file->FindTypeId(
@@ -283,8 +292,8 @@
ASSERT_TRUE(field_id != NULL);
uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id);
- ArtField* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), StaticObjectRead,
- sizeof(Object*), true);
+ ArtField* field = FindFieldFromCode<StaticObjectRead, true>(field_idx, clinit, Thread::Current(),
+ sizeof(Object*));
Object* s0 = field->GetObj(klass);
EXPECT_TRUE(s0 != NULL);
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index a505ed0..9d76c6b 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -17,6 +17,7 @@
#include "stack_trace_element.h"
#include "class.h"
+#include "class-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "object-inl.h"
#include "string.h"
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index f8a0e53..9c93f17 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -17,13 +17,14 @@
#include "string.h"
#include "array.h"
+#include "class-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "intern_table.h"
#include "object-inl.h"
#include "runtime.h"
#include "sirt_ref.h"
#include "thread.h"
-#include "utf.h"
+#include "utf-inl.h"
namespace art {
namespace mirror {
@@ -32,6 +33,10 @@
return GetFieldObject<const CharArray*>(ValueOffset(), false);
}
+CharArray* String::GetCharArray() {
+ return GetFieldObject<CharArray*>(ValueOffset(), false);
+}
+
void String::ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()));
}
@@ -285,4 +290,3 @@
} // namespace mirror
} // namespace art
-
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 81fe42f..01d8f31 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -44,6 +44,7 @@
}
const CharArray* GetCharArray() const;
+ CharArray* GetCharArray();
int32_t GetOffset() const {
int32_t result = GetField32(OffsetOffset(), false);
@@ -155,8 +156,8 @@
private:
CharArray* ASCII_;
Object* CASE_INSENSITIVE_ORDER_;
- int64_t serialVersionUID_;
uint32_t REPLACEMENT_CHAR_;
+ int64_t serialVersionUID_;
friend struct art::StringClassOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(StringClass);
};
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 34f4af8..4e365be 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -46,6 +46,7 @@
static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
static const uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
static const uint32_t kAccPreverified = 0x00080000; // method (dex only)
+static const uint32_t kAccFastNative = 0x0080000; // method (dex only)
// Special runtime-only flags.
// Note: if only kAccClassIsReference is set, we have a soft reference.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 088d1f7..2abfd3d 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -23,6 +23,7 @@
#include "class_linker.h"
#include "dex_file-inl.h"
#include "dex_instruction.h"
+#include "lock_word-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
@@ -37,36 +38,20 @@
namespace art {
/*
- * Every Object has a monitor associated with it, but not every Object is
- * actually locked. Even the ones that are locked do not need a
- * full-fledged monitor until a) there is actual contention or b) wait()
- * is called on the Object.
+ * Every Object has a monitor associated with it, but not every Object is actually locked. Even
+ * the ones that are locked do not need a full-fledged monitor until a) there is actual contention
+ * or b) wait() is called on the Object.
*
- * For Android, we have implemented a scheme similar to the one described
- * in Bacon et al.'s "Thin locks: featherweight synchronization for Java"
- * (ACM 1998). Things are even easier for us, though, because we have
- * a full 32 bits to work with.
+ * For Android, we have implemented a scheme similar to the one described in Bacon et al.'s
+ * "Thin locks: featherweight synchronization for Java" (ACM 1998). Things are even easier for us,
+ * though, because we have a full 32 bits to work with.
*
- * The two states of an Object's lock are referred to as "thin" and
- * "fat". A lock may transition from the "thin" state to the "fat"
- * state and this transition is referred to as inflation. Once a lock
- * has been inflated it remains in the "fat" state indefinitely.
+ * The two states of an Object's lock are referred to as "thin" and "fat". A lock may transition
+ * from the "thin" state to the "fat" state and this transition is referred to as inflation. Once
+ * a lock has been inflated it remains in the "fat" state indefinitely.
*
- * The lock value itself is stored in Object.lock. The LSB of the
- * lock encodes its state. When cleared, the lock is in the "thin"
- * state and its bits are formatted as follows:
- *
- * [31 ---- 19] [18 ---- 3] [2 ---- 1] [0]
- * lock count thread id hash state 0
- *
- * When set, the lock is in the "fat" state and its bits are formatted
- * as follows:
- *
- * [31 ---- 3] [2 ---- 1] [0]
- * pointer hash state 1
- *
- * For an in-depth description of the mechanics of thin-vs-fat locking,
- * read the paper referred to above.
+ * The lock value itself is stored in mirror::Object::monitor_ and the representation is described
+ * in the LockWord value type.
*
* Monitors provide:
* - mutually exclusive access to resources
@@ -74,32 +59,11 @@
*
* In effect, they fill the role of both mutexes and condition variables.
*
- * Only one thread can own the monitor at any time. There may be several
- * threads waiting on it (the wait call unlocks it). One or more waiting
- * threads may be getting interrupted or notified at any given time.
- *
- * TODO: the various members of monitor are not SMP-safe.
+ * Only one thread can own the monitor at any time. There may be several threads waiting on it
+ * (the wait call unlocks it). One or more waiting threads may be getting interrupted or notified
+ * at any given time.
*/
-// The shape is the bottom bit; either LW_SHAPE_THIN or LW_SHAPE_FAT.
-#define LW_SHAPE_MASK 0x1
-#define LW_SHAPE(x) static_cast<int>((x) & LW_SHAPE_MASK)
-
-/*
- * Monitor accessor. Extracts a monitor structure pointer from a fat
- * lock. Performs no error checking.
- */
-#define LW_MONITOR(x) \
- (reinterpret_cast<Monitor*>((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | LW_SHAPE_MASK)))
-
-/*
- * Lock recursion count field. Contains a count of the number of times
- * a lock has been recursively acquired.
- */
-#define LW_LOCK_COUNT_MASK 0x1fff
-#define LW_LOCK_COUNT_SHIFT 19
-#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK)
-
bool (*Monitor::is_sensitive_thread_hook_)() = NULL;
uint32_t Monitor::lock_profiling_threshold_ = 0;
@@ -115,31 +79,69 @@
is_sensitive_thread_hook_ = is_sensitive_thread_hook;
}
-Monitor::Monitor(Thread* owner, mirror::Object* obj)
+Monitor::Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code)
: monitor_lock_("a monitor lock", kMonitorLock),
+ monitor_contenders_("monitor contenders", monitor_lock_),
owner_(owner),
lock_count_(0),
obj_(obj),
wait_set_(NULL),
+ hash_code_(hash_code),
locking_method_(NULL),
locking_dex_pc_(0) {
- monitor_lock_.Lock(owner);
- // Propagate the lock state.
- uint32_t thin = *obj->GetRawLockWordAddress();
- lock_count_ = LW_LOCK_COUNT(thin);
- thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT;
- thin |= reinterpret_cast<uint32_t>(this) | LW_SHAPE_FAT;
- // Publish the updated lock word.
- android_atomic_release_store(thin, obj->GetRawLockWordAddress());
- // Lock profiling.
- if (lock_profiling_threshold_ != 0) {
- locking_method_ = owner->GetCurrentMethod(&locking_dex_pc_);
+ // We should only inflate a lock if the owner is ourselves or suspended. This avoids a race
+ // with the owner unlocking the thin-lock.
+ CHECK(owner == nullptr || owner == Thread::Current() || owner->IsSuspended());
+ // The identity hash code is set for the life time of the monitor.
+}
+
+int32_t Monitor::GetHashCode() {
+ while (!HasHashCode()) {
+ if (hash_code_.compare_and_swap(0, mirror::Object::GenerateIdentityHashCode())) {
+ break;
+ }
}
+ DCHECK(HasHashCode());
+ return hash_code_.load();
+}
+
+bool Monitor::Install(Thread* self) {
+ MutexLock mu(self, monitor_lock_); // Uncontended mutex acquisition as monitor isn't yet public.
+ CHECK(owner_ == nullptr || owner_ == self || owner_->IsSuspended());
+ // Propagate the lock state.
+ LockWord lw(obj_->GetLockWord());
+ switch (lw.GetState()) {
+ case LockWord::kThinLocked: {
+ CHECK_EQ(owner_->GetThreadId(), lw.ThinLockOwner());
+ lock_count_ = lw.ThinLockCount();
+ break;
+ }
+ case LockWord::kHashCode: {
+ CHECK_EQ(hash_code_, static_cast<int32_t>(lw.GetHashCode()));
+ break;
+ }
+ case LockWord::kFatLocked: {
+ // The owner_ is suspended but another thread beat us to install a monitor.
+ return false;
+ }
+ case LockWord::kUnlocked: {
+ LOG(FATAL) << "Inflating unlocked lock word";
+ break;
+ }
+ }
+ LockWord fat(this);
+ // Publish the updated lock word, which may race with other threads.
+ bool success = obj_->CasLockWord(lw, fat);
+ // Lock profiling.
+ if (success && owner_ != nullptr && lock_profiling_threshold_ != 0) {
+ locking_method_ = owner_->GetCurrentMethod(&locking_dex_pc_);
+ }
+ return success;
}
Monitor::~Monitor() {
- DCHECK(obj_ != NULL);
- DCHECK_EQ(LW_SHAPE(*obj_->GetRawLockWordAddress()), LW_SHAPE_FAT);
+ CHECK(obj_ != NULL);
+ CHECK_EQ(obj_->GetLockWord().GetState(), LockWord::kFatLocked);
}
/*
@@ -190,60 +192,56 @@
}
}
-mirror::Object* Monitor::GetObject() {
- return obj_;
+void Monitor::SetObject(mirror::Object* object) {
+ obj_ = object;
}
void Monitor::Lock(Thread* self) {
- if (owner_ == self) {
- lock_count_++;
- return;
- }
-
- if (!monitor_lock_.TryLock(self)) {
- uint64_t waitStart = 0;
- uint64_t waitEnd = 0;
- uint32_t wait_threshold = lock_profiling_threshold_;
- const mirror::ArtMethod* current_locking_method = NULL;
- uint32_t current_locking_dex_pc = 0;
+ MutexLock mu(self, monitor_lock_);
+ while (true) {
+ if (owner_ == NULL) { // Unowned.
+ owner_ = self;
+ CHECK_EQ(lock_count_, 0);
+ // When debugging, save the current monitor holder for future
+ // acquisition failures to use in sampled logging.
+ if (lock_profiling_threshold_ != 0) {
+ locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
+ }
+ return;
+ } else if (owner_ == self) { // Recursive.
+ lock_count_++;
+ return;
+ }
+ // Contended.
+ const bool log_contention = (lock_profiling_threshold_ != 0);
+ uint64_t wait_start_ms = log_contention ? 0 : MilliTime();
+ const mirror::ArtMethod* owners_method = locking_method_;
+ uint32_t owners_dex_pc = locking_dex_pc_;
+ monitor_lock_.Unlock(self); // Let go of locks in order.
{
- ScopedThreadStateChange tsc(self, kBlocked);
- if (wait_threshold != 0) {
- waitStart = NanoTime() / 1000;
- }
- current_locking_method = locking_method_;
- current_locking_dex_pc = locking_dex_pc_;
-
- monitor_lock_.Lock(self);
- if (wait_threshold != 0) {
- waitEnd = NanoTime() / 1000;
+ ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_.
+ MutexLock mu2(self, monitor_lock_); // Reacquire monitor_lock_ without mutator_lock_ for Wait.
+ if (owner_ != NULL) { // Did the owner_ give the lock up?
+ monitor_contenders_.Wait(self); // Still contended so wait.
+ // Woken from contention.
+ if (log_contention) {
+ uint64_t wait_ms = MilliTime() - wait_start_ms;
+ uint32_t sample_percent;
+ if (wait_ms >= lock_profiling_threshold_) {
+ sample_percent = 100;
+ } else {
+ sample_percent = 100 * wait_ms / lock_profiling_threshold_;
+ }
+ if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
+ const char* owners_filename;
+ uint32_t owners_line_number;
+ TranslateLocation(owners_method, owners_dex_pc, &owners_filename, &owners_line_number);
+ LogContentionEvent(self, wait_ms, sample_percent, owners_filename, owners_line_number);
+ }
+ }
}
}
-
- if (wait_threshold != 0) {
- uint64_t wait_ms = (waitEnd - waitStart) / 1000;
- uint32_t sample_percent;
- if (wait_ms >= wait_threshold) {
- sample_percent = 100;
- } else {
- sample_percent = 100 * wait_ms / wait_threshold;
- }
- if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
- const char* current_locking_filename;
- uint32_t current_locking_line_number;
- TranslateLocation(current_locking_method, current_locking_dex_pc,
- current_locking_filename, current_locking_line_number);
- LogContentionEvent(self, wait_ms, sample_percent, current_locking_filename, current_locking_line_number);
- }
- }
- }
- owner_ = self;
- DCHECK_EQ(lock_count_, 0);
-
- // When debugging, save the current monitor holder for future
- // acquisition failures to use in sampled logging.
- if (lock_profiling_threshold_ != 0) {
- locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
+ monitor_lock_.Lock(self); // Reacquire locks in order.
}
}
@@ -257,11 +255,11 @@
Thread* self = Thread::Current();
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewExceptionV(throw_location, "Ljava/lang/IllegalMonitorStateException;", fmt, args);
- if (!Runtime::Current()->IsStarted()) {
+ if (!Runtime::Current()->IsStarted() || VLOG_IS_ON(monitor)) {
std::ostringstream ss;
self->Dump(ss);
- std::string str(ss.str());
- LOG(ERROR) << "IllegalMonitorStateException: " << str;
+ LOG(Runtime::Current()->IsStarted() ? INFO : ERROR)
+ << self->GetException(NULL)->Dump() << "\n" << ss.str();
}
va_end(args);
}
@@ -287,7 +285,7 @@
// Acquire thread list lock so threads won't disappear from under us.
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
// Re-read owner now that we hold lock.
- current_owner = (monitor != NULL) ? monitor->owner_ : NULL;
+ current_owner = (monitor != NULL) ? monitor->GetOwner() : NULL;
// Get short descriptions of the threads involved.
current_owner_string = ThreadToString(current_owner);
expected_owner_string = ThreadToString(expected_owner);
@@ -335,8 +333,9 @@
}
}
-bool Monitor::Unlock(Thread* self, bool for_wait) {
+bool Monitor::Unlock(Thread* self) {
DCHECK(self != NULL);
+ MutexLock mu(self, monitor_lock_);
Thread* owner = owner_;
if (owner == self) {
// We own the monitor, so nobody else can be in here.
@@ -344,17 +343,11 @@
owner_ = NULL;
locking_method_ = NULL;
locking_dex_pc_ = 0;
- monitor_lock_.Unlock(self);
+ // Wake a contender.
+ monitor_contenders_.Signal(self);
} else {
--lock_count_;
}
- } else if (for_wait) {
- // Wait should have already cleared the fields.
- DCHECK_EQ(lock_count_, 0);
- DCHECK(owner == NULL);
- DCHECK(locking_method_ == NULL);
- DCHECK_EQ(locking_dex_pc_, 0u);
- monitor_lock_.Unlock(self);
} else {
// We don't own this, so we're not allowed to unlock it.
// The JNI spec says that we should throw IllegalMonitorStateException
@@ -393,12 +386,14 @@
DCHECK(self != NULL);
DCHECK(why == kTimedWaiting || why == kWaiting || why == kSleeping);
+ monitor_lock_.Lock(self);
+
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()");
+ monitor_lock_.Unlock(self);
return;
}
- monitor_lock_.AssertHeld(self);
// We need to turn a zero-length timed wait into a regular wait because
// Object.wait(0, 0) is defined as Object.wait(0), which is defined as Object.wait().
@@ -406,16 +401,12 @@
why = kWaiting;
}
- WaitWithLock(self, ms, ns, interruptShouldThrow, why);
-}
-
-void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns,
- bool interruptShouldThrow, ThreadState why) {
// Enforce the timeout range.
if (ms < 0 || ns < 0 || ns > 999999) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;",
"timeout arguments out of range: ms=%lld ns=%d", ms, ns);
+ monitor_lock_.Unlock(self);
return;
}
@@ -457,7 +448,8 @@
self->wait_monitor_ = this;
// Release the monitor lock.
- Unlock(self, true);
+ monitor_contenders_.Signal(self);
+ monitor_lock_.Unlock(self);
// Handle the case where the thread was interrupted before we called wait().
if (self->interrupted_) {
@@ -490,9 +482,9 @@
self->wait_monitor_ = NULL;
}
- // Re-acquire the monitor lock.
+ // Re-acquire the monitor and lock.
Lock(self);
-
+ monitor_lock_.Lock(self);
self->wait_mutex_->AssertNotHeld(self);
/*
@@ -524,20 +516,17 @@
self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL);
}
}
+ monitor_lock_.Unlock(self);
}
void Monitor::Notify(Thread* self) {
DCHECK(self != NULL);
+ MutexLock mu(self, monitor_lock_);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
return;
}
- monitor_lock_.AssertHeld(self);
- NotifyWithLock(self);
-}
-
-void Monitor::NotifyWithLock(Thread* self) {
// Signal the first waiting thread in the wait set.
while (wait_set_ != NULL) {
Thread* thread = wait_set_;
@@ -555,16 +544,12 @@
void Monitor::NotifyAll(Thread* self) {
DCHECK(self != NULL);
+ MutexLock mu(self, monitor_lock_);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()");
return;
}
- monitor_lock_.AssertHeld(self);
- NotifyAllWithLock();
-}
-
-void Monitor::NotifyAllWithLock() {
// Signal all threads in the wait set.
while (wait_set_ != NULL) {
Thread* thread = wait_set_;
@@ -575,182 +560,147 @@
}
/*
- * Changes the shape of a monitor from thin to fat, preserving the
- * internal lock state. The calling thread must own the 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 and so the caller should read the monitor following the call.
*/
-void Monitor::Inflate(Thread* self, mirror::Object* obj) {
+void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) {
DCHECK(self != NULL);
DCHECK(obj != NULL);
- DCHECK_EQ(LW_SHAPE(*obj->GetRawLockWordAddress()), LW_SHAPE_THIN);
- DCHECK_EQ(LW_LOCK_OWNER(*obj->GetRawLockWordAddress()), static_cast<int32_t>(self->GetThinLockId()));
-
// Allocate and acquire a new monitor.
- Monitor* m = new Monitor(self, obj);
- VLOG(monitor) << "monitor: thread " << self->GetThinLockId()
- << " created monitor " << m << " for object " << obj;
- Runtime::Current()->GetMonitorList()->Add(m);
+ UniquePtr<Monitor> m(new Monitor(owner, obj, hash_code));
+ if (m->Install(self)) {
+ VLOG(monitor) << "monitor: thread " << owner->GetThreadId()
+ << " created monitor " << m.get() << " for object " << obj;
+ Runtime::Current()->GetMonitorList()->Add(m.release());
+ CHECK_EQ(obj->GetLockWord().GetState(), LockWord::kFatLocked);
+ }
+}
+
+void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word,
+ uint32_t hash_code) {
+ DCHECK_EQ(lock_word.GetState(), LockWord::kThinLocked);
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id == self->GetThreadId()) {
+ // We own the monitor, we can easily inflate it.
+ Inflate(self, self, obj, hash_code);
+ } else {
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+ // Suspend the owner, inflate. First change to blocked and give up mutator_lock_.
+ ScopedThreadStateChange tsc(self, kBlocked);
+ if (lock_word == obj->GetLockWord()) { // If lock word hasn't changed.
+ bool timed_out;
+ Thread* owner = thread_list->SuspendThreadByThreadId(lock_word.ThinLockOwner(), false,
+ &timed_out);
+ if (owner != nullptr) {
+ // We succeeded in suspending the thread, check the lock's status didn't change.
+ lock_word = obj->GetLockWord();
+ if (lock_word.GetState() == LockWord::kThinLocked &&
+ lock_word.ThinLockOwner() == owner_thread_id) {
+ // Go ahead and inflate the lock.
+ Inflate(self, owner, obj, hash_code);
+ }
+ thread_list->Resume(owner, false);
+ }
+ }
+ }
}
void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) {
- volatile int32_t* thinp = obj->GetRawLockWordAddress();
- uint32_t sleepDelayNs;
- uint32_t minSleepDelayNs = 1000000; /* 1 millisecond */
- uint32_t maxSleepDelayNs = 1000000000; /* 1 second */
- uint32_t thin, newThin;
-
DCHECK(self != NULL);
DCHECK(obj != NULL);
- uint32_t threadId = self->GetThinLockId();
- retry:
- thin = *thinp;
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- /*
- * The lock is a thin lock. The owner field is used to
- * determine the acquire method, ordered by cost.
- */
- if (LW_LOCK_OWNER(thin) == threadId) {
- /*
- * The calling thread owns the lock. Increment the
- * value of the recursion count field.
- */
- *thinp += 1 << LW_LOCK_COUNT_SHIFT;
- if (LW_LOCK_COUNT(*thinp) == LW_LOCK_COUNT_MASK) {
- /*
- * The reacquisition limit has been reached. Inflate
- * the lock so the next acquire will not overflow the
- * recursion count field.
- */
- Inflate(self, obj);
+ uint32_t thread_id = self->GetThreadId();
+ size_t contention_count = 0;
+ while (true) {
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kUnlocked: {
+ LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0));
+ if (obj->CasLockWord(lock_word, thin_locked)) {
+ return; // Success!
+ }
+ continue; // Go again.
}
- } else if (LW_LOCK_OWNER(thin) == 0) {
- // The lock is unowned. Install the thread id of the calling thread into the owner field.
- // This is the common case: compiled code will have tried this before calling back into
- // the runtime.
- newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
- if (android_atomic_acquire_cas(thin, newThin, thinp) != 0) {
- // The acquire failed. Try again.
- goto retry;
- }
- } else {
- VLOG(monitor) << StringPrintf("monitor: thread %d spin on lock %p (a %s) owned by %d",
- threadId, thinp, PrettyTypeOf(obj).c_str(), LW_LOCK_OWNER(thin));
- // The lock is owned by another thread. Notify the runtime that we are about to wait.
- self->monitor_enter_object_ = obj;
- self->TransitionFromRunnableToSuspended(kBlocked);
- // Spin until the thin lock is released or inflated.
- sleepDelayNs = 0;
- for (;;) {
- thin = *thinp;
- // Check the shape of the lock word. Another thread
- // may have inflated the lock while we were waiting.
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- if (LW_LOCK_OWNER(thin) == 0) {
- // The lock has been released. Install the thread id of the
- // calling thread into the owner field.
- newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
- if (android_atomic_acquire_cas(thin, newThin, thinp) == 0) {
- // The acquire succeed. Break out of the loop and proceed to inflate the lock.
- break;
- }
+ case LockWord::kThinLocked: {
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id == thread_id) {
+ // We own the lock, increase the recursion count.
+ uint32_t new_count = lock_word.ThinLockCount() + 1;
+ if (LIKELY(new_count <= LockWord::kThinLockMaxCount)) {
+ LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count));
+ obj->SetLockWord(thin_locked);
+ return; // Success!
} else {
- // The lock has not been released. Yield so the owning thread can run.
- if (sleepDelayNs == 0) {
- sched_yield();
- sleepDelayNs = minSleepDelayNs;
- } else {
- NanoSleep(sleepDelayNs);
- // Prepare the next delay value. Wrap to avoid once a second polls for eternity.
- if (sleepDelayNs < maxSleepDelayNs / 2) {
- sleepDelayNs *= 2;
- } else {
- sleepDelayNs = minSleepDelayNs;
- }
- }
+ // We'd overflow the recursion count, so inflate the monitor.
+ InflateThinLocked(self, obj, lock_word, 0);
}
} else {
- // The thin lock was inflated by another thread. Let the runtime know we are no longer
- // waiting and try again.
- VLOG(monitor) << StringPrintf("monitor: thread %d found lock %p surprise-fattened by another thread", threadId, thinp);
- self->monitor_enter_object_ = NULL;
- self->TransitionFromSuspendedToRunnable();
- goto retry;
+ // Contention.
+ contention_count++;
+ Runtime* runtime = Runtime::Current();
+ if (contention_count <= runtime->GetMaxSpinsBeforeThinkLockInflation()) {
+ NanoSleep(1000); // Sleep for 1us and re-attempt.
+ } else {
+ contention_count = 0;
+ InflateThinLocked(self, obj, lock_word, 0);
+ }
}
+ continue; // Start from the beginning.
}
- VLOG(monitor) << StringPrintf("monitor: thread %d spin on lock %p done", threadId, thinp);
- // We have acquired the thin lock. Let the runtime know that we are no longer waiting.
- self->monitor_enter_object_ = NULL;
- self->TransitionFromSuspendedToRunnable();
- // Fatten the lock.
- Inflate(self, obj);
- VLOG(monitor) << StringPrintf("monitor: thread %d fattened lock %p", threadId, thinp);
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ mon->Lock(self);
+ return; // Success!
+ }
+ case LockWord::kHashCode: {
+ // Inflate with the existing hashcode.
+ Inflate(self, nullptr, obj, lock_word.GetHashCode());
+ break;
+ }
}
- } else {
- // The lock is a fat lock.
- VLOG(monitor) << StringPrintf("monitor: thread %d locking fat lock %p (%p) %p on a %s",
- threadId, thinp, LW_MONITOR(*thinp),
- reinterpret_cast<void*>(*thinp), PrettyTypeOf(obj).c_str());
- DCHECK(LW_MONITOR(*thinp) != NULL);
- LW_MONITOR(*thinp)->Lock(self);
}
}
bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) {
- volatile int32_t* thinp = obj->GetRawLockWordAddress();
-
DCHECK(self != NULL);
- // DCHECK_EQ(self->GetState(), kRunnable);
DCHECK(obj != NULL);
- /*
- * Cache the lock word as its value can change while we are
- * examining its state.
- */
- uint32_t thin = *thinp;
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- /*
- * The lock is thin. We must ensure that the lock is owned
- * by the given thread before unlocking it.
- */
- if (LW_LOCK_OWNER(thin) == self->GetThinLockId()) {
- /*
- * We are the lock owner. It is safe to update the lock
- * without CAS as lock ownership guards the lock itself.
- */
- if (LW_LOCK_COUNT(thin) == 0) {
- /*
- * The lock was not recursively acquired, the common
- * case. Unlock by clearing all bits except for the
- * hash state.
- */
- thin &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
- android_atomic_release_store(thin, thinp);
- } else {
- /*
- * The object was recursively acquired. Decrement the
- * lock recursion count field.
- */
- *thinp -= 1 << LW_LOCK_COUNT_SHIFT;
- }
- } else {
- /*
- * We do not own the lock. The JVM spec requires that we
- * throw an exception in this case.
- */
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
FailedUnlock(obj, self, NULL, NULL);
- return false;
+ return false; // Failure.
+ case LockWord::kThinLocked: {
+ uint32_t thread_id = self->GetThreadId();
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id != thread_id) {
+ // TODO: there's a race here with the owner dying while we unlock.
+ Thread* owner =
+ Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+ FailedUnlock(obj, self, owner, NULL);
+ return false; // Failure.
+ } else {
+ // We own the lock, decrease the recursion count.
+ if (lock_word.ThinLockCount() != 0) {
+ uint32_t new_count = lock_word.ThinLockCount() - 1;
+ LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count));
+ obj->SetLockWord(thin_locked);
+ } else {
+ obj->SetLockWord(LockWord());
+ }
+ return true; // Success!
+ }
}
- } else {
- /*
- * The lock is fat. We must check to see if Unlock has
- * raised any exceptions before continuing.
- */
- DCHECK(LW_MONITOR(*thinp) != NULL);
- if (!LW_MONITOR(*thinp)->Unlock(self, false)) {
- // An exception has been raised. Do not fall through.
- return false;
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ return mon->Unlock(self);
}
+ default:
+ LOG(FATAL) << "Unreachable";
+ return false;
}
- return true;
}
/*
@@ -758,84 +708,97 @@
*/
void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why) {
- volatile int32_t* thinp = obj->GetRawLockWordAddress();
+ DCHECK(self != NULL);
+ DCHECK(obj != NULL);
- // If the lock is still thin, we need to fatten it.
- uint32_t thin = *thinp;
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- // Make sure that 'self' holds the lock.
- if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()");
- return;
+ return; // Failure.
+ case LockWord::kThinLocked: {
+ uint32_t thread_id = self->GetThreadId();
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id != thread_id) {
+ ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()");
+ return; // Failure.
+ } else {
+ // We own the lock, inflate to enqueue ourself on the Monitor.
+ Inflate(self, self, obj, 0);
+ lock_word = obj->GetLockWord();
+ }
+ break;
}
-
- /* This thread holds the lock. We need to fatten the lock
- * so 'self' can block on it. Don't update the object lock
- * field yet, because 'self' needs to acquire the lock before
- * any other thread gets a chance.
- */
- Inflate(self, obj);
- VLOG(monitor) << StringPrintf("monitor: thread %d fattened lock %p by wait()", self->GetThinLockId(), thinp);
+ case LockWord::kFatLocked:
+ break; // Already set for a wait.
}
- LW_MONITOR(*thinp)->Wait(self, ms, ns, interruptShouldThrow, why);
+ Monitor* mon = lock_word.FatLockMonitor();
+ mon->Wait(self, ms, ns, interruptShouldThrow, why);
}
-void Monitor::Notify(Thread* self, mirror::Object *obj) {
- uint32_t thin = *obj->GetRawLockWordAddress();
+void Monitor::DoNotify(Thread* self, mirror::Object* obj, bool notify_all) {
+ DCHECK(self != NULL);
+ DCHECK(obj != NULL);
- // If the lock is still thin, there aren't any waiters;
- // waiting on an object forces lock fattening.
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- // Make sure that 'self' holds the lock.
- if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
- return;
+ return; // Failure.
+ case LockWord::kThinLocked: {
+ uint32_t thread_id = self->GetThreadId();
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id != thread_id) {
+ ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
+ return; // Failure.
+ } else {
+ // We own the lock but there's no Monitor and therefore no waiters.
+ return; // Success.
+ }
}
- // no-op; there are no waiters to notify.
- // We inflate here in case the Notify is in a tight loop. Without inflation here the waiter
- // will struggle to get in. Bug 6961405.
- Inflate(self, obj);
- } else {
- // It's a fat lock.
- LW_MONITOR(thin)->Notify(self);
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ if (notify_all) {
+ mon->NotifyAll(self);
+ } else {
+ mon->Notify(self);
+ }
+ return; // Success.
+ }
}
}
-void Monitor::NotifyAll(Thread* self, mirror::Object *obj) {
- uint32_t thin = *obj->GetRawLockWordAddress();
+uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) {
+ DCHECK(obj != NULL);
- // If the lock is still thin, there aren't any waiters;
- // waiting on an object forces lock fattening.
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- // Make sure that 'self' holds the lock.
- if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
- ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()");
- return;
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
+ return ThreadList::kInvalidThreadId;
+ case LockWord::kThinLocked:
+ return lock_word.ThinLockOwner();
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ return mon->GetOwnerThreadId();
}
- // no-op; there are no waiters to notify.
- // We inflate here in case the NotifyAll is in a tight loop. Without inflation here the waiter
- // will struggle to get in. Bug 6961405.
- Inflate(self, obj);
- } else {
- // It's a fat lock.
- LW_MONITOR(thin)->NotifyAll(self);
- }
-}
-
-uint32_t Monitor::GetThinLockId(uint32_t raw_lock_word) {
- if (LW_SHAPE(raw_lock_word) == LW_SHAPE_THIN) {
- return LW_LOCK_OWNER(raw_lock_word);
- } else {
- Thread* owner = LW_MONITOR(raw_lock_word)->owner_;
- return owner ? owner->GetThinLockId() : 0;
+ default:
+ LOG(FATAL) << "Unreachable";
+ return ThreadList::kInvalidThreadId;
}
}
void Monitor::DescribeWait(std::ostream& os, const Thread* thread) {
ThreadState state = thread->GetState();
- mirror::Object* object = NULL;
- uint32_t lock_owner = ThreadList::kInvalidId;
+ int32_t object_identity_hashcode = 0;
+ uint32_t lock_owner = ThreadList::kInvalidThreadId;
+ std::string pretty_type;
if (state == kWaiting || state == kTimedWaiting || state == kSleeping) {
if (state == kSleeping) {
os << " - sleeping on ";
@@ -847,14 +810,18 @@
MutexLock mu(self, *thread->wait_mutex_);
Monitor* monitor = thread->wait_monitor_;
if (monitor != NULL) {
- object = monitor->obj_;
+ mirror::Object* object = monitor->obj_;
+ object_identity_hashcode = object->IdentityHashCode();
+ pretty_type = PrettyTypeOf(object);
}
}
} else if (state == kBlocked) {
os << " - waiting to lock ";
- object = thread->monitor_enter_object_;
+ mirror::Object* object = thread->monitor_enter_object_;
if (object != NULL) {
- lock_owner = object->GetThinLockId();
+ object_identity_hashcode = object->IdentityHashCode();
+ lock_owner = object->GetLockOwnerThreadId();
+ pretty_type = PrettyTypeOf(object);
}
} else {
// We're not waiting on anything.
@@ -862,10 +829,10 @@
}
// - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
- os << "<" << object << "> (a " << PrettyTypeOf(object) << ")";
+ os << StringPrintf("<0x%08x> (a %s)", object_identity_hashcode, pretty_type.c_str());
// - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5
- if (lock_owner != ThreadList::kInvalidId) {
+ if (lock_owner != ThreadList::kInvalidThreadId) {
os << " held by thread " << lock_owner;
}
@@ -876,18 +843,15 @@
// This is used to implement JDWP's ThreadReference.CurrentContendedMonitor, and has a bizarre
// definition of contended that includes a monitor a thread is trying to enter...
mirror::Object* result = thread->monitor_enter_object_;
- if (result != NULL) {
- return result;
- }
- // ...but also a monitor that the thread is waiting on.
- {
+ if (result == NULL) {
+ // ...but also a monitor that the thread is waiting on.
MutexLock mu(Thread::Current(), *thread->wait_mutex_);
Monitor* monitor = thread->wait_monitor_;
if (monitor != NULL) {
- return monitor->obj_;
+ result = monitor->GetObject();
}
}
- return NULL;
+ return result;
}
void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::Object*, void*),
@@ -952,41 +916,63 @@
}
}
-bool Monitor::IsValidLockWord(int32_t lock_word) {
- if (lock_word == 0) {
- return true;
- } else if (LW_SHAPE(lock_word) == LW_SHAPE_FAT) {
- Monitor* mon = LW_MONITOR(lock_word);
- MonitorList* list = Runtime::Current()->GetMonitorList();
- MutexLock mu(Thread::Current(), list->monitor_list_lock_);
- bool found = false;
- for (Monitor* list_mon : list->list_) {
- if (mon == list_mon) {
- found = true;
- break;
+bool Monitor::IsValidLockWord(LockWord lock_word) {
+ switch (lock_word.GetState()) {
+ case LockWord::kUnlocked:
+ // Nothing to check.
+ return true;
+ case LockWord::kThinLocked:
+ // Basic sanity check of owner.
+ return lock_word.ThinLockOwner() != ThreadList::kInvalidThreadId;
+ case LockWord::kFatLocked: {
+ // Check the monitor appears in the monitor list.
+ Monitor* mon = lock_word.FatLockMonitor();
+ MonitorList* list = Runtime::Current()->GetMonitorList();
+ MutexLock mu(Thread::Current(), list->monitor_list_lock_);
+ for (Monitor* list_mon : list->list_) {
+ if (mon == list_mon) {
+ return true; // Found our monitor.
+ }
}
+ return false; // Fail - unowned monitor in an object.
}
- return found;
- } else {
- // TODO: thin lock validity checking.
- return LW_SHAPE(lock_word) == LW_SHAPE_THIN;
+ case LockWord::kHashCode:
+ return true;
+ default:
+ LOG(FATAL) << "Unreachable";
+ return false;
}
}
+bool Monitor::IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ MutexLock mu(Thread::Current(), monitor_lock_);
+ return owner_ != nullptr;
+}
+
void Monitor::TranslateLocation(const mirror::ArtMethod* method, uint32_t dex_pc,
- const char*& source_file, uint32_t& line_number) const {
+ const char** source_file, uint32_t* line_number) const {
// If method is null, location is unknown
if (method == NULL) {
- source_file = "";
- line_number = 0;
+ *source_file = "";
+ *line_number = 0;
return;
}
MethodHelper mh(method);
- source_file = mh.GetDeclaringClassSourceFile();
- if (source_file == NULL) {
- source_file = "";
+ *source_file = mh.GetDeclaringClassSourceFile();
+ if (*source_file == NULL) {
+ *source_file = "";
}
- line_number = mh.GetLineNumFromDexPC(dex_pc);
+ *line_number = mh.GetLineNumFromDexPC(dex_pc);
+}
+
+uint32_t Monitor::GetOwnerThreadId() {
+ MutexLock mu(Thread::Current(), monitor_lock_);
+ Thread* owner = owner_;
+ if (owner != NULL) {
+ return owner->GetThreadId();
+ } else {
+ return ThreadList::kInvalidThreadId;
+ }
}
MonitorList::MonitorList()
@@ -1020,36 +1006,46 @@
list_.push_front(m);
}
-void MonitorList::SweepMonitorList(IsMarkedTester is_marked, void* arg) {
+void MonitorList::SweepMonitorList(RootVisitor visitor, void* arg) {
MutexLock mu(Thread::Current(), monitor_list_lock_);
for (auto it = list_.begin(); it != list_.end(); ) {
Monitor* m = *it;
- if (!is_marked(m->GetObject(), arg)) {
- VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object " << m->GetObject();
+ mirror::Object* obj = m->GetObject();
+ mirror::Object* new_obj = visitor(obj, arg);
+ if (new_obj == nullptr) {
+ VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object "
+ << m->GetObject();
delete m;
it = list_.erase(it);
} else {
+ m->SetObject(new_obj);
++it;
}
}
}
-MonitorInfo::MonitorInfo(mirror::Object* o) : owner(NULL), entry_count(0) {
- uint32_t lock_word = *o->GetRawLockWordAddress();
- if (LW_SHAPE(lock_word) == LW_SHAPE_THIN) {
- uint32_t owner_thin_lock_id = LW_LOCK_OWNER(lock_word);
- if (owner_thin_lock_id != 0) {
- owner = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(owner_thin_lock_id);
- entry_count = 1 + LW_LOCK_COUNT(lock_word);
- }
- // Thin locks have no waiters.
- } else {
- CHECK_EQ(LW_SHAPE(lock_word), LW_SHAPE_FAT);
- Monitor* monitor = LW_MONITOR(lock_word);
- owner = monitor->owner_;
- entry_count = 1 + monitor->lock_count_;
- for (Thread* waiter = monitor->wait_set_; waiter != NULL; waiter = waiter->wait_next_) {
- waiters.push_back(waiter);
+MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) {
+ DCHECK(obj != NULL);
+
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kUnlocked:
+ // Fall-through.
+ case LockWord::kHashCode:
+ break;
+ case LockWord::kThinLocked:
+ owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+ entry_count_ = 1 + lock_word.ThinLockCount();
+ // Thin locks have no waiters.
+ break;
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ owner_ = mon->owner_;
+ entry_count_ = 1 + mon->lock_count_;
+ for (Thread* waiter = mon->wait_set_; waiter != NULL; waiter = waiter->wait_next_) {
+ waiters_.push_back(waiter);
+ }
+ break;
}
}
}
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 0b5b7e5..09cfafa 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -24,53 +24,35 @@
#include <list>
#include <vector>
+#include "atomic_integer.h"
#include "base/mutex.h"
#include "root_visitor.h"
#include "thread_state.h"
namespace art {
-/*
- * Monitor shape field. Used to distinguish thin locks from fat locks.
- */
-#define LW_SHAPE_THIN 0
-#define LW_SHAPE_FAT 1
-
-/*
- * Hash state field. Used to signify that an object has had its
- * identity hash code exposed or relocated.
- */
-#define LW_HASH_STATE_UNHASHED 0
-#define LW_HASH_STATE_HASHED 1
-#define LW_HASH_STATE_HASHED_AND_MOVED 3
-#define LW_HASH_STATE_MASK 0x3
-#define LW_HASH_STATE_SHIFT 1
-#define LW_HASH_STATE(x) (((x) >> LW_HASH_STATE_SHIFT) & LW_HASH_STATE_MASK)
-
-/*
- * Lock owner field. Contains the thread id of the thread currently
- * holding the lock.
- */
-#define LW_LOCK_OWNER_MASK 0xffff
-#define LW_LOCK_OWNER_SHIFT 3
-#define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK)
-
namespace mirror {
class ArtMethod;
class Object;
} // namespace mirror
+class LockWord;
class Thread;
class StackVisitor;
class Monitor {
public:
+ // The default number of spins that are done before thread suspension is used to forcibly inflate
+ // a lock word. See Runtime::max_spins_before_thin_lock_inflation_.
+ constexpr static size_t kDefaultMaxSpinsBeforeThinLockInflation = 50;
+
~Monitor();
static bool IsSensitiveThread();
static void Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread_hook)());
- static uint32_t GetThinLockId(uint32_t raw_lock_word)
- NO_THREAD_SAFETY_ANALYSIS; // Reading lock owner without holding lock is racy.
+ // Return the thread id of the lock owner or 0 when there is no owner.
+ static uint32_t GetLockOwnerThreadId(mirror::Object* obj)
+ NO_THREAD_SAFETY_ANALYSIS; // TODO: Reading lock owner without holding lock is racy.
static void MonitorEnter(Thread* thread, mirror::Object* obj)
EXCLUSIVE_LOCK_FUNCTION(monitor_lock_)
@@ -80,9 +62,13 @@
UNLOCK_FUNCTION(monitor_lock_);
static void Notify(Thread* self, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DoNotify(self, obj, false);
+ }
static void NotifyAll(Thread* self, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DoNotify(self, obj, true);
+ }
static void Wait(Thread* self, mirror::Object* obj, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -92,7 +78,8 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Used to implement JDWP's ThreadReference.CurrentContendedMonitor.
- static mirror::Object* GetContendedMonitor(Thread* thread);
+ static mirror::Object* GetContendedMonitor(Thread* thread)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Calls 'callback' once for each lock held in the single stack frame represented by
// the current state of 'stack_visitor'.
@@ -100,18 +87,43 @@
void* callback_context)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static bool IsValidLockWord(int32_t lock_word);
+ static bool IsValidLockWord(LockWord lock_word);
- mirror::Object* GetObject();
+ mirror::Object* GetObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return obj_;
+ }
+
+ void SetObject(mirror::Object* object);
+
+ Thread* GetOwner() const NO_THREAD_SAFETY_ANALYSIS {
+ return owner_;
+ }
+
+ int32_t GetHashCode();
+
+ bool IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool HasHashCode() const {
+ return hash_code_.load() != 0;
+ }
+
+ static void InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word,
+ uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS;
private:
- explicit Monitor(Thread* owner, mirror::Object* obj)
+ explicit Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Install the monitor into its object, may fail if another thread installs a different monitor
+ // first.
+ bool Install(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
- static void Inflate(Thread* self, mirror::Object* obj)
+ static void Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample_percent,
@@ -122,48 +134,57 @@
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Lock(Thread* self) EXCLUSIVE_LOCK_FUNCTION(monitor_lock_);
- bool Unlock(Thread* thread, bool for_wait) UNLOCK_FUNCTION(monitor_lock_);
-
- void Notify(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
- void NotifyWithLock(Thread* self)
- EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_)
+ void Lock(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool Unlock(Thread* thread)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void NotifyAll(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
- void NotifyAllWithLock()
- EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_)
+ static void DoNotify(Thread* self, mirror::Object* obj, bool notify_all)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void Notify(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void NotifyAll(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Wait(Thread* self, int64_t msec, int32_t nsec, bool interruptShouldThrow, ThreadState why)
- NO_THREAD_SAFETY_ANALYSIS;
- void WaitWithLock(Thread* self, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why)
- EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Translates the provided method and pc into its declaring class' source file and line number.
void TranslateLocation(const mirror::ArtMethod* method, uint32_t pc,
- const char*& source_file, uint32_t& line_number) const
+ const char** source_file, uint32_t* line_number) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint32_t GetOwnerThreadId();
+
static bool (*is_sensitive_thread_hook_)();
static uint32_t lock_profiling_threshold_;
Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ ConditionVariable monitor_contenders_ GUARDED_BY(monitor_lock_);
// Which thread currently owns the lock?
- Thread* volatile owner_;
+ Thread* volatile owner_ GUARDED_BY(monitor_lock_);
// Owner's recursive lock depth.
int lock_count_ GUARDED_BY(monitor_lock_);
- // What object are we part of (for debugging).
- mirror::Object* const obj_;
+ // What object are we part of.
+ mirror::Object* obj_;
// Threads currently waiting on this monitor.
Thread* wait_set_ GUARDED_BY(monitor_lock_);
+ // Stored object hash code, generated lazily by GetHashCode.
+ AtomicInteger hash_code_;
+
// Method and dex pc where the lock owner acquired the lock, used when lock
// sampling is enabled. locking_method_ may be null if the lock is currently
// unlocked, or if the lock is acquired by the system when the stack is empty.
@@ -182,10 +203,11 @@
~MonitorList();
void Add(Monitor* m);
- void SweepMonitorList(IsMarkedTester is_marked, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+
+ void SweepMonitorList(RootVisitor visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DisallowNewMonitors();
void AllowNewMonitors();
+
private:
bool allow_new_monitors_ GUARDED_BY(monitor_list_lock_);
Mutex monitor_list_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -203,9 +225,9 @@
public:
explicit MonitorInfo(mirror::Object* o) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
- Thread* owner;
- size_t entry_count;
- std::vector<Thread*> waiters;
+ Thread* owner_;
+ size_t entry_count_;
+ std::vector<Thread*> waiters_;
private:
DISALLOW_COPY_AND_ASSIGN(MonitorInfo);
diff --git a/runtime/monitor_android.cc b/runtime/monitor_android.cc
index 8efa072..d89290b 100644
--- a/runtime/monitor_android.cc
+++ b/runtime/monitor_android.cc
@@ -81,7 +81,7 @@
mirror::ArtMethod* m = self->GetCurrentMethod(&pc);
const char* filename;
uint32_t line_number;
- TranslateLocation(m, pc, filename, line_number);
+ TranslateLocation(m, pc, &filename, &line_number);
cp = EventLogWriteString(cp, filename, strlen(filename));
// Emit the source code line number, 5 bytes.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 823013a..ab5eab3 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -89,43 +89,40 @@
if (sourceName.c_str() == NULL) {
return 0;
}
- std::string dex_location(sourceName.c_str());
NullableScopedUtfChars outputName(env, javaOutputName);
if (env->ExceptionCheck()) {
return 0;
}
- ScopedObjectAccess soa(env);
uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
- LOG(WARNING) << "Failed to compute checksum: " << dex_location;
- ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
- soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;",
- "Unable to get checksum of dex file: %s", dex_location.c_str());
+ std::string error_msg;
+ if (!DexFile::GetChecksum(sourceName.c_str(), &dex_location_checksum, &error_msg)) {
+ ScopedObjectAccess soa(env);
+ DCHECK(!error_msg.empty());
+ ThrowIOException("%s", error_msg.c_str());
return 0;
}
ClassLinker* linker = Runtime::Current()->GetClassLinker();
const DexFile* dex_file;
- if (outputName.c_str() == NULL) {
- dex_file = linker->FindDexFileInOatFileFromDexLocation(dex_location, dex_location_checksum);
+ if (outputName.c_str() == nullptr) {
+ dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(),
+ dex_location_checksum, &error_msg);
} else {
- std::string oat_location(outputName.c_str());
- dex_file = linker->FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum, oat_location);
+ dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum,
+ outputName.c_str(), &error_msg);
}
- if (dex_file == NULL) {
- LOG(WARNING) << "Failed to open dex file: " << dex_location;
- ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
- soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;",
- "Unable to open dex file: %s", dex_location.c_str());
+ if (dex_file == nullptr) {
+ CHECK_EQ(env->ExceptionCheck(), JNI_TRUE);
return 0;
}
return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
}
-static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static const DexFile* toDexFile(int dex_file_address, JNIEnv* env) {
const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
- if (dex_file == NULL) {
+ if (UNLIKELY(dex_file == nullptr)) {
+ ScopedObjectAccess soa(env);
ThrowNullPointerException(NULL, "dex_file == null");
}
return dex_file;
@@ -133,11 +130,8 @@
static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
const DexFile* dex_file;
- {
- ScopedObjectAccess soa(env);
- dex_file = toDexFile(cookie);
- }
- if (dex_file == NULL) {
+ dex_file = toDexFile(cookie, env);
+ if (dex_file == nullptr) {
return;
}
if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
@@ -148,8 +142,7 @@
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
jint cookie) {
- ScopedObjectAccess soa(env);
- const DexFile* dex_file = toDexFile(cookie);
+ const DexFile* dex_file = toDexFile(cookie, env);
if (dex_file == NULL) {
VLOG(class_linker) << "Failed to find dex_file";
return NULL;
@@ -165,6 +158,7 @@
VLOG(class_linker) << "Failed to find dex_class_def";
return NULL;
}
+ ScopedObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
class_linker->RegisterDexFile(*dex_file);
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
@@ -176,12 +170,9 @@
static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
const DexFile* dex_file;
- {
- ScopedObjectAccess soa(env);
- dex_file = toDexFile(cookie);
- }
- if (dex_file == NULL) {
- return NULL;
+ dex_file = toDexFile(cookie, env);
+ if (dex_file == nullptr) {
+ return nullptr;
}
std::vector<std::string> class_names;
@@ -194,21 +185,17 @@
}
static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
- bool debug_logging = false;
+ const bool kVerboseLogging = false; // Spammy logging.
+ const bool kDebugLogging = true; // Logging useful for debugging.
ScopedUtfChars filename(env, javaFilename);
- if (filename.c_str() == NULL) {
- LOG(ERROR) << "DexFile_isDexOptNeeded null filename";
- return JNI_TRUE;
- }
- if (!OS::FileExists(filename.c_str())) {
+ if ((filename.c_str() == nullptr) || !OS::FileExists(filename.c_str())) {
LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist";
- ScopedObjectAccess soa(env);
- ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
- soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;",
- "%s", filename.c_str());
- return JNI_TRUE;
+ ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
+ const char* message = (filename.c_str() == nullptr) ? "<empty file name>" : filename.c_str();
+ env->ThrowNew(fnfe.get(), message);
+ return JNI_FALSE;
}
// Always treat elements of the bootclasspath as up-to-date. The
@@ -218,7 +205,7 @@
const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath();
for (size_t i = 0; i < boot_class_path.size(); i++) {
if (boot_class_path[i]->GetLocation() == filename.c_str()) {
- if (debug_logging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str();
}
return JNI_FALSE;
@@ -227,40 +214,55 @@
// Check if we have an odex file next to the dex file.
std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str()));
- UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false));
- if (oat_file.get() != NULL) {
- ScopedObjectAccess soa(env);
- const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL);
- if (oat_dex_file == NULL) {
- if (debug_logging) {
- LOG(INFO) << "DexFile_isDexOptNeeded GetOatDexFile failed";
- }
- } else {
+ std::string error_msg;
+ UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false,
+ &error_msg));
+ if (oat_file.get() == nullptr) {
+ if (kVerboseLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename.c_str()
+ << "': " << error_msg;
+ }
+ error_msg.clear();
+ } else {
+ const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL,
+ kDebugLogging);
+ if (oat_dex_file != nullptr) {
uint32_t location_checksum;
- // If we have no classes.dex checksum such as in a user build, assume up-to-date.
- if (!DexFile::GetChecksum(filename.c_str(), &location_checksum)) {
- if (debug_logging) {
+ // If its not possible to read the classes.dex assume up-to-date as we won't be able to
+ // compile it anyway.
+ if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: "
- << filename.c_str();
+ << filename.c_str() << ": " << error_msg;
}
return JNI_FALSE;
}
- if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) {
- if (debug_logging) {
+ if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum,
+ &error_msg)) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename
- << " is up-to-date checksum compared to " << filename.c_str();
+ << " has an up-to-date checksum compared to " << filename.c_str();
}
return JNI_FALSE;
+ } else {
+ if (kVerboseLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename
+ << " with an out-of-date checksum compared to " << filename.c_str()
+ << ": " << error_msg;
+ }
+ error_msg.clear();
}
}
}
// Check if we have an oat file in the cache
std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str()));
- oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false));
- if (oat_file.get() == NULL) {
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " does not exist for " << filename.c_str();
+ oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false, &error_msg));
+ if (oat_file.get() == nullptr) {
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " does not exist for " << filename.c_str() << ": " << error_msg;
+ }
return JNI_TRUE;
}
@@ -268,41 +270,53 @@
if (space->IsImageSpace()) {
// TODO: Ensure this works with multiple image spaces.
const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader();
- if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) {
- ScopedObjectAccess soa(env);
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " has out-of-date oat checksum compared to "
- << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() !=
+ image_header.GetOatChecksum()) {
+ if (kDebugLogging) {
+ ScopedObjectAccess soa(env);
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " has out-of-date oat checksum compared to "
+ << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ }
return JNI_TRUE;
}
if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
!= reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) {
- ScopedObjectAccess soa(env);
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " has out-of-date oat begin compared to "
- << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ if (kDebugLogging) {
+ ScopedObjectAccess soa(env);
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " has out-of-date oat begin compared to "
+ << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ }
return JNI_TRUE;
}
}
}
- ScopedObjectAccess soa(env);
uint32_t location_checksum;
- if (!DexFile::GetChecksum(filename.c_str(), &location_checksum)) {
- LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str();
+ if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) {
+ if (kDebugLogging) {
+ LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str()
+ << " (error " << error_msg << ")";
+ }
return JNI_TRUE;
}
- if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) {
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " has out-of-date checksum compared to " << filename.c_str();
+ if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum,
+ &error_msg)) {
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " has out-of-date checksum compared to " << filename.c_str()
+ << " (error " << error_msg << ")";
+ }
return JNI_TRUE;
}
- if (debug_logging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " is up-to-date for " << filename.c_str();
}
+ CHECK(error_msg.empty()) << error_msg;
return JNI_FALSE;
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 5fc8bd5..aef000c 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -20,14 +20,18 @@
#include "common_throws.h"
#include "debugger.h"
#include "dex_file-inl.h"
+#include "gc/accounting/card_table-inl.h"
#include "gc/allocator/dlmalloc.h"
#include "gc/heap.h"
#include "gc/space/dlmalloc_space.h"
+#include "intern_table.h"
#include "jni_internal.h"
+#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
-#include "mirror/object.h"
+#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "object_utils.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
#include "thread_list.h"
@@ -49,8 +53,11 @@
static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) {
}
-static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) {
- ScopedObjectAccess soa(env);
+static jobject VMRuntime_newNonMovableArray(JNIEnv* env,
+ jobject,
+ jclass javaElementClass,
+ jint length) {
+ ScopedFastNativeObjectAccess soa(env);
#ifdef MOVING_GARBAGE_COLLECTOR
// TODO: right now, we don't have a copying collector, so there's no need
// to do anything special here, but we ought to pass the non-movability
@@ -81,7 +88,7 @@
if (javaArray == NULL) { // Most likely allocation failed
return 0;
}
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
if (!array->IsArrayInstance()) {
ThrowIllegalArgumentException(NULL, "not an array");
@@ -128,7 +135,7 @@
return env->NewStringUTF(kIsDebugBuild ? "libartd.so" : "libart.so");
}
-static void VMRuntime_setTargetSdkVersion(JNIEnv* env, jobject, jint targetSdkVersion) {
+static void VMRuntime_setTargetSdkVersionNative(JNIEnv* env, jobject, jint targetSdkVersion) {
// This is the target SDK version of the app we're about to run.
// Note that targetSdkVersion may be CUR_DEVELOPMENT (10000).
// Note that targetSdkVersion may be 0, meaning "current".
@@ -147,21 +154,21 @@
}
static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) {
- ScopedObjectAccess soa(env);
- if (bytes < 0) {
+ if (UNLIKELY(bytes < 0)) {
+ ScopedObjectAccess soa(env);
ThrowRuntimeException("allocation size negative %d", bytes);
return;
}
- Runtime::Current()->GetHeap()->RegisterNativeAllocation(bytes);
+ Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, bytes);
}
static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
- ScopedObjectAccess soa(env);
- if (bytes < 0) {
+ if (UNLIKELY(bytes < 0)) {
+ ScopedObjectAccess soa(env);
ThrowRuntimeException("allocation size negative %d", bytes);
return;
}
- Runtime::Current()->GetHeap()->RegisterNativeFree(bytes);
+ Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes);
}
static void VMRuntime_trimHeap(JNIEnv*, jobject) {
@@ -169,10 +176,8 @@
// Trim the managed heap.
gc::Heap* heap = Runtime::Current()->GetHeap();
- gc::space::DlMallocSpace* alloc_space = heap->GetAllocSpace();
- size_t alloc_space_size = alloc_space->Size();
- float managed_utilization =
- static_cast<float>(alloc_space->GetBytesAllocated()) / alloc_space_size;
+ float managed_utilization = (static_cast<float>(heap->GetBytesAllocated()) /
+ heap->GetTotalMemory());
size_t managed_reclaimed = heap->Trim();
uint64_t gc_heap_end_ns = NanoTime();
@@ -192,12 +197,317 @@
}
static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
- Thread* self = static_cast<JNIEnvExt*>(env)->self;
+ Thread* self = ThreadForEnv(env);
Runtime::Current()->GetHeap()->ConcurrentGC(self);
}
+typedef std::map<std::string, mirror::String*> StringTable;
+
+static mirror::Object* PreloadDexCachesStringsVisitor(mirror::Object* root, void* arg) {
+ StringTable& table = *reinterpret_cast<StringTable*>(arg);
+ mirror::String* string = const_cast<mirror::Object*>(root)->AsString();
+ // LOG(INFO) << "VMRuntime.preloadDexCaches interned=" << string->ToModifiedUtf8();
+ table[string->ToModifiedUtf8()] = string;
+ return root;
+}
+
+// Based on ClassLinker::ResolveString.
+static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache,
+ uint32_t string_idx,
+ StringTable& strings)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::String* string = dex_cache->GetResolvedString(string_idx);
+ if (string != NULL) {
+ return;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ const char* utf8 = dex_file->StringDataByIdx(string_idx);
+ string = strings[utf8];
+ if (string == NULL) {
+ return;
+ }
+ // LOG(INFO) << "VMRuntime.preloadDexCaches resolved string=" << utf8;
+ dex_cache->SetResolvedString(string_idx, string);
+}
+
+// Based on ClassLinker::ResolveType.
+static void PreloadDexCachesResolveType(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) {
+ return;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ const char* class_name = dex_file->StringByTypeIdx(type_idx);
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ if (class_name[1] == '\0') {
+ klass = linker->FindPrimitiveClass(class_name[0]);
+ } else {
+ klass = linker->LookupClass(class_name, NULL);
+ }
+ if (klass == NULL) {
+ return;
+ }
+ // LOG(INFO) << "VMRuntime.preloadDexCaches resolved klass=" << class_name;
+ dex_cache->SetResolvedType(type_idx, klass);
+ // Skip uninitialized classes because filled static storage entry implies it is initialized.
+ if (!klass->IsInitialized()) {
+ // LOG(INFO) << "VMRuntime.preloadDexCaches uninitialized klass=" << class_name;
+ return;
+ }
+ // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name;
+ dex_cache->GetInitializedStaticStorage()->Set(type_idx, klass);
+}
+
+// Based on ClassLinker::ResolveField.
+static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache,
+ uint32_t field_idx,
+ bool is_static)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtField* field = dex_cache->GetResolvedField(field_idx);
+ if (field != NULL) {
+ return;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx);
+ mirror::Class* klass = dex_cache->GetResolvedType(field_id.class_idx_);
+ if (klass == NULL) {
+ return;
+ }
+ if (is_static) {
+ field = klass->FindStaticField(dex_cache, field_idx);
+ } else {
+ field = klass->FindInstanceField(dex_cache, field_idx);
+ }
+ if (field == NULL) {
+ return;
+ }
+ // LOG(INFO) << "VMRuntime.preloadDexCaches resolved field " << PrettyField(field);
+ dex_cache->SetResolvedField(field_idx, field);
+}
+
+// Based on ClassLinker::ResolveMethod.
+static void PreloadDexCachesResolveMethod(mirror::DexCache* dex_cache,
+ uint32_t method_idx,
+ InvokeType invoke_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx);
+ if (method != NULL) {
+ return;
+ }
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
+ mirror::Class* klass = dex_cache->GetResolvedType(method_id.class_idx_);
+ if (klass == NULL) {
+ return;
+ }
+ switch (invoke_type) {
+ case kDirect:
+ case kStatic:
+ method = klass->FindDirectMethod(dex_cache, method_idx);
+ break;
+ case kInterface:
+ method = klass->FindInterfaceMethod(dex_cache, method_idx);
+ break;
+ case kSuper:
+ case kVirtual:
+ method = klass->FindVirtualMethod(dex_cache, method_idx);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable - invocation type: " << invoke_type;
+ }
+ if (method == NULL) {
+ return;
+ }
+ // LOG(INFO) << "VMRuntime.preloadDexCaches resolved method " << PrettyMethod(method);
+ dex_cache->SetResolvedMethod(method_idx, method);
+}
+
+struct DexCacheStats {
+ uint32_t num_strings;
+ uint32_t num_types;
+ uint32_t num_fields;
+ uint32_t num_methods;
+ uint32_t num_static_storage;
+ DexCacheStats() : num_strings(0),
+ num_types(0),
+ num_fields(0),
+ num_methods(0),
+ num_static_storage(0) {}
+};
+
+static const bool kPreloadDexCachesEnabled = true;
+
+// Disabled because it takes a long time (extra half second) but
+// gives almost no benefit in terms of saving private dirty pages.
+static const bool kPreloadDexCachesStrings = false;
+
+static const bool kPreloadDexCachesTypes = true;
+static const bool kPreloadDexCachesFieldsAndMethods = true;
+
+static const bool kPreloadDexCachesCollectStats = true;
+
+static void PreloadDexCachesStatsTotal(DexCacheStats* total) {
+ if (!kPreloadDexCachesCollectStats) {
+ return;
+ }
+
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
+ for (size_t i = 0; i< boot_class_path.size(); i++) {
+ const DexFile* dex_file = boot_class_path[i];
+ CHECK(dex_file != NULL);
+ total->num_strings += dex_file->NumStringIds();
+ total->num_fields += dex_file->NumFieldIds();
+ total->num_methods += dex_file->NumMethodIds();
+ total->num_types += dex_file->NumTypeIds();
+ total->num_static_storage += dex_file->NumTypeIds();
+ }
+}
+
+static void PreloadDexCachesStatsFilled(DexCacheStats* filled)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (!kPreloadDexCachesCollectStats) {
+ return;
+ }
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
+ for (size_t i = 0; i< boot_class_path.size(); i++) {
+ const DexFile* dex_file = boot_class_path[i];
+ CHECK(dex_file != NULL);
+ mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file);
+ for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
+ mirror::String* string = dex_cache->GetResolvedString(i);
+ if (string != NULL) {
+ filled->num_strings++;
+ }
+ }
+ for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
+ mirror::Class* klass = dex_cache->GetResolvedType(i);
+ if (klass != NULL) {
+ filled->num_types++;
+ }
+ }
+ for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
+ mirror::ArtField* field = dex_cache->GetResolvedField(i);
+ if (field != NULL) {
+ filled->num_fields++;
+ }
+ }
+ for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
+ mirror::ArtMethod* method = dex_cache->GetResolvedMethod(i);
+ if (method != NULL) {
+ filled->num_methods++;
+ }
+ }
+ for (size_t i = 0; i < dex_cache->NumInitializedStaticStorage(); i++) {
+ mirror::StaticStorageBase* klass = dex_cache->GetInitializedStaticStorage()->Get(i);
+ if (klass != NULL) {
+ filled->num_static_storage++;
+ }
+ }
+ }
+}
+
+// TODO: http://b/11309598 This code was ported over based on the
+// Dalvik version. However, ART has similar code in other places such
+// as the CompilerDriver. This code could probably be refactored to
+// serve both uses.
+static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
+ if (!kPreloadDexCachesEnabled) {
+ return;
+ }
+
+ ScopedObjectAccess soa(env);
+
+ DexCacheStats total;
+ DexCacheStats before;
+ if (kPreloadDexCachesCollectStats) {
+ LOG(INFO) << "VMRuntime.preloadDexCaches starting";
+ PreloadDexCachesStatsTotal(&total);
+ PreloadDexCachesStatsFilled(&before);
+ }
+
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* linker = runtime->GetClassLinker();
+
+ // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings
+ StringTable strings;
+ if (kPreloadDexCachesStrings) {
+ runtime->GetInternTable()->VisitRoots(PreloadDexCachesStringsVisitor, &strings, false, false);
+ }
+
+ const std::vector<const DexFile*>& boot_class_path = linker->GetBootClassPath();
+ for (size_t i = 0; i< boot_class_path.size(); i++) {
+ const DexFile* dex_file = boot_class_path[i];
+ CHECK(dex_file != NULL);
+ mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file);
+
+ if (kPreloadDexCachesStrings) {
+ for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
+ PreloadDexCachesResolveString(dex_cache, i, strings);
+ }
+ }
+
+ if (kPreloadDexCachesTypes) {
+ for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
+ PreloadDexCachesResolveType(dex_cache, i);
+ }
+ }
+
+ if (kPreloadDexCachesFieldsAndMethods) {
+ 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 byte* class_data = dex_file->GetClassData(class_def);
+ if (class_data == NULL) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex_file, class_data);
+ for (; it.HasNextStaticField(); it.Next()) {
+ uint32_t field_idx = it.GetMemberIndex();
+ PreloadDexCachesResolveField(dex_cache, field_idx, true);
+ }
+ for (; it.HasNextInstanceField(); it.Next()) {
+ uint32_t field_idx = it.GetMemberIndex();
+ PreloadDexCachesResolveField(dex_cache, field_idx, false);
+ }
+ for (; it.HasNextDirectMethod(); it.Next()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ InvokeType invoke_type = it.GetMethodInvokeType(class_def);
+ PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type);
+ }
+ for (; it.HasNextVirtualMethod(); it.Next()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ InvokeType invoke_type = it.GetMethodInvokeType(class_def);
+ PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type);
+ }
+ }
+ }
+ }
+
+ if (kPreloadDexCachesCollectStats) {
+ DexCacheStats after;
+ PreloadDexCachesStatsFilled(&after);
+ LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches strings total=%d before=%d after=%d",
+ total.num_strings, before.num_strings, after.num_strings);
+ LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches types total=%d before=%d after=%d",
+ total.num_types, before.num_types, after.num_types);
+ LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches fields total=%d before=%d after=%d",
+ total.num_fields, before.num_fields, after.num_fields);
+ LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches methods total=%d before=%d after=%d",
+ total.num_methods, before.num_methods, after.num_methods);
+ LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches storage total=%d before=%d after=%d",
+ total.num_static_storage,
+ before.num_static_storage,
+ after.num_static_storage);
+ LOG(INFO) << StringPrintf("VMRuntime.preloadDexCaches finished");
+ }
+}
+
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
@@ -206,15 +516,16 @@
NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
- NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+ NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
- NATIVE_METHOD(VMRuntime, setTargetSdkVersion, "(I)V"),
+ NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
NATIVE_METHOD(VMRuntime, startJitCompilation, "()V"),
NATIVE_METHOD(VMRuntime, trimHeap, "()V"),
NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
+ NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index eaf67b8..f915365 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -20,6 +20,7 @@
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "thread_list.h"
@@ -34,7 +35,7 @@
}
// Suspend thread to build stack trace.
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out);
+ Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
if (thread != NULL) {
jobject trace;
{
@@ -42,7 +43,7 @@
trace = thread->CreateInternalStackTrace(soa);
}
// Restart suspended thread.
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ Runtime::Current()->GetThreadList()->Resume(thread, false);
return trace;
} else {
if (timed_out) {
@@ -66,7 +67,7 @@
// Returns the defining class loader of the caller's caller.
static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
NthCallerVisitor visitor(soa.Self(), 2);
visitor.WalkStack();
return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
@@ -93,7 +94,7 @@
mirror::Object* system;
mirror::Object* class_loader;
};
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* bootstrap = soa.Decode<mirror::Object*>(javaBootstrap);
mirror::Object* system = soa.Decode<mirror::Object*>(javaSystem);
ClosestUserClassLoaderVisitor visitor(soa.Self(), bootstrap, system);
@@ -103,7 +104,7 @@
// Returns the class of the caller's caller's caller.
static jclass VMStack_getStackClass2(JNIEnv* env, jclass) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
NthCallerVisitor visitor(soa.Self(), 3);
visitor.WalkStack();
return soa.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass());
@@ -119,9 +120,9 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"),
- NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"),
- NATIVE_METHOD(VMStack, getClosestUserClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
- NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"),
+ NATIVE_METHOD(VMStack, getCallingClassLoader, "!()Ljava/lang/ClassLoader;"),
+ NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
+ NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"),
NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"),
};
diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc
index 2acacc9..2e3d6a6 100644
--- a/runtime/native/dalvik_system_Zygote.cc
+++ b/runtime/native/dalvik_system_Zygote.cc
@@ -35,7 +35,8 @@
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#include "thread.h"
+#include "thread-inl.h"
+#include "utils.h"
#if defined(HAVE_PRCTL)
#include <sys/prctl.h>
@@ -230,18 +231,18 @@
static void SetCapabilities(int64_t permitted, int64_t effective) {
__user_cap_header_struct capheader;
- __user_cap_data_struct capdata;
-
memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
-
- capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
capheader.pid = 0;
- capdata.effective = effective;
- capdata.permitted = permitted;
+ __user_cap_data_struct capdata[2];
+ memset(&capdata, 0, sizeof(capdata));
+ capdata[0].effective = effective;
+ capdata[1].effective = effective >> 32;
+ capdata[0].permitted = permitted;
+ capdata[1].permitted = permitted >> 32;
- if (capset(&capheader, &capdata) != 0) {
+ if (capset(&capheader, &capdata[0]) == -1) {
PLOG(FATAL) << "capset(" << permitted << ", " << effective << ") failed";
}
}
@@ -496,6 +497,15 @@
<< (is_system_server ? "true" : "false") << ", "
<< "\"" << se_info_c_str << "\", \"" << se_name_c_str << "\") failed";
}
+
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_info_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ if (se_info_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
}
#else
UNUSED(is_system_server);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index d3011cb..3591611 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -24,13 +24,14 @@
#include "mirror/proxy.h"
#include "object_utils.h"
#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
#include "well_known_classes.h"
namespace art {
-static mirror::Class* DecodeClass(const ScopedObjectAccess& soa, jobject java_class)
+static mirror::Class* DecodeClass(const ScopedFastNativeObjectAccess& soa, jobject java_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
DCHECK(c != NULL);
@@ -79,13 +80,13 @@
}
static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Class* c = DecodeClass(soa, javaThis);
return soa.AddLocalReference<jstring>(c->ComputeName());
}
static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::SynthesizedProxyClass* c =
down_cast<mirror::SynthesizedProxyClass*>(DecodeClass(soa, javaThis));
return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
@@ -93,8 +94,8 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
- NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
- NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"),
+ NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"),
+ NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"),
};
void register_java_lang_Class(JNIEnv* env) {
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index f8eeb29..51cd5b8 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -17,16 +17,16 @@
#include "dex_file.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "well_known_classes.h"
namespace art {
static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
// Should only be called while holding the lock on the dex cache.
- DCHECK_EQ(dex_cache->GetThinLockId(), soa.Self()->GetThinLockId());
+ DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId());
const DexFile* dex_file = dex_cache->GetDexFile();
if (dex_file == NULL) {
return NULL;
@@ -46,7 +46,7 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DexCache, getDexNative, "()Lcom/android/dex/Dex;"),
+ NATIVE_METHOD(DexCache, getDexNative, "!()Lcom/android/dex/Dex;"),
};
void register_java_lang_DexCache(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index 5db7a33..4768f48 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -16,7 +16,7 @@
#include "jni_internal.h"
#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
// TODO: better support for overloading.
#undef NATIVE_METHOD
@@ -26,41 +26,41 @@
namespace art {
static jobject Object_internalClone(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
return soa.AddLocalReference<jobject>(o->Clone(soa.Self()));
}
static void Object_notify(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->Notify(soa.Self());
}
static void Object_notifyAll(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->NotifyAll(soa.Self());
}
static void Object_wait(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->Wait(soa.Self());
}
static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->Wait(soa.Self(), ms, ns);
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;", internalClone),
- NATIVE_METHOD(Object, notify, "()V", notify),
- NATIVE_METHOD(Object, notifyAll, "()V", notifyAll),
- NATIVE_METHOD(Object, wait, "()V", wait),
- NATIVE_METHOD(Object, wait, "(JI)V", waitJI),
+ NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;", internalClone),
+ NATIVE_METHOD(Object, notify, "!()V", notify),
+ NATIVE_METHOD(Object, notifyAll, "!()V", notifyAll),
+ NATIVE_METHOD(Object, wait, "!()V", wait),
+ NATIVE_METHOD(Object, wait, "!(JI)V", waitJI),
};
void register_java_lang_Object(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index 55575cf..e969fcf 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -41,7 +41,6 @@
}
static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) {
- ScopedObjectAccess soa(env);
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
@@ -62,12 +61,15 @@
}
}
- mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader);
std::string detail;
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, detail);
- if (success) {
- return NULL;
+ {
+ ScopedObjectAccess soa(env);
+ mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader);
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail);
+ if (success) {
+ return nullptr;
+ }
}
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 3e9c3f3..c401d50 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -17,13 +17,14 @@
#include "common_throws.h"
#include "jni_internal.h"
#include "mirror/string.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
namespace art {
static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
if (UNLIKELY(javaRhs == NULL)) {
ThrowNullPointerException(NULL, "rhs == null");
return -1;
@@ -33,7 +34,7 @@
}
static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
// This method does not handle supplementary characters. They're dealt with in managed code.
DCHECK_LE(ch, 0xffff);
@@ -42,16 +43,16 @@
}
static jstring String_intern(JNIEnv* env, jobject javaThis) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(javaThis);
mirror::String* result = s->Intern();
return soa.AddLocalReference<jstring>(result);
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"),
- NATIVE_METHOD(String, fastIndexOf, "(II)I"),
- NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
+ NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"),
+ NATIVE_METHOD(String, fastIndexOf, "!(II)I"),
+ NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"),
};
void register_java_lang_String(JNIEnv* env) {
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 30b4dc7..ea78e04 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -22,7 +22,7 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
/*
* We make guarantees about the atomicity of accesses to primitive
@@ -179,7 +179,7 @@
}
static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
// Null pointer checks.
if (UNLIKELY(javaSrc == NULL)) {
@@ -316,15 +316,41 @@
}
}
+static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
+ ScopedFastNativeObjectAccess soa(env);
+ DCHECK(javaSrc != NULL);
+ DCHECK(javaDst != NULL);
+ mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc);
+ mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst);
+ DCHECK(srcObject->IsArrayInstance());
+ DCHECK(dstObject->IsArrayInstance());
+ mirror::Array* srcArray = srcObject->AsArray();
+ mirror::Array* dstArray = dstObject->AsArray();
+ DCHECK(srcPos >= 0 && dstPos >= 0 && length >= 0 &&
+ srcPos + length <= srcArray->GetLength() && dstPos + length <= dstArray->GetLength());
+ DCHECK_EQ(srcArray->GetClass()->GetComponentType(), dstArray->GetClass()->GetComponentType());
+ DCHECK(srcArray->GetClass()->GetComponentType()->IsPrimitive());
+ DCHECK(dstArray->GetClass()->GetComponentType()->IsPrimitive());
+ DCHECK_EQ(srcArray->GetClass()->GetComponentSize(), static_cast<size_t>(2));
+ DCHECK_EQ(dstArray->GetClass()->GetComponentSize(), static_cast<size_t>(2));
+ uint8_t* dstBytes = reinterpret_cast<uint8_t*>(dstArray->GetRawData(2));
+ const uint8_t* srcBytes = reinterpret_cast<const uint8_t*>(srcArray->GetRawData(2));
+ move16(dstBytes + dstPos * 2, srcBytes + srcPos * 2, length * 2);
+}
+
static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
- ScopedObjectAccess soa(env);
+ if (javaObject == nullptr) {
+ return 0;
+ }
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(javaObject);
return static_cast<jint>(o->IdentityHashCode());
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
- NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"),
+ NATIVE_METHOD(System, arraycopy, "!(Ljava/lang/Object;ILjava/lang/Object;II)V"),
+ NATIVE_METHOD(System, arraycopyCharUnchecked, "!([CI[CII)V"),
+ NATIVE_METHOD(System, identityHashCode, "!(Ljava/lang/Object;)I"),
};
void register_java_lang_System(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index e85ef09..5b34cfb 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -19,6 +19,7 @@
#include "jni_internal.h"
#include "monitor.h"
#include "mirror/object.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedUtfChars.h"
#include "thread.h"
@@ -27,7 +28,7 @@
namespace art {
static jobject Thread_currentThread(JNIEnv* env, jclass) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
}
@@ -122,13 +123,13 @@
// thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
// in the DDMS send code.
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out);
+ Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
if (thread != NULL) {
{
ScopedObjectAccess soa(env);
thread->SetThreadName(name.c_str());
}
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ Runtime::Current()->GetThreadList()->Resume(thread, false);
} else if (timed_out) {
LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
"failed to suspend within a generous timeout.";
@@ -150,7 +151,7 @@
}
static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock);
Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping);
}
@@ -166,7 +167,7 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"),
+ NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"),
NATIVE_METHOD(Thread, interrupted, "()Z"),
NATIVE_METHOD(Thread, isInterrupted, "()Z"),
NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
@@ -175,7 +176,7 @@
NATIVE_METHOD(Thread, nativeInterrupt, "()V"),
NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),
- NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"),
NATIVE_METHOD(Thread, yield, "()V"),
};
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index c23b08c..af1b548 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -72,8 +72,10 @@
}
const DexFile* dex_file = path[index];
const std::string& location(dex_file->GetLocation());
- UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(location));
- if (zip_archive.get() == NULL) {
+ std::string error_msg;
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(location.c_str(), &error_msg));
+ if (zip_archive.get() == nullptr) {
+ LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg;
return NULL;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(name.c_str()));
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 45ec0ad..a2d6b18 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -21,13 +21,13 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "object_utils.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "sirt_ref.h"
namespace art {
static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
DCHECK(javaElementClass != NULL);
mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
DCHECK(element_class->IsClass());
@@ -41,7 +41,7 @@
}
static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
DCHECK(javaElementClass != NULL);
mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
if (UNLIKELY(length < 0)) {
@@ -63,8 +63,8 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Array, createMultiArray, "(Ljava/lang/Class;[I)Ljava/lang/Object;"),
- NATIVE_METHOD(Array, createObjectArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+ NATIVE_METHOD(Array, createMultiArray, "!(Ljava/lang/Class;[I)Ljava/lang/Object;"),
+ NATIVE_METHOD(Array, createObjectArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
};
void register_java_lang_reflect_Array(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 85556ac..aa72755 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -35,6 +35,7 @@
* with an interface, array, or primitive class.
*/
static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
+ // TODO: ScopedFastNativeObjectAccess
ScopedObjectAccess soa(env);
jobject art_method = soa.Env()->GetObjectField(
javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod);
@@ -68,7 +69,7 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Constructor, newInstance, "([Ljava/lang/Object;)Ljava/lang/Object;"),
+ NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"),
};
void register_java_lang_reflect_Constructor(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 00f89b6..4d69a68 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -23,12 +23,12 @@
#include "mirror/class-inl.h"
#include "object_utils.h"
#include "reflection.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
namespace art {
-static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirror::ArtField* f,
- JValue& value, bool allow_references)
+static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o,
+ mirror::ArtField* f, JValue& value, bool allow_references)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_EQ(value.GetJ(), 0LL);
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(),
@@ -77,8 +77,8 @@
return false;
}
-static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::ArtField* f,
- mirror::Object*& class_or_rcvr)
+static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, jobject j_rcvr,
+ mirror::ArtField* f, mirror::Object*& class_or_rcvr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (f->IsStatic()) {
class_or_rcvr = f->GetDeclaringClass();
@@ -94,7 +94,7 @@
}
static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
mirror::Object* o = NULL;
if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -112,7 +112,7 @@
static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
char dst_descriptor) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
mirror::Object* o = NULL;
if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -221,7 +221,7 @@
}
static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
// Unbox the value, if necessary.
@@ -242,7 +242,7 @@
static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
const JValue& new_value) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
mirror::Object* o = NULL;
if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -316,24 +316,24 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Field, get, "(Ljava/lang/Object;)Ljava/lang/Object;"),
- NATIVE_METHOD(Field, getBoolean, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(Field, getByte, "(Ljava/lang/Object;)B"),
- NATIVE_METHOD(Field, getChar, "(Ljava/lang/Object;)C"),
- NATIVE_METHOD(Field, getDouble, "(Ljava/lang/Object;)D"),
- NATIVE_METHOD(Field, getFloat, "(Ljava/lang/Object;)F"),
- NATIVE_METHOD(Field, getInt, "(Ljava/lang/Object;)I"),
- NATIVE_METHOD(Field, getLong, "(Ljava/lang/Object;)J"),
- NATIVE_METHOD(Field, getShort, "(Ljava/lang/Object;)S"),
- NATIVE_METHOD(Field, set, "(Ljava/lang/Object;Ljava/lang/Object;)V"),
- NATIVE_METHOD(Field, setBoolean, "(Ljava/lang/Object;Z)V"),
- NATIVE_METHOD(Field, setByte, "(Ljava/lang/Object;B)V"),
- NATIVE_METHOD(Field, setChar, "(Ljava/lang/Object;C)V"),
- NATIVE_METHOD(Field, setDouble, "(Ljava/lang/Object;D)V"),
- NATIVE_METHOD(Field, setFloat, "(Ljava/lang/Object;F)V"),
- NATIVE_METHOD(Field, setInt, "(Ljava/lang/Object;I)V"),
- NATIVE_METHOD(Field, setLong, "(Ljava/lang/Object;J)V"),
- NATIVE_METHOD(Field, setShort, "(Ljava/lang/Object;S)V"),
+ NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;)Ljava/lang/Object;"),
+ NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;)B"),
+ NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"),
+ NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"),
+ NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;)F"),
+ NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;)I"),
+ NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;)S"),
+ NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;)V"),
+ NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"),
+ NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;B)V"),
+ NATIVE_METHOD(Field, setChar, "!(Ljava/lang/Object;C)V"),
+ NATIVE_METHOD(Field, setDouble, "!(Ljava/lang/Object;D)V"),
+ NATIVE_METHOD(Field, setFloat, "!(Ljava/lang/Object;F)V"),
+ NATIVE_METHOD(Field, setInt, "!(Ljava/lang/Object;I)V"),
+ NATIVE_METHOD(Field, setLong, "!(Ljava/lang/Object;J)V"),
+ NATIVE_METHOD(Field, setShort, "!(Ljava/lang/Object;S)V"),
};
void register_java_lang_reflect_Field(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index d7cd18d..163ae20 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,21 +16,21 @@
#include "base/logging.h"
#include "debugger.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "ScopedPrimitiveArray.h"
namespace art {
static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type,
jbyteArray javaData, jint offset, jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
ScopedByteArrayRO data(env, javaData);
DCHECK_LE(offset + length, static_cast<int32_t>(data.size()));
Dbg::DdmSendChunk(type, length, reinterpret_cast<const uint8_t*>(&data[offset]));
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DdmServer, nativeSendChunk, "(I[BII)V"),
+ NATIVE_METHOD(DdmServer, nativeSendChunk, "!(I[BII)V"),
};
void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 13cd978..4f81a0b 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -44,22 +44,10 @@
* NULL on failure, e.g. if the threadId couldn't be found.
*/
static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
- ScopedLocalRef<jobject> peer(env, NULL);
- {
- Thread* t = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(thin_lock_id);
- if (t == NULL) {
- return NULL;
- }
- ScopedObjectAccess soa(env);
- peer.reset(soa.AddLocalReference<jobject>(t->GetPeer()));
- }
- if (peer.get() == NULL) {
- return NULL;
- }
-
// Suspend thread to build stack trace.
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer.get(), true, &timed_out);
+ Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out);
if (thread != NULL) {
jobject trace;
{
@@ -67,7 +55,7 @@
trace = thread->CreateInternalStackTrace(soa);
}
// Restart suspended thread.
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ thread_list->Resume(thread, false);
return Thread::InternalStackTraceToStackTraceElementArray(env, trace);
} else {
if (timed_out) {
@@ -109,11 +97,13 @@
* be removed from a future version.
*/
char native_thread_state;
- int utime, stime, task_cpu;
- GetTaskStats(t->GetTid(), native_thread_state, utime, stime, task_cpu);
+ int utime;
+ int stime;
+ int task_cpu;
+ GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu);
std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context);
- JDWP::Append4BE(bytes, t->GetThinLockId());
+ JDWP::Append4BE(bytes, t->GetThreadId());
JDWP::Append1BE(bytes, Dbg::ToJdwpThreadStatus(t->GetState()));
JDWP::Append4BE(bytes, t->GetTid());
JDWP::Append4BE(bytes, utime);
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
new file mode 100644
index 0000000..d941ec3
--- /dev/null
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -0,0 +1,103 @@
+/*
+ * 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_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
+#define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
+
+#include "base/casts.h"
+#include "jni_internal.h"
+#include "thread-inl.h"
+#include "mirror/art_method.h"
+
+namespace art {
+
+// Variant of ScopedObjectAccess that does no runnable transitions. Should only be used by "fast"
+// JNI methods.
+class ScopedFastNativeObjectAccess {
+ public:
+ explicit ScopedFastNativeObjectAccess(JNIEnv* env)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
+ : env_(down_cast<JNIEnvExt*>(env)), self_(ThreadForEnv(env)) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+ }
+
+ ~ScopedFastNativeObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
+ }
+
+ Thread* Self() const {
+ return self_;
+ }
+
+ JNIEnvExt* Env() const {
+ return env_;
+ }
+
+ template<typename T>
+ T Decode(jobject obj) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+ return down_cast<T>(Self()->DecodeJObject(obj));
+ }
+
+ mirror::ArtField* DecodeField(jfieldID fid) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+#ifdef MOVING_GARBAGE_COLLECTOR
+ // TODO: we should make these unique weak globals if Field instances can ever move.
+ UNIMPLEMENTED(WARNING);
+#endif
+ return reinterpret_cast<mirror::ArtField*>(fid);
+ }
+
+ /*
+ * Variant of ScopedObjectAccessUnched::AddLocalReference that without JNI work arounds
+ * or check JNI that should be being used by fast native methods.
+ */
+ template<typename T>
+ T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
+
+ IndirectReferenceTable& locals = Env()->locals;
+
+ uint32_t cookie = Env()->local_ref_cookie;
+ IndirectRef ref = locals.Add(cookie, obj);
+
+ return reinterpret_cast<T>(ref);
+ }
+
+ private:
+ JNIEnvExt* const env_;
+ Thread* const self_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index eece81a..2c6d281 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -19,12 +19,12 @@
#include "jni_internal.h"
#include "mirror/object.h"
#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
namespace art {
static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
@@ -34,7 +34,7 @@
}
static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
volatile int64_t* address = reinterpret_cast<volatile int64_t*>(raw_addr);
@@ -44,7 +44,7 @@
}
static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
@@ -60,97 +60,97 @@
}
static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField32(MemberOffset(offset), false);
}
static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField32(MemberOffset(offset), true);
}
static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField32(MemberOffset(offset), newValue, false);
}
static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField32(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
ANDROID_MEMBAR_STORE();
obj->SetField32(MemberOffset(offset), newValue, false);
}
static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField64(MemberOffset(offset), false);
}
static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField64(MemberOffset(offset), true);
}
static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField64(MemberOffset(offset), newValue, false);
}
static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField64(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
ANDROID_MEMBAR_STORE();
obj->SetField64(MemberOffset(offset), newValue, false);
}
static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), true);
return soa.AddLocalReference<jobject>(value);
}
static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), false);
return soa.AddLocalReference<jobject>(value);
}
static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
obj->SetFieldObject(MemberOffset(offset), newValue, false);
}
static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
obj->SetFieldObject(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
ANDROID_MEMBAR_STORE();
@@ -158,24 +158,24 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
- NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
- NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
- NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
- NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
- NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
- NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
- NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
- NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
- NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
- NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
- NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
- NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
- NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
+ NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
+ NATIVE_METHOD(Unsafe, compareAndSwapObject, "!(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(Unsafe, getIntVolatile, "!(Ljava/lang/Object;J)I"),
+ NATIVE_METHOD(Unsafe, putIntVolatile, "!(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Unsafe, getLongVolatile, "!(Ljava/lang/Object;J)J"),
+ NATIVE_METHOD(Unsafe, putLongVolatile, "!(Ljava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, getObjectVolatile, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ NATIVE_METHOD(Unsafe, putObjectVolatile, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, getInt, "!(Ljava/lang/Object;J)I"),
+ NATIVE_METHOD(Unsafe, putInt, "!(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Unsafe, putOrderedInt, "!(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Unsafe, getLong, "!(Ljava/lang/Object;J)J"),
+ NATIVE_METHOD(Unsafe, putLong, "!(Ljava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, putOrderedLong, "!(Ljava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, getObject, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ NATIVE_METHOD(Unsafe, putObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
};
void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index c01f77c..9489795 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,13 +22,14 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '0', '7', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '1', '0', '\0' };
OatHeader::OatHeader() {
memset(this, 0, sizeof(*this));
}
OatHeader::OatHeader(InstructionSet instruction_set,
+ const InstructionSetFeatures& instruction_set_features,
const std::vector<const DexFile*>* dex_files,
uint32_t image_file_location_oat_checksum,
uint32_t image_file_location_oat_data_begin,
@@ -42,6 +43,9 @@
instruction_set_ = instruction_set;
UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
+ instruction_set_features_ = instruction_set_features;
+ UpdateChecksum(&instruction_set_features_, sizeof(instruction_set_features_));
+
dex_file_count_ = dex_files->size();
UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
@@ -60,8 +64,10 @@
interpreter_to_interpreter_bridge_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_imt_conflict_trampoline_offset_ = 0;
quick_resolution_trampoline_offset_ = 0;
quick_to_interpreter_bridge_offset_ = 0;
}
@@ -97,6 +103,11 @@
return instruction_set_;
}
+const InstructionSetFeatures& OatHeader::GetInstructionSetFeatures() const {
+ CHECK(IsValid());
+ return instruction_set_features_;
+}
+
uint32_t OatHeader::GetExecutableOffset() const {
DCHECK(IsValid());
DCHECK_ALIGNED(executable_offset_, kPageSize);
@@ -171,18 +182,37 @@
UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset));
}
+const void* OatHeader::GetPortableImtConflictTrampoline() const {
+ return reinterpret_cast<const uint8_t*>(this) + GetPortableImtConflictTrampolineOffset();
+}
+
+uint32_t OatHeader::GetPortableImtConflictTrampolineOffset() const {
+ DCHECK(IsValid());
+ CHECK_GE(portable_imt_conflict_trampoline_offset_, jni_dlsym_lookup_offset_);
+ return portable_imt_conflict_trampoline_offset_;
+}
+
+void OatHeader::SetPortableImtConflictTrampolineOffset(uint32_t offset) {
+ CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
+ DCHECK(IsValid());
+ DCHECK_EQ(portable_imt_conflict_trampoline_offset_, 0U) << offset;
+
+ portable_imt_conflict_trampoline_offset_ = offset;
+ UpdateChecksum(&portable_imt_conflict_trampoline_offset_, sizeof(offset));
+}
+
const void* OatHeader::GetPortableResolutionTrampoline() const {
return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset();
}
uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const {
DCHECK(IsValid());
- CHECK_GE(portable_resolution_trampoline_offset_, jni_dlsym_lookup_offset_);
+ CHECK_GE(portable_resolution_trampoline_offset_, portable_imt_conflict_trampoline_offset_);
return portable_resolution_trampoline_offset_;
}
void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
+ CHECK(offset == 0 || offset >= portable_imt_conflict_trampoline_offset_);
DCHECK(IsValid());
DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset;
@@ -209,18 +239,37 @@
UpdateChecksum(&portable_to_interpreter_bridge_offset_, sizeof(offset));
}
+const void* OatHeader::GetQuickImtConflictTrampoline() const {
+ return reinterpret_cast<const uint8_t*>(this) + GetQuickImtConflictTrampolineOffset();
+}
+
+uint32_t OatHeader::GetQuickImtConflictTrampolineOffset() const {
+ DCHECK(IsValid());
+ CHECK_GE(quick_imt_conflict_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+ return quick_imt_conflict_trampoline_offset_;
+}
+
+void OatHeader::SetQuickImtConflictTrampolineOffset(uint32_t offset) {
+ CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+ DCHECK(IsValid());
+ DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
+
+ quick_imt_conflict_trampoline_offset_ = offset;
+ UpdateChecksum(&quick_imt_conflict_trampoline_offset_, sizeof(offset));
+}
+
const void* OatHeader::GetQuickResolutionTrampoline() const {
return reinterpret_cast<const uint8_t*>(this) + GetQuickResolutionTrampolineOffset();
}
uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const {
DCHECK(IsValid());
- CHECK_GE(quick_resolution_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+ CHECK_GE(quick_resolution_trampoline_offset_, quick_imt_conflict_trampoline_offset_);
return quick_resolution_trampoline_offset_;
}
void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+ CHECK(offset == 0 || offset >= quick_imt_conflict_trampoline_offset_);
DCHECK(IsValid());
DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
diff --git a/runtime/oat.h b/runtime/oat.h
index a653cf8..de840b5 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,6 +32,7 @@
OatHeader();
OatHeader(InstructionSet instruction_set,
+ const InstructionSetFeatures& instruction_set_features,
const std::vector<const DexFile*>* dex_files,
uint32_t image_file_location_oat_checksum,
uint32_t image_file_location_oat_data_begin,
@@ -62,6 +63,9 @@
const void* GetPortableResolutionTrampoline() const;
uint32_t GetPortableResolutionTrampolineOffset() const;
void SetPortableResolutionTrampolineOffset(uint32_t offset);
+ const void* GetPortableImtConflictTrampoline() const;
+ uint32_t GetPortableImtConflictTrampolineOffset() const;
+ void SetPortableImtConflictTrampolineOffset(uint32_t offset);
const void* GetPortableToInterpreterBridge() const;
uint32_t GetPortableToInterpreterBridgeOffset() const;
void SetPortableToInterpreterBridgeOffset(uint32_t offset);
@@ -69,11 +73,15 @@
const void* GetQuickResolutionTrampoline() const;
uint32_t GetQuickResolutionTrampolineOffset() const;
void SetQuickResolutionTrampolineOffset(uint32_t offset);
+ const void* GetQuickImtConflictTrampoline() const;
+ uint32_t GetQuickImtConflictTrampolineOffset() const;
+ void SetQuickImtConflictTrampolineOffset(uint32_t offset);
const void* GetQuickToInterpreterBridge() const;
uint32_t GetQuickToInterpreterBridgeOffset() const;
void SetQuickToInterpreterBridgeOffset(uint32_t offset);
InstructionSet GetInstructionSet() const;
+ const InstructionSetFeatures& GetInstructionSetFeatures() const;
uint32_t GetImageFileLocationOatChecksum() const;
uint32_t GetImageFileLocationOatDataBegin() const;
uint32_t GetImageFileLocationSize() const;
@@ -86,13 +94,16 @@
uint32_t adler32_checksum_;
InstructionSet instruction_set_;
+ InstructionSetFeatures instruction_set_features_;
uint32_t dex_file_count_;
uint32_t executable_offset_;
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
+ uint32_t portable_imt_conflict_trampoline_offset_;
uint32_t portable_resolution_trampoline_offset_;
uint32_t portable_to_interpreter_bridge_offset_;
+ uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
@@ -104,6 +115,19 @@
DISALLOW_COPY_AND_ASSIGN(OatHeader);
};
+// OatMethodOffsets are currently 7x32-bits=224-bits long, so if we can
+// save even one OatMethodOffsets struct, the more complicated encoding
+// using a bitmap pays for itself since few classes will have 224
+// methods.
+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.
+ kOatClassMax = 3,
+};
+
+std::ostream& operator<<(std::ostream& os, const OatClassType& rhs);
+
class PACKED(4) OatMethodOffsets {
public:
OatMethodOffsets();
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 7ecaf01..fa2b485 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -18,6 +18,7 @@
#include <dlfcn.h>
+#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "elf_file.h"
@@ -48,19 +49,21 @@
}
OatFile* OatFile::OpenMemory(std::vector<uint8_t>& oat_contents,
- const std::string& location) {
+ const std::string& location,
+ std::string* error_msg) {
CHECK(!oat_contents.empty()) << location;
CheckLocation(location);
UniquePtr<OatFile> oat_file(new OatFile(location));
oat_file->begin_ = &oat_contents[0];
oat_file->end_ = &oat_contents[oat_contents.size()];
- return oat_file->Setup() ? oat_file.release() : NULL;
+ return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
}
OatFile* OatFile::Open(const std::string& filename,
const std::string& location,
byte* requested_base,
- bool executable) {
+ bool executable,
+ std::string* error_msg) {
CHECK(!filename.empty()) << location;
CheckLocation(filename);
#ifdef ART_USE_PORTABLE_COMPILER
@@ -70,7 +73,7 @@
// open a generated dex file by name, remove the file, then open
// another generated dex file with the same name. http://b/10614658
if (executable) {
- return OpenDlopen(filename, location, requested_base);
+ return OpenDlopen(filename, location, requested_base, error_msg);
}
#endif
// If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
@@ -83,21 +86,22 @@
if (file.get() == NULL) {
return NULL;
}
- return OpenElfFile(file.get(), location, requested_base, false, executable);
+ return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg);
}
-OatFile* OatFile::OpenWritable(File* file, const std::string& location) {
+OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, true, false);
+ return OpenElfFile(file, location, NULL, true, false, error_msg);
}
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
const std::string& location,
- byte* requested_base) {
+ byte* requested_base,
+ std::string* error_msg) {
UniquePtr<OatFile> oat_file(new OatFile(location));
- bool success = oat_file->Dlopen(elf_filename, requested_base);
+ bool success = oat_file->Dlopen(elf_filename, requested_base, error_msg);
if (!success) {
- return NULL;
+ return nullptr;
}
return oat_file.release();
}
@@ -106,11 +110,13 @@
const std::string& location,
byte* requested_base,
bool writable,
- bool executable) {
+ bool executable,
+ std::string* error_msg) {
UniquePtr<OatFile> oat_file(new OatFile(location));
- bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable);
+ bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
if (!success) {
- return NULL;
+ CHECK(!error_msg->empty());
+ return nullptr;
}
return oat_file.release();
}
@@ -127,120 +133,117 @@
}
}
-bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base) {
+bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base,
+ std::string* error_msg) {
char* absolute_path = realpath(elf_filename.c_str(), NULL);
if (absolute_path == NULL) {
- VLOG(class_linker) << "Failed to find absolute path for " << elf_filename;
+ *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
return false;
}
dlopen_handle_ = dlopen(absolute_path, RTLD_NOW);
free(absolute_path);
if (dlopen_handle_ == NULL) {
- VLOG(class_linker) << "Failed to dlopen " << elf_filename << ": " << dlerror();
+ *error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror());
return false;
}
begin_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatdata"));
if (begin_ == NULL) {
- LOG(WARNING) << "Failed to find oatdata symbol in " << elf_filename << ": " << dlerror();
+ *error_msg = StringPrintf("Failed to find oatdata symbol in '%s': %s", elf_filename.c_str(),
+ dlerror());
return false;
}
if (requested_base != NULL && begin_ != requested_base) {
- std::string maps;
- ReadFileToString("/proc/self/maps", &maps);
- LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata="
- << reinterpret_cast<const void*>(begin_) << " != expected="
- << reinterpret_cast<const void*>(requested_base)
- << " /proc/self/maps:\n" << maps;
+ *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
+ "oatdata=%p != expected=%p /proc/self/maps:\n",
+ begin_, requested_base);
+ ReadFileToString("/proc/self/maps", error_msg);
return false;
}
end_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatlastword"));
if (end_ == NULL) {
- LOG(WARNING) << "Failed to find oatlastword symbol in " << elf_filename << ": " << dlerror();
+ *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s': %s", elf_filename.c_str(),
+ dlerror());
return false;
}
// Readjust to be non-inclusive upper bound.
end_ += sizeof(uint32_t);
- return Setup();
+ return Setup(error_msg);
}
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable) {
- elf_file_.reset(ElfFile::Open(file, writable, true));
- if (elf_file_.get() == NULL) {
- if (writable) {
- PLOG(WARNING) << "Failed to open ELF file for " << file->GetPath();
- }
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ std::string* error_msg) {
+ elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+ if (elf_file_.get() == nullptr) {
+ DCHECK(!error_msg->empty());
return false;
}
- bool loaded = elf_file_->Load(executable);
+ bool loaded = elf_file_->Load(executable, error_msg);
if (!loaded) {
- LOG(WARNING) << "Failed to load ELF file " << file->GetPath();
+ DCHECK(!error_msg->empty());
return false;
}
begin_ = elf_file_->FindDynamicSymbolAddress("oatdata");
if (begin_ == NULL) {
- LOG(WARNING) << "Failed to find oatdata symbol in " << file->GetPath();
+ *error_msg = StringPrintf("Failed to find oatdata symbol in '%s'", file->GetPath().c_str());
return false;
}
if (requested_base != NULL && begin_ != requested_base) {
- std::string maps;
- ReadFileToString("/proc/self/maps", &maps);
- LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata="
- << reinterpret_cast<const void*>(begin_) << " != expected="
- << reinterpret_cast<const void*>(requested_base)
- << " /proc/self/maps:\n" << maps;
+ *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
+ "oatdata=%p != expected=%p /proc/self/maps:\n",
+ begin_, requested_base);
+ ReadFileToString("/proc/self/maps", error_msg);
return false;
}
end_ = elf_file_->FindDynamicSymbolAddress("oatlastword");
if (end_ == NULL) {
- LOG(WARNING) << "Failed to find oatlastword symbol in " << file->GetPath();
+ *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s'", file->GetPath().c_str());
return false;
}
// Readjust to be non-inclusive upper bound.
end_ += sizeof(uint32_t);
- return Setup();
+ return Setup(error_msg);
}
-bool OatFile::Setup() {
+bool OatFile::Setup(std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
- LOG(WARNING) << "Invalid oat magic for " << GetLocation();
+ *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str());
return false;
}
const byte* oat = Begin();
oat += sizeof(OatHeader);
if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found truncated OatHeader";
+ *error_msg = StringPrintf("In oat file '%s' found truncated OatHeader", GetLocation().c_str());
return false;
}
oat += GetOatHeader().GetImageFileLocationSize();
if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found truncated image file location: "
- << reinterpret_cast<const void*>(Begin())
- << "+" << sizeof(OatHeader)
- << "+" << GetOatHeader().GetImageFileLocationSize()
- << "<=" << reinterpret_cast<const void*>(End());
+ *error_msg = StringPrintf("In oat file '%s' found truncated image file location: "
+ "%p + %zd + %ud <= %p", GetLocation().c_str(),
+ Begin(), sizeof(OatHeader), GetOatHeader().GetImageFileLocationSize(),
+ End());
return false;
}
for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) {
size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
- if (dex_file_location_size == 0U) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " with empty location name";
+ if (UNLIKELY(dex_file_location_size == 0U)) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with empty location name",
+ GetLocation().c_str(), i);
return false;
}
oat += sizeof(dex_file_location_size);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " truncated after dex file location size";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd truncated after dex file "
+ "location size", GetLocation().c_str(), i);
return false;
}
const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
oat += dex_file_location_size;
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " with truncated dex file location";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with truncated dex file "
+ "location", GetLocation().c_str(), i);
return false;
}
@@ -248,55 +251,54 @@
uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
oat += sizeof(dex_file_checksum);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " truncated after dex file checksum";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated after "
+ "dex file checksum", GetLocation().c_str(), i,
+ dex_file_location.c_str());
return false;
}
uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
- if (dex_file_offset == 0U) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with zero dex file offset";
+ if (UNLIKELY(dex_file_offset == 0U)) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with zero dex "
+ "file offset", GetLocation().c_str(), i, dex_file_location.c_str());
return false;
}
- if (dex_file_offset > Size()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with dex file offset" << dex_file_offset << " > " << Size();
+ if (UNLIKELY(dex_file_offset > Size())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with dex file "
+ "offset %ud > %zd", GetLocation().c_str(), i,
+ dex_file_location.c_str(), dex_file_offset, Size());
return false;
}
oat += sizeof(dex_file_offset);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " truncated after dex file offset";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated "
+ " after dex file offsets", GetLocation().c_str(), i,
+ dex_file_location.c_str());
return false;
}
const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
- if (!DexFile::IsMagicValid(dex_file_pointer)) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with invalid dex file magic: " << dex_file_pointer;
+ if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
+ " dex file magic '%s'", GetLocation().c_str(), i,
+ dex_file_location.c_str(), dex_file_pointer);
return false;
}
- if (!DexFile::IsVersionValid(dex_file_pointer)) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with invalid dex file version: " << dex_file_pointer;
+ if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
+ " dex file version '%s'", GetLocation().c_str(), i,
+ dex_file_location.c_str(), dex_file_pointer);
return false;
}
const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);
oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with truncated method offsets";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "
+ " method offsets", GetLocation().c_str(), i,
+ dex_file_location.c_str());
return false;
}
@@ -323,8 +325,8 @@
return end_;
}
-const OatFile::OatDexFile* OatFile::GetOatDexFile(const std::string& dex_location,
- const uint32_t* const dex_location_checksum,
+const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
+ const uint32_t* dex_location_checksum,
bool warn_if_not_found) const {
Table::const_iterator it = oat_dex_files_.find(dex_location);
if (it != oat_dex_files_.end()) {
@@ -373,9 +375,9 @@
return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_;
}
-const DexFile* OatFile::OatDexFile::OpenDexFile() const {
+const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_);
+ dex_file_location_checksum_, error_msg);
}
const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const {
@@ -383,29 +385,92 @@
const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset;
CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation();
- mirror::Class::Status status = *reinterpret_cast<const mirror::Class::Status*>(oat_class_pointer);
- const byte* methods_pointer = oat_class_pointer + sizeof(status);
+ const byte* status_pointer = oat_class_pointer;
+ CHECK_LT(status_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ mirror::Class::Status status =
+ static_cast<mirror::Class::Status>(*reinterpret_cast<const int16_t*>(status_pointer));
+ CHECK_LT(status, mirror::Class::kStatusMax);
+
+ const byte* type_pointer = status_pointer + sizeof(uint16_t);
+ CHECK_LT(type_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ OatClassType type = static_cast<OatClassType>(*reinterpret_cast<const uint16_t*>(type_pointer));
+ CHECK_LT(type, kOatClassMax);
+
+ const byte* bitmap_pointer = type_pointer + sizeof(int16_t);
+ CHECK_LT(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ uint32_t bitmap_size = 0;
+ if (type == kOatClassSomeCompiled) {
+ bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(bitmap_pointer));
+ bitmap_pointer += sizeof(bitmap_size);
+ CHECK_LT(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ }
+
+ const byte* methods_pointer = bitmap_pointer + bitmap_size;
CHECK_LT(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
return new OatClass(oat_file_,
status,
+ type,
+ bitmap_size,
+ reinterpret_cast<const uint32_t*>(bitmap_pointer),
reinterpret_cast<const OatMethodOffsets*>(methods_pointer));
}
OatFile::OatClass::OatClass(const OatFile* oat_file,
mirror::Class::Status status,
+ OatClassType type,
+ uint32_t bitmap_size,
+ const uint32_t* bitmap_pointer,
const OatMethodOffsets* methods_pointer)
- : oat_file_(oat_file), status_(status), methods_pointer_(methods_pointer) {}
+ : oat_file_(oat_file), status_(status), type_(type),
+ bitmap_(NULL), methods_pointer_(methods_pointer) {
+ switch (type_) {
+ case kOatClassAllCompiled: {
+ CHECK_EQ(0U, bitmap_size);
+ break;
+ }
+ case kOatClassSomeCompiled: {
+ CHECK_NE(0U, bitmap_size);
+ bitmap_ = new BitVector(0, false, Allocator::GetNoopAllocator(), bitmap_size,
+ const_cast<uint32_t*>(bitmap_pointer));
+ break;
+ }
+ case kOatClassNoneCompiled: {
+ CHECK_EQ(0U, bitmap_size);
+ methods_pointer_ = NULL;
+ break;
+ }
+ case kOatClassMax: {
+ LOG(FATAL) << "Invalid OatClassType " << type_;
+ break;
+ }
+ }
+}
-OatFile::OatClass::~OatClass() {}
-
-mirror::Class::Status OatFile::OatClass::GetStatus() const {
- return status_;
+OatFile::OatClass::~OatClass() {
+ delete bitmap_;
}
const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const {
- const OatMethodOffsets& oat_method_offsets = methods_pointer_[method_index];
+ if (methods_pointer_ == NULL) {
+ CHECK_EQ(kOatClassNoneCompiled, type_);
+ return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ }
+ size_t methods_pointer_index;
+ if (bitmap_ == NULL) {
+ CHECK_EQ(kOatClassAllCompiled, type_);
+ methods_pointer_index = method_index;
+ } else {
+ CHECK_EQ(kOatClassSomeCompiled, type_);
+ if (!bitmap_->IsBitSet(method_index)) {
+ return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ }
+ size_t num_set_bits = bitmap_->NumSetBits(method_index);
+ CHECK_NE(0U, num_set_bits);
+ methods_pointer_index = num_set_bits - 1;
+ }
+ const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index];
return OatMethod(
oat_file_->Begin(),
oat_method_offsets.code_offset_,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 270976f..887a9d1 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -29,6 +29,7 @@
namespace art {
+class BitVector;
class ElfFile;
class MemMap;
class OatMethodOffsets;
@@ -45,18 +46,20 @@
static OatFile* Open(const std::string& filename,
const std::string& location,
byte* requested_base,
- bool executable);
+ bool executable,
+ std::string* error_msg);
// Open an oat file from an already opened File.
// Does not use dlopen underneath so cannot be used for runtime use
// where relocations may be required. Currently used from
// ImageWriter which wants to open a writable version from an existing
// file descriptor for patching.
- static OatFile* OpenWritable(File* file, const std::string& location);
+ static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
// Open an oat file backed by a std::vector with the given location.
static OatFile* OpenMemory(std::vector<uint8_t>& oat_contents,
- const std::string& location);
+ const std::string& location,
+ std::string* error_msg);
~OatFile();
@@ -143,7 +146,13 @@
class OatClass {
public:
- mirror::Class::Status GetStatus() const;
+ mirror::Class::Status GetStatus() const {
+ return status_;
+ }
+
+ OatClassType GetType() const {
+ return type_;
+ }
// get the OatMethod entry based on its index into the class
// defintion. direct methods come first, followed by virtual
@@ -155,10 +164,21 @@
private:
OatClass(const OatFile* oat_file,
mirror::Class::Status status,
+ OatClassType type,
+ uint32_t bitmap_size,
+ const uint32_t* bitmap_pointer,
const OatMethodOffsets* methods_pointer);
const OatFile* oat_file_;
+
const mirror::Class::Status status_;
+ COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits);
+
+ OatClassType type_;
+ COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits);
+
+ const BitVector* bitmap_;
+
const OatMethodOffsets* methods_pointer_;
friend class OatDexFile;
@@ -167,7 +187,7 @@
class OatDexFile {
public:
// Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
- const DexFile* OpenDexFile() const;
+ const DexFile* OpenDexFile(std::string* error_msg) const;
// Returns the size of the DexFile refered to by this OatDexFile.
size_t FileSize() const;
@@ -204,10 +224,10 @@
DISALLOW_COPY_AND_ASSIGN(OatDexFile);
};
- const OatDexFile* GetOatDexFile(const std::string& dex_location,
+ const OatDexFile* GetOatDexFile(const char* dex_location,
const uint32_t* const dex_location_checksum,
- bool exception_if_not_found = true) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool exception_if_not_found = true) const;
+
std::vector<const OatDexFile*> GetOatDexFiles() const;
size_t Size() const {
@@ -219,18 +239,21 @@
static OatFile* OpenDlopen(const std::string& elf_filename,
const std::string& location,
- byte* requested_base);
+ byte* requested_base,
+ std::string* error_msg);
static OatFile* OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
bool writable,
- bool executable);
+ bool executable,
+ std::string* error_msg);
explicit OatFile(const std::string& filename);
- bool Dlopen(const std::string& elf_filename, byte* requested_base);
- bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable);
- bool Setup();
+ bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
+ bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ std::string* error_msg);
+ bool Setup(std::string* error_msg);
const byte* Begin() const;
const byte* End() const;
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index 6ee3016..f724776 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -36,7 +36,8 @@
class ObjectLock {
public:
- explicit ObjectLock(Thread* self, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ explicit ObjectLock(Thread* self, mirror::Object* object)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: self_(self), obj_(object) {
CHECK(object != NULL);
obj_->MonitorEnter(self_);
@@ -267,53 +268,56 @@
}
field_ = new_f;
}
+
const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- return dex_file.GetFieldName(dex_file.GetFieldId(field_index));
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
DCHECK(field_->IsStatic());
DCHECK_LT(field_index, 2U);
return field_index == 0 ? "interfaces" : "throws";
}
+ const DexFile& dex_file = GetDexFile();
+ return dex_file.GetFieldName(dex_file.GetFieldId(field_index));
}
+
mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_);
- if (resolve && (type == NULL)) {
- type = GetClassLinker()->ResolveType(field_id.type_idx_, field_);
- CHECK(type != NULL || Thread::Current()->IsExceptionPending());
- }
- return type;
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
return GetClassLinker()->FindSystemClass(GetTypeDescriptor());
}
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_);
+ if (resolve && (type == NULL)) {
+ type = GetClassLinker()->ResolveType(field_id.type_idx_, field_);
+ CHECK(type != NULL || Thread::Current()->IsExceptionPending());
+ }
+ return type;
}
+
const char* GetTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- return dex_file.GetFieldTypeDescriptor(field_id);
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
DCHECK(field_->IsStatic());
DCHECK_LT(field_index, 2U);
// 0 == Class[] interfaces; 1 == Class[][] throws;
return field_index == 0 ? "[Ljava/lang/Class;" : "[[Ljava/lang/Class;";
}
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ return dex_file.GetFieldTypeDescriptor(field_id);
}
+
Primitive::Type GetTypeAsPrimitiveType()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return Primitive::GetType(GetTypeDescriptor()[0]);
}
+
bool IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Primitive::Type type = GetTypeAsPrimitiveType();
return type != Primitive::kPrimNot;
}
+
size_t FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Primitive::Type type = GetTypeAsPrimitiveType();
return Primitive::FieldSize(type);
@@ -324,11 +328,7 @@
const char* GetDeclaringClassDescriptor()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- return dex_file.GetFieldDeclaringClassDescriptor(field_id);
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
DCHECK(field_->IsStatic());
DCHECK_LT(field_index, 2U);
// 0 == Class[] interfaces; 1 == Class[][] throws;
@@ -336,6 +336,9 @@
declaring_class_descriptor_ = kh.GetDescriptor();
return declaring_class_descriptor_.c_str();
}
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ return dex_file.GetFieldDeclaringClassDescriptor(field_id);
}
private:
@@ -417,12 +420,14 @@
const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
- if (dex_method_idx != DexFile::kDexNoIndex) {
+ if (LIKELY(dex_method_idx != DexFile::kDexNoIndex)) {
return dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
} else {
Runtime* runtime = Runtime::Current();
if (method_ == runtime->GetResolutionMethod()) {
return "<runtime internal resolution method>";
+ } else if (method_ == runtime->GetImtConflictMethod()) {
+ return "<runtime internal imt conflict method>";
} else if (method_ == runtime->GetCalleeSaveMethod(Runtime::kSaveAll)) {
return "<runtime internal callee-save all registers method>";
} else if (method_ == runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)) {
@@ -460,13 +465,13 @@
return shorty_len_;
}
- const std::string GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const Signature GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
if (dex_method_idx != DexFile::kDexNoIndex) {
return dex_file.GetMethodSignature(dex_file.GetMethodId(dex_method_idx));
} else {
- return "<no signature>";
+ return Signature::NoSignature();
}
}
@@ -508,11 +513,10 @@
const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
- if (dex_method_idx != DexFile::kDexNoIndex) {
- return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx));
- } else {
+ if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) {
return "<runtime method>";
}
+ return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx));
}
const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -536,7 +540,7 @@
}
bool IsClassInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return IsStatic() && StringPiece(GetName()) == "<clinit>";
+ return method_->IsConstructor() && IsStatic();
}
size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -569,16 +573,21 @@
bool HasSameNameAndSignature(MethodHelper* other)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex());
if (GetDexCache() == other->GetDexCache()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex());
const DexFile::MethodId& other_mid =
dex_file.GetMethodId(other->method_->GetDexMethodIndex());
return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_;
}
- StringPiece name(GetName());
- StringPiece other_name(other->GetName());
- return name == other_name && GetSignature() == other->GetSignature();
+ const DexFile& other_dex_file = other->GetDexFile();
+ const DexFile::MethodId& other_mid =
+ other_dex_file.GetMethodId(other->method_->GetDexMethodIndex());
+ if (!DexFileStringEquals(&dex_file, mid.name_idx_,
+ &other_dex_file, other_mid.name_idx_)) {
+ return false; // Name mismatch.
+ }
+ return dex_file.GetMethodSignature(mid) == other_dex_file.GetMethodSignature(other_mid);
}
const DexFile::CodeItem* GetCodeItem()
@@ -639,6 +648,46 @@
return s;
}
+ uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DexFile& dexfile = GetDexFile();
+ if (&dexfile == &other_dexfile) {
+ return method_->GetDexMethodIndex();
+ }
+ const DexFile::MethodId& mid = dexfile.GetMethodId(method_->GetDexMethodIndex());
+ const char* mid_declaring_class_descriptor = dexfile.StringByTypeIdx(mid.class_idx_);
+ const DexFile::StringId* other_descriptor =
+ other_dexfile.FindStringId(mid_declaring_class_descriptor);
+ if (other_descriptor != nullptr) {
+ const DexFile::TypeId* other_type_id =
+ other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor));
+ if (other_type_id != nullptr) {
+ const char* mid_name = dexfile.GetMethodName(mid);
+ const DexFile::StringId* other_name = other_dexfile.FindStringId(mid_name);
+ if (other_name != nullptr) {
+ uint16_t other_return_type_idx;
+ std::vector<uint16_t> other_param_type_idxs;
+ bool success = other_dexfile.CreateTypeList(dexfile.GetMethodSignature(mid).ToString(),
+ &other_return_type_idx,
+ &other_param_type_idxs);
+ if (success) {
+ const DexFile::ProtoId* other_sig =
+ other_dexfile.FindProtoId(other_return_type_idx, other_param_type_idxs);
+ if (other_sig != nullptr) {
+ const DexFile::MethodId* other_mid = other_dexfile.FindMethodId(*other_type_id,
+ *other_name,
+ *other_sig);
+ if (other_mid != nullptr) {
+ return other_dexfile.GetIndexForMethodId(*other_mid);
+ }
+ }
+ }
+ }
+ }
+ }
+ return DexFile::kDexNoIndex;
+ }
+
private:
// Set the method_ field, for proxy methods looking up the interface method via the resolved
// methods table.
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 8e23cbb..e95fdb9 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -38,16 +38,16 @@
ReferenceTable::~ReferenceTable() {
}
-void ReferenceTable::Add(const mirror::Object* obj) {
+void ReferenceTable::Add(mirror::Object* obj) {
DCHECK(obj != NULL);
- if (entries_.size() == max_size_) {
+ if (entries_.size() >= max_size_) {
LOG(FATAL) << "ReferenceTable '" << name_ << "' "
<< "overflowed (" << max_size_ << " entries)";
}
entries_.push_back(obj);
}
-void ReferenceTable::Remove(const mirror::Object* obj) {
+void ReferenceTable::Remove(mirror::Object* obj) {
// We iterate backwards on the assumption that references are LIFO.
for (int i = entries_.size() - 1; i >= 0; --i) {
if (entries_[i] == obj) {
@@ -232,8 +232,8 @@
}
void ReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) {
- for (const auto& ref : entries_) {
- visitor(ref, arg);
+ for (auto& ref : entries_) {
+ ref = visitor(const_cast<mirror::Object*>(ref), arg);
}
}
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index e369fd0..37b3172 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -39,9 +39,9 @@
ReferenceTable(const char* name, size_t initial_size, size_t max_size);
~ReferenceTable();
- void Add(const mirror::Object* obj);
+ void Add(mirror::Object* obj);
- void Remove(const mirror::Object* obj);
+ void Remove(mirror::Object* obj);
size_t Size() const;
@@ -50,7 +50,7 @@
void VisitRoots(RootVisitor* visitor, void* arg);
private:
- typedef std::vector<const mirror::Object*> Table;
+ typedef std::vector<mirror::Object*> Table;
static void Dump(std::ostream& os, const Table& entries)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
friend class IndirectReferenceTable; // For Dump.
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 3e58b4b..80e16aa 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -323,7 +323,7 @@
}
JValue boxed_value;
- std::string src_descriptor(ClassHelper(o->GetClass()).GetDescriptor());
+ const StringPiece src_descriptor(ClassHelper(o->GetClass()).GetDescriptor());
mirror::Class* src_class = NULL;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
mirror::ArtField* primitive_field = o->GetClass()->GetIFields()->Get(0);
@@ -356,7 +356,7 @@
StringPrintf("%s has type %s, got %s",
UnboxingFailureKind(m, index, f).c_str(),
PrettyDescriptor(dst_class).c_str(),
- PrettyDescriptor(src_descriptor.c_str()).c_str()).c_str());
+ PrettyDescriptor(src_descriptor.data()).c_str()).c_str());
return false;
}
diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h
index 3aa9b4b..a2d898b 100644
--- a/runtime/root_visitor.h
+++ b/runtime/root_visitor.h
@@ -23,7 +23,8 @@
} // namespace mirror
class StackVisitor;
-typedef void (RootVisitor)(const mirror::Object* root, void* arg);
+typedef mirror::Object* (RootVisitor)(mirror::Object* root, void* arg)
+ __attribute__((warn_unused_result));
typedef void (VerifyRootVisitor)(const mirror::Object* root, void* arg, size_t vreg,
const StackVisitor* visitor);
typedef bool (IsMarkedTester)(const mirror::Object* object, void* arg);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b9f54e5..34cf45b 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -75,6 +75,7 @@
is_explicit_gc_disabled_(false),
default_stack_size_(0),
heap_(NULL),
+ max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
monitor_list_(NULL),
thread_list_(NULL),
intern_table_(NULL),
@@ -83,6 +84,8 @@
java_vm_(NULL),
pre_allocated_OutOfMemoryError_(NULL),
resolution_method_(NULL),
+ imt_conflict_method_(NULL),
+ default_imt_(NULL),
threads_being_born_(0),
shutdown_cond_(new ConditionVariable("Runtime shutdown", *Locks::runtime_shutdown_lock_)),
shutting_down_(false),
@@ -99,7 +102,8 @@
use_compile_time_class_path_(false),
main_thread_group_(NULL),
system_thread_group_(NULL),
- system_class_loader_(NULL) {
+ system_class_loader_(NULL),
+ quick_alloc_entry_points_instrumentation_counter_(0) {
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
callee_save_methods_[i] = NULL;
}
@@ -319,6 +323,12 @@
return result;
}
+void Runtime::SweepSystemWeaks(RootVisitor* visitor, void* arg) {
+ GetInternTable()->SweepInternTableWeaks(visitor, arg);
+ GetMonitorList()->SweepMonitorList(visitor, arg);
+ GetJavaVM()->SweepJniWeakGlobals(visitor, arg);
+}
+
Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {
UniquePtr<ParsedOptions> parsed(new ParsedOptions());
const char* boot_class_path_string = getenv("BOOTCLASSPATH");
@@ -343,6 +353,7 @@
// Only the main GC thread, no workers.
parsed->conc_gc_threads_ = 0;
parsed->stack_size_ = 0; // 0 means default.
+ parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
parsed->low_memory_mode_ = false;
parsed->is_compiler_ = false;
@@ -503,6 +514,10 @@
return NULL;
}
parsed->stack_size_ = size;
+ } else if (StartsWith(option, "-XX:MaxSpinsBeforeThinLockInflation=")) {
+ parsed->max_spins_before_thin_lock_inflation_ =
+ strtoul(option.substr(strlen("-XX:MaxSpinsBeforeThinLockInflation=")).c_str(),
+ nullptr, 10);
} else if (option == "-XX:LongPauseLogThreshold") {
parsed->long_pause_log_threshold_ =
ParseMemoryOption(option.substr(strlen("-XX:LongPauseLogThreshold=")).c_str(), 1024);
@@ -865,6 +880,8 @@
default_stack_size_ = options->stack_size_;
stack_trace_file_ = options->stack_trace_file_;
+ max_spins_before_thin_lock_inflation_ = options->max_spins_before_thin_lock_inflation_;
+
monitor_list_ = new MonitorList;
thread_list_ = new ThreadList;
intern_table_ = new InternTable;
@@ -900,7 +917,7 @@
// objects. We can't supply a thread group yet; it will be fixed later. Since we are the main
// thread, we do not get a java peer.
Thread* self = Thread::Attach("main", false, NULL, false);
- CHECK_EQ(self->thin_lock_id_, ThreadList::kMainId);
+ CHECK_EQ(self->thin_lock_thread_id_, ThreadList::kMainThreadId);
CHECK(self != NULL);
// Set us to runnable so tools using a runtime can allocate and GC by default
@@ -962,7 +979,7 @@
std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
std::string reason;
self->TransitionFromSuspendedToRunnable();
- if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, reason)) {
+ if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, &reason)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
}
self->TransitionFromRunnableToSuspended(kNative);
@@ -1060,6 +1077,9 @@
GetStats()->Clear(~0);
// TODO: wouldn't it make more sense to clear _all_ threads' stats?
Thread::Current()->GetStats()->Clear(~0);
+ InstrumentQuickAllocEntryPoints();
+ } else {
+ UninstrumentQuickAllocEntryPoints();
}
stats_enabled_ = new_state;
}
@@ -1150,12 +1170,21 @@
void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) {
java_vm_->VisitRoots(visitor, arg);
- if (pre_allocated_OutOfMemoryError_ != NULL) {
- visitor(pre_allocated_OutOfMemoryError_, arg);
+ if (pre_allocated_OutOfMemoryError_ != nullptr) {
+ pre_allocated_OutOfMemoryError_ = reinterpret_cast<mirror::Throwable*>(
+ visitor(pre_allocated_OutOfMemoryError_, arg));
+ DCHECK(pre_allocated_OutOfMemoryError_ != nullptr);
}
- visitor(resolution_method_, arg);
+ resolution_method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(resolution_method_, arg));
+ DCHECK(resolution_method_ != nullptr);
+ imt_conflict_method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(imt_conflict_method_, arg));
+ DCHECK(imt_conflict_method_ != nullptr);
+ default_imt_ = reinterpret_cast<mirror::ObjectArray<mirror::ArtMethod>*>(visitor(default_imt_, arg));
+ DCHECK(default_imt_ != nullptr);
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
- visitor(callee_save_methods_[i], arg);
+ callee_save_methods_[i] = reinterpret_cast<mirror::ArtMethod*>(
+ visitor(callee_save_methods_[i], arg));
+ DCHECK(callee_save_methods_[i] != nullptr);
}
}
@@ -1169,6 +1198,31 @@
VisitNonConcurrentRoots(visitor, arg);
}
+mirror::ObjectArray<mirror::ArtMethod>* Runtime::CreateDefaultImt(ClassLinker* cl) {
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, cl->AllocArtMethodArray(self, 64));
+ mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
+ for (size_t i = 0; i < 64; i++) {
+ imtable->Set(i, imt_conflict_method);
+ }
+ return imtable.get();
+}
+
+mirror::ArtMethod* Runtime::CreateImtConflictMethod() {
+ mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod();
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ArtMethod>
+ method(self, down_cast<mirror::ArtMethod*>(method_class->AllocObject(self)));
+ method->SetDeclaringClass(method_class);
+ // TODO: use a special method for imt conflict method saves
+ method->SetDexMethodIndex(DexFile::kDexNoIndex);
+ // When compiling, the code pointer will get set later when the image is loaded.
+ Runtime* r = Runtime::Current();
+ ClassLinker* cl = r->GetClassLinker();
+ method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetImtConflictTrampoline(cl));
+ return method.get();
+}
+
mirror::ArtMethod* Runtime::CreateResolutionMethod() {
mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod();
Thread* self = Thread::Current();
@@ -1282,4 +1336,46 @@
compile_time_class_paths_.Put(class_loader, class_path);
}
+static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) {
+ thread->ResetQuickAllocEntryPointsForThread();
+}
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented);
+
+void Runtime::InstrumentQuickAllocEntryPoints() {
+ ThreadList* tl = thread_list_;
+ Thread* self = Thread::Current();
+ tl->SuspendAll();
+ {
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_, 0);
+ int old_counter = quick_alloc_entry_points_instrumentation_counter_++;
+ if (old_counter == 0) {
+ // If it was disabled, enable it.
+ SetQuickAllocEntryPointsInstrumented(true);
+ tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL);
+ }
+ }
+ tl->ResumeAll();
+}
+
+void Runtime::UninstrumentQuickAllocEntryPoints() {
+ ThreadList* tl = thread_list_;
+ Thread* self = Thread::Current();
+ tl->SuspendAll();
+ {
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_, 0);
+ int new_counter = --quick_alloc_entry_points_instrumentation_counter_;
+ if (new_counter == 0) {
+ // Disable it if the counter becomes zero.
+ SetQuickAllocEntryPointsInstrumented(false);
+ tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL);
+ }
+ }
+ tl->ResumeAll();
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index bc5c8b0..0ce2642 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -45,6 +45,7 @@
namespace mirror {
class ArtMethod;
class ClassLoader;
+ template<class T> class ObjectArray;
template<class T> class PrimitiveArray;
typedef PrimitiveArray<int8_t> ByteArray;
class String;
@@ -112,6 +113,7 @@
size_t parallel_gc_threads_;
size_t conc_gc_threads_;
size_t stack_size_;
+ size_t max_spins_before_thin_lock_inflation_;
bool low_memory_mode_;
size_t lock_profiling_threshold_;
std::string stack_trace_file_;
@@ -279,6 +281,7 @@
}
InternTable* GetInternTable() const {
+ DCHECK(intern_table_ != NULL);
return intern_table_;
}
@@ -286,6 +289,10 @@
return java_vm_;
}
+ size_t GetMaxSpinsBeforeThinkLockInflation() const {
+ return max_spins_before_thin_lock_inflation_;
+ }
+
MonitorList* GetMonitorList() const {
return monitor_list_;
}
@@ -323,6 +330,11 @@
void VisitNonConcurrentRoots(RootVisitor* visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Sweep system weaks, the system weak is deleted if the visitor return nullptr. Otherwise, the
+ // system weak is updated to be the visitor's returned value.
+ void SweepSystemWeaks(RootVisitor* visitor, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Returns a special method that calls into a trampoline for runtime method resolution
mirror::ArtMethod* GetResolutionMethod() const {
CHECK(HasResolutionMethod());
@@ -339,6 +351,39 @@
mirror::ArtMethod* CreateResolutionMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns a special method that calls into a trampoline for runtime imt conflicts
+ mirror::ArtMethod* GetImtConflictMethod() const {
+ CHECK(HasImtConflictMethod());
+ return imt_conflict_method_;
+ }
+
+ bool HasImtConflictMethod() const {
+ return imt_conflict_method_ != NULL;
+ }
+
+ void SetImtConflictMethod(mirror::ArtMethod* method) {
+ imt_conflict_method_ = method;
+ }
+
+ mirror::ArtMethod* CreateImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Returns an imt with every entry set to conflict, used as default imt for all classes.
+ mirror::ObjectArray<mirror::ArtMethod>* GetDefaultImt() const {
+ CHECK(HasDefaultImt());
+ return default_imt_;
+ }
+
+ bool HasDefaultImt() const {
+ return default_imt_ != NULL;
+ }
+
+ void SetDefaultImt(mirror::ObjectArray<mirror::ArtMethod>* imt) {
+ default_imt_ = imt;
+ }
+
+ mirror::ObjectArray<mirror::ArtMethod>* CreateDefaultImt(ClassLinker* cl)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Returns a special method that describes all callee saves being spilled to the stack.
enum CalleeSaveType {
kSaveAll,
@@ -397,6 +442,9 @@
const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader);
void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path);
+ void InstrumentQuickAllocEntryPoints();
+ void UninstrumentQuickAllocEntryPoints();
+
private:
static void InitPlatformSignalHandlers();
@@ -451,6 +499,8 @@
gc::Heap* heap_;
+ // The number of spins that are done before thread suspension is used to forcibly inflate.
+ size_t max_spins_before_thin_lock_inflation_;
MonitorList* monitor_list_;
ThreadList* thread_list_;
@@ -470,6 +520,10 @@
mirror::ArtMethod* resolution_method_;
+ mirror::ArtMethod* imt_conflict_method_;
+
+ mirror::ObjectArray<mirror::ArtMethod>* default_imt_;
+
// A non-zero value indicates that a thread has been created but not yet initialized. Guarded by
// the shutdown lock so that threads aren't born while we're shutting down.
size_t threads_being_born_ GUARDED_BY(Locks::runtime_shutdown_lock_);
@@ -513,6 +567,8 @@
// As returned by ClassLoader.getSystemClassLoader().
jobject system_class_loader_;
+ int quick_alloc_entry_points_instrumentation_counter_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index d3f3a88..c39cdb2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -18,7 +18,6 @@
#define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
#include "base/casts.h"
-#include "jni_internal.h"
#include "thread-inl.h"
namespace art {
@@ -122,14 +121,14 @@
explicit ScopedObjectAccessUnchecked(JNIEnv* env)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) ALWAYS_INLINE
: ScopedThreadStateChange(ThreadForEnv(env), kRunnable),
- env_(reinterpret_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
+ env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
self_->VerifyStack();
}
explicit ScopedObjectAccessUnchecked(Thread* self)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
: ScopedThreadStateChange(self, kRunnable),
- env_(reinterpret_cast<JNIEnvExt*>(self->GetJniEnv())),
+ env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
vm_(env_ != NULL ? env_->vm : NULL) {
self_->VerifyStack();
}
@@ -137,7 +136,7 @@
// Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
// change into Runnable or acquire a share on the mutator_lock_.
explicit ScopedObjectAccessUnchecked(JavaVM* vm)
- : ScopedThreadStateChange(), env_(NULL), vm_(reinterpret_cast<JavaVMExt*>(vm)) {}
+ : ScopedThreadStateChange(), env_(NULL), vm_(down_cast<JavaVMExt*>(vm)) {}
// Here purely to force inlining.
~ScopedObjectAccessUnchecked() ALWAYS_INLINE {
@@ -162,6 +161,7 @@
*/
template<typename T>
T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states.
if (obj == NULL) {
return NULL;
@@ -245,11 +245,6 @@
}
private:
- static Thread* ThreadForEnv(JNIEnv* env) {
- JNIEnvExt* full_env(reinterpret_cast<JNIEnvExt*>(env));
- return full_env->self;
- }
-
// The full JNIEnv.
JNIEnvExt* const env_;
// The full JavaVM.
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 15eb27d..fe62e25 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -147,7 +147,6 @@
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
if (self->ReadFlag(kCheckpointRequest)) {
self->RunCheckpointFunction();
- self->AtomicClearFlag(kCheckpointRequest);
}
self->EndAssertNoThreadSuspension(old_cause);
thread_list->ResumeAll();
diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h
index 81f0dff..a1f8a66 100644
--- a/runtime/sirt_ref.h
+++ b/runtime/sirt_ref.h
@@ -30,7 +30,8 @@
self_->PushSirt(&sirt_);
}
~SirtRef() {
- CHECK(self_->PopSirt() == &sirt_);
+ StackIndirectReferenceTable* top_sirt = self_->PopSirt();
+ DCHECK_EQ(top_sirt, &sirt_);
}
T& operator*() const { return *get(); }
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 206bff3..5d3a9a5 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -148,8 +148,8 @@
const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
DCHECK(code_item != NULL) << PrettyMethod(m); // Can't be NULL or how would we compile its instructions?
size_t frame_size = m->GetFrameSizeInBytes();
- return GetVReg(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(),
- frame_size, vreg);
+ return *GetVRegAddr(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(),
+ frame_size, vreg);
}
} else {
return cur_shadow_frame_->GetVReg(vreg);
@@ -253,7 +253,7 @@
return result;
}
-instrumentation::InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const {
+instrumentation::InstrumentationStackFrame& StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const {
return thread_->GetInstrumentationStack()->at(depth);
}
@@ -309,7 +309,7 @@
// While profiling, the return pc is restored from the side stack, except when walking
// the stack for an exception where the side stack will be unwound in VisitFrame.
if (GetQuickInstrumentationExitPc() == return_pc) {
- instrumentation::InstrumentationStackFrame instrumentation_frame =
+ const instrumentation::InstrumentationStackFrame& instrumentation_frame =
GetInstrumentationStackFrame(instrumentation_stack_depth);
instrumentation_stack_depth++;
if (GetMethod() == Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveAll)) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 8ecf8f0..a4b93bc 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -68,8 +68,7 @@
static ShadowFrame* Create(uint32_t num_vregs, ShadowFrame* link,
mirror::ArtMethod* method, uint32_t dex_pc) {
uint8_t* memory = new uint8_t[ComputeSize(num_vregs)];
- ShadowFrame* sf = new (memory) ShadowFrame(num_vregs, link, method, dex_pc, true);
- return sf;
+ return Create(num_vregs, link, method, dex_pc, memory);
}
// Create ShadowFrame for interpreter using provided memory.
@@ -138,19 +137,28 @@
int64_t GetVRegLong(size_t i) const {
DCHECK_LT(i, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
- return *reinterpret_cast<const int64_t*>(vreg);
+ // Alignment attribute required for GCC 4.8
+ typedef const int64_t unaligned_int64 __attribute__ ((aligned (4)));
+ return *reinterpret_cast<unaligned_int64*>(vreg);
}
double GetVRegDouble(size_t i) const {
DCHECK_LT(i, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
- return *reinterpret_cast<const double*>(vreg);
+ // Alignment attribute required for GCC 4.8
+ typedef const double unaligned_double __attribute__ ((aligned (4)));
+ return *reinterpret_cast<unaligned_double*>(vreg);
}
mirror::Object* GetVRegReference(size_t i) const {
DCHECK_LT(i, NumberOfVRegs());
if (HasReferenceArray()) {
- return References()[i];
+ mirror::Object* ref = References()[i];
+ // If the vreg reference is not equal to the vreg then the vreg reference is stale.
+ if (reinterpret_cast<uint32_t>(ref) != vregs_[i]) {
+ return nullptr;
+ }
+ return ref;
} else {
const uint32_t* vreg = &vregs_[i];
return *reinterpret_cast<mirror::Object* const*>(vreg);
@@ -177,13 +185,17 @@
void SetVRegLong(size_t i, int64_t val) {
DCHECK_LT(i, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
- *reinterpret_cast<int64_t*>(vreg) = val;
+ // Alignment attribute required for GCC 4.8
+ typedef int64_t unaligned_int64 __attribute__ ((aligned (4)));
+ *reinterpret_cast<unaligned_int64*>(vreg) = val;
}
void SetVRegDouble(size_t i, double val) {
DCHECK_LT(i, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
- *reinterpret_cast<double*>(vreg) = val;
+ // Alignment attribute required for GCC 4.8
+ typedef double unaligned_double __attribute__ ((aligned (4)));
+ *reinterpret_cast<unaligned_double*>(vreg) = val;
}
void SetVRegReference(size_t i, mirror::Object* val) {
@@ -459,13 +471,14 @@
uintptr_t GetGPR(uint32_t reg) const;
void SetGPR(uint32_t reg, uintptr_t value);
- uint32_t GetVReg(mirror::ArtMethod** cur_quick_frame, const DexFile::CodeItem* code_item,
+ // This is a fast-path for getting/setting values in a quick frame.
+ uint32_t* GetVRegAddr(mirror::ArtMethod** cur_quick_frame, const DexFile::CodeItem* code_item,
uint32_t core_spills, uint32_t fp_spills, size_t frame_size,
uint16_t vreg) const {
int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
DCHECK_EQ(cur_quick_frame, GetCurrentQuickFrame());
byte* vreg_addr = reinterpret_cast<byte*>(cur_quick_frame) + offset;
- return *reinterpret_cast<uint32_t*>(vreg_addr);
+ return reinterpret_cast<uint32_t*>(vreg_addr);
}
uintptr_t GetReturnPc() const;
@@ -554,7 +567,7 @@
static void DescribeStack(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
- instrumentation::InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const;
+ instrumentation::InstrumentationStackFrame& GetInstrumentationStackFrame(uint32_t depth) const;
void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index c22f2cd..8449607 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -19,11 +19,32 @@
#include "thread.h"
+#include <pthread.h>
+
+#include "base/casts.h"
#include "base/mutex-inl.h"
#include "cutils/atomic-inline.h"
+#include "jni_internal.h"
namespace art {
+// Quickly access the current thread from a JNIEnv.
+static inline Thread* ThreadForEnv(JNIEnv* env) {
+ JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
+ return full_env->self;
+}
+
+inline Thread* Thread::Current() {
+ // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious
+ // that we can replace this with a direct %fs access on x86.
+ if (!is_started_) {
+ return NULL;
+ } else {
+ void* thread = pthread_getspecific(Thread::pthread_key_self_);
+ return reinterpret_cast<Thread*>(thread);
+ }
+}
+
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.
@@ -67,17 +88,16 @@
union StateAndFlags new_state_and_flags;
do {
old_state_and_flags = state_and_flags_;
+ if (UNLIKELY((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0)) {
+ RunCheckpointFunction();
+ continue;
+ }
// Copy over flags and try to clear the checkpoint bit if it is set.
new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags & ~kCheckpointRequest;
new_state_and_flags.as_struct.state = new_state;
// CAS the value without a memory barrier, that will occur in the unlock below.
} while (UNLIKELY(android_atomic_cas(old_state_and_flags.as_int, new_state_and_flags.as_int,
&state_and_flags_.as_int) != 0));
- // If we toggled the checkpoint flag we must have cleared it.
- uint16_t flag_change = new_state_and_flags.as_struct.flags ^ old_state_and_flags.as_struct.flags;
- if (UNLIKELY((flag_change & kCheckpointRequest) != 0)) {
- RunCheckpointFunction();
- }
// Release share on mutator_lock_.
Locks::mutator_lock_->SharedUnlock(this);
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 23cafe8..9751076 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -108,6 +108,12 @@
&quick_entrypoints_);
}
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
+
+void Thread::ResetQuickAllocEntryPointsForThread() {
+ ResetQuickAllocEntryPoints(&quick_entrypoints_);
+}
+
void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf) {
deoptimization_shadow_frame_ = sf;
}
@@ -305,7 +311,7 @@
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self");
DCHECK_EQ(Thread::Current(), this);
- thin_lock_id_ = thread_list->AllocThreadId(this);
+ thin_lock_thread_id_ = thread_list->AllocThreadId(this);
InitStackHwm();
jni_env_ = new JNIEnvExt(this, java_vm);
@@ -417,7 +423,7 @@
void Thread::InitStackHwm() {
void* stack_base;
size_t stack_size;
- GetThreadStack(pthread_self_, stack_base, stack_size);
+ GetThreadStack(pthread_self_, &stack_base, &stack_size);
// TODO: include this in the thread dumps; potentially useful in SIGQUIT output?
VLOG(threads) << StringPrintf("Native stack is at %p (%s)", stack_base, PrettySize(stack_size).c_str());
@@ -470,9 +476,9 @@
void Thread::ShortDump(std::ostream& os) const {
os << "Thread[";
- if (GetThinLockId() != 0) {
+ if (GetThreadId() != 0) {
// If we're in kStarting, we won't have a thin lock id or tid yet.
- os << GetThinLockId()
+ os << GetThreadId()
<< ",tid=" << GetTid() << ',';
}
os << GetState()
@@ -568,18 +574,32 @@
ATRACE_BEGIN("Checkpoint function");
checkpoint_function_->Run(this);
ATRACE_END();
+ checkpoint_function_ = NULL;
+ AtomicClearFlag(kCheckpointRequest);
}
bool Thread::RequestCheckpoint(Closure* function) {
- CHECK(!ReadFlag(kCheckpointRequest)) << "Already have a pending checkpoint request";
- checkpoint_function_ = function;
union StateAndFlags old_state_and_flags = state_and_flags_;
+ if (old_state_and_flags.as_struct.state != kRunnable) {
+ return false; // Fail, thread is suspended and so can't run a checkpoint.
+ }
+ if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) {
+ return false; // Fail, already a checkpoint pending.
+ }
+ CHECK(checkpoint_function_ == NULL);
+ checkpoint_function_ = function;
+ // Checkpoint function installed now install flag bit.
// We must be runnable to request a checkpoint.
old_state_and_flags.as_struct.state = kRunnable;
union StateAndFlags new_state_and_flags = old_state_and_flags;
new_state_and_flags.as_struct.flags |= kCheckpointRequest;
int succeeded = android_atomic_cmpxchg(old_state_and_flags.as_int, new_state_and_flags.as_int,
&state_and_flags_.as_int);
+ if (UNLIKELY(succeeded != 0)) {
+ // The thread changed state before the checkpoint was installed.
+ CHECK(checkpoint_function_ == function);
+ checkpoint_function_ = NULL;
+ }
return succeeded == 0;
}
@@ -594,88 +614,6 @@
VLOG(threads) << this << " self-reviving";
}
-Thread* Thread::SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out) {
- static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
- useconds_t total_delay_us = 0;
- useconds_t delay_us = 0;
- bool did_suspend_request = false;
- *timed_out = false;
- while (true) {
- Thread* thread;
- {
- ScopedObjectAccess soa(Thread::Current());
- Thread* self = soa.Self();
- MutexLock mu(self, *Locks::thread_list_lock_);
- thread = Thread::FromManagedThread(soa, peer);
- if (thread == NULL) {
- JNIEnv* env = self->GetJniEnv();
- ScopedLocalRef<jstring> scoped_name_string(env,
- (jstring)env->GetObjectField(peer,
- WellKnownClasses::java_lang_Thread_name));
- ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
- if (scoped_name_chars.c_str() == NULL) {
- LOG(WARNING) << "No such thread for suspend: " << peer;
- env->ExceptionClear();
- } else {
- LOG(WARNING) << "No such thread for suspend: " << peer << ":" << scoped_name_chars.c_str();
- }
-
- return NULL;
- }
- {
- MutexLock mu(soa.Self(), *Locks::thread_suspend_count_lock_);
- if (request_suspension) {
- thread->ModifySuspendCount(soa.Self(), +1, true /* for_debugger */);
- request_suspension = false;
- did_suspend_request = true;
- }
- // IsSuspended on the current thread will fail as the current thread is changed into
- // Runnable above. As the suspend count is now raised if this is the current thread
- // it will self suspend on transition to Runnable, making it hard to work with. It's simpler
- // to just explicitly handle the current thread in the callers to this code.
- CHECK_NE(thread, soa.Self()) << "Attempt to suspend the current thread for the debugger";
- // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend
- // count, or else we've waited and it has self suspended) or is the current thread, we're
- // done.
- if (thread->IsSuspended()) {
- return thread;
- }
- if (total_delay_us >= kTimeoutUs) {
- LOG(ERROR) << "Thread suspension timed out: " << peer;
- if (did_suspend_request) {
- thread->ModifySuspendCount(soa.Self(), -1, true /* for_debugger */);
- }
- *timed_out = true;
- return NULL;
- }
- }
- // Release locks and come out of runnable state.
- }
- for (int i = kLockLevelCount - 1; i >= 0; --i) {
- BaseMutex* held_mutex = Thread::Current()->GetHeldMutex(static_cast<LockLevel>(i));
- if (held_mutex != NULL) {
- LOG(FATAL) << "Holding " << held_mutex->GetName()
- << " while sleeping for thread suspension";
- }
- }
- {
- useconds_t new_delay_us = delay_us * 2;
- CHECK_GE(new_delay_us, delay_us);
- if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s.
- delay_us = new_delay_us;
- }
- }
- if (delay_us == 0) {
- sched_yield();
- // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep).
- delay_us = 500;
- } else {
- usleep(delay_us);
- total_delay_us += delay_us;
- }
- }
-}
-
void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) {
std::string group_name;
int priority;
@@ -712,7 +650,7 @@
os << " daemon";
}
os << " prio=" << priority
- << " tid=" << thread->GetThinLockId()
+ << " tid=" << thread->GetThreadId()
<< " " << thread->GetState();
if (thread->IsStillStarting()) {
os << " (still starting up)";
@@ -757,7 +695,7 @@
int utime = 0;
int stime = 0;
int task_cpu = 0;
- GetTaskStats(tid, native_thread_state, utime, stime, task_cpu);
+ GetTaskStats(tid, &native_thread_state, &utime, &stime, &task_cpu);
os << " | state=" << native_thread_state
<< " schedstat=( " << scheduler_stats << " )"
@@ -962,9 +900,9 @@
jpeer_(NULL),
stack_begin_(NULL),
stack_size_(0),
+ thin_lock_thread_id_(0),
stack_trace_sample_(NULL),
trace_clock_base_(0),
- thin_lock_id_(0),
tid_(0),
wait_mutex_(new Mutex("a thread wait mutex")),
wait_cond_(new ConditionVariable("a thread wait condition variable", *wait_mutex_)),
@@ -1012,9 +950,10 @@
}
}
-static void MonitorExitVisitor(const mirror::Object* object, void* arg) NO_THREAD_SAFETY_ANALYSIS {
+static mirror::Object* MonitorExitVisitor(mirror::Object* object, void* arg)
+ NO_THREAD_SAFETY_ANALYSIS {
Thread* self = reinterpret_cast<Thread*>(arg);
- mirror::Object* entered_monitor = const_cast<mirror::Object*>(object);
+ mirror::Object* entered_monitor = object;
if (self->HoldsLock(entered_monitor)) {
LOG(WARNING) << "Calling MonitorExit on object "
<< object << " (" << PrettyTypeOf(object) << ")"
@@ -1022,6 +961,7 @@
<< *Thread::Current() << " which is detaching";
entered_monitor->MonitorExit(self);
}
+ return object;
}
void Thread::Destroy() {
@@ -1151,8 +1091,12 @@
size_t num_refs = cur->NumberOfReferences();
for (size_t j = 0; j < num_refs; j++) {
mirror::Object* object = cur->GetReference(j);
- if (object != NULL) {
- visitor(object, arg);
+ if (object != nullptr) {
+ const mirror::Object* new_obj = visitor(object, arg);
+ DCHECK(new_obj != nullptr);
+ if (new_obj != object) {
+ cur->SetReference(j, const_cast<mirror::Object*>(new_obj));
+ }
}
}
}
@@ -1381,24 +1325,23 @@
// Transition into runnable state to work on Object*/Array*
ScopedObjectAccess soa(env);
// Decode the internal stack trace into the depth, method trace and PC trace
- mirror::ObjectArray<mirror::Object>* method_trace =
- soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal);
- int32_t depth = method_trace->GetLength() - 1;
- mirror::IntArray* pc_trace = down_cast<mirror::IntArray*>(method_trace->Get(depth));
+ int32_t depth = soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal)->GetLength() - 1;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
jobjectArray result;
- mirror::ObjectArray<mirror::StackTraceElement>* java_traces;
+
if (output_array != NULL) {
// Reuse the array we were given.
result = output_array;
- java_traces = soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(output_array);
// ...adjusting the number of frames we'll write to not exceed the array length.
- depth = std::min(depth, java_traces->GetLength());
+ const int32_t traces_length =
+ soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->GetLength();
+ depth = std::min(depth, traces_length);
} else {
// Create java_trace array and place in local reference table
- java_traces = class_linker->AllocStackTraceElementArray(soa.Self(), depth);
+ mirror::ObjectArray<mirror::StackTraceElement>* java_traces =
+ class_linker->AllocStackTraceElementArray(soa.Self(), depth);
if (java_traces == NULL) {
return NULL;
}
@@ -1411,9 +1354,12 @@
MethodHelper mh;
for (int32_t i = 0; i < depth; ++i) {
+ mirror::ObjectArray<mirror::Object>* method_trace =
+ soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal);
// Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
mirror::ArtMethod* method = down_cast<mirror::ArtMethod*>(method_trace->Get(i));
mh.ChangeMethod(method);
+ mirror::IntArray* pc_trace = down_cast<mirror::IntArray*>(method_trace->Get(depth));
uint32_t dex_pc = pc_trace->Get(i);
int32_t line_number = mh.GetLineNumFromDexPC(dex_pc);
// Allocate element, potentially triggering GC
@@ -1436,8 +1382,9 @@
return NULL;
}
const char* source_file = mh.GetDeclaringClassSourceFile();
- SirtRef<mirror::String> source_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(),
- source_file));
+ SirtRef<mirror::String> source_name_object(soa.Self(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(),
+ source_file));
mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(soa.Self(),
class_name_object.get(),
method_name_object.get(),
@@ -1446,13 +1393,7 @@
if (obj == NULL) {
return NULL;
}
-#ifdef MOVING_GARBAGE_COLLECTOR
- // Re-read after potential GC
- java_traces = Decode<ObjectArray<Object>*>(soa.Env(), result);
- method_trace = down_cast<ObjectArray<Object>*>(Decode<Object*>(soa.Env(), internal));
- pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
-#endif
- java_traces->Set(i, obj);
+ soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set(i, obj);
}
return result;
}
@@ -1614,6 +1555,7 @@
INTERPRETER_ENTRY_POINT_INFO(pInterpreterToInterpreterBridge),
INTERPRETER_ENTRY_POINT_INFO(pInterpreterToCompiledCodeBridge),
JNI_ENTRY_POINT_INFO(pDlsymLookup),
+ PORTABLE_ENTRY_POINT_INFO(pPortableImtConflictTrampoline),
PORTABLE_ENTRY_POINT_INFO(pPortableResolutionTrampoline),
PORTABLE_ENTRY_POINT_INFO(pPortableToInterpreterBridge),
QUICK_ENTRY_POINT_INFO(pAllocArray),
@@ -1623,7 +1565,6 @@
QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray),
QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck),
QUICK_ENTRY_POINT_INFO(pInstanceofNonTrivial),
- QUICK_ENTRY_POINT_INFO(pCanPutArrayElement),
QUICK_ENTRY_POINT_INFO(pCheckCast),
QUICK_ENTRY_POINT_INFO(pInitializeStaticStorage),
QUICK_ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccess),
@@ -1641,6 +1582,9 @@
QUICK_ENTRY_POINT_INFO(pGet64Static),
QUICK_ENTRY_POINT_INFO(pGetObjInstance),
QUICK_ENTRY_POINT_INFO(pGetObjStatic),
+ QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck),
+ QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck),
+ QUICK_ENTRY_POINT_INFO(pAputObject),
QUICK_ENTRY_POINT_INFO(pHandleFillArrayData),
QUICK_ENTRY_POINT_INFO(pJniMethodStart),
QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized),
@@ -1665,7 +1609,7 @@
QUICK_ENTRY_POINT_INFO(pD2l),
QUICK_ENTRY_POINT_INFO(pF2l),
QUICK_ENTRY_POINT_INFO(pLdiv),
- QUICK_ENTRY_POINT_INFO(pLdivmod),
+ QUICK_ENTRY_POINT_INFO(pLmod),
QUICK_ENTRY_POINT_INFO(pLmul),
QUICK_ENTRY_POINT_INFO(pShlLong),
QUICK_ENTRY_POINT_INFO(pShrLong),
@@ -1674,10 +1618,10 @@
QUICK_ENTRY_POINT_INFO(pMemcmp16),
QUICK_ENTRY_POINT_INFO(pStringCompareTo),
QUICK_ENTRY_POINT_INFO(pMemcpy),
+ QUICK_ENTRY_POINT_INFO(pQuickImtConflictTrampoline),
QUICK_ENTRY_POINT_INFO(pQuickResolutionTrampoline),
QUICK_ENTRY_POINT_INFO(pQuickToInterpreterBridge),
QUICK_ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck),
- QUICK_ENTRY_POINT_INFO(pInvokeInterfaceTrampoline),
QUICK_ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck),
QUICK_ENTRY_POINT_INFO(pInvokeStaticTrampolineWithAccessCheck),
QUICK_ENTRY_POINT_INFO(pInvokeSuperTrampolineWithAccessCheck),
@@ -1709,7 +1653,7 @@
DO_THREAD_OFFSET(self_);
DO_THREAD_OFFSET(stack_end_);
DO_THREAD_OFFSET(suspend_count_);
- DO_THREAD_OFFSET(thin_lock_id_);
+ DO_THREAD_OFFSET(thin_lock_thread_id_);
// DO_THREAD_OFFSET(top_of_managed_stack_);
// DO_THREAD_OFFSET(top_of_managed_stack_pc_);
DO_THREAD_OFFSET(top_sirt_);
@@ -1992,7 +1936,7 @@
if (object == NULL) {
return false;
}
- return object->GetThinLockId() == thin_lock_id_;
+ return object->GetLockOwnerThreadId() == thin_lock_thread_id_;
}
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor).
@@ -2016,8 +1960,11 @@
// SIRT for JNI or References for interpreter.
for (size_t reg = 0; reg < num_regs; ++reg) {
mirror::Object* ref = shadow_frame->GetVRegReference(reg);
- if (ref != NULL) {
- visitor_(ref, reg, this);
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (new_ref != ref) {
+ shadow_frame->SetVRegReference(reg, new_ref);
+ }
}
}
} else {
@@ -2037,8 +1984,11 @@
for (size_t reg = 0; reg < num_regs; ++reg) {
if (TestBitmap(reg, reg_bitmap)) {
mirror::Object* ref = shadow_frame->GetVRegReference(reg);
- if (ref != NULL) {
- visitor_(ref, reg, this);
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (new_ref != ref) {
+ shadow_frame->SetVRegReference(reg, new_ref);
+ }
}
}
}
@@ -2069,19 +2019,25 @@
// Does this register hold a reference?
if (TestBitmap(reg, reg_bitmap)) {
uint32_t vmap_offset;
- mirror::Object* ref;
if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
- uintptr_t val = GetGPR(vmap_table.ComputeRegister(core_spills, vmap_offset,
- kReferenceVReg));
- ref = reinterpret_cast<mirror::Object*>(val);
+ int vmap_reg = vmap_table.ComputeRegister(core_spills, vmap_offset, kReferenceVReg);
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(GetGPR(vmap_reg));
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (ref != new_ref) {
+ SetGPR(vmap_reg, reinterpret_cast<uintptr_t>(new_ref));
+ }
+ }
} else {
- ref = reinterpret_cast<mirror::Object*>(GetVReg(cur_quick_frame, code_item,
- core_spills, fp_spills, frame_size,
- reg));
- }
-
- if (ref != NULL) {
- visitor_(ref, reg, this);
+ uint32_t* reg_addr =
+ GetVRegAddr(cur_quick_frame, code_item, core_spills, fp_spills, frame_size, reg);
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(*reg_addr);
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (ref != new_ref) {
+ *reg_addr = reinterpret_cast<uint32_t>(new_ref);
+ }
+ }
}
}
}
@@ -2107,8 +2063,8 @@
public:
RootCallbackVisitor(RootVisitor* visitor, void* arg) : visitor_(visitor), arg_(arg) {}
- void operator()(const mirror::Object* obj, size_t, const StackVisitor*) const {
- visitor_(obj, arg_);
+ mirror::Object* operator()(mirror::Object* obj, size_t, const StackVisitor*) const {
+ return visitor_(obj, arg_);
}
private:
@@ -2132,67 +2088,17 @@
void* const arg_;
};
-struct VerifyRootWrapperArg {
- VerifyRootVisitor* visitor;
- void* arg;
-};
-
-static void VerifyRootWrapperCallback(const mirror::Object* root, void* arg) {
- VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
- wrapperArg->visitor(root, wrapperArg->arg, 0, NULL);
-}
-
-void Thread::VerifyRoots(VerifyRootVisitor* visitor, void* arg) {
- // We need to map from a RootVisitor to VerifyRootVisitor, so pass in nulls for arguments we
- // don't have.
- VerifyRootWrapperArg wrapperArg;
- wrapperArg.arg = arg;
- wrapperArg.visitor = visitor;
-
- if (opeer_ != NULL) {
- VerifyRootWrapperCallback(opeer_, &wrapperArg);
- }
- if (exception_ != NULL) {
- VerifyRootWrapperCallback(exception_, &wrapperArg);
- }
- throw_location_.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
- if (class_loader_override_ != NULL) {
- VerifyRootWrapperCallback(class_loader_override_, &wrapperArg);
- }
- jni_env_->locals.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
- jni_env_->monitors.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
-
- SirtVisitRoots(VerifyRootWrapperCallback, &wrapperArg);
-
- // Visit roots on this thread's stack
- Context* context = GetLongJumpContext();
- VerifyCallbackVisitor visitorToCallback(visitor, arg);
- ReferenceMapVisitor<VerifyCallbackVisitor> mapper(this, context, visitorToCallback);
- mapper.WalkStack();
- ReleaseLongJumpContext(context);
-
- std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack = GetInstrumentationStack();
- typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It;
- for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) {
- mirror::Object* this_object = (*it).this_object_;
- if (this_object != NULL) {
- VerifyRootWrapperCallback(this_object, &wrapperArg);
- }
- mirror::ArtMethod* method = (*it).method_;
- VerifyRootWrapperCallback(method, &wrapperArg);
- }
-}
-
void Thread::VisitRoots(RootVisitor* visitor, void* arg) {
- if (opeer_ != NULL) {
- visitor(opeer_, arg);
+ if (opeer_ != nullptr) {
+ opeer_ = visitor(opeer_, arg);
}
- if (exception_ != NULL) {
- visitor(exception_, arg);
+ if (exception_ != nullptr) {
+ exception_ = reinterpret_cast<mirror::Throwable*>(visitor(exception_, arg));
}
throw_location_.VisitRoots(visitor, arg);
- if (class_loader_override_ != NULL) {
- visitor(class_loader_override_, arg);
+ if (class_loader_override_ != nullptr) {
+ class_loader_override_ = reinterpret_cast<mirror::ClassLoader*>(
+ visitor(class_loader_override_, arg));
}
jni_env_->locals.VisitRoots(visitor, arg);
jni_env_->monitors.VisitRoots(visitor, arg);
@@ -2206,24 +2112,26 @@
mapper.WalkStack();
ReleaseLongJumpContext(context);
- for (const instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
- mirror::Object* this_object = frame.this_object_;
- if (this_object != NULL) {
- visitor(this_object, arg);
+ for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
+ if (frame.this_object_ != nullptr) {
+ frame.this_object_ = visitor(frame.this_object_, arg);
+ DCHECK(frame.this_object_ != nullptr);
}
- mirror::ArtMethod* method = frame.method_;
- visitor(method, arg);
+ frame.method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(frame.method_, arg));
+ DCHECK(frame.method_ != nullptr);
}
}
-static void VerifyObject(const mirror::Object* root, void* arg) {
- gc::Heap* heap = reinterpret_cast<gc::Heap*>(arg);
- heap->VerifyObject(root);
+static mirror::Object* VerifyRoot(mirror::Object* root, void* arg) {
+ DCHECK(root != nullptr);
+ DCHECK(arg != nullptr);
+ reinterpret_cast<gc::Heap*>(arg)->VerifyObject(root);
+ return root;
}
void Thread::VerifyStackImpl() {
UniquePtr<Context> context(Context::Create());
- RootCallbackVisitor visitorToCallback(VerifyObject, Runtime::Current()->GetHeap());
+ RootCallbackVisitor visitorToCallback(VerifyRoot, Runtime::Current()->GetHeap());
ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context.get(), visitorToCallback);
mapper.WalkStack();
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 40e3f5f..3aa1373 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -17,8 +17,6 @@
#ifndef ART_RUNTIME_THREAD_H_
#define ART_RUNTIME_THREAD_H_
-#include <pthread.h>
-
#include <bitset>
#include <deque>
#include <iosfwd>
@@ -104,16 +102,7 @@
// Reset internal state of child thread after fork.
void InitAfterFork();
- static Thread* Current() {
- // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious
- // that we can replace this with a direct %fs access on x86.
- if (!is_started_) {
- return NULL;
- } else {
- void* thread = pthread_getspecific(Thread::pthread_key_self_);
- return reinterpret_cast<Thread*>(thread);
- }
- }
+ static Thread* Current();
static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts,
mirror::Object* thread_peer)
@@ -165,7 +154,8 @@
void ModifySuspendCount(Thread* self, int delta, bool for_debugger)
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_);
- bool RequestCheckpoint(Closure* function);
+ bool RequestCheckpoint(Closure* function)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_);
// Called when thread detected that the thread_suspend_count_ was non-zero. Gives up share of
// mutator_lock_ and waits until it is resumed and thread_suspend_count_ is zero.
@@ -186,14 +176,6 @@
UNLOCK_FUNCTION(Locks::mutator_lock_)
ALWAYS_INLINE;
- // Wait for a debugger suspension on the thread associated with the given peer. Returns the
- // thread on success, else NULL. If the thread should be suspended then request_suspension should
- // be true on entry. If the suspension times out then *timeout is set to true.
- static Thread* SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out)
- LOCKS_EXCLUDED(Locks::mutator_lock_,
- Locks::thread_list_lock_,
- Locks::thread_suspend_count_lock_);
-
// Once called thread suspension will cause an assertion failure.
#ifndef NDEBUG
const char* StartAssertNoThreadSuspension(const char* cause) {
@@ -230,7 +212,7 @@
return daemon_;
}
- bool HoldsLock(mirror::Object*);
+ bool HoldsLock(mirror::Object*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Changes the priority of this thread to match that of the java.lang.Thread object.
@@ -248,8 +230,8 @@
*/
static int GetNativePriority();
- uint32_t GetThinLockId() const {
- return thin_lock_id_;
+ uint32_t GetThreadId() const {
+ return thin_lock_thread_id_;
}
pid_t GetTid() const {
@@ -406,9 +388,6 @@
void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VerifyRoots(VerifyRootVisitor* visitor, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
//
@@ -428,7 +407,7 @@
}
static ThreadOffset ThinLockIdOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
}
static ThreadOffset CardTableOffset() {
@@ -590,6 +569,8 @@
void AtomicClearFlag(ThreadFlag flag);
+ void ResetQuickAllocEntryPointsForThread();
+
private:
// We have no control over the size of 'bool', but want our boolean fields
// to be 4-byte quantities.
@@ -714,18 +695,18 @@
// Size of the stack
size_t stack_size_;
- // Pointer to previous stack trace captured by sampling profiler.
- std::vector<mirror::ArtMethod*>* stack_trace_sample_;
-
- // The clock base used for tracing.
- uint64_t trace_clock_base_;
-
// Thin lock thread id. This is a small integer used by the thin lock implementation.
// This is not to be confused with the native thread's tid, nor is it the value returned
// by java.lang.Thread.getId --- this is a distinct value, used only for locking. One
// important difference between this id and the ids visible to managed code is that these
// ones get reused (to ensure that they fit in the number of bits available).
- uint32_t thin_lock_id_;
+ uint32_t thin_lock_thread_id_;
+
+ // Pointer to previous stack trace captured by sampling profiler.
+ std::vector<mirror::ArtMethod*>* stack_trace_sample_;
+
+ // The clock base used for tracing.
+ uint64_t trace_clock_base_;
// System thread id.
pid_t tid_;
@@ -734,13 +715,16 @@
// Guards the 'interrupted_' and 'wait_monitor_' members.
mutable Mutex* wait_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Condition variable waited upon during a wait.
ConditionVariable* wait_cond_ GUARDED_BY(wait_mutex_);
- // Pointer to the monitor lock we're currently waiting on (or NULL).
+ // Pointer to the monitor lock we're currently waiting on or NULL if not waiting.
Monitor* wait_monitor_ GUARDED_BY(wait_mutex_);
// Thread "interrupted" status; stays raised until queried or thrown.
bool32_t interrupted_ GUARDED_BY(wait_mutex_);
- // The next thread in the wait set this thread is part of.
+ // The next thread in the wait set this thread is part of or NULL if not waiting.
Thread* wait_next_;
+
+
// If we're blocked in MonitorEnter, this is the object we're trying to lock.
mirror::Object* monitor_enter_object_;
@@ -797,7 +781,8 @@
// Cause for last suspension.
const char* last_no_thread_suspension_cause_;
- // Pending checkpoint functions.
+ // Pending checkpoint function or NULL if non-pending. Installation guarding by
+ // Locks::thread_suspend_count_lock_.
Closure* checkpoint_function_;
public:
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 671924a..ff1ed2a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -17,14 +17,22 @@
#include "thread_list.h"
#include <dirent.h>
+#include <ScopedLocalRef.h>
+#include <ScopedUtfChars.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/mutex.h"
+#include "base/mutex-inl.h"
#include "base/timing_logger.h"
#include "debugger.h"
+#include "jni_internal.h"
+#include "lock_word.h"
+#include "monitor.h"
+#include "scoped_thread_state_change.h"
#include "thread.h"
#include "utils.h"
+#include "well_known_classes.h"
namespace art {
@@ -32,6 +40,7 @@
: allocated_ids_lock_("allocated thread ids lock"),
suspend_all_count_(0), debug_suspend_all_count_(0),
thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_) {
+ CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1)));
}
ThreadList::~ThreadList() {
@@ -159,18 +168,19 @@
// Call a checkpoint function for each thread, threads which are suspend get their checkpoint
// manually called.
MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
for (const auto& thread : list_) {
if (thread != self) {
- for (;;) {
+ while (true) {
if (thread->RequestCheckpoint(checkpoint_function)) {
// This thread will run it's checkpoint some time in the near future.
count++;
break;
} else {
// We are probably suspended, try to make sure that we stay suspended.
- MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
// The thread switched back to runnable.
if (thread->GetState() == kRunnable) {
+ // Spurious fail, try again.
continue;
}
thread->ModifySuspendCount(self, +1, false);
@@ -203,7 +213,7 @@
}
}
// We know for sure that the thread is suspended at this point.
- thread->RunCheckpointFunction();
+ checkpoint_function->Run(thread);
{
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
thread->ModifySuspendCount(self, -1, false);
@@ -321,6 +331,178 @@
VLOG(threads) << "Resume(" << *thread << ") complete";
}
+static void ThreadSuspendByPeerWarning(Thread* self, int level, const char* message, jobject peer) {
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedLocalRef<jstring>
+ scoped_name_string(env, (jstring)env->GetObjectField(peer,
+ WellKnownClasses::java_lang_Thread_name));
+ ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
+ if (scoped_name_chars.c_str() == NULL) {
+ LOG(level) << message << ": " << peer;
+ env->ExceptionClear();
+ } else {
+ LOG(level) << message << ": " << peer << ":" << scoped_name_chars.c_str();
+ }
+}
+
+// Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an
+// individual thread requires polling. delay_us is the requested sleep and total_delay_us
+// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield,
+// subsequently sleeps increase delay_us from 1ms to 500ms by doubling.
+static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) {
+ for (int i = kLockLevelCount - 1; i >= 0; --i) {
+ BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
+ if (held_mutex != NULL) {
+ LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension";
+ }
+ }
+ {
+ useconds_t new_delay_us = (*delay_us) * 2;
+ CHECK_GE(new_delay_us, *delay_us);
+ if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s.
+ *delay_us = new_delay_us;
+ }
+ }
+ if ((*delay_us) == 0) {
+ sched_yield();
+ // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep).
+ (*delay_us) = 500;
+ } else {
+ usleep(*delay_us);
+ (*total_delay_us) += (*delay_us);
+ }
+}
+
+Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
+ bool debug_suspension, bool* timed_out) {
+ static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
+ useconds_t total_delay_us = 0;
+ useconds_t delay_us = 0;
+ bool did_suspend_request = false;
+ *timed_out = false;
+ Thread* self = Thread::Current();
+ while (true) {
+ Thread* thread;
+ {
+ ScopedObjectAccess soa(self);
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ thread = Thread::FromManagedThread(soa, peer);
+ if (thread == NULL) {
+ ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer);
+ return NULL;
+ }
+ {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ if (request_suspension) {
+ thread->ModifySuspendCount(self, +1, debug_suspension);
+ request_suspension = false;
+ did_suspend_request = true;
+ } else {
+ // If the caller isn't requesting suspension, a suspension should have already occurred.
+ CHECK_GT(thread->GetSuspendCount(), 0);
+ }
+ // IsSuspended on the current thread will fail as the current thread is changed into
+ // Runnable above. As the suspend count is now raised if this is the current thread
+ // it will self suspend on transition to Runnable, making it hard to work with. It's simpler
+ // to just explicitly handle the current thread in the callers to this code.
+ CHECK_NE(thread, self) << "Attempt to suspend the current thread for the debugger";
+ // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend
+ // count, or else we've waited and it has self suspended) or is the current thread, we're
+ // done.
+ if (thread->IsSuspended()) {
+ return thread;
+ }
+ if (total_delay_us >= kTimeoutUs) {
+ ThreadSuspendByPeerWarning(self, ERROR, "Thread suspension timed out", peer);
+ if (did_suspend_request) {
+ thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+ }
+ *timed_out = true;
+ return NULL;
+ }
+ }
+ // Release locks and come out of runnable state.
+ }
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+ }
+}
+
+static void ThreadSuspendByThreadIdWarning(int level, const char* message, uint32_t thread_id) {
+ LOG(level) << StringPrintf("%s: %d", message, thread_id);
+}
+
+Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension,
+ bool* timed_out) {
+ static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
+ useconds_t total_delay_us = 0;
+ useconds_t delay_us = 0;
+ bool did_suspend_request = false;
+ *timed_out = false;
+ Thread* self = Thread::Current();
+ CHECK_NE(thread_id, kInvalidThreadId);
+ while (true) {
+ Thread* thread = NULL;
+ {
+ ScopedObjectAccess soa(self);
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ for (const auto& it : list_) {
+ if (it->GetThreadId() == thread_id) {
+ thread = it;
+ break;
+ }
+ }
+ if (thread == NULL) {
+ // There's a race in inflating a lock and the owner giving up ownership and then dying.
+ ThreadSuspendByThreadIdWarning(WARNING, "No such thread id for suspend", thread_id);
+ return NULL;
+ }
+ {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ if (!did_suspend_request) {
+ thread->ModifySuspendCount(self, +1, debug_suspension);
+ did_suspend_request = true;
+ } else {
+ // If the caller isn't requesting suspension, a suspension should have already occurred.
+ CHECK_GT(thread->GetSuspendCount(), 0);
+ }
+ // IsSuspended on the current thread will fail as the current thread is changed into
+ // Runnable above. As the suspend count is now raised if this is the current thread
+ // it will self suspend on transition to Runnable, making it hard to work with. It's simpler
+ // to just explicitly handle the current thread in the callers to this code.
+ CHECK_NE(thread, self) << "Attempt to suspend the current thread for the debugger";
+ // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend
+ // count, or else we've waited and it has self suspended) or is the current thread, we're
+ // done.
+ if (thread->IsSuspended()) {
+ return thread;
+ }
+ if (total_delay_us >= kTimeoutUs) {
+ ThreadSuspendByThreadIdWarning(ERROR, "Thread suspension timed out", thread_id);
+ if (did_suspend_request) {
+ thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+ }
+ *timed_out = true;
+ return NULL;
+ }
+ }
+ // Release locks and come out of runnable state.
+ }
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+ }
+}
+
+Thread* ThreadList::FindThreadByThreadId(uint32_t thin_lock_id) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ for (const auto& thread : list_) {
+ if (thread->GetThreadId() == thin_lock_id) {
+ CHECK(thread == self || thread->IsSuspended());
+ return thread;
+ }
+ }
+ return NULL;
+}
+
void ThreadList::SuspendAllForDebugger() {
Thread* self = Thread::Current();
Thread* debug_thread = Dbg::GetDebugThread();
@@ -527,8 +709,8 @@
// suspend and so on, must happen at this point, and not in ~Thread.
self->Destroy();
- uint32_t thin_lock_id = self->thin_lock_id_;
- self->thin_lock_id_ = 0;
+ uint32_t thin_lock_id = self->thin_lock_thread_id_;
+ self->thin_lock_thread_id_ = 0;
ReleaseThreadId(self, thin_lock_id);
while (self != NULL) {
// Remove and delete the Thread* while holding the thread_list_lock_ and
@@ -568,10 +750,24 @@
}
}
+struct VerifyRootWrapperArg {
+ VerifyRootVisitor* visitor;
+ void* arg;
+};
+
+static mirror::Object* VerifyRootWrapperCallback(mirror::Object* root, void* arg) {
+ VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
+ wrapperArg->visitor(root, wrapperArg->arg, 0, NULL);
+ return root;
+}
+
void ThreadList::VerifyRoots(VerifyRootVisitor* visitor, void* arg) const {
+ VerifyRootWrapperArg wrapper;
+ wrapper.visitor = visitor;
+ wrapper.arg = arg;
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
for (const auto& thread : list_) {
- thread->VerifyRoots(visitor, arg);
+ thread->VisitRoots(VerifyRootWrapperCallback, &wrapper);
}
}
@@ -594,14 +790,4 @@
allocated_ids_.reset(id);
}
-Thread* ThreadList::FindThreadByThinLockId(uint32_t thin_lock_id) {
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- for (const auto& thread : list_) {
- if (thread->GetThinLockId() == thin_lock_id) {
- return thread;
- }
- }
- return NULL;
-}
-
} // namespace art
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 3df3e2c..b1b3e88 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_THREAD_LIST_H_
#include "base/mutex.h"
+#include "jni.h"
#include "root_visitor.h"
#include <bitset>
@@ -31,8 +32,8 @@
class ThreadList {
public:
static const uint32_t kMaxThreadId = 0xFFFF;
- static const uint32_t kInvalidId = 0;
- static const uint32_t kMainId = 1;
+ static const uint32_t kInvalidThreadId = 0;
+ static const uint32_t kMainThreadId = 1;
explicit ThreadList();
~ThreadList();
@@ -59,6 +60,30 @@
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
+
+ // Suspend a thread using a peer, typically used by the debugger. Returns the thread on success,
+ // else NULL. The peer is used to identify the thread to avoid races with the thread terminating.
+ // If the thread should be suspended then value of request_suspension should be true otherwise
+ // the routine will wait for a previous suspend request. If the suspension times out then *timeout
+ // is set to true.
+ static Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension,
+ bool* timed_out)
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
+
+ // Suspend a thread using its thread id, typically used by lock/monitor inflation. Returns the
+ // thread on success else NULL. The thread id is used to identify the thread to avoid races with
+ // the thread terminating. Note that as thread ids are recycled this may not suspend the expected
+ // thread, that may be terminating. If the suspension times out then *timeout is set to true.
+ Thread* SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, bool* timed_out)
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_list_lock_,
+ Locks::thread_suspend_count_lock_);
+
+ // Find an already suspended thread (or self) by its id.
+ Thread* FindThreadByThreadId(uint32_t thin_lock_id);
+
// Run a checkpoint on threads, running threads are not suspended but run the checkpoint inside
// of the suspend check. Returns how many checkpoints we should expect to run.
size_t RunCheckpoint(Closure* checkpoint_function);
@@ -99,8 +124,6 @@
return list_;
}
- Thread* FindThreadByThinLockId(uint32_t thin_lock_id);
-
private:
uint32_t AllocThreadId(Thread* self);
void ReleaseThreadId(Thread* self, uint32_t id) LOCKS_EXCLUDED(allocated_ids_lock_);
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 674ab9d..bb6c475 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -19,7 +19,7 @@
#include "base/casts.h"
#include "base/stl_util.h"
#include "runtime.h"
-#include "thread.h"
+#include "thread-inl.h"
namespace art {
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
index e428511..01497ef 100644
--- a/runtime/throw_location.cc
+++ b/runtime/throw_location.cc
@@ -34,11 +34,14 @@
}
void ThrowLocation::VisitRoots(RootVisitor* visitor, void* arg) {
- if (this_object_ != NULL) {
- visitor(this_object_, arg);
+ if (this_object_ != nullptr) {
+ this_object_ = const_cast<mirror::Object*>(visitor(this_object_, arg));
+ DCHECK(this_object_ != nullptr);
}
- if (method_ != NULL) {
- visitor(method_, arg);
+ if (method_ != nullptr) {
+ method_ = const_cast<mirror::ArtMethod*>(
+ reinterpret_cast<const mirror::ArtMethod*>(visitor(method_, arg)));
+ DCHECK(method_ != nullptr);
}
}
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 7b25306..ec95a87 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -667,7 +667,7 @@
mh.ChangeMethod(method);
os << StringPrintf("%p\t%s\t%s\t%s\t%s\n", method,
PrettyDescriptor(mh.GetDeclaringClassDescriptor()).c_str(), mh.GetName(),
- mh.GetSignature().c_str(), mh.GetDeclaringClassSourceFile());
+ mh.GetSignature().ToString().c_str(), mh.GetDeclaringClassSourceFile());
}
}
diff --git a/runtime/utf-inl.h b/runtime/utf-inl.h
new file mode 100644
index 0000000..d8c258b
--- /dev/null
+++ b/runtime/utf-inl.h
@@ -0,0 +1,61 @@
+/*
+ * 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_UTF_INL_H_
+#define ART_RUNTIME_UTF_INL_H_
+
+#include "utf.h"
+
+namespace art {
+
+inline uint16_t GetUtf16FromUtf8(const char** utf8_data_in) {
+ uint8_t one = *(*utf8_data_in)++;
+ if ((one & 0x80) == 0) {
+ // one-byte encoding
+ return one;
+ }
+ // two- or three-byte encoding
+ uint8_t two = *(*utf8_data_in)++;
+ if ((one & 0x20) == 0) {
+ // two-byte encoding
+ return ((one & 0x1f) << 6) | (two & 0x3f);
+ }
+ // three-byte encoding
+ uint8_t three = *(*utf8_data_in)++;
+ return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+}
+
+inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1,
+ const char* utf8_2) {
+ for (;;) {
+ if (*utf8_1 == '\0') {
+ return (*utf8_2 == '\0') ? 0 : -1;
+ } else if (*utf8_2 == '\0') {
+ return 1;
+ }
+
+ int c1 = GetUtf16FromUtf8(&utf8_1);
+ int c2 = GetUtf16FromUtf8(&utf8_2);
+
+ if (c1 != c2) {
+ return c1 > c2 ? 1 : -1;
+ }
+ }
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_UTF_INL_H_
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 1add7d9..5ec2ea1 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -19,6 +19,7 @@
#include "base/logging.h"
#include "mirror/array.h"
#include "mirror/object-inl.h"
+#include "utf-inl.h"
namespace art {
@@ -84,41 +85,6 @@
return hash;
}
-
-uint16_t GetUtf16FromUtf8(const char** utf8_data_in) {
- uint8_t one = *(*utf8_data_in)++;
- if ((one & 0x80) == 0) {
- // one-byte encoding
- return one;
- }
- // two- or three-byte encoding
- uint8_t two = *(*utf8_data_in)++;
- if ((one & 0x20) == 0) {
- // two-byte encoding
- return ((one & 0x1f) << 6) | (two & 0x3f);
- }
- // three-byte encoding
- uint8_t three = *(*utf8_data_in)++;
- return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
-}
-
-int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, const char* utf8_2) {
- for (;;) {
- if (*utf8_1 == '\0') {
- return (*utf8_2 == '\0') ? 0 : -1;
- } else if (*utf8_2 == '\0') {
- return 1;
- }
-
- int c1 = GetUtf16FromUtf8(&utf8_1);
- int c2 = GetUtf16FromUtf8(&utf8_2);
-
- if (c1 != c2) {
- return c1 > c2 ? 1 : -1;
- }
- }
-}
-
int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) {
for (;;) {
if (*utf8_1 == '\0') {
diff --git a/runtime/utf.h b/runtime/utf.h
index 4c9a1d9..cc5e6d4 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -29,9 +29,10 @@
* See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details.
*/
namespace art {
+
namespace mirror {
-template<class T> class PrimitiveArray;
-typedef PrimitiveArray<uint16_t> CharArray;
+ template<class T> class PrimitiveArray;
+ typedef PrimitiveArray<uint16_t> CharArray;
} // namespace mirror
/*
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 8e810a7..2339903 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -34,7 +34,7 @@
#include "mirror/string.h"
#include "object_utils.h"
#include "os.h"
-#include "utf.h"
+#include "utf-inl.h"
#if !defined(HAVE_POSIX_CLOCKS)
#include <sys/time.h>
@@ -49,8 +49,7 @@
#include <sys/syscall.h>
#endif
-#include <corkscrew/backtrace.h> // For DumpNativeStack.
-#include <corkscrew/demangle.h> // For DumpNativeStack.
+#include <backtrace/Backtrace.h> // For DumpNativeStack.
#if defined(__linux__)
#include <linux/unistd.h>
@@ -79,23 +78,23 @@
return result;
}
-void GetThreadStack(pthread_t thread, void*& stack_base, size_t& stack_size) {
+void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size) {
#if defined(__APPLE__)
- stack_size = pthread_get_stacksize_np(thread);
+ *stack_size = pthread_get_stacksize_np(thread);
void* stack_addr = pthread_get_stackaddr_np(thread);
// Check whether stack_addr is the base or end of the stack.
// (On Mac OS 10.7, it's the end.)
int stack_variable;
if (stack_addr > &stack_variable) {
- stack_base = reinterpret_cast<byte*>(stack_addr) - stack_size;
+ *stack_base = reinterpret_cast<byte*>(stack_addr) - *stack_size;
} else {
- stack_base = stack_addr;
+ *stack_base = stack_addr;
}
#else
pthread_attr_t attributes;
CHECK_PTHREAD_CALL(pthread_getattr_np, (thread, &attributes), __FUNCTION__);
- CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, &stack_base, &stack_size), __FUNCTION__);
+ CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__);
#endif
}
@@ -367,11 +366,13 @@
result += '.';
result += mh.GetName();
if (with_signature) {
- std::string signature(mh.GetSignature());
- if (signature == "<no signature>") {
- return result + signature;
+ const Signature signature = mh.GetSignature();
+ std::string sig_as_string(signature.ToString());
+ if (signature == Signature::NoSignature()) {
+ return result + sig_as_string;
}
- result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str());
+ result = PrettyReturnType(sig_as_string.c_str()) + " " + result +
+ PrettyArguments(sig_as_string.c_str());
}
return result;
}
@@ -385,11 +386,13 @@
result += '.';
result += dex_file.GetMethodName(method_id);
if (with_signature) {
- std::string signature(dex_file.GetMethodSignature(method_id));
- if (signature == "<no signature>") {
- return result + signature;
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ std::string sig_as_string(signature.ToString());
+ if (signature == Signature::NoSignature()) {
+ return result + sig_as_string;
}
- result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str());
+ result = PrettyReturnType(sig_as_string.c_str()) + " " + result +
+ PrettyArguments(sig_as_string.c_str());
}
return result;
}
@@ -641,7 +644,7 @@
long_name += JniShortName(m);
long_name += "__";
- std::string signature(MethodHelper(m).GetSignature());
+ std::string signature(MethodHelper(m).GetSignature().ToString());
signature.erase(0, 1);
signature.erase(signature.begin() + signature.find(')'), signature.end());
@@ -714,9 +717,9 @@
* this function returns false, then the given pointer may only have
* been partially advanced.
*/
-bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
+static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
uint8_t c = (uint8_t) **pUtf8Ptr;
- if (c <= 0x7f) {
+ if (LIKELY(c <= 0x7f)) {
// It's low-ascii, so check the table.
uint32_t wordIdx = c >> 5;
uint32_t bitIdx = c & 0x1f;
@@ -757,7 +760,7 @@
}
enum ClassNameType { kName, kDescriptor };
-bool IsValidClassName(const char* s, ClassNameType type, char separator) {
+static bool IsValidClassName(const char* s, ClassNameType type, char separator) {
int arrayCount = 0;
while (*s == '[') {
arrayCount++;
@@ -886,6 +889,35 @@
}
}
+std::string Trim(std::string s) {
+ std::string result;
+ unsigned int start_index = 0;
+ unsigned int end_index = s.size() - 1;
+
+ // Skip initial whitespace.
+ while (start_index < s.size()) {
+ if (!isspace(s[start_index])) {
+ break;
+ }
+ start_index++;
+ }
+
+ // Skip terminating whitespace.
+ while (end_index >= start_index) {
+ if (!isspace(s[end_index])) {
+ break;
+ }
+ end_index--;
+ }
+
+ // All spaces, no beef.
+ if (end_index < start_index) {
+ return "";
+ }
+ // Start_index is the first non-space, end_index is the last one.
+ return s.substr(start_index, end_index - start_index + 1);
+}
+
template <typename StringT>
std::string Join(std::vector<StringT>& strings, char separator) {
if (strings.empty()) {
@@ -955,8 +987,8 @@
#endif
}
-void GetTaskStats(pid_t tid, char& state, int& utime, int& stime, int& task_cpu) {
- utime = stime = task_cpu = 0;
+void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) {
+ *utime = *stime = *task_cpu = 0;
std::string stats;
if (!ReadFileToString(StringPrintf("/proc/self/task/%d/stat", tid), &stats)) {
return;
@@ -966,10 +998,10 @@
// Extract the three fields we care about.
std::vector<std::string> fields;
Split(stats, ' ', fields);
- state = fields[0][0];
- utime = strtoull(fields[11].c_str(), NULL, 10);
- stime = strtoull(fields[12].c_str(), NULL, 10);
- task_cpu = strtoull(fields[36].c_str(), NULL, 10);
+ *state = fields[0][0];
+ *utime = strtoull(fields[11].c_str(), NULL, 10);
+ *stime = strtoull(fields[12].c_str(), NULL, 10);
+ *task_cpu = strtoull(fields[36].c_str(), NULL, 10);
}
std::string GetSchedulerGroupName(pid_t tid) {
@@ -997,10 +1029,9 @@
return "";
}
-static const char* CleanMapName(const backtrace_symbol_t* symbol) {
- const char* map_name = symbol->map_name;
+static const char* CleanMapName(const char* map_name) {
if (map_name == NULL) {
- map_name = "???";
+ return "???";
}
// Turn "/usr/local/google/home/enh/clean-dalvik-dev/out/host/linux-x86/lib/libartd.so"
// into "libartd.so".
@@ -1011,89 +1042,36 @@
return map_name;
}
-static void FindSymbolInElf(const backtrace_frame_t* frame, const backtrace_symbol_t* symbol,
- std::string& symbol_name, uint32_t& pc_offset) {
- symbol_table_t* symbol_table = NULL;
- if (symbol->map_name != NULL) {
- symbol_table = load_symbol_table(symbol->map_name);
- }
- const symbol_t* elf_symbol = NULL;
- bool was_relative = true;
- if (symbol_table != NULL) {
- elf_symbol = find_symbol(symbol_table, symbol->relative_pc);
- if (elf_symbol == NULL) {
- elf_symbol = find_symbol(symbol_table, frame->absolute_pc);
- was_relative = false;
- }
- }
- if (elf_symbol != NULL) {
- const char* demangled_symbol_name = demangle_symbol_name(elf_symbol->name);
- if (demangled_symbol_name != NULL) {
- symbol_name = demangled_symbol_name;
- } else {
- symbol_name = elf_symbol->name;
- }
-
- // TODO: is it a libcorkscrew bug that we have to do this?
- pc_offset = (was_relative ? symbol->relative_pc : frame->absolute_pc) - elf_symbol->start;
- } else {
- symbol_name = "???";
- }
- free_symbol_table(symbol_table);
-}
-
void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) {
- // Ensure libcorkscrew doesn't use a stale cache of /proc/self/maps.
- flush_my_map_info_list();
-
- const size_t MAX_DEPTH = 32;
- UniquePtr<backtrace_frame_t[]> frames(new backtrace_frame_t[MAX_DEPTH]);
- size_t ignore_count = 2; // Don't include unwind_backtrace_thread or DumpNativeStack.
- ssize_t frame_count = unwind_backtrace_thread(tid, frames.get(), ignore_count, MAX_DEPTH);
- if (frame_count == -1) {
- os << prefix << "(unwind_backtrace_thread failed for thread " << tid << ")\n";
+ UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+ if (!backtrace->Unwind(0)) {
+ os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
return;
- } else if (frame_count == 0) {
+ } else if (backtrace->NumFrames() == 0) {
os << prefix << "(no native stack frames for thread " << tid << ")\n";
return;
}
- UniquePtr<backtrace_symbol_t[]> backtrace_symbols(new backtrace_symbol_t[frame_count]);
- get_backtrace_symbols(frames.get(), frame_count, backtrace_symbols.get());
-
- for (size_t i = 0; i < static_cast<size_t>(frame_count); ++i) {
- const backtrace_frame_t* frame = &frames[i];
- const backtrace_symbol_t* symbol = &backtrace_symbols[i];
-
+ for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
// We produce output like this:
- // ] #00 unwind_backtrace_thread+536 [0x55d75bb8] (libcorkscrew.so)
-
- std::string symbol_name;
- uint32_t pc_offset = 0;
- if (symbol->demangled_name != NULL) {
- symbol_name = symbol->demangled_name;
- pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
- } else if (symbol->symbol_name != NULL) {
- symbol_name = symbol->symbol_name;
- pc_offset = symbol->relative_pc - symbol->relative_symbol_addr;
- } else {
- // dladdr(3) didn't find a symbol; maybe it's static? Look in the ELF file...
- FindSymbolInElf(frame, symbol, symbol_name, pc_offset);
- }
+ // ] #00 unwind_backtrace_thread+536 [0x55d75bb8] (libbacktrace.so)
+ const backtrace_frame_data_t* frame = backtrace->GetFrame(i);
os << prefix;
if (include_count) {
- os << StringPrintf("#%02zd ", i);
+ os << StringPrintf("#%02zu ", i);
}
- os << symbol_name;
- if (pc_offset != 0) {
- os << "+" << pc_offset;
+ if (frame->func_name) {
+ os << frame->func_name;
+ } else {
+ os << "???";
}
- os << StringPrintf(" [%p] (%s)\n",
- reinterpret_cast<void*>(frame->absolute_pc), CleanMapName(symbol));
+ if (frame->func_offset != 0) {
+ os << "+" << frame->func_offset;
+ }
+ os << StringPrintf(" [%p]", reinterpret_cast<void*>(frame->pc));
+ os << " (" << CleanMapName(frame->map_name) << ")\n";
}
-
- free_backtrace_symbols(backtrace_symbols.get(), frame_count);
}
#if defined(__APPLE__)
@@ -1190,13 +1168,13 @@
return dalvik_cache;
}
-std::string GetDalvikCacheFilenameOrDie(const std::string& location) {
+std::string GetDalvikCacheFilenameOrDie(const char* location) {
std::string dalvik_cache(GetDalvikCacheOrDie(GetAndroidData()));
if (location[0] != '/') {
LOG(FATAL) << "Expected path in location to be absolute: "<< location;
}
- std::string cache_file(location, 1); // skip leading slash
- if (!EndsWith(location, ".dex") || !EndsWith(location, ".art")) {
+ std::string cache_file(&location[1]); // skip leading slash
+ if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) {
cache_file += "/";
cache_file += DexFile::kClassesDex;
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 812a581..6850e8b 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -119,6 +119,7 @@
typedef B value;
};
+// For rounding integers.
template<typename T>
static inline T RoundDown(T x, int n) {
CHECK(IsPowerOfTwo(n));
@@ -130,6 +131,18 @@
return RoundDown(x + n - 1, n);
}
+// For aligning pointers.
+template<typename T>
+static inline T* AlignDown(T* x, int n) {
+ CHECK(IsPowerOfTwo(n));
+ return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(x) & -static_cast<uintptr_t>(n));
+}
+
+template<typename T>
+static inline T* AlignUp(T* x, int n) {
+ return AlignDown(reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(x) + static_cast<uintptr_t>(n - 1)), n);
+}
+
// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
// figure 3-3, page 48, where the function is called clp2.
static inline uint32_t RoundUpToPowerOfTwo(uint32_t x) {
@@ -239,7 +252,7 @@
// Get the appropriate unit for a nanosecond duration.
TimeUnit GetAppropriateTimeUnit(uint64_t nano_duration);
-// Get the divisor to convert from a nanoseconds to a time unit
+// Get the divisor to convert from a nanoseconds to a time unit.
uint64_t GetNsToTimeUnitDivisor(TimeUnit time_unit);
// Performs JNI name mangling as described in section 11.3 "Linking Native Methods"
@@ -313,6 +326,9 @@
// strings. Empty strings will be omitted.
void Split(const std::string& s, char separator, std::vector<std::string>& result);
+// Trims whitespace off both ends of the given string.
+std::string Trim(std::string s);
+
// Joins a vector of strings into a single string, using the given separator.
template <typename StringT> std::string Join(std::vector<StringT>& strings, char separator);
@@ -323,10 +339,10 @@
std::string GetThreadName(pid_t tid);
// Returns details of the given thread's stack.
-void GetThreadStack(pthread_t thread, void*& stack_base, size_t& stack_size);
+void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size);
// Reads data from "/proc/self/task/${tid}/stat".
-void GetTaskStats(pid_t tid, char& state, int& utime, int& stime, int& task_cpu);
+void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu);
// Returns the name of the scheduler group for the given thread the current process, or the empty string.
std::string GetSchedulerGroupName(pid_t tid);
@@ -341,17 +357,17 @@
// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true);
-// Find $ANDROID_ROOT, /system, or abort
+// Find $ANDROID_ROOT, /system, or abort.
const char* GetAndroidRoot();
-// Find $ANDROID_DATA, /data, or abort
+// Find $ANDROID_DATA, /data, or abort.
const char* GetAndroidData();
// Returns the dalvik-cache location, or dies trying.
std::string GetDalvikCacheOrDie(const char* android_data);
// Returns the dalvik-cache location for a DexFile or OatFile, or dies trying.
-std::string GetDalvikCacheFilenameOrDie(const std::string& location);
+std::string GetDalvikCacheFilenameOrDie(const char* location);
// Check whether the given magic matches a known file type.
bool IsZipMagic(uint32_t magic);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 2633964..b43177b 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -335,4 +335,18 @@
EXPECT_FALSE(EndsWith("oo", "foo"));
}
+void CheckGetDalvikCacheFilenameOrDie(const char* in, const char* out) {
+ std::string expected(getenv("ANDROID_DATA"));
+ expected += "/dalvik-cache/";
+ expected += out;
+ EXPECT_STREQ(expected.c_str(), GetDalvikCacheFilenameOrDie(in).c_str());
+}
+
+TEST_F(UtilsTest, GetDalvikCacheFilenameOrDie) {
+ CheckGetDalvikCacheFilenameOrDie("/system/app/Foo.apk", "system@app@Foo.apk@classes.dex");
+ CheckGetDalvikCacheFilenameOrDie("/data/app/foo-1.apk", "data@app@foo-1.apk@classes.dex");
+ CheckGetDalvikCacheFilenameOrDie("/system/framework/core.jar", "system@framework@core.jar@classes.dex");
+ CheckGetDalvikCacheFilenameOrDie("/system/framework/boot.art", "system@framework@boot.art");
+}
+
} // namespace art
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 34ebeb1..9f98061 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -51,7 +51,8 @@
uint32_t insns_size, uint16_t registers_size,
MethodVerifier* verifier) {
DCHECK_GT(insns_size, 0U);
-
+ register_lines_.reset(new RegisterLine*[insns_size]());
+ size_ = insns_size;
for (uint32_t i = 0; i < insns_size; i++) {
bool interesting = false;
switch (mode) {
@@ -68,7 +69,16 @@
break;
}
if (interesting) {
- pc_to_register_line_.Put(i, new RegisterLine(registers_size, verifier));
+ register_lines_[i] = RegisterLine::Create(registers_size, verifier);
+ }
+ }
+}
+
+PcToRegisterLineTable::~PcToRegisterLineTable() {
+ for (size_t i = 0; i < size_; i++) {
+ delete register_lines_[i];
+ if (kIsDebugBuild) {
+ register_lines_[i] = nullptr;
}
}
}
@@ -80,7 +90,7 @@
return kNoFailure;
}
mirror::Class* super = klass->GetSuperClass();
- if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") {
+ if (super == NULL && strcmp("Ljava/lang/Object;", ClassHelper(klass).GetDescriptor()) != 0) {
*error = "Verifier rejected class ";
*error += PrettyDescriptor(klass);
*error += " that has no super class";
@@ -293,6 +303,7 @@
dex_method_idx_(dex_method_idx),
mirror_method_(method),
method_access_flags_(method_access_flags),
+ return_type_(nullptr),
dex_file_(dex_file),
dex_cache_(dex_cache),
class_loader_(class_loader),
@@ -300,7 +311,7 @@
code_item_(code_item),
declaring_class_(NULL),
interesting_dex_pc_(-1),
- monitor_enter_dex_pcs_(NULL),
+ monitor_enter_dex_pcs_(nullptr),
have_pending_hard_failure_(false),
have_pending_runtime_throw_failure_(false),
new_instance_count_(0),
@@ -309,7 +320,7 @@
allow_soft_failures_(allow_soft_failures),
has_check_casts_(false),
has_virtual_or_interface_invokes_(false) {
- DCHECK(class_def != NULL);
+ DCHECK(class_def != nullptr);
}
void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc,
@@ -495,17 +506,25 @@
while (dex_pc < insns_size) {
Instruction::Code opcode = inst->Opcode();
- if (opcode == Instruction::NEW_INSTANCE) {
- new_instance_count++;
- } else if (opcode == Instruction::MONITOR_ENTER) {
- monitor_enter_count++;
- } else if (opcode == Instruction::CHECK_CAST) {
- has_check_casts_ = true;
- } else if ((inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
- (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE)) {
- has_virtual_or_interface_invokes_ = true;
+ switch (opcode) {
+ case Instruction::APUT_OBJECT:
+ case Instruction::CHECK_CAST:
+ has_check_casts_ = true;
+ break;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ has_virtual_or_interface_invokes_ = true;
+ break;
+ case Instruction::MONITOR_ENTER:
+ monitor_enter_count++;
+ break;
+ case Instruction::NEW_INSTANCE:
+ new_instance_count++;
+ break;
+ default:
+ break;
}
size_t inst_size = inst->SizeInCodeUnits();
insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
@@ -1034,8 +1053,8 @@
this);
- work_line_.reset(new RegisterLine(registers_size, this));
- saved_line_.reset(new RegisterLine(registers_size, this));
+ work_line_.reset(RegisterLine::Create(registers_size, this));
+ saved_line_.reset(RegisterLine::Create(registers_size, this));
/* Initialize register types of method arguments. */
if (!SetTypesFromSignature()) {
@@ -1935,7 +1954,7 @@
if (!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
!cast_type.GetClass()->IsInterface() && !cast_type.IsAssignableFrom(orig_type)) {
- RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this);
+ RegisterLine* update_line = RegisterLine::Create(code_item_->registers_size_, this);
if (inst->Opcode() == Instruction::IF_EQZ) {
fallthrough_line.reset(update_line);
} else {
@@ -2131,20 +2150,30 @@
inst->Opcode() == Instruction::INVOKE_SUPER_RANGE);
mirror::ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL,
is_range, is_super);
- const char* descriptor;
- if (called_method == NULL) {
+ const RegType* return_type = nullptr;
+ if (called_method != nullptr) {
+ MethodHelper mh(called_method);
+ mirror::Class* return_type_class = mh.GetReturnType();
+ if (return_type_class != nullptr) {
+ return_type = ®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class,
+ return_type_class->CannotBeAssignedFromOtherTypes());
+ } else {
+ Thread* self = Thread::Current();
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ }
+ if (return_type == nullptr) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
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_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
+ const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
+ return_type = ®_types_.FromDescriptor(class_loader_, descriptor, false);
}
- const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
- if (!return_type.IsLowHalf()) {
- work_line_->SetResultRegisterType(return_type);
+ if (!return_type->IsLowHalf()) {
+ work_line_->SetResultRegisterType(*return_type);
} else {
- work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
+ work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(®_types_));
}
just_set_result = true;
break;
@@ -2159,7 +2188,7 @@
if (called_method == NULL) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- is_constructor = StringPiece(dex_file_->GetMethodName(method_id)) == "<init>";
+ is_constructor = strcmp("<init>", dex_file_->StringDataByIdx(method_id.name_idx_)) == 0;
uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
return_type_descriptor = dex_file_->StringByTypeIdx(return_type_idx);
} else {
@@ -2863,11 +2892,14 @@
// as that is caught at runtime
common_super = &exception;
} else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
- // We don't know enough about the type and the common path merge will result in
- // Conflict. Fail here knowing the correct thing can be done at runtime.
- Fail(exception.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS :
- VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
- return reg_types_.Conflict();
+ if (exception.IsUnresolvedTypes()) {
+ // We don't know enough about the type. Fail here and let runtime handle it.
+ Fail(VERIFY_ERROR_NO_CLASS) << "unresolved exception class " << exception;
+ return exception;
+ } else {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception;
+ return reg_types_.Conflict();
+ }
} else if (common_super->Equals(exception)) {
// odd case, but nothing to do
} else {
@@ -2889,7 +2921,7 @@
}
mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx,
- MethodType method_type) {
+ MethodType method_type) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
if (klass_type.IsConflict()) {
@@ -2906,7 +2938,7 @@
mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
if (res_method == NULL) {
const char* name = dex_file_->GetMethodName(method_id);
- std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL));
+ const Signature signature = dex_file_->GetMethodSignature(method_id);
if (method_type == METHOD_DIRECT || method_type == METHOD_STATIC) {
res_method = klass->FindDirectMethod(name, signature);
@@ -3111,6 +3143,8 @@
const RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range);
if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
return NULL;
+ } else if (actual_arg_type.IsZero()) { // Invoke on "null" instance: we can't go further.
+ return NULL;
}
mirror::Class* this_class = NULL;
if (!actual_arg_type.IsUnresolvedTypes()) {
@@ -3504,22 +3538,26 @@
const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
field = GetInstanceField(object_type, field_idx);
}
- const char* descriptor;
- mirror::ClassLoader* loader;
+ const RegType* field_type = nullptr;
if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = class_loader_;
+ FieldHelper fh(field);
+ mirror::Class* field_type_class = fh.GetType(false);
+ if (field_type_class != nullptr) {
+ field_type = ®_types_.FromClass(fh.GetTypeDescriptor(), field_type_class,
+ field_type_class->CannotBeAssignedFromOtherTypes());
+ }
}
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ if (field_type == nullptr) {
+ const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+ const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+ mirror::ClassLoader* loader = class_loader_;
+ field_type = ®_types_.FromDescriptor(loader, descriptor, false);
+ }
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
if (is_primitive) {
- if (field_type.Equals(insn_type) ||
- (field_type.IsFloat() && insn_type.IsInteger()) ||
- (field_type.IsDouble() && insn_type.IsLong())) {
+ if (field_type->Equals(insn_type) ||
+ (field_type->IsFloat() && insn_type.IsInteger()) ||
+ (field_type->IsDouble() && insn_type.IsLong())) {
// expected that read is of the correct primitive type or that int reads are reading
// floats or long reads are reading doubles
} else {
@@ -3532,7 +3570,7 @@
return;
}
} else {
- if (!insn_type.IsAssignableFrom(field_type)) {
+ if (!insn_type.IsAssignableFrom(*field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
<< " to be compatible with type '" << insn_type
<< "' but found type '" << field_type
@@ -3541,10 +3579,10 @@
return;
}
}
- if (!field_type.IsLowHalf()) {
- work_line_->SetRegisterType(vregA, field_type);
+ if (!field_type->IsLowHalf()) {
+ work_line_->SetRegisterType(vregA, *field_type);
} else {
- work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_));
+ work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_));
}
}
@@ -3558,36 +3596,38 @@
const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
field = GetInstanceField(object_type, field_idx);
}
- const char* descriptor;
- mirror::ClassLoader* loader;
- if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = class_loader_;
- }
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ const RegType* field_type = nullptr;
if (field != NULL) {
if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
<< " from other class " << GetDeclaringClass();
return;
}
+ FieldHelper fh(field);
+ mirror::Class* field_type_class = fh.GetType(false);
+ if (field_type_class != nullptr) {
+ field_type = ®_types_.FromClass(fh.GetTypeDescriptor(), field_type_class,
+ field_type_class->CannotBeAssignedFromOtherTypes());
+ }
+ }
+ if (field_type == nullptr) {
+ const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+ const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+ mirror::ClassLoader* loader = class_loader_;
+ field_type = ®_types_.FromDescriptor(loader, descriptor, false);
}
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
if (is_primitive) {
- VerifyPrimitivePut(field_type, insn_type, vregA);
+ VerifyPrimitivePut(*field_type, insn_type, vregA);
} else {
- if (!insn_type.IsAssignableFrom(field_type)) {
+ if (!insn_type.IsAssignableFrom(*field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
<< " to be compatible with type '" << insn_type
<< "' but found type '" << field_type
<< "' in put-object";
return;
}
- work_line_->VerifyRegisterType(vregA, field_type);
+ work_line_->VerifyRegisterType(vregA, *field_type);
}
}
@@ -3655,14 +3695,21 @@
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name();
return;
}
- const char* descriptor = FieldHelper(field).GetTypeDescriptor();
- mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ FieldHelper fh(field);
+ mirror::Class* field_type_class = fh.GetType(false);
+ const RegType* field_type;
+ if (field_type_class != nullptr) {
+ field_type = ®_types_.FromClass(fh.GetTypeDescriptor(), field_type_class,
+ field_type_class->CannotBeAssignedFromOtherTypes());
+ } else {
+ field_type = ®_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(),
+ fh.GetTypeDescriptor(), false);
+ }
const uint32_t vregA = inst->VRegA_22c();
if (is_primitive) {
- if (field_type.Equals(insn_type) ||
- (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
- (field_type.IsDouble() && insn_type.IsLongTypes())) {
+ if (field_type->Equals(insn_type) ||
+ (field_type->IsFloat() && insn_type.IsIntegralTypes()) ||
+ (field_type->IsDouble() && insn_type.IsLongTypes())) {
// expected that read is of the correct primitive type or that int reads are reading
// floats or long reads are reading doubles
} else {
@@ -3675,7 +3722,7 @@
return;
}
} else {
- if (!insn_type.IsAssignableFrom(field_type)) {
+ if (!insn_type.IsAssignableFrom(*field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
<< " to be compatible with type '" << insn_type
<< "' but found type '" << field_type
@@ -3684,10 +3731,10 @@
return;
}
}
- if (!field_type.IsLowHalf()) {
- work_line_->SetRegisterType(vregA, field_type);
+ if (!field_type->IsLowHalf()) {
+ work_line_->SetRegisterType(vregA, *field_type);
} else {
- work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(®_types_));
+ work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_));
}
}
@@ -3801,7 +3848,7 @@
}
} else {
UniquePtr<RegisterLine> copy(gDebugVerify ?
- new RegisterLine(target_line->NumRegs(), this) :
+ RegisterLine::Create(target_line->NumRegs(), this) :
NULL);
if (gDebugVerify) {
copy->CopyFromLine(target_line);
@@ -3829,11 +3876,28 @@
}
const RegType& MethodVerifier::GetMethodReturnType() {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- 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 reg_types_.FromDescriptor(class_loader_, descriptor, false);
+ if (return_type_ == nullptr) {
+ if (mirror_method_ != NULL) {
+ MethodHelper mh(mirror_method_);
+ mirror::Class* return_type_class = mh.GetReturnType();
+ if (return_type_class != nullptr) {
+ return_type_ =®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class,
+ return_type_class->CannotBeAssignedFromOtherTypes());
+ } else {
+ Thread* self = Thread::Current();
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ }
+ if (return_type_ == nullptr) {
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
+ 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_, descriptor, false);
+ }
+ }
+ return *return_type_;
}
const RegType& MethodVerifier::GetDeclaringClass() {
@@ -3889,18 +3953,32 @@
code_item_->insns_size_in_code_units_);
for (; inst < end; inst = inst->Next()) {
- if (Instruction::CHECK_CAST != inst->Opcode()) {
- continue;
- }
- uint32_t dex_pc = inst->GetDexPc(code_item_->insns_);
- RegisterLine* line = reg_table_.GetLine(dex_pc);
- const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
- const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
- if (cast_type.IsStrictlyAssignableFrom(reg_type)) {
- if (mscs.get() == NULL) {
- mscs.reset(new MethodSafeCastSet());
+ Instruction::Code code = inst->Opcode();
+ if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
+ uint32_t dex_pc = inst->GetDexPc(code_item_->insns_);
+ RegisterLine* line = reg_table_.GetLine(dex_pc);
+ bool is_safe_cast = false;
+ if (code == Instruction::CHECK_CAST) {
+ const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
+ const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+ is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
+ } else {
+ const RegType& array_type(line->GetRegisterType(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()) {
+ const RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
+ const RegType& component_type(reg_types_.GetComponentType(array_type, class_loader_));
+ is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
+ }
}
- mscs->insert(dex_pc);
+ if (is_safe_cast) {
+ if (mscs.get() == NULL) {
+ mscs.reset(new MethodSafeCastSet());
+ }
+ mscs->insert(dex_pc);
+ }
}
}
return mscs.release();
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 073a2f7..57fde1d 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -110,10 +110,8 @@
// execution of that instruction.
class PcToRegisterLineTable {
public:
- PcToRegisterLineTable() {}
- ~PcToRegisterLineTable() {
- STLDeleteValues(&pc_to_register_line_);
- }
+ PcToRegisterLineTable() : size_(0) {}
+ ~PcToRegisterLineTable();
// Initialize the RegisterTable. Every instruction address can have a different set of information
// about what's in which register, but for verification purposes we only need to store it at
@@ -122,17 +120,13 @@
uint16_t registers_size, MethodVerifier* verifier);
RegisterLine* GetLine(size_t idx) {
- auto result = pc_to_register_line_.find(idx);
- if (result == pc_to_register_line_.end()) {
- return NULL;
- } else {
- return result->second;
- }
+ DCHECK_LT(idx, size_);
+ return register_lines_[idx];
}
private:
- typedef SafeMap<int32_t, RegisterLine*> Table;
- Table pc_to_register_line_;
+ UniquePtr<RegisterLine*[]> register_lines_;
+ size_t size_;
};
// The verifier
@@ -688,6 +682,7 @@
// Its object representation if known.
mirror::ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_);
const uint32_t method_access_flags_; // Method's access flags.
+ 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.
mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_);
@@ -729,10 +724,12 @@
// running and the verifier is called from the class linker.
const bool allow_soft_failures_;
- // Indicates if the method being verified contains at least one check-cast instruction.
+ // Indicates the method being verified contains at least one check-cast or aput-object
+ // instruction. Aput-object operations implicitly check for array-store exceptions, similar to
+ // check-cast.
bool has_check_casts_;
- // Indicates if the method being verified contains at least one invoke-virtual/range
+ // Indicates the method being verified contains at least one invoke-virtual/range
// or invoke-interface/range.
bool has_virtual_or_interface_invokes_;
};
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 25f840c..50d1583 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -99,7 +99,7 @@
}
std::string BooleanType::Dump() const {
- return "boolean";
+ return "Boolean";
}
std::string ConflictType::Dump() const {
@@ -111,7 +111,7 @@
}
std::string ShortType::Dump() const {
- return "short";
+ return "Short";
}
std::string CharType::Dump() const {
@@ -119,15 +119,15 @@
}
std::string FloatType::Dump() const {
- return "float";
+ return "Float";
}
std::string LongLoType::Dump() const {
- return "long (Low Half)";
+ return "Long (Low Half)";
}
std::string LongHiType::Dump() const {
- return "long (High Half)";
+ return "Long (High Half)";
}
std::string DoubleLoType::Dump() const {
@@ -461,7 +461,6 @@
std::stringstream result;
uint32_t val = ConstantValue();
if (val == 0) {
- CHECK(IsPreciseConstant());
result << "Zero/null";
} else {
result << "Imprecise ";
@@ -762,11 +761,6 @@
return AssignableFrom(*this, src, true);
}
-int32_t ConstantType::ConstantValue() const {
- DCHECK(IsConstantTypes());
- return constant_;
-}
-
int32_t ConstantType::ConstantValueLo() const {
DCHECK(IsConstantLo());
return constant_;
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 865ba20..f371733 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -574,9 +574,12 @@
// 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.
- virtual int32_t ConstantValue() const;
- virtual int32_t ConstantValueLo() const;
- virtual int32_t ConstantValueHi() const;
+ int32_t ConstantValue() const {
+ DCHECK(IsConstantTypes());
+ return constant_;
+ }
+ int32_t ConstantValueLo() const;
+ int32_t ConstantValueHi() const;
bool IsZero() const {
return IsPreciseConstant() && ConstantValue() == 0;
diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h
index 295e271..fc9e5c9 100644
--- a/runtime/verifier/reg_type_cache-inl.h
+++ b/runtime/verifier/reg_type_cache-inl.h
@@ -23,17 +23,6 @@
namespace art {
namespace verifier {
-template <class Type>
-Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
- mirror::Class* klass = NULL;
- // Try loading the class from linker.
- if (!descriptor.empty()) {
- klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
- }
- Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
- RegTypeCache::primitive_count_++;
- return entry;
-}
inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const {
DCHECK_LT(id, entries_.size());
@@ -41,6 +30,16 @@
DCHECK(result != NULL);
return *result;
}
+
+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)) {
+ return *small_precise_constants_[value - kMinSmallConstant];
+ }
+ return FromCat1NonSmallConstant(value, precise);
+}
+
} // 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 2c18132..446dd00 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -26,8 +26,8 @@
namespace verifier {
bool RegTypeCache::primitive_initialized_ = false;
-uint16_t RegTypeCache::primitive_start_ = 0;
uint16_t RegTypeCache::primitive_count_ = 0;
+PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
static bool MatchingPrecisionForClass(RegType* entry, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -44,7 +44,7 @@
}
}
-void RegTypeCache::FillPrimitiveTypes() {
+void RegTypeCache::FillPrimitiveAndSmallConstantTypes() {
entries_.push_back(UndefinedType::GetInstance());
entries_.push_back(ConflictType::GetInstance());
entries_.push_back(BooleanType::GetInstance());
@@ -57,6 +57,11 @@
entries_.push_back(FloatType::GetInstance());
entries_.push_back(DoubleLoType::GetInstance());
entries_.push_back(DoubleHiType::GetInstance());
+ for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
+ int32_t i = value - kMinSmallConstant;
+ DCHECK_EQ(entries_.size(), small_precise_constants_[i]->GetId());
+ entries_.push_back(small_precise_constants_[i]);
+ }
DCHECK_EQ(entries_.size(), primitive_count_);
}
@@ -205,6 +210,7 @@
}
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
// primitive classes are final.
@@ -232,12 +238,12 @@
RegTypeCache::~RegTypeCache() {
CHECK_LE(primitive_count_, entries_.size());
// Delete only the non primitive types.
- if (entries_.size() == kNumPrimitives) {
- // All entries are primitive, nothing to delete.
+ if (entries_.size() == kNumPrimitivesAndSmallConstants) {
+ // All entries are from the global pool, nothing to delete.
return;
}
std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
- std::advance(non_primitive_begin, kNumPrimitives);
+ std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants);
STLDeleteContainerPointers(non_primitive_begin, entries_.end());
}
@@ -255,12 +261,29 @@
FloatType::Destroy();
DoubleLoType::Destroy();
DoubleHiType::Destroy();
+ for (uint16_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
+ PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
+ delete type;
+ }
+
RegTypeCache::primitive_initialized_ = false;
RegTypeCache::primitive_count_ = 0;
}
}
-void RegTypeCache::CreatePrimitiveTypes() {
+template <class Type>
+Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
+ mirror::Class* klass = NULL;
+ // Try loading the class from linker.
+ if (!descriptor.empty()) {
+ klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
+ }
+ Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
+ RegTypeCache::primitive_count_++;
+ return entry;
+}
+
+void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() {
CreatePrimitiveTypeInstance<UndefinedType>("");
CreatePrimitiveTypeInstance<ConflictType>("");
CreatePrimitiveTypeInstance<BooleanType>("Z");
@@ -273,6 +296,11 @@
CreatePrimitiveTypeInstance<FloatType>("F");
CreatePrimitiveTypeInstance<DoubleLoType>("D");
CreatePrimitiveTypeInstance<DoubleHiType>("D");
+ for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
+ PreciseConstType* type = new PreciseConstType(value, primitive_count_);
+ small_precise_constants_[value - kMinSmallConstant] = type;
+ primitive_count_++;
+ }
}
const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) {
@@ -331,29 +359,28 @@
return *entry;
}
-const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
- RegType* entry = NULL;
- RegType* cur_entry = NULL;
+const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
+ UninitializedType* entry = NULL;
const std::string& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- cur_entry = entries_[i];
+ RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedAndUninitializedReference() &&
down_cast<UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc() == allocation_pc &&
(cur_entry->GetDescriptor() == descriptor)) {
- return *cur_entry;
+ return *down_cast<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++) {
- cur_entry = entries_[i];
+ RegType* cur_entry = entries_[i];
if (cur_entry->IsUninitializedReference() &&
down_cast<UninitializedReferenceType*>(cur_entry)
->GetAllocationPc() == allocation_pc &&
cur_entry->GetClass() == klass) {
- return *cur_entry;
+ return *down_cast<UninitializedReferenceType*>(cur_entry);
}
}
entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size());
@@ -404,27 +431,33 @@
return *entry;
}
-const RegType& RegTypeCache::ByteConstant() {
- return FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+const ImpreciseConstType& RegTypeCache::ByteConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
}
-const RegType& RegTypeCache::ShortConstant() {
- return FromCat1Const(std::numeric_limits<jshort>::min(), false);
+const ImpreciseConstType& RegTypeCache::ShortConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jshort>::min(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
}
-const RegType& RegTypeCache::IntConstant() {
- return FromCat1Const(std::numeric_limits<jint>::max(), false);
+const ImpreciseConstType& RegTypeCache::IntConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
}
-const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
- RegType* entry;
+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];
if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
cur_entry->GetDescriptor() == descriptor) {
- return *cur_entry;
+ return *down_cast<UninitializedType*>(cur_entry);
}
}
entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size());
@@ -433,7 +466,7 @@
for (size_t i = primitive_count_; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
- return *cur_entry;
+ return *down_cast<UninitializedType*>(cur_entry);
}
}
entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size());
@@ -442,16 +475,16 @@
return *entry;
}
-const RegType& RegTypeCache::FromCat1Const(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];
if (cur_entry->klass_ == NULL && cur_entry->IsConstant() &&
cur_entry->IsPreciseConstant() == precise &&
(down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) {
- return *cur_entry;
+ return *down_cast<ConstantType*>(cur_entry);
}
}
- RegType* entry;
+ ConstantType* entry;
if (precise) {
entry = new PreciseConstType(value, entries_.size());
} else {
@@ -461,15 +494,15 @@
return *entry;
}
-const RegType& 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];
if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) &&
(down_cast<ConstantType*>(cur_entry))->ConstantValueLo() == value) {
- return *cur_entry;
+ return *down_cast<ConstantType*>(cur_entry);
}
}
- RegType* entry;
+ ConstantType* entry;
if (precise) {
entry = new PreciseConstLoType(value, entries_.size());
} else {
@@ -479,15 +512,15 @@
return *entry;
}
-const RegType& 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];
if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) &&
(down_cast<ConstantType*>(cur_entry))->ConstantValueHi() == value) {
- return *cur_entry;
+ return *down_cast<ConstantType*>(cur_entry);
}
}
- RegType* entry;
+ ConstantType* entry;
if (precise) {
entry = new PreciseConstHiType(value, entries_.size());
} else {
@@ -498,8 +531,9 @@
}
const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) {
- CHECK(array.IsArrayTypes());
- if (array.IsUnresolvedTypes()) {
+ if (!array.IsArrayTypes()) {
+ return Conflict();
+ } else if (array.IsUnresolvedTypes()) {
const std::string& descriptor(array.GetDescriptor());
const std::string component(descriptor.substr(1, descriptor.size() - 1));
return FromDescriptor(loader, component.c_str(), false);
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index 77f5893..a9f8bff 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -35,19 +35,18 @@
class RegType;
-const size_t kNumPrimitives = 12;
class RegTypeCache {
public:
explicit RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
entries_.reserve(64);
- FillPrimitiveTypes();
+ FillPrimitiveAndSmallConstantTypes();
}
~RegTypeCache();
static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (!RegTypeCache::primitive_initialized_) {
CHECK_EQ(RegTypeCache::primitive_count_, 0);
- CreatePrimitiveTypes();
- CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitives);
+ CreatePrimitiveAndSmallConstantTypes();
+ CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitivesAndSmallConstants);
RegTypeCache::primitive_initialized_ = true;
}
}
@@ -55,17 +54,13 @@
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_);
- template <class Type>
- static Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void FillPrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat1Const(int32_t value, bool precise)
+ const ConstantType& FromCat1Const(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat2ConstLo(int32_t value, bool precise)
+ const ConstantType& FromCat2ConstLo(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat2ConstHi(int32_t value, bool precise)
+ const ConstantType& FromCat2ConstHi(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -129,34 +124,56 @@
const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return From(NULL, "Ljava/lang/Object;", precise);
}
- const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc)
+ 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.
- const RegType& UninitializedThisArgument(const RegType& type)
+ const UninitializedType& UninitializedThisArgument(const RegType& type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromUninitialized(const RegType& uninit_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& ByteConstant() 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 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_);
const RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
private:
- std::vector<RegType*> entries_;
- static bool primitive_initialized_;
- static uint16_t primitive_start_;
- static uint16_t primitive_count_;
- static void CreatePrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Whether or not we're allowed to load classes.
- const bool can_load_classes_;
+ void FillPrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void ClearException();
bool MatchDescriptor(size_t idx, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <class Type>
+ static 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 constexpr size_t kNumPrimitivesAndSmallConstants =
+ 12 + (kMaxSmallConstant - kMinSmallConstant + 1);
+
+ // Have the well known global primitives been created?
+ static bool primitive_initialized_;
+
+ // Number of well known primitives that will be copied into a RegTypeCache upon construction.
+ static uint16_t primitive_count_;
+
+ // Whether or not we're allowed to load classes.
+ const bool can_load_classes_;
+
DISALLOW_COPY_AND_ASSIGN(RegTypeCache);
};
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index a615cc1..31b0113 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -456,8 +456,7 @@
bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) {
bool changed = false;
- CHECK(NULL != incoming_line);
- CHECK(NULL != line_.get());
+ DCHECK(incoming_line != nullptr);
for (size_t idx = 0; idx < num_regs_; idx++) {
if (line_[idx] != incoming_line->line_[idx]) {
const RegType& incoming_reg_type = incoming_line->GetRegisterType(idx);
@@ -468,8 +467,8 @@
}
}
if (monitors_.size() != incoming_line->monitors_.size()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "mismatched stack depths (depth="
- << MonitorStackDepth() << ", incoming depth=" << incoming_line->MonitorStackDepth() << ")";
+ 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);
@@ -478,8 +477,8 @@
if (depths == 0 || incoming_depths == 0) {
reg_to_lock_depths_.erase(idx);
} else {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "mismatched stack depths for register v" << idx
- << ": " << depths << " != " << incoming_depths;
+ LOG(WARNING) << "mismatched stack depths for register v" << idx
+ << ": " << depths << " != " << incoming_depths;
break;
}
}
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index f19dcca..8b2dadb 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_H_
#define ART_RUNTIME_VERIFIER_REGISTER_LINE_H_
-#include <deque>
#include <vector>
#include "dex_instruction.h"
@@ -51,12 +50,10 @@
// stack of entered monitors (identified by code unit offset).
class RegisterLine {
public:
- RegisterLine(size_t num_regs, MethodVerifier* verifier)
- : line_(new uint16_t[num_regs]),
- verifier_(verifier),
- num_regs_(num_regs) {
- memset(line_.get(), 0, num_regs_ * sizeof(uint16_t));
- SetResultTypeToUnknown();
+ static RegisterLine* Create(size_t num_regs, MethodVerifier* verifier) {
+ uint8_t* memory = new uint8_t[sizeof(RegisterLine) + (num_regs * sizeof(uint16_t))];
+ RegisterLine* rl = new (memory) RegisterLine(num_regs, verifier);
+ return rl;
}
// Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst".
@@ -108,7 +105,7 @@
void CopyFromLine(const RegisterLine* src) {
DCHECK_EQ(num_regs_, src->num_regs_);
- memcpy(line_.get(), src->line_.get(), num_regs_ * sizeof(uint16_t));
+ memcpy(&line_, &src->line_, num_regs_ * sizeof(uint16_t));
monitors_ = src->monitors_;
reg_to_lock_depths_ = src->reg_to_lock_depths_;
}
@@ -116,7 +113,7 @@
std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void FillWithGarbage() {
- memset(line_.get(), 0xf1, num_regs_ * sizeof(uint16_t));
+ memset(&line_, 0xf1, num_regs_ * sizeof(uint16_t));
while (!monitors_.empty()) {
monitors_.pop_back();
}
@@ -161,7 +158,7 @@
int CompareLine(const RegisterLine* line2) const {
DCHECK(monitors_ == line2->monitors_);
// TODO: DCHECK(reg_to_lock_depths_ == line2->reg_to_lock_depths_);
- return memcmp(line_.get(), line2->line_.get(), num_regs_ * sizeof(uint16_t));
+ return memcmp(&line_, &line2->line_, num_regs_ * sizeof(uint16_t));
}
size_t NumRegs() const {
@@ -339,23 +336,30 @@
reg_to_lock_depths_.erase(reg);
}
+ RegisterLine(size_t num_regs, MethodVerifier* verifier)
+ : verifier_(verifier),
+ num_regs_(num_regs) {
+ memset(&line_, 0, num_regs_ * sizeof(uint16_t));
+ SetResultTypeToUnknown();
+ }
+
// Storage for the result register's type, valid after an invocation
uint16_t result_[2];
- // An array of RegType Ids associated with each dex register
- UniquePtr<uint16_t[]> line_;
-
// Back link to the verifier
MethodVerifier* verifier_;
// Length of reg_types_
const uint32_t num_regs_;
// A stack of monitor enter locations
- std::deque<uint32_t> monitors_;
+ std::vector<uint32_t> monitors_;
// A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor
// stack we verify that monitor-enter/exit are correctly nested. That is, if there was a
// monitor-enter on v5 and then on v6, we expect the monitor-exit to be on v6 then on v5
SafeMap<uint32_t, uint32_t> reg_to_lock_depths_;
+
+ // An array of RegType Ids associated with each dex register.
+ uint16_t line_[0];
};
std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 8de020a..e3946f7 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -21,7 +21,7 @@
#include "base/logging.h"
#include "mirror/class.h"
#include "ScopedLocalRef.h"
-#include "thread.h"
+#include "thread-inl.h"
namespace art {
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index 8e09e78..db273ec 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -19,10 +19,12 @@
#include <vector>
#include <fcntl.h>
+#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "UniquePtr.h"
@@ -247,35 +249,38 @@
return true;
}
-bool ZipEntry::ExtractToFile(File& file) {
+bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
uint32_t length = GetUncompressedLength();
int result = TEMP_FAILURE_RETRY(ftruncate(file.Fd(), length));
if (result == -1) {
- PLOG(WARNING) << "Zip: failed to ftruncate " << file.GetPath() << " to length " << length;
+ *error_msg = StringPrintf("Zip: failed to ftruncate '%s' to length %ud", file.GetPath().c_str(),
+ length);
return false;
}
- UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0));
+ UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0,
+ file.GetPath().c_str(), error_msg));
if (map.get() == NULL) {
- LOG(WARNING) << "Zip: failed to mmap space for " << file.GetPath();
+ *error_msg = StringPrintf("Zip: failed to mmap space for '%s': %s", file.GetPath().c_str(),
+ error_msg->c_str());
return false;
}
- return ExtractToMemory(map->Begin(), map->Size());
+ return ExtractToMemory(map->Begin(), map->Size(), error_msg);
}
-bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size) {
+bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg) {
// If size is zero, data offset will be meaningless, so bail out early.
if (size == 0) {
return true;
}
off64_t data_offset = GetDataOffset();
if (data_offset == -1) {
- LOG(WARNING) << "Zip: data_offset=" << data_offset;
+ *error_msg = StringPrintf("Zip: data_offset=%lld", data_offset);
return false;
}
if (lseek64(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) {
- PLOG(WARNING) << "Zip: lseek to data at " << data_offset << " failed";
+ *error_msg = StringPrintf("Zip: lseek to data at %lld failed", data_offset);
return false;
}
@@ -288,25 +293,25 @@
return InflateToMemory(begin, size, zip_archive_->fd_,
GetUncompressedLength(), GetCompressedLength());
default:
- LOG(WARNING) << "Zip: unknown compression method " << std::hex << GetCompressionMethod();
+ *error_msg = StringPrintf("Zip: unknown compression method 0x%x", GetCompressionMethod());
return false;
}
}
-MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename) {
+MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error_msg) {
std::string name(entry_filename);
name += " extracted in memory from ";
name += entry_filename;
UniquePtr<MemMap> map(MemMap::MapAnonymous(name.c_str(),
NULL,
GetUncompressedLength(),
- PROT_READ | PROT_WRITE));
- if (map.get() == NULL) {
- LOG(ERROR) << "Zip: mmap for '" << entry_filename << "' failed";
+ PROT_READ | PROT_WRITE, error_msg));
+ if (map.get() == nullptr) {
+ DCHECK(!error_msg->empty());
return NULL;
}
- bool success = ExtractToMemory(map->Begin(), map->Size());
+ bool success = ExtractToMemory(map->Begin(), map->Size(), error_msg);
if (!success) {
LOG(ERROR) << "Zip: Failed to extract '" << entry_filename << "' to memory";
return NULL;
@@ -329,27 +334,25 @@
}
}
-ZipArchive* ZipArchive::Open(const std::string& filename) {
- DCHECK(!filename.empty());
- int fd = open(filename.c_str(), O_RDONLY, 0);
+ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) {
+ DCHECK(filename != nullptr);
+ int fd = open(filename, O_RDONLY, 0);
if (fd == -1) {
- PLOG(WARNING) << "Unable to open '" << filename << "'";
+ *error_msg = StringPrintf("Zip: unable to open '%s': %s", filename, strerror(errno));
return NULL;
}
- return OpenFromFd(fd);
+ return OpenFromFd(fd, filename, error_msg);
}
-ZipArchive* ZipArchive::OpenFromFd(int fd) {
+ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
SetCloseOnExec(fd);
- UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd));
- if (zip_archive.get() == NULL) {
- return NULL;
- }
- if (!zip_archive->MapCentralDirectory()) {
+ UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd, filename));
+ CHECK(zip_archive.get() != nullptr);
+ if (!zip_archive->MapCentralDirectory(error_msg)) {
zip_archive->Close();
return NULL;
}
- if (!zip_archive->Parse()) {
+ if (!zip_archive->Parse(error_msg)) {
zip_archive->Close();
return NULL;
}
@@ -374,19 +377,28 @@
dir_offset_ = 0;
}
+std::string ZipArchive::ErrorStringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string result(StringPrintf("Zip '%s' : ", filename_.c_str()));
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
// Find the zip Central Directory and memory-map it.
//
// On success, returns true after populating fields from the EOCD area:
// num_entries_
// dir_offset_
// dir_map_
-bool ZipArchive::MapCentralDirectory() {
+bool ZipArchive::MapCentralDirectory(std::string* error_msg) {
/*
* Get and test file length.
*/
off64_t file_length = lseek64(fd_, 0, SEEK_END);
if (file_length < kEOCDLen) {
- LOG(WARNING) << "Zip: length " << file_length << " is too small to be zip";
+ *error_msg = ErrorStringPrintf("length %lld is too small to be zip", file_length);
return false;
}
@@ -396,27 +408,26 @@
}
UniquePtr<uint8_t[]> scan_buf(new uint8_t[read_amount]);
- if (scan_buf.get() == NULL) {
- return false;
- }
+ CHECK(scan_buf.get() != nullptr);
/*
* Make sure this is a Zip archive.
*/
if (lseek64(fd_, 0, SEEK_SET) != 0) {
- PLOG(WARNING) << "seek to start failed: ";
+ *error_msg = ErrorStringPrintf("seek to start failed: %s", strerror(errno));
return false;
}
ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), sizeof(int32_t)));
if (actual != static_cast<ssize_t>(sizeof(int32_t))) {
- PLOG(INFO) << "couldn't read first signature from zip archive: ";
+ *error_msg = ErrorStringPrintf("couldn\'t read first signature from zip archive: %s",
+ strerror(errno));
return false;
}
unsigned int header = Le32ToHost(scan_buf.get());
if (header != kLFHSignature) {
- LOG(VERBOSE) << "Not a Zip archive (found " << std::hex << header << ")";
+ *error_msg = ErrorStringPrintf("not a zip archive (found 0x%x)", header);
return false;
}
@@ -433,12 +444,13 @@
off64_t search_start = file_length - read_amount;
if (lseek64(fd_, search_start, SEEK_SET) != search_start) {
- PLOG(WARNING) << "Zip: seek " << search_start << " failed";
+ *error_msg = ErrorStringPrintf("seek %lld failed: %s", search_start, strerror(errno));
return false;
}
actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount));
if (actual != static_cast<ssize_t>(read_amount)) {
- PLOG(WARNING) << "Zip: read " << actual << ", expected " << read_amount << ". failed";
+ *error_msg = ErrorStringPrintf("read %lld, expected %zd. %s", search_start, read_amount,
+ strerror(errno));
return false;
}
@@ -454,14 +466,14 @@
}
}
if (i < 0) {
- LOG(WARNING) << "Zip: EOCD not found, not a zip file";
+ *error_msg = ErrorStringPrintf("EOCD not found, not a zip file");
return false;
}
off64_t eocd_offset = search_start + i;
const byte* eocd_ptr = scan_buf.get() + i;
- DCHECK(eocd_offset < file_length);
+ CHECK(eocd_offset < file_length);
// Grab the CD offset and size, and the number of entries in the
// archive. Verify that they look reasonable.
@@ -474,29 +486,28 @@
uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize);
if ((uint64_t) dir_offset + (uint64_t) dir_size > (uint64_t) eocd_offset) {
- LOG(WARNING) << "Zip: bad offsets ("
- << "dir=" << dir_offset << ", "
- << "size=" << dir_size << ", "
- << "eocd=" << eocd_offset << ")";
+ *error_msg = ErrorStringPrintf("bad offsets (dir=%ud, size=%ud, eocd=%lld)",
+ dir_offset, dir_size, eocd_offset);
return false;
}
if (num_entries == 0) {
- LOG(WARNING) << "Zip: empty archive?";
+ *error_msg = ErrorStringPrintf("empty archive?");
return false;
} else if (num_entries != total_num_entries || disk_number != 0 || disk_with_central_dir != 0) {
- LOG(WARNING) << "spanned archives not supported";
+ *error_msg = ErrorStringPrintf("spanned archives not supported");
return false;
}
// Check to see if comment is a sane size
if ((comment_size > (file_length - kEOCDLen))
|| (eocd_offset > (file_length - kEOCDLen) - comment_size)) {
- LOG(WARNING) << "comment size runs off end of file";
+ *error_msg = ErrorStringPrintf("comment size runs off end of file");
return false;
}
// It all looks good. Create a mapping for the CD.
- dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset));
+ dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset,
+ filename_.c_str(), error_msg));
if (dir_map_.get() == NULL) {
return false;
}
@@ -506,7 +517,7 @@
return true;
}
-bool ZipArchive::Parse() {
+bool ZipArchive::Parse(std::string* error_msg) {
const byte* cd_ptr = dir_map_->Begin();
size_t cd_length = dir_map_->Size();
@@ -515,23 +526,23 @@
const byte* ptr = cd_ptr;
for (int i = 0; i < num_entries_; i++) {
if (Le32ToHost(ptr) != kCDESignature) {
- LOG(WARNING) << "Zip: missed a central dir sig (at " << i << ")";
+ *error_msg = ErrorStringPrintf("missed a central dir sig (at %d)", i);
return false;
}
if (ptr + kCDELen > cd_ptr + cd_length) {
- LOG(WARNING) << "Zip: ran off the end (at " << i << ")";
+ *error_msg = ErrorStringPrintf("ran off the end (at %d)", i);
return false;
}
int64_t local_hdr_offset = Le32ToHost(ptr + kCDELocalOffset);
if (local_hdr_offset >= dir_offset_) {
- LOG(WARNING) << "Zip: bad LFH offset " << local_hdr_offset << " at entry " << i;
+ *error_msg = ErrorStringPrintf("bad LFH offset %lld at entry %d", local_hdr_offset, i);
return false;
}
uint16_t gpbf = Le16ToHost(ptr + kCDEGPBFlags);
if ((gpbf & kGPFUnsupportedMask) != 0) {
- LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf;
+ *error_msg = ErrorStringPrintf("invalid general purpose bit flag %x", gpbf);
return false;
}
@@ -544,16 +555,15 @@
// Check name for NULL characters
if (memchr(name, 0, name_len) != NULL) {
- LOG(WARNING) << "Filename contains NUL byte";
+ *error_msg = ErrorStringPrintf("filename contains NUL byte");
return false;
}
dir_entries_.Put(StringPiece(name, name_len), ptr);
ptr += kCDELen + name_len + extra_len + comment_len;
if (ptr > cd_ptr + cd_length) {
- LOG(WARNING) << "Zip: bad CD advance "
- << "(" << ptr << " vs " << (cd_ptr + cd_length) << ") "
- << "at entry " << i;
+ *error_msg = ErrorStringPrintf("bad CD advance (%p vs %p) at entry %d",
+ ptr, cd_ptr + cd_length, i);
return false;
}
}
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index d9ccba2..8ff952b 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <zlib.h>
+#include <string>
#include "base/logging.h"
#include "base/stringpiece.h"
@@ -36,9 +37,9 @@
class ZipEntry {
public:
- bool ExtractToFile(File& file);
- bool ExtractToMemory(uint8_t* begin, size_t size);
- MemMap* ExtractToMemMap(const char* entry_filename);
+ bool ExtractToFile(File& file, std::string* error_msg);
+ bool ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg);
+ MemMap* ExtractToMemMap(const char* entry_filename, std::string* error_msg);
uint32_t GetUncompressedLength();
uint32_t GetCrc32();
@@ -109,8 +110,8 @@
static const int32_t kGPFUnsupportedMask = (kGPFEncryptedFlag);
// return new ZipArchive instance on success, NULL on error.
- static ZipArchive* Open(const std::string& filename);
- static ZipArchive* OpenFromFd(int fd);
+ static ZipArchive* Open(const char* filename, std::string* error_msg);
+ static ZipArchive* OpenFromFd(int fd, const char* filename, std::string* error_msg);
ZipEntry* Find(const char* name) const;
@@ -119,11 +120,14 @@
}
private:
- explicit ZipArchive(int fd) : fd_(fd), num_entries_(0), dir_offset_(0) {}
+ explicit ZipArchive(int fd, const char* filename)
+ : fd_(fd), num_entries_(0), dir_offset_(0), filename_(filename) {}
- bool MapCentralDirectory();
- bool Parse();
+ bool MapCentralDirectory(std::string* error_msg);
+ bool Parse(std::string* error_msg);
void Close();
+ std::string ErrorStringPrintf(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
int fd_;
uint16_t num_entries_;
@@ -131,6 +135,8 @@
UniquePtr<MemMap> dir_map_;
typedef SafeMap<StringPiece, const byte*> DirEntries;
DirEntries dir_entries_;
+ // Containing file for error reporting.
+ const std::string filename_;
friend class ZipEntry;
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index 9bdc24b..622dc89 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -29,8 +29,10 @@
class ZipArchiveTest : public CommonTest {};
TEST_F(ZipArchiveTest, FindAndExtract) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName()));
- ASSERT_TRUE(zip_archive.get() != false);
+ std::string error_msg;
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName().c_str(), &error_msg));
+ ASSERT_TRUE(zip_archive.get() != false) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
UniquePtr<ZipEntry> zip_entry(zip_archive->Find("classes.dex"));
ASSERT_TRUE(zip_entry.get() != false);
@@ -38,8 +40,9 @@
ASSERT_NE(-1, tmp.GetFd());
UniquePtr<File> file(new File(tmp.GetFd(), tmp.GetFilename()));
ASSERT_TRUE(file.get() != NULL);
- bool success = zip_entry->ExtractToFile(*file);
- ASSERT_TRUE(success);
+ bool success = zip_entry->ExtractToFile(*file, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
file.reset(NULL);
uint32_t computed_crc = crc32(0L, Z_NULL, 0);
diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt
index eafaf1d..13e3a28 100644
--- a/test/044-proxy/expected.txt
+++ b/test/044-proxy/expected.txt
@@ -82,7 +82,7 @@
Invoke public abstract void InterfaceW1.bothThrowBase() throws BaseException,SubException,SubSubException
(no args)
Got expected exception
-Proxy methods: [public final boolean $Proxy7.equals(java.lang.Object), public final java.lang.Object $Proxy7.foo(), public final java.lang.String $Proxy7.foo(), public final int $Proxy7.hashCode(), public final java.lang.String $Proxy7.toString()]
+Proxy methods: [public final boolean $Proxy3.equals(java.lang.Object), public final java.lang.Object $Proxy3.foo(), public final java.lang.String $Proxy3.foo(), public final int $Proxy3.hashCode(), public final java.lang.String $Proxy3.toString()]
Invocation of public abstract java.lang.String NarrowingTest$I2.foo()
Invoking foo using I2 type: hello
Invocation of public abstract java.lang.Object NarrowingTest$I1.foo()
diff --git a/test/055-enum-performance/run b/test/055-enum-performance/run
index 1436ce2..e27a622 100755
--- a/test/055-enum-performance/run
+++ b/test/055-enum-performance/run
@@ -14,5 +14,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# As this is a performance test we always run -O
-exec ${RUN} -O "$@"
+# As this is a performance test we always use the non-debug build.
+exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 967f167..3d87ebc 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -35,7 +35,7 @@
62 (class java.lang.Long)
14 (class java.lang.Short)
[public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)]
-[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR]
+[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID]
[void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
diff --git a/test/Android.mk b/test/Android.mk
index e4885f3..1bf6074 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -23,8 +23,8 @@
TEST_DEX_DIRECTORIES := \
AbstractMethod \
AllFields \
- CreateMethodSignature \
ExceptionHandle \
+ GetMethodSignature \
Interfaces \
Main \
MyClass \
@@ -44,6 +44,7 @@
Main \
HelloWorld \
\
+ InterfaceTest \
JniTest \
NativeAllocations \
ParallelGC \
diff --git a/test/CreateMethodSignature/CreateMethodSignature.java b/test/GetMethodSignature/GetMethodSignature.java
similarity index 86%
rename from test/CreateMethodSignature/CreateMethodSignature.java
rename to test/GetMethodSignature/GetMethodSignature.java
index f6cd6ae..c2ba948 100644
--- a/test/CreateMethodSignature/CreateMethodSignature.java
+++ b/test/GetMethodSignature/GetMethodSignature.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-class CreateMethodSignature {
+class GetMethodSignature {
Float m1(int a, double b, long c, Object d) { return null; }
- CreateMethodSignature m2(boolean x, short y, char z) { return null; }
+ GetMethodSignature m2(boolean x, short y, char z) { return null; }
}
diff --git a/test/InterfaceTest/InterfaceTest.java b/test/InterfaceTest/InterfaceTest.java
new file mode 100644
index 0000000..ed18eb3
--- /dev/null
+++ b/test/InterfaceTest/InterfaceTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+import java.util.Map;
+import java.util.HashMap;
+
+class InterfaceTest {
+
+ public static long test_virtual(HashMap map) {
+ Integer intobj = new Integer(0);
+ String s = "asdf";
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < 1000000; i++) {
+ map.put(intobj, s);
+ }
+ long end = System.currentTimeMillis();
+ return (end - start);
+ }
+
+ public static long test_interface(Map map) {
+ Integer intobj = new Integer(0);
+ String s = "asdf";
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < 1000000; i++) {
+ map.put(intobj, s);
+ }
+ long end = System.currentTimeMillis();
+ return (end - start);
+ }
+
+ public static void main(String[] args) {
+ HashMap hashmap = new HashMap();
+ long elapsed = test_virtual(hashmap);
+ System.logI("virtual map put: " + elapsed);
+ hashmap.clear();
+
+ elapsed = test_interface(hashmap);
+ System.logI("interface map put: " + elapsed);
+ }
+}
diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java
index 431056a..7014ef9 100644
--- a/test/JniTest/JniTest.java
+++ b/test/JniTest/JniTest.java
@@ -18,7 +18,28 @@
public static void main(String[] args) {
System.loadLibrary("arttest");
testFindClassOnAttachedNativeThread();
+ testCallStaticVoidMethodOnSubClass();
}
private static native void testFindClassOnAttachedNativeThread();
+
+ private static void testCallStaticVoidMethodOnSubClass() {
+ testCallStaticVoidMethodOnSubClassNative();
+ if (!testCallStaticVoidMethodOnSubClass_SuperClass.executed) {
+ throw new AssertionError();
+ }
+ }
+
+ private static native void testCallStaticVoidMethodOnSubClassNative();
+
+ private static class testCallStaticVoidMethodOnSubClass_SuperClass {
+ private static boolean executed = false;
+ private static void execute() {
+ executed = true;
+ }
+ }
+
+ private static class testCallStaticVoidMethodOnSubClass_SubClass
+ extends testCallStaticVoidMethodOnSubClass_SuperClass {
+ }
}
diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc
index ed69d39..72a3309 100644
--- a/test/JniTest/jni_test.cc
+++ b/test/JniTest/jni_test.cc
@@ -54,6 +54,7 @@
return NULL;
}
+// http://b/10994325
extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindClassOnAttachedNativeThread(JNIEnv*,
jclass) {
pthread_t pthread;
@@ -65,3 +66,18 @@
int pthread_join_result = pthread_join(pthread, NULL);
assert(pthread_join_result == 0);
}
+
+// http://b/11243757
+extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
+ jclass) {
+ jclass super_class = env->FindClass("JniTest$testCallStaticVoidMethodOnSubClass_SuperClass");
+ assert(super_class != NULL);
+
+ jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V");
+ assert(execute != NULL);
+
+ jclass sub_class = env->FindClass("JniTest$testCallStaticVoidMethodOnSubClass_SubClass");
+ assert(sub_class != NULL);
+
+ env->CallStaticVoidMethod(sub_class, execute);
+}
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 357fb5a..da74532 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -9,7 +9,6 @@
fi
}
-LIB="libartd.so"
DEBUGGER="n"
GDB="n"
INTERPRETER="n"
@@ -23,11 +22,17 @@
if [ "x$1" = "x--quiet" ]; then
QUIET="y"
shift
- elif [ "x$1" = "x-lib" ]; then
+ elif [ "x$1" = "x--lib" ]; then
shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --lib" 1>&2
+ exit 1
+ fi
LIB="$1"
- elif [ "x$1" = "x-O" ]; then
- LIB="libart.so"
+ shift
+ elif [ "x$1" = "x--boot" ]; then
+ shift
+ BOOT_OPT="$1"
shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"
@@ -38,6 +43,10 @@
shift
elif [ "x$1" = "x--invoke-with" ]; then
shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --invoke-with" 1>&2
+ exit 1
+ fi
if [ "x$INVOKE_WITH" = "x" ]; then
INVOKE_WITH="$1"
else
@@ -106,7 +115,9 @@
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+if [ "$DEV_MODE" = "y" ]; then
+ echo $cmdline "$@"
+fi
+
cd $ANDROID_BUILD_TOP
-$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB -Ximage:$ANDROID_ROOT/framework/core.art \
- $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS \
- -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@"
+$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@"
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index cc28592..ff75d32 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -9,7 +9,6 @@
fi
}
-LIB="libartd.so"
GDB="n"
DEBUGGER="n"
INTERPRETER="n"
@@ -24,11 +23,17 @@
if [ "x$1" = "x--quiet" ]; then
QUIET="y"
shift
- elif [ "x$1" = "x-lib" ]; then
+ elif [ "x$1" = "x--lib" ]; then
shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --lib" 1>&2
+ exit 1
+ fi
LIB="$1"
- elif [ "x$1" = "x-O" ]; then
- LIB="libart.so"
+ shift
+ elif [ "x$1" = "x--boot" ]; then
+ shift
+ BOOT_OPT="$1"
shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"
@@ -49,6 +54,10 @@
shift
elif [ "x$1" = "x--invoke-with" ]; then
shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --invoke-with" 1>&2
+ exit 1
+ fi
if [ "x$INVOKE_WITH" = "x" ]; then
INVOKE_WITH="$1"
else
@@ -132,7 +141,7 @@
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
cmdline="cd $DEX_LOCATION && mkdir dalvik-cache && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
- $INVOKE_WITH $gdb dalvikvm $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS -Ximage:/data/art-test/core.art -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+ $INVOKE_WITH $gdb dalvikvm $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $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-test b/test/run-test
index 11dcfc5..f706110 100755
--- a/test/run-test
+++ b/test/run-test
@@ -58,12 +58,14 @@
expected="expected.txt"
output="output.txt"
build_output="build-output.txt"
+lib="libartd.so"
run_args="--quiet"
target_mode="yes"
dev_mode="no"
update_mode="no"
debug_mode="no"
+dalvik_mode="no"
usage="no"
build_only="no"
@@ -79,7 +81,16 @@
NEED_DEX="false"
shift
elif [ "x$1" = "x-O" ]; then
- run_args="${run_args} -O"
+ lib="libart.so"
+ shift
+ elif [ "x$1" = "x--dalvik" ]; then
+ lib="libdvm.so"
+ dalvik_mode="yes"
+ shift
+ elif [ "x$1" = "x--image" ]; then
+ shift
+ image="$1"
+ run_args="${run_args} --image $image"
shift
elif [ "x$1" = "x--debug" ]; then
run_args="${run_args} --debug"
@@ -106,6 +117,11 @@
elif [ "x$1" = "x--invoke-with" ]; then
shift
what="$1"
+ if [ "x$what" = "x" ]; then
+ echo "$0 missing argument to --invoke-with" 1>&2
+ usage="yes"
+ break
+ fi
run_args="${run_args} --invoke-with ${what}"
shift
elif [ "x$1" = "x--dev" ]; then
@@ -118,6 +134,11 @@
elif [ "x$1" = "x--output-path" ]; then
shift
tmp_dir=$1
+ if [ "x$tmp_dir" = "x" ]; then
+ echo "$0 missing argument to --output-path" 1>&2
+ usage="yes"
+ break
+ fi
shift
elif [ "x$1" = "x--update" ]; then
update_mode="yes"
@@ -134,6 +155,24 @@
fi
done
+run_args="${run_args} --lib $lib"
+
+if [ "$dalvik_mode" = "no" ]; then
+ if [ "$target_mode" = "no" ]; then
+ run_args="${run_args} --boot -Ximage:${ANDROID_HOST_OUT}/framework/core.art"
+ else
+ run_args="${run_args} --boot -Ximage:/data/art-test/core.art"
+ fi
+else
+ if [ "$target_mode" = "no" ]; then
+ framework="${OUT}/system/framework"
+ bpath="${framework}/core.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/core-junit.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
+ run_args="${run_args} --boot -Xbootclasspath:${bpath}"
+ else
+ true # defaults to using target BOOTCLASSPATH
+ fi
+fi
+
if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
echo "--dev and --update are mutually exclusive" 1>&2
usage="yes"
@@ -185,6 +224,7 @@
echo " other runtime options are ignored."
echo " --host Use the host-mode virtual machine."
echo " --invoke-with Pass --invoke-with option to runtime."
+ echo " --dalvik Use Dalvik (off by default)."
echo " --jvm Use a host-local RI virtual machine."
echo " --output-path [path] Location where to store the build" \
"files."
@@ -237,7 +277,7 @@
if [ "$build_exit" = '0' ]; then
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" 2>&1
- run_exit="$?"
+ run_exit="$?"
echo "run exit status: $run_exit" 1>&2
if [ "$run_exit" = "0" ]; then
good="yes"
@@ -269,7 +309,7 @@
fi
fi
# Clean up extraneous files that are not used by tests.
- find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$build_output\|$expected\)" | xargs rm -rf
+ find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$output\|$expected\)" | xargs rm -rf
exit 0
else
"./${build}" >"$build_output" 2>&1