Merge "Fix SEGV when dumping MIR CFG"
diff --git a/Android.mk b/Android.mk
index 0492d3c..612fc40 100644
--- a/Android.mk
+++ b/Android.mk
@@ -264,9 +264,9 @@
.PHONY: oat-target-$(1)
oat-target-$(1): $$(OUT_OAT_FILE)
-$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY)
+$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(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) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system
+ $(DEX2OAT) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --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
@@ -275,12 +275,12 @@
$(foreach file,\
$(filter-out\
- $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(TARGET_BOOT_JARS))),\
+ $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(LIBART_TARGET_BOOT_JARS))),\
$(wildcard $(TARGET_OUT_APPS)/*.apk) $(wildcard $(TARGET_OUT_JAVA_LIBRARIES)/*.jar)),\
$(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file)))))
.PHONY: oat-target
-oat-target: $(ART_TARGET_DEPENDENCIES) $(TARGET_BOOT_OAT_OUT) $(OAT_TARGET_TARGETS)
+oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_TARGETS)
.PHONY: oat-target-sync
oat-target-sync: oat-target
@@ -303,6 +303,11 @@
ART_DUMP_OAT_PATH ?= $(OUT_DIR)
+OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX)
+OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX)
+# TODO: for now, override with debug version for better error reporting
+OATDUMP := $(OATDUMPD)
+
.PHONY: dump-oat
dump-oat: dump-oat-core dump-oat-boot
@@ -325,14 +330,14 @@
.PHONY: dump-oat-boot
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-dump-oat-boot: $(TARGET_BOOT_IMG_OUT) $(OATDUMP)
- $(OATDUMP) --image=$(TARGET_BOOT_IMG_OUT) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt
+dump-oat-boot: $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP)
+ $(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt
@echo Output in $(ART_DUMP_OAT_PATH)/boot.oatdump.txt
endif
.PHONY: dump-oat-Calculator
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(TARGET_BOOT_IMG_OUT) $(OATDUMP)
+dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP)
$(OATDUMP) --oat-file=$< --output=$(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt
@echo Output in $(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt
endif
diff --git a/build/Android.common.mk b/build/Android.common.mk
index a2c2e1a..30d7dcb 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -140,19 +140,10 @@
-DDYNAMIC_ANNOTATIONS_ENABLED=1 \
-UNDEBUG
-# start of image reserved address space
-IMG_HOST_BASE_ADDRESS := 0x60000000
-
-ifeq ($(TARGET_ARCH),mips)
-IMG_TARGET_BASE_ADDRESS := 0x30000000
-else
-IMG_TARGET_BASE_ADDRESS := 0x60000000
-endif
-
-ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS)
+ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_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)
+ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
ifeq ($(TARGET_CPU_SMP),true)
ART_TARGET_CFLAGS += -DANDROID_SMP=1
else
@@ -205,10 +196,6 @@
ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags)
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-PARALLEL_ART_COMPILE_JOBS := -j8
-endif
-
ART_BUILD_TARGET := false
ART_BUILD_HOST := false
ART_BUILD_NDEBUG := false
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index c04b38b..ec6efbc 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -14,31 +14,12 @@
# limitations under the License.
#
-# DEX2OAT defined in build/core/config.mk
-DEX2OATD := $(HOST_OUT_EXECUTABLES)/dex2oatd$(HOST_EXECUTABLE_SUFFIX)
-
-LIBART_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libart-compiler$(HOST_SHLIB_SUFFIX)
-LIBARTD_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libartd-compiler$(HOST_SHLIB_SUFFIX)
-
-# TODO: for now, override with debug version for better error reporting
-DEX2OAT := $(DEX2OATD)
-LIBART_COMPILER := $(LIBARTD_COMPILER)
-
-# 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)
-DEX2OAT_DEPENDENCY += $(LIBART_COMPILER)
-
-OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX)
-OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX)
-# TODO: for now, override with debug version for better error reporting
-OATDUMP := $(OATDUMPD)
-
-PRELOADED_CLASSES := frameworks/base/preloaded-classes
-
########################################################################
-# A smaller libcore only oat file
+# Rules to build a smaller "core" image to support core libraries
+# (that is, non-Android frameworks) testing on the host and target
+#
+# The main rules to build the default "boot" image are in
+# build/core/dex_preopt_libart.mk
TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle
HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS))
@@ -62,17 +43,17 @@
$(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 \
+ $(hide) $(DEX2OAT) --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) \
+ --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(LIBART_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 \
+ $(hide) $(DEX2OAT) --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) \
+ --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(LIBART_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)
@@ -100,32 +81,6 @@
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))
-TARGET_BOOT_JARS := $(foreach jar,$(TARGET_BOOT_JARS),$(patsubst core, core-libart,$(jar)))
-TARGET_BOOT_DEX_LOCATIONS := $(foreach jar,$(TARGET_BOOT_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar)
-TARGET_BOOT_DEX_FILES := $(foreach jar,$(TARGET_BOOT_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),,COMMON)/javalib.jar)
-
-TARGET_BOOT_IMG_OUT := $(DEFAULT_DEX_PREOPT_IMAGE)
-TARGET_BOOT_OAT_OUT := $(patsubst %.art,%.oat,$(TARGET_BOOT_IMG_OUT))
-TARGET_BOOT_OAT := $(subst $(PRODUCT_OUT),,$(TARGET_BOOT_OAT_OUT))
-TARGET_BOOT_OAT_UNSTRIPPED_OUT := $(TARGET_OUT_UNSTRIPPED)$(TARGET_BOOT_OAT)
-
-$(TARGET_BOOT_IMG_OUT): $(TARGET_BOOT_DEX_FILES) $(DEX2OAT_DEPENDENCY)
- @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) --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)
-
-$(TARGET_BOOT_OAT_OUT): $(TARGET_BOOT_OAT_UNSTRIPPED_OUT)
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
include $(CLEAR_VARS)
@@ -133,6 +88,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_BOOT_IMG_OUT) $(TARGET_BOOT_OAT_OUT)
+LOCAL_ADDITIONAL_DEPENDENCIES += $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE)
include $(BUILD_PHONY_PACKAGE)
endif
+endif
diff --git a/compiler/Android.mk b/compiler/Android.mk
index a2419d5..d9a573f 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -57,6 +57,7 @@
dex/frontend.cc \
dex/mir_graph.cc \
dex/mir_analysis.cc \
+ dex/verified_methods_data.cc \
dex/vreg_analysis.cc \
dex/ssa_transformation.cc \
driver/compiler_driver.cc \
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index d73f148..35d04ae 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -302,27 +302,6 @@
kThrowStackOverflow,
};
-enum SpecialCaseHandler {
- kNoHandler,
- kNullMethod,
- kConstFunction,
- kIGet,
- kIGetBoolean,
- kIGetObject,
- kIGetByte,
- kIGetChar,
- kIGetShort,
- kIGetWide,
- kIPut,
- kIPutBoolean,
- kIPutObject,
- kIPutByte,
- kIPutChar,
- kIPutShort,
- kIPutWide,
- kIdentity,
-};
-
enum DividePattern {
DivideNone,
Divide3,
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 6aabb2a..adfbf2f 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -63,21 +63,12 @@
LLVMInfo::~LLVMInfo() {
}
-QuickCompilerContext::QuickCompilerContext()
- : inliner_map_(new DexFileToMethodInlinerMap()) {
-}
-
-QuickCompilerContext::~QuickCompilerContext() {
-}
-
extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver) {
CHECK(driver.GetCompilerContext() == NULL);
- driver.SetCompilerContext(new QuickCompilerContext());
}
extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver) {
- delete reinterpret_cast<QuickCompilerContext*>(driver.GetCompilerContext());
- driver.SetCompilerContext(NULL);
+ CHECK(driver.GetCompilerContext() == NULL);
}
/* Default optimizer/debug setting for the compiler. */
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
index bcb8bf0..8eb6684 100644
--- a/compiler/dex/frontend.h
+++ b/compiler/dex/frontend.h
@@ -113,19 +113,6 @@
UniquePtr<art::llvm::IRBuilder> ir_builder_;
};
-class QuickCompilerContext {
- public:
- QuickCompilerContext();
- ~QuickCompilerContext();
-
- DexFileToMethodInlinerMap* GetInlinerMap() {
- return inliner_map_.get();
- }
-
- private:
- UniquePtr<DexFileToMethodInlinerMap> inliner_map_;
-};
-
struct CompilationUnit;
struct BasicBlock;
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 89af06e..ab55333 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -16,6 +16,8 @@
#include "compiler_internals.h"
#include "dataflow_iterator-inl.h"
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
namespace art {
@@ -1052,7 +1054,9 @@
}
// Filter 3: if this method is a special pattern, go ahead and emit the canned pattern.
- if (IsSpecialCase()) {
+ if (cu_->compiler_driver->GetMethodInlinerMap() != nullptr &&
+ cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
+ ->IsSpecial(cu_->method_idx)) {
return false;
}
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 2a18280..79edb87 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -20,40 +20,13 @@
#include "leb128.h"
#include "mir_graph.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/quick/dex_file_method_inliner.h"
+
namespace art {
#define MAX_PATTERN_LEN 5
-struct CodePattern {
- const Instruction::Code opcodes[MAX_PATTERN_LEN];
- const SpecialCaseHandler handler_code;
-};
-
-static const CodePattern special_patterns[] = {
- {{Instruction::RETURN_VOID}, kNullMethod},
- {{Instruction::CONST, Instruction::RETURN}, kConstFunction},
- {{Instruction::CONST_4, Instruction::RETURN}, kConstFunction},
- {{Instruction::CONST_4, Instruction::RETURN_OBJECT}, kConstFunction},
- {{Instruction::CONST_16, Instruction::RETURN}, kConstFunction},
- {{Instruction::IGET, Instruction:: RETURN}, kIGet},
- {{Instruction::IGET_BOOLEAN, Instruction::RETURN}, kIGetBoolean},
- {{Instruction::IGET_OBJECT, Instruction::RETURN_OBJECT}, kIGetObject},
- {{Instruction::IGET_BYTE, Instruction::RETURN}, kIGetByte},
- {{Instruction::IGET_CHAR, Instruction::RETURN}, kIGetChar},
- {{Instruction::IGET_SHORT, Instruction::RETURN}, kIGetShort},
- {{Instruction::IGET_WIDE, Instruction::RETURN_WIDE}, kIGetWide},
- {{Instruction::IPUT, Instruction::RETURN_VOID}, kIPut},
- {{Instruction::IPUT_BOOLEAN, Instruction::RETURN_VOID}, kIPutBoolean},
- {{Instruction::IPUT_OBJECT, Instruction::RETURN_VOID}, kIPutObject},
- {{Instruction::IPUT_BYTE, Instruction::RETURN_VOID}, kIPutByte},
- {{Instruction::IPUT_CHAR, Instruction::RETURN_VOID}, kIPutChar},
- {{Instruction::IPUT_SHORT, Instruction::RETURN_VOID}, kIPutShort},
- {{Instruction::IPUT_WIDE, Instruction::RETURN_VOID}, kIPutWide},
- {{Instruction::RETURN}, kIdentity},
- {{Instruction::RETURN_OBJECT}, kIdentity},
- {{Instruction::RETURN_WIDE}, kIdentity},
-};
-
const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = {
"Phi",
"Copy",
@@ -107,7 +80,6 @@
method_sreg_(0),
attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke.
checkstats_(NULL),
- special_case_(kNoHandler),
arena_(arena),
backward_branches_(0),
forward_branches_(0) {
@@ -612,13 +584,6 @@
/* Identify code range in try blocks and set up the empty catch blocks */
ProcessTryCatchBlocks();
- /* Set up for simple method detection */
- int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]);
- bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch));
- bool* dead_pattern =
- static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_patterns, ArenaAllocator::kAllocMisc));
- int pattern_pos = 0;
-
/* Parse all instructions and put them into containing basic blocks */
while (code_ptr < code_end) {
MIR *insn = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR));
@@ -631,23 +596,6 @@
opcode_count_[static_cast<int>(opcode)]++;
}
- /* Possible simple method? */
- if (live_pattern) {
- live_pattern = false;
- special_case_ = kNoHandler;
- for (int i = 0; i < num_patterns; i++) {
- if (!dead_pattern[i]) {
- if (special_patterns[i].opcodes[pattern_pos] == opcode) {
- live_pattern = true;
- special_case_ = special_patterns[i].handler_code;
- } else {
- dead_pattern[i] = true;
- }
- }
- }
- pattern_pos++;
- }
-
int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode);
uint64_t df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode];
@@ -736,6 +684,7 @@
cur_block = next_block;
}
}
+
if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
DumpCFG("/sdcard/1_post_parse_cfg/", true);
}
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 76c13f3..a80c32d 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -577,14 +577,6 @@
return reg_location_[method_sreg_];
}
- bool IsSpecialCase() {
- return special_case_ != kNoHandler;
- }
-
- SpecialCaseHandler GetSpecialCase() {
- return special_case_;
- }
-
bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
return ((target_bb_id != NullBasicBlockId) &&
(GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset));
@@ -789,7 +781,6 @@
int method_sreg_;
unsigned int attributes_;
Checkstats* checkstats_;
- SpecialCaseHandler special_case_;
ArenaAllocator* arena_;
int backward_branches_;
int forward_branches_;
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 23ea407..8226b24 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -18,6 +18,7 @@
#include "arm_lir.h"
#include "codegen_arm.h"
+#include "dex/quick/dex_file_method_inliner.h"
#include "dex/quick/mir_to_lir-inl.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -217,58 +218,43 @@
* Special-case code genration for simple non-throwing leaf methods.
*/
void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
- SpecialCaseHandler special_case) {
+ const InlineMethod& special) {
+ // TODO: Generate the method using only the data in special. (Requires FastInstance() field
+ // validation in DexFileMethodInliner::AnalyseIGetMethod()/AnalyseIPutMethod().)
+ DCHECK(special.flags & kInlineSpecial);
current_dalvik_offset_ = mir->offset;
MIR* next_mir = NULL;
- switch (special_case) {
- case kNullMethod:
+ switch (special.opcode) {
+ case kInlineOpNop:
DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID);
next_mir = mir;
break;
- case kConstFunction:
+ case kInlineOpConst:
ArmMir2Lir::GenPrintLabel(mir);
- LoadConstant(rARM_RET0, mir->dalvikInsn.vB);
+ LoadConstant(rARM_RET0, special.data);
next_mir = GetNextMir(&bb, mir);
break;
- case kIGet:
- next_mir = SpecialIGet(&bb, mir, kWord, false, false);
+ case kInlineOpIGet: {
+ InlineIGetIPutData data;
+ data.data = special.data;
+ OpSize op_size = static_cast<OpSize>(data.d.op_size);
+ DCHECK_NE(data.d.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong.
+ bool long_or_double = (data.d.op_size == kLong);
+ bool is_object = data.d.is_object;
+ next_mir = SpecialIGet(&bb, mir, op_size, long_or_double, is_object);
break;
- case kIGetBoolean:
- case kIGetByte:
- next_mir = SpecialIGet(&bb, mir, kUnsignedByte, false, false);
+ }
+ case kInlineOpIPut: {
+ InlineIGetIPutData data;
+ data.data = special.data;
+ OpSize op_size = static_cast<OpSize>(data.d.op_size);
+ DCHECK_NE(data.d.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong.
+ bool long_or_double = (data.d.op_size == kLong);
+ bool is_object = data.d.is_object;
+ next_mir = SpecialIPut(&bb, mir, op_size, long_or_double, is_object);
break;
- case kIGetObject:
- next_mir = SpecialIGet(&bb, mir, kWord, false, true);
- break;
- case kIGetChar:
- next_mir = SpecialIGet(&bb, mir, kUnsignedHalf, false, false);
- break;
- case kIGetShort:
- next_mir = SpecialIGet(&bb, mir, kSignedHalf, false, false);
- break;
- case kIGetWide:
- next_mir = SpecialIGet(&bb, mir, kLong, true, false);
- break;
- case kIPut:
- next_mir = SpecialIPut(&bb, mir, kWord, false, false);
- break;
- case kIPutBoolean:
- case kIPutByte:
- next_mir = SpecialIPut(&bb, mir, kUnsignedByte, false, false);
- break;
- case kIPutObject:
- next_mir = SpecialIPut(&bb, mir, kWord, false, true);
- break;
- case kIPutChar:
- next_mir = SpecialIPut(&bb, mir, kUnsignedHalf, false, false);
- break;
- case kIPutShort:
- next_mir = SpecialIPut(&bb, mir, kSignedHalf, false, false);
- break;
- case kIPutWide:
- next_mir = SpecialIPut(&bb, mir, kLong, true, false);
- break;
- case kIdentity:
+ }
+ case kInlineOpReturnArg:
next_mir = SpecialIdentity(mir);
break;
default:
@@ -276,7 +262,7 @@
}
if (next_mir != NULL) {
current_dalvik_offset_ = next_mir->offset;
- if (special_case != kIdentity) {
+ if (special.opcode != kInlineOpReturnArg) {
ArmMir2Lir::GenPrintLabel(next_mir);
}
NewLIR1(kThumbBx, rARM_LR);
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 25ddc94..c04f1d6 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -135,7 +135,7 @@
void GenNegFloat(RegLocation rl_dest, 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);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
// Required for target - single operation generators.
LIR* OpUnconditionalBranch(LIR* target);
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 5d78ed5..65286d5 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -19,6 +19,9 @@
#include "gc_map.h"
#include "mapping_table.h"
#include "mir_to_lir-inl.h"
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/verified_methods_data.h"
#include "verifier/dex_gc_map.h"
#include "verifier/method_verifier.h"
@@ -746,7 +749,8 @@
}
}
MethodReference method_ref(cu_->dex_file, cu_->method_idx);
- const std::vector<uint8_t>* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref);
+ const std::vector<uint8_t>* gc_map_raw =
+ cu_->compiler_driver->GetVerifiedMethodsData()->GetDexGcMap(method_ref);
verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[0]);
DCHECK_EQ(gc_map_raw->size(), dex_gc_map.RawSize());
// Compute native offset to references size.
@@ -981,8 +985,7 @@
core_spill_mask_(0),
fp_spill_mask_(0),
first_lir_insn_(NULL),
- last_lir_insn_(NULL),
- inliner_(nullptr) {
+ last_lir_insn_(NULL) {
promotion_map_ = static_cast<PromotionMap*>
(arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) *
sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc));
@@ -998,15 +1001,16 @@
/* Allocate Registers using simple local allocation scheme */
SimpleRegAlloc();
- if (mir_graph_->IsSpecialCase()) {
- /*
- * Custom codegen for special cases. If for any reason the
- * special codegen doesn't succeed, first_lir_insn_ will
- * set to NULL;
- */
- cu_->NewTimingSplit("SpecialMIR2LIR");
- SpecialMIR2LIR(mir_graph_->GetSpecialCase());
- }
+ /*
+ * Custom codegen for special cases. If for any reason the
+ * special codegen doesn't succeed, first_lir_insn_ will
+ * set to NULL;
+ */
+ // TODO: Clean up GenSpecial() and return true only if special implementation is emitted.
+ // Currently, GenSpecial() returns IsSpecial() but doesn't check after SpecialMIR2LIR().
+ DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
+ cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
+ ->GenSpecial(this, cu_->method_idx);
/* Convert MIR to LIR, etc. */
if (first_lir_insn_ == NULL) {
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index b21e37e..c21ddcc 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -16,14 +16,21 @@
#include <algorithm>
#include "base/macros.h"
+#include "base/mutex.h"
+#include "base/mutex-inl.h"
+#include "locks.h"
+#include "thread.h"
+#include "thread-inl.h"
#include "dex/mir_graph.h"
+#include "dex_instruction.h"
+#include "dex_instruction-inl.h"
#include "dex_file_method_inliner.h"
namespace art {
const uint32_t DexFileMethodInliner::kIndexUnresolved;
-const char* DexFileMethodInliner::kClassCacheNames[] = {
+const char* const DexFileMethodInliner::kClassCacheNames[] = {
"Z", // kClassCacheBoolean
"B", // kClassCacheByte
"C", // kClassCacheChar
@@ -47,7 +54,7 @@
"Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe
};
-const char* DexFileMethodInliner::kNameCacheNames[] = {
+const char* const DexFileMethodInliner::kNameCacheNames[] = {
"reverseBytes", // kNameCacheReverseBytes
"doubleToRawLongBits", // kNameCacheDoubleToRawLongBits
"longBitsToDouble", // kNameCacheLongBitsToDouble
@@ -160,7 +167,7 @@
const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
#define INTRINSIC(c, n, p, o, d) \
- { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } }
+ { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, d } }
INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0),
@@ -228,7 +235,8 @@
};
DexFileMethodInliner::DexFileMethodInliner()
- : dex_file_(NULL) {
+ : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock),
+ dex_file_(NULL) {
COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0);
COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames);
COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0);
@@ -240,16 +248,75 @@
DexFileMethodInliner::~DexFileMethodInliner() {
}
-bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) const {
- return intrinsics_.find(method_index) != intrinsics_.end();
+bool DexFileMethodInliner::AnalyseMethodCode(uint32_t method_idx,
+ const DexFile::CodeItem* code_item) {
+ // We currently support only plain return or 2-instruction methods.
+
+ DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
+ const Instruction* instruction = Instruction::At(code_item->insns_);
+ Instruction::Code opcode = instruction->Opcode();
+
+ switch (opcode) {
+ case Instruction::RETURN_VOID:
+ return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0);
+ case Instruction::RETURN:
+ case Instruction::RETURN_OBJECT:
+ return AnalyseReturnMethod(method_idx, code_item, kWord);
+ case Instruction::RETURN_WIDE:
+ return AnalyseReturnMethod(method_idx, code_item, kLong);
+ case Instruction::CONST:
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ case Instruction::CONST_HIGH16:
+ // TODO: Support wide constants (RETURN_WIDE).
+ return AnalyseConstMethod(method_idx, code_item);
+ case Instruction::IGET:
+ return AnalyseIGetMethod(method_idx, code_item, kWord, false);
+ case Instruction::IGET_OBJECT:
+ return AnalyseIGetMethod(method_idx, code_item, kWord, true);
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ return AnalyseIGetMethod(method_idx, code_item, kSignedByte, false);
+ case Instruction::IGET_CHAR:
+ return AnalyseIGetMethod(method_idx, code_item, kUnsignedHalf, false);
+ case Instruction::IGET_SHORT:
+ return AnalyseIGetMethod(method_idx, code_item, kSignedHalf, false);
+ case Instruction::IGET_WIDE:
+ return AnalyseIGetMethod(method_idx, code_item, kLong, false);
+ case Instruction::IPUT:
+ return AnalyseIPutMethod(method_idx, code_item, kWord, false);
+ case Instruction::IPUT_OBJECT:
+ return AnalyseIPutMethod(method_idx, code_item, kWord, true);
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ return AnalyseIPutMethod(method_idx, code_item, kSignedByte, false);
+ case Instruction::IPUT_CHAR:
+ return AnalyseIPutMethod(method_idx, code_item, kUnsignedHalf, false);
+ case Instruction::IPUT_SHORT:
+ return AnalyseIPutMethod(method_idx, code_item, kSignedHalf, false);
+ case Instruction::IPUT_WIDE:
+ return AnalyseIPutMethod(method_idx, code_item, kLong, false);
+ default:
+ return false;
+ }
}
-bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) const {
- auto it = intrinsics_.find(info->index);
- if (it == intrinsics_.end()) {
- return false;
+bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_index);
+ return it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0;
+}
+
+bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) {
+ InlineMethod intrinsic;
+ {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(info->index);
+ if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) {
+ return false;
+ }
+ intrinsic = it->second;
}
- const Intrinsic& intrinsic = it->second;
switch (intrinsic.opcode) {
case kIntrinsicDoubleCvt:
return backend->GenInlinedDoubleCvt(info);
@@ -296,6 +363,27 @@
}
}
+bool DexFileMethodInliner::IsSpecial(uint32_t method_index) {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_index);
+ return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0;
+}
+
+bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) {
+ InlineMethod special;
+ {
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ auto it = inline_methods_.find(method_idx);
+ if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
+ return false;
+ }
+ special = it->second;
+ }
+ // TODO: Return true only if special implementation is emitted.
+ backend->SpecialMIR2LIR(special);
+ return true;
+}
+
uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache,
ClassCacheIndex index) {
uint32_t* class_index = &cache->class_indexes[index];
@@ -408,13 +496,150 @@
DCHECK(dex_file_ == nullptr);
IndexCache cache;
for (const IntrinsicDef& def : kIntrinsicMethods) {
- uint32_t method_id = FindMethodIndex(dex_file, &cache, def.method_def);
- if (method_id != kIndexNotFound) {
- DCHECK(intrinsics_.find(method_id) == intrinsics_.end());
- intrinsics_[method_id] = def.intrinsic;
+ uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def);
+ if (method_idx != kIndexNotFound) {
+ DCHECK(inline_methods_.find(method_idx) == inline_methods_.end());
+ inline_methods_[method_idx] = def.intrinsic;
}
}
dex_file_ = dex_file;
}
+bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode,
+ uint16_t flags, uint32_t data) {
+ WriterMutexLock mu(Thread::Current(), lock_);
+ InlineMethod* im = &inline_methods_[method_idx];
+ if (im->flags == 0) {
+ im->opcode = opcode;
+ im->flags = flags;
+ im->data = data;
+ return true;
+ } else {
+ // TODO: Warning about a method being already inlined?
+ LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline, "
+ << im->flags;
+ return false;
+ }
+}
+
+bool DexFileMethodInliner::AnalyseReturnMethod(int32_t method_idx,
+ const DexFile::CodeItem* code_item, OpSize size) {
+ const Instruction* return_instruction = Instruction::At(code_item->insns_);
+ if (return_instruction->Opcode() == Instruction::RETURN_VOID) {
+ return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0);
+ }
+ uint32_t reg = return_instruction->VRegA_11x();
+ uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ DCHECK_GE(reg, arg_start);
+ DCHECK_LT(size == kLong ? reg + 1 : reg, code_item->registers_size_);
+
+ InlineReturnArgData data;
+ data.d.arg = reg - arg_start;
+ data.d.op_size = size;
+ data.d.reserved = 0;
+ return AddInlineMethod(method_idx, kInlineOpReturnArg, kInlineSpecial, data.data);
+}
+
+bool DexFileMethodInliner::AnalyseConstMethod(int32_t method_idx,
+ const DexFile::CodeItem* code_item) {
+ const Instruction* instruction = Instruction::At(code_item->insns_);
+ const Instruction* return_instruction = instruction->Next();
+ Instruction::Code return_opcode = return_instruction->Opcode();
+ if (return_opcode != Instruction::RETURN &&
+ return_opcode != Instruction::RETURN_OBJECT) {
+ return false;
+ }
+
+ uint32_t return_reg = return_instruction->VRegA_11x();
+ DCHECK_LT(return_reg, code_item->registers_size_);
+
+ uint32_t vA, vB, dummy;
+ uint64_t dummy_wide;
+ instruction->Decode(vA, vB, dummy_wide, dummy, nullptr);
+ if (instruction->Opcode() == Instruction::CONST_HIGH16) {
+ vB <<= 16;
+ }
+ DCHECK_LT(vA, code_item->registers_size_);
+ if (vA != return_reg) {
+ return false; // Not returning the value set by const?
+ }
+ if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) {
+ return false; // Returning non-null reference constant?
+ }
+ return AddInlineMethod(method_idx, kInlineOpConst, kInlineSpecial, vB);
+}
+
+bool DexFileMethodInliner::AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+ OpSize size, bool is_object) {
+ const Instruction* instruction = Instruction::At(code_item->insns_);
+ Instruction::Code opcode = instruction->Opcode();
+ const Instruction* return_instruction = instruction->Next();
+ Instruction::Code return_opcode = return_instruction->Opcode();
+ if (!(return_opcode == Instruction::RETURN && size != kLong) &&
+ !(return_opcode == Instruction::RETURN_WIDE && size == kLong) &&
+ !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT)) {
+ return false;
+ }
+
+ uint32_t return_reg = return_instruction->VRegA_11x();
+ DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
+ code_item->registers_size_);
+
+ uint32_t vA, vB, vC;
+ uint64_t dummy_wide;
+ instruction->Decode(vA, vB, dummy_wide, vC, nullptr);
+ uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ DCHECK_GE(vB, arg_start);
+ DCHECK_LT(vB, code_item->registers_size_);
+ DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_);
+ if (vA != return_reg) {
+ return false; // Not returning the value retrieved by iget?
+ }
+
+ // TODO: Check that the field is FastInstance().
+
+ InlineIGetIPutData data;
+ data.d.field = vC;
+ data.d.op_size = size;
+ data.d.is_object = is_object;
+ data.d.object_arg = vB - arg_start; // Allow iget on any register, not just "this"
+ data.d.src_arg = 0;
+ data.d.reserved = 0;
+ return AddInlineMethod(method_idx, kInlineOpIGet, kInlineSpecial, data.data);
+}
+
+bool DexFileMethodInliner::AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+ OpSize size, bool is_object) {
+ const Instruction* instruction = Instruction::At(code_item->insns_);
+ const Instruction* return_instruction = instruction->Next();
+ if (return_instruction->Opcode() != Instruction::RETURN_VOID) {
+ // TODO: Support returning an argument.
+ // This is needed by builder classes and generated accessor setters.
+ // builder.setX(value): iput value, this, fieldX; return-object this;
+ // object.access$nnn(value): iput value, this, fieldX; return value;
+ // Use InlineIGetIPutData::d::reserved to hold the information.
+ return false;
+ }
+
+ uint32_t vA, vB, vC;
+ uint64_t dummy_wide;
+ instruction->Decode(vA, vB, dummy_wide, vC, nullptr);
+ uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ DCHECK_GE(vB, arg_start);
+ DCHECK_GE(vA, arg_start);
+ DCHECK_LT(vB, code_item->registers_size_);
+ DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_);
+
+ // TODO: Check that the field (vC) is FastInstance().
+
+ InlineIGetIPutData data;
+ data.d.field = vC;
+ data.d.op_size = size;
+ data.d.is_object = is_object;
+ data.d.object_arg = vB - arg_start; // Allow iput on any register, not just "this"
+ data.d.src_arg = vA - arg_start;
+ data.d.reserved = 0;
+ return AddInlineMethod(method_idx, kInlineOpIPut, kInlineSpecial, data.data);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 948f4bb..1d99731 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -19,14 +19,18 @@
#include <stdint.h>
#include <map>
+#include "base/mutex.h"
+#include "base/macros.h"
+#include "dex/compiler_enums.h"
+#include "dex_file.h"
+#include "locks.h"
namespace art {
class CallInfo;
-class DexFile;
class Mir2Lir;
-enum IntrinsicOpcode {
+enum InlineMethodOpcode : uint16_t {
kIntrinsicDoubleCvt,
kIntrinsicFloatCvt,
kIntrinsicReverseBytes,
@@ -44,8 +48,26 @@
kIntrinsicCas,
kIntrinsicUnsafeGet,
kIntrinsicUnsafePut,
+
+ kInlineOpNop,
+ kInlineOpReturnArg,
+ kInlineOpConst,
+ kInlineOpIGet,
+ kInlineOpIPut,
};
+enum InlineMethodFlags {
+ kInlineIntrinsic = 0x0001,
+ kInlineSpecial = 0x0002,
+};
+
+struct InlineMethod {
+ uint16_t opcode;
+ uint16_t flags;
+ uint32_t data;
+};
+
+// IntrinsicFlags are stored in InlineMethod::data
enum IntrinsicFlags {
kIntrinsicFlagNone = 0,
@@ -70,10 +92,32 @@
kIntrinsicFlagIsOrdered = 8,
};
-struct Intrinsic {
- IntrinsicOpcode opcode;
+// Check that OpSize fits into 3 bits (at least the values the inliner uses).
+COMPILE_ASSERT(kWord < 8 && kLong < 8 && kSingle < 8 && kDouble < 8 && kUnsignedHalf < 8 &&
+ kSignedHalf < 8 && kUnsignedByte < 8 && kSignedByte < 8, op_size_field_too_narrow);
+
+union InlineIGetIPutData {
uint32_t data;
+ struct {
+ uint16_t field;
+ uint32_t op_size : 3; // OpSize
+ uint32_t is_object : 1;
+ uint32_t object_arg : 4;
+ uint32_t src_arg : 4; // iput only
+ uint32_t reserved : 4;
+ } d;
};
+COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint32_t), InvalidSizeOfInlineIGetIPutData);
+
+union InlineReturnArgData {
+ uint32_t data;
+ struct {
+ uint16_t arg;
+ uint32_t op_size : 3; // OpSize
+ uint32_t reserved : 13;
+ } d;
+};
+COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint32_t), InvalidSizeOfInlineReturnArgData);
/**
* Handles inlining of methods from a particular DexFile.
@@ -93,14 +137,34 @@
~DexFileMethodInliner();
/**
+ * Analyse method code to determine if the method is a candidate for inlining.
+ * If it is, record its data for later.
+ *
+ * @param method_idx the index of the inlining candidate.
+ * @param code_item a previously verified code item of the method.
+ */
+ bool AnalyseMethodCode(uint32_t method_idx,
+ const DexFile::CodeItem* code_item) LOCKS_EXCLUDED(lock_);
+
+ /**
* Check whether a particular method index corresponds to an intrinsic function.
*/
- bool IsIntrinsic(uint32_t method_index) const;
+ bool IsIntrinsic(uint32_t method_index) LOCKS_EXCLUDED(lock_);
/**
* Generate code for an intrinsic function invocation.
*/
- bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) const;
+ bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_);
+
+ /**
+ * Check whether a particular method index corresponds to a special function.
+ */
+ bool IsSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+
+ /**
+ * Generate code for a special function.
+ */
+ bool GenSpecial(Mir2Lir* backend, uint32_t method_idx);
private:
/**
@@ -258,7 +322,7 @@
*/
struct IntrinsicDef {
MethodDef method_def;
- Intrinsic intrinsic;
+ InlineMethod intrinsic;
};
/**
@@ -278,8 +342,8 @@
uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst];
};
- static const char* kClassCacheNames[];
- static const char* kNameCacheNames[];
+ static const char* const kClassCacheNames[];
+ static const char* const kNameCacheNames[];
static const ProtoDef kProtoCacheDefs[];
static const IntrinsicDef kIntrinsicMethods[];
@@ -300,14 +364,27 @@
*
* Only DexFileToMethodInlinerMap may call this function to initialize the inliner.
*/
- void FindIntrinsics(const DexFile* dex_file);
+ void FindIntrinsics(const DexFile* dex_file) EXCLUSIVE_LOCKS_REQUIRED(lock_);
friend class DexFileToMethodInlinerMap;
+ bool AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode,
+ uint16_t flags, uint32_t data) LOCKS_EXCLUDED(lock_);
+
+ bool AnalyseReturnMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+ OpSize size) LOCKS_EXCLUDED(lock_);
+ bool AnalyseConstMethod(int32_t method_idx, const DexFile::CodeItem* code_item)
+ LOCKS_EXCLUDED(lock_);
+ bool AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+ OpSize size, bool is_object) LOCKS_EXCLUDED(lock_);
+ bool AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+ OpSize size, bool is_object) LOCKS_EXCLUDED(lock_);
+
+ ReaderWriterMutex lock_;
/*
* Maps method indexes (for the particular DexFile) to Intrinsic defintions.
*/
- std::map<uint32_t, Intrinsic> intrinsics_;
+ std::map<uint32_t, InlineMethod> inline_methods_ GUARDED_BY(lock_);
const DexFile* dex_file_;
DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner);
diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc
index 0107ed3..2fec183 100644
--- a/compiler/dex/quick/dex_file_to_method_inliner_map.cc
+++ b/compiler/dex/quick/dex_file_to_method_inliner_map.cc
@@ -28,7 +28,7 @@
namespace art {
DexFileToMethodInlinerMap::DexFileToMethodInlinerMap()
- : lock_("inline_helper_mutex") {
+ : lock_("DexFileToMethodInlinerMap lock", kDexFileToMethodInlinerMapLock) {
}
DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() {
@@ -37,26 +37,37 @@
}
}
-const DexFileMethodInliner& DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) {
+DexFileMethodInliner* DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) {
Thread* self = Thread::Current();
{
- ReaderMutexLock lock(self, lock_);
+ ReaderMutexLock mu(self, lock_);
auto it = inliners_.find(dex_file);
if (it != inliners_.end()) {
- return *it->second;
+ return it->second;
}
}
- WriterMutexLock lock(self, lock_);
- DexFileMethodInliner** inliner = &inliners_[dex_file]; // inserts new entry if not found
- if (*inliner) {
- return **inliner;
+ // We need to acquire our lock_ to modify inliners_ but we want to release it
+ // before we initialize the new inliner. However, we need to acquire the
+ // new inliner's lock_ before we release our lock_ to prevent another thread
+ // from using the uninitialized inliner. This requires explicit calls to
+ // ExclusiveLock()/ExclusiveUnlock() on one of the locks, the other one
+ // can use WriterMutexLock.
+ DexFileMethodInliner* locked_inliner;
+ {
+ WriterMutexLock mu(self, lock_);
+ DexFileMethodInliner** inliner = &inliners_[dex_file]; // inserts new entry if not found
+ if (*inliner) {
+ return *inliner;
+ }
+ *inliner = new DexFileMethodInliner;
+ DCHECK(*inliner != nullptr);
+ locked_inliner = *inliner;
+ locked_inliner->lock_.ExclusiveLock(self); // Acquire inliner's lock_ before releasing lock_.
}
- *inliner = new DexFileMethodInliner();
- DCHECK(*inliner != nullptr);
- // TODO: per-dex file locking for the intrinsics container filling.
- (*inliner)->FindIntrinsics(dex_file);
- return **inliner;
+ locked_inliner->FindIntrinsics(dex_file);
+ locked_inliner->lock_.ExclusiveUnlock(self);
+ return locked_inliner;
}
} // namespace art
diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h
index 476f002..6d5b889 100644
--- a/compiler/dex/quick/dex_file_to_method_inliner_map.h
+++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h
@@ -40,7 +40,7 @@
DexFileToMethodInlinerMap();
~DexFileToMethodInlinerMap();
- const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_);
+ DexFileMethodInliner* GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_);
private:
ReaderWriterMutex lock_;
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index e66d4ea..82a1932 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1239,12 +1239,9 @@
void Mir2Lir::GenInvoke(CallInfo* info) {
if (!(info->opt_flags & MIR_INLINED)) {
- if (inliner_ == nullptr) {
- QuickCompilerContext* context = reinterpret_cast<QuickCompilerContext*>(
- cu_->compiler_driver->GetCompilerContext());
- inliner_ = &context->GetInlinerMap()->GetMethodInliner(cu_->dex_file);
- }
- if (inliner_->GenIntrinsic(this, info)) {
+ DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
+ if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
+ ->GenIntrinsic(this, info)) {
return;
}
}
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 21d5563..14f49aa 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -24,7 +24,7 @@
namespace art {
void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
- SpecialCaseHandler special_case) {
+ const InlineMethod& special) {
// TODO
}
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 450a44f..97dc2b3 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -133,7 +133,7 @@
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 GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
// Required for target - single operation generators.
LIR* OpUnconditionalBranch(LIR* target);
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 19d04be..c5bbae1 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -789,7 +789,8 @@
return false;
}
-void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) {
+void Mir2Lir::SpecialMIR2LIR(const InlineMethod& special) {
+ cu_->NewTimingSplit("SpecialMIR2LIR");
// Find the first DalvikByteCode block.
int num_reachable_blocks = mir_graph_->GetNumReachableBlocks();
BasicBlock*bb = NULL;
@@ -815,7 +816,7 @@
ResetDefTracking();
ClobberAllRegs();
- GenSpecialCase(bb, mir, special_case);
+ GenSpecialCase(bb, mir, special);
}
void Mir2Lir::MethodMIR2LIR() {
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 2a54eb3..2a25f2f 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -105,6 +105,7 @@
struct BasicBlock;
struct CallInfo;
struct CompilationUnit;
+struct InlineMethod;
struct MIR;
struct LIR;
struct RegLocation;
@@ -582,7 +583,7 @@
void CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list);
void HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir);
bool MethodBlockCodeGen(BasicBlock* bb);
- void SpecialMIR2LIR(SpecialCaseHandler special_case);
+ void SpecialMIR2LIR(const InlineMethod& special);
void MethodMIR2LIR();
@@ -703,7 +704,7 @@
virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) = 0;
virtual void GenSpecialCase(BasicBlock* bb, MIR* mir,
- SpecialCaseHandler special_case) = 0;
+ const InlineMethod& special) = 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,
@@ -826,8 +827,6 @@
unsigned int fp_spill_mask_;
LIR* first_lir_insn_;
LIR* last_lir_insn_;
- // Lazily retrieved method inliner for intrinsics.
- const DexFileMethodInliner* inliner_;
}; // Class Mir2Lir
} // namespace art
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index e4e345c..c24f0e3 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -246,9 +246,9 @@
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" },
- { kX86Push32R, kRegOpcode, IS_UNARY_OP | REG_USE0 | IS_STORE, { 0, 0, 0x50, 0, 0, 0, 0, 0 }, "Push32R", "!0r" },
- { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!0r" },
+ { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" },
+ { kX86Push32R, kRegOpcode, IS_UNARY_OP | REG_USE0 | REG_USE_SP | REG_DEF_SP | IS_STORE, { 0, 0, 0x50, 0, 0, 0, 0, 0 }, "Push32R", "!0r" },
+ { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | REG_USE_SP | REG_DEF_SP | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!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" }, \
@@ -289,7 +289,7 @@
{ kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" },
{ kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" },
{ kX86SqrtsdRR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0xF2, 0, 0x0F, 0x51, 0, 0, 0, 0 }, "SqrtsdRR", "!0r,!1r" },
- { kX86FSTPdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" },
+ { kX86FstpdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" },
EXT_0F_ENCODING_MAP(Movdxr, 0x66, 0x6E, REG_DEF0),
{ kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" },
@@ -772,6 +772,13 @@
EmitImm(entry, imm);
}
+void X86Mir2Lir::EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm) {
+ EmitPrefixAndOpcode(entry);
+ EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp);
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ EmitImm(entry, imm);
+}
+
void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) {
EmitPrefixAndOpcode(entry);
uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP;
@@ -1127,6 +1134,9 @@
case kMemReg: // lir operands - 0: base, 1: disp, 2: reg
EmitMemReg(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
break;
+ case kMemImm: // lir operands - 0: base, 1: disp, 2: immediate
+ EmitMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
+ break;
case kArrayReg: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
EmitArrayReg(entry, lir->operands[0], lir->operands[1], lir->operands[2],
lir->operands[3], lir->operands[4]);
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 17924b0..4267b5b 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -23,7 +23,7 @@
namespace art {
void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
- SpecialCaseHandler special_case) {
+ const InlineMethod& special) {
// TODO
}
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 6552607..22c4452 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -115,6 +115,8 @@
void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
ThrowKind kind);
+ LIR* GenMemImmedCheck(ConditionCode c_code, int base, int offset, int check_value,
+ ThrowKind kind);
RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -133,7 +135,7 @@
void GenNegFloat(RegLocation rl_dest, 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);
+ void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
// Single operation generators.
LIR* OpUnconditionalBranch(LIR* target);
@@ -184,6 +186,7 @@
void EmitOpArray(const X86EncodingMap* entry, uint8_t base, uint8_t index,
int scale, int disp);
void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg);
+ void EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm);
void EmitRegMem(const X86EncodingMap* entry, uint8_t reg, uint8_t base, int disp);
void EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index,
int scale, int disp);
@@ -206,6 +209,8 @@
int scale, int table_or_disp);
void EmitMacro(const X86EncodingMap* entry, uint8_t reg, int offset);
void EmitUnimplemented(const X86EncodingMap* entry, LIR* lir);
+ void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
+ int64_t val, ConditionCode ccode);
};
} // namespace art
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 0133a0a..56cf7e9 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -38,6 +38,20 @@
}
/*
+ * Perform a compare of memory to immediate value
+ */
+LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code,
+ int base, int offset, int check_value, ThrowKind kind) {
+ LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
+ current_dalvik_offset_, base, check_value, 0);
+ NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base, offset, check_value);
+ LIR* branch = OpCondBranch(c_code, tgt);
+ // Remember branch target - will process later
+ throw_launchpads_.Insert(tgt);
+ return branch;
+}
+
+/*
* Compare two 64-bit values
* x = y return 0
* x < y return -1
@@ -169,11 +183,23 @@
LIR* taken = &block_label_list_[bb->taken];
RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+ ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+
+ if (rl_src1.is_const) {
+ std::swap(rl_src1, rl_src2);
+ ccode = FlipComparisonOrder(ccode);
+ }
+ if (rl_src2.is_const) {
+ // Do special compare/branch against simple const operand
+ int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+ GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
+ return;
+ }
+
FlushAllRegs();
LockCallTemps(); // Prepare for explicit register usage
LoadValueDirectWideFixed(rl_src1, r0, r1);
LoadValueDirectWideFixed(rl_src2, r2, r3);
- ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
// Swap operands and condition code to prevent use of zero flag.
if (ccode == kCondLe || ccode == kCondGt) {
// Compute (r3:r2) = (r3:r2) - (r1:r0)
@@ -204,6 +230,56 @@
OpCondBranch(ccode, taken);
}
+void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
+ int64_t val, ConditionCode ccode) {
+ int32_t val_lo = Low32Bits(val);
+ int32_t val_hi = High32Bits(val);
+ 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;
+
+ if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
+ int t_reg = AllocTemp();
+ OpRegRegReg(kOpOr, t_reg, low_reg, high_reg);
+ FreeTemp(t_reg);
+ OpCondBranch(ccode, taken);
+ return;
+ }
+
+ OpRegImm(kOpCmp, high_reg, val_hi);
+ switch (ccode) {
+ case kCondEq:
+ case kCondNe:
+ OpCondBranch(kCondNe, (ccode == kCondEq) ? not_taken : taken);
+ break;
+ case kCondLt:
+ OpCondBranch(kCondLt, taken);
+ OpCondBranch(kCondGt, not_taken);
+ ccode = kCondUlt;
+ break;
+ case kCondLe:
+ OpCondBranch(kCondLt, taken);
+ OpCondBranch(kCondGt, not_taken);
+ ccode = kCondLs;
+ break;
+ case kCondGt:
+ OpCondBranch(kCondGt, taken);
+ OpCondBranch(kCondLt, not_taken);
+ ccode = kCondHi;
+ break;
+ case kCondGe:
+ OpCondBranch(kCondGt, taken);
+ OpCondBranch(kCondLt, not_taken);
+ ccode = kCondUge;
+ break;
+ default:
+ LOG(FATAL) << "Unexpected ccode: " << ccode;
+ }
+ OpCmpImmBranch(ccode, low_reg, val_lo, taken);
+}
+
RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo,
int lit, bool is_div) {
LOG(FATAL) << "Unexpected use of GenDivRemLit for x86";
@@ -295,16 +371,17 @@
if (is_long) {
FlushAllRegs();
LockCallTemps();
+ LoadValueDirectWideFixed(rl_src_expected, rAX, rDX);
+ LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX);
NewLIR1(kX86Push32R, rDI);
MarkTemp(rDI);
LockTemp(rDI);
NewLIR1(kX86Push32R, rSI);
MarkTemp(rSI);
LockTemp(rSI);
- LoadValueDirectFixed(rl_src_obj, rDI);
- LoadValueDirectFixed(rl_src_offset, rSI);
- LoadValueDirectWideFixed(rl_src_expected, rAX, rDX);
- LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX);
+ const int push_offset = 4 /* push edi */ + 4 /* push esi */;
+ LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_obj.s_reg_low) + push_offset, rDI);
+ LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_offset.s_reg_low) + push_offset, rSI);
NewLIR4(kX86LockCmpxchg8bA, rDI, rSI, 0, 0);
FreeTemp(rSI);
UnmarkTemp(rSI);
@@ -520,41 +597,49 @@
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;
RegLocation rl_result;
rl_array = LoadValue(rl_array, kCoreReg);
- rl_index = LoadValue(rl_index, kCoreReg);
+ 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();
}
+ bool constant_index = rl_index.is_const;
+ int32_t constant_index_value = 0;
+ if (!constant_index) {
+ rl_index = LoadValue(rl_index, kCoreReg);
+ } else {
+ constant_index_value = mir_graph_->ConstantValue(rl_index);
+ // If index is constant, just fold it into the data offset
+ data_offset += constant_index_value << scale;
+ // treat as non array below
+ rl_index.low_reg = INVALID_REG;
+ }
+
/* null object? */
GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
- /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
- GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
- len_offset, kThrowArrayBounds);
+ if (constant_index) {
+ GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
+ constant_index_value, kThrowConstantArrayBounds);
+ } else {
+ GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+ len_offset, kThrowArrayBounds);
+ }
}
+ rl_result = EvalLoc(rl_dest, reg_class, true);
if ((size == kLong) || (size == kDouble)) {
- int reg_addr = AllocTemp();
- OpLea(reg_addr, rl_array.low_reg, rl_index.low_reg, scale, data_offset);
- FreeTemp(rl_array.low_reg);
- FreeTemp(rl_index.low_reg);
- rl_result = EvalLoc(rl_dest, reg_class, true);
- LoadBaseIndexedDisp(reg_addr, INVALID_REG, 0, 0, rl_result.low_reg,
+ LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg,
rl_result.high_reg, size, INVALID_SREG);
StoreValueWide(rl_dest, rl_result);
} else {
- rl_result = EvalLoc(rl_dest, reg_class, true);
-
LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale,
data_offset, rl_result.low_reg, INVALID_REG, size,
INVALID_SREG);
-
StoreValue(rl_dest, rl_result);
}
}
@@ -576,14 +661,29 @@
}
rl_array = LoadValue(rl_array, kCoreReg);
- rl_index = LoadValue(rl_index, kCoreReg);
+ bool constant_index = rl_index.is_const;
+ int32_t constant_index_value = 0;
+ if (!constant_index) {
+ rl_index = LoadValue(rl_index, kCoreReg);
+ } else {
+ // If index is constant, just fold it into the data offset
+ constant_index_value = mir_graph_->ConstantValue(rl_index);
+ data_offset += constant_index_value << scale;
+ // treat as non array below
+ rl_index.low_reg = INVALID_REG;
+ }
/* null object? */
GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
- /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
- GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, len_offset, kThrowArrayBounds);
+ if (constant_index) {
+ GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
+ constant_index_value, kThrowConstantArrayBounds);
+ } else {
+ GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+ len_offset, kThrowArrayBounds);
+ }
}
if ((size == kLong) || (size == kDouble)) {
rl_src = LoadValueWide(rl_src, reg_class);
@@ -602,7 +702,9 @@
}
if (card_mark) {
// Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
- FreeTemp(rl_index.low_reg);
+ if (!constant_index) {
+ FreeTemp(rl_index.low_reg);
+ }
MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index ca5a234..a2d5c3e 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -349,7 +349,7 @@
kX86PsrlqRI, // right shift of floating point registers
kX86PsllqRI, // left shift of floating point registers
kX86SqrtsdRR, // sqrt of floating point register
- kX86FSTPdM, // Store and pop top x87 fp stack
+ kX86FstpdM, // Store and pop top x87 fp stack
Binary0fOpCode(kX86Movdxr), // move into xmm from gpr
kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR, // move into reg from xmm
kX86Set8R, kX86Set8M, kX86Set8A, // set byte depending on condition operand
diff --git a/compiler/dex/verified_methods_data.cc b/compiler/dex/verified_methods_data.cc
new file mode 100644
index 0000000..e6c4dda
--- /dev/null
+++ b/compiler/dex/verified_methods_data.cc
@@ -0,0 +1,454 @@
+/*
+ * 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 "base/stl_util.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
+#include "dex_instruction-inl.h"
+#include "base/mutex.h"
+#include "base/mutex-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object.h"
+#include "mirror/object-inl.h"
+#include "verified_methods_data.h"
+#include "verifier/dex_gc_map.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
+#include "verifier/register_line.h"
+#include "verifier/register_line-inl.h"
+
+namespace art {
+
+VerifiedMethodsData::VerifiedMethodsData()
+ : dex_gc_maps_lock_("compiler GC maps lock"),
+ dex_gc_maps_(),
+ safecast_map_lock_("compiler Cast Elision lock"),
+ safecast_map_(),
+ devirt_maps_lock_("compiler Devirtualization lock"),
+ devirt_maps_(),
+ rejected_classes_lock_("compiler rejected classes lock"),
+ rejected_classes_() {
+}
+
+VerifiedMethodsData::~VerifiedMethodsData() {
+ Thread* self = Thread::Current();
+ {
+ WriterMutexLock mu(self, dex_gc_maps_lock_);
+ STLDeleteValues(&dex_gc_maps_);
+ }
+ {
+ WriterMutexLock mu(self, safecast_map_lock_);
+ STLDeleteValues(&safecast_map_);
+ }
+ {
+ WriterMutexLock mu(self, devirt_maps_lock_);
+ STLDeleteValues(&devirt_maps_);
+ }
+}
+
+bool VerifiedMethodsData::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+ MethodReference ref = method_verifier->GetMethodReference();
+ bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
+ if (compile) {
+ /* Generate a register map and add it to the method. */
+ const std::vector<uint8_t>* dex_gc_map = GenerateGcMap(method_verifier);
+ if (dex_gc_map == NULL) {
+ DCHECK(method_verifier->HasFailures());
+ return false; // Not a real failure, but a failure to encode
+ }
+ if (kIsDebugBuild) {
+ VerifyGcMap(method_verifier, *dex_gc_map);
+ }
+ SetDexGcMap(ref, dex_gc_map);
+
+ // TODO: move this out when DEX-to-DEX supports devirtualization.
+ if (method_verifier->HasVirtualOrInterfaceInvokes()) {
+ PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(method_verifier);
+ if (pc_to_concrete_method != NULL) {
+ SetDevirtMap(ref, pc_to_concrete_method);
+ }
+ }
+ }
+
+ if (method_verifier->HasCheckCasts()) {
+ MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(method_verifier);
+ if (method_to_safe_casts != NULL) {
+ SetSafeCastMap(ref, method_to_safe_casts);
+ }
+ }
+ return true;
+}
+
+const std::vector<uint8_t>* VerifiedMethodsData::GetDexGcMap(MethodReference ref) {
+ ReaderMutexLock mu(Thread::Current(), dex_gc_maps_lock_);
+ DexGcMapTable::const_iterator it = dex_gc_maps_.find(ref);
+ CHECK(it != dex_gc_maps_.end())
+ << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file);
+ CHECK(it->second != NULL);
+ return it->second;
+}
+
+const MethodReference* VerifiedMethodsData::GetDevirtMap(const MethodReference& ref,
+ uint32_t dex_pc) {
+ ReaderMutexLock mu(Thread::Current(), devirt_maps_lock_);
+ DevirtualizationMapTable::const_iterator it = devirt_maps_.find(ref);
+ if (it == devirt_maps_.end()) {
+ return NULL;
+ }
+
+ // Look up the PC in the map, get the concrete method to execute and return its reference.
+ PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc);
+ if (pc_to_concrete_method != it->second->end()) {
+ return &(pc_to_concrete_method->second);
+ } else {
+ return NULL;
+ }
+}
+
+bool VerifiedMethodsData::IsSafeCast(MethodReference ref, uint32_t pc) {
+ ReaderMutexLock mu(Thread::Current(), safecast_map_lock_);
+ SafeCastMap::const_iterator it = safecast_map_.find(ref);
+ if (it == safecast_map_.end()) {
+ return false;
+ }
+
+ // Look up the cast address in the set of safe casts
+ // Use binary_search for lookup in the sorted vector.
+ return std::binary_search(it->second->begin(), it->second->end(), pc);
+}
+
+void VerifiedMethodsData::AddRejectedClass(ClassReference ref) {
+ {
+ WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
+ rejected_classes_.insert(ref);
+ }
+ DCHECK(IsClassRejected(ref));
+}
+
+bool VerifiedMethodsData::IsClassRejected(ClassReference ref) {
+ ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
+ return (rejected_classes_.find(ref) != rejected_classes_.end());
+}
+
+bool VerifiedMethodsData::IsCandidateForCompilation(MethodReference& method_ref,
+ const uint32_t access_flags) {
+#ifdef ART_SEA_IR_MODE
+ bool use_sea = Runtime::Current()->IsSeaIRMode();
+ use_sea = use_sea && (std::string::npos != PrettyMethod(
+ method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
+ if (use_sea) return true;
+#endif
+ // Don't compile class initializers, ever.
+ if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
+ return false;
+ }
+ return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
+}
+
+const std::vector<uint8_t>* VerifiedMethodsData::GenerateGcMap(
+ verifier::MethodVerifier* method_verifier) {
+ size_t num_entries, ref_bitmap_bits, pc_bits;
+ ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits);
+ // There's a single byte to encode the size of each bitmap
+ if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
+ // TODO: either a better GC map format or per method failures
+ method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+ << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers";
+ return NULL;
+ }
+ size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
+ // There are 2 bytes to encode the number of entries
+ if (num_entries >= 65536) {
+ // TODO: either a better GC map format or per method failures
+ method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+ << "Cannot encode GC map for method with " << num_entries << " entries";
+ return NULL;
+ }
+ size_t pc_bytes;
+ verifier::RegisterMapFormat format;
+ if (pc_bits <= 8) {
+ format = verifier::kRegMapFormatCompact8;
+ pc_bytes = 1;
+ } else if (pc_bits <= 16) {
+ format = verifier::kRegMapFormatCompact16;
+ pc_bytes = 2;
+ } else {
+ // TODO: either a better GC map format or per method failures
+ method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+ << "Cannot encode GC map for method with "
+ << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
+ return NULL;
+ }
+ size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
+ std::vector<uint8_t>* table = new std::vector<uint8_t>;
+ if (table == NULL) {
+ method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+ << "Failed to encode GC map (size=" << table_size << ")";
+ return NULL;
+ }
+ table->reserve(table_size);
+ // Write table header
+ table->push_back(format | ((ref_bitmap_bytes & ~0xFF) >> 5));
+ table->push_back(ref_bitmap_bytes & 0xFF);
+ table->push_back(num_entries & 0xFF);
+ table->push_back((num_entries >> 8) & 0xFF);
+ // Write table data
+ const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+ for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
+ if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
+ table->push_back(i & 0xFF);
+ if (pc_bytes == 2) {
+ table->push_back((i >> 8) & 0xFF);
+ }
+ verifier::RegisterLine* line = method_verifier->GetRegLine(i);
+ line->WriteReferenceBitMap(*table, ref_bitmap_bytes);
+ }
+ }
+ DCHECK_EQ(table->size(), table_size);
+ return table;
+}
+
+void VerifiedMethodsData::VerifyGcMap(verifier::MethodVerifier* method_verifier,
+ const std::vector<uint8_t>& data) {
+ // Check that for every GC point there is a map entry, there aren't entries for non-GC points,
+ // that the table data is well formed and all references are marked (or not) in the bitmap
+ verifier::DexPcToReferenceMap map(&data[0]);
+ DCHECK_EQ(data.size(), map.RawSize());
+ size_t map_index = 0;
+ const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+ for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
+ const uint8_t* reg_bitmap = map.FindBitMap(i, false);
+ if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
+ CHECK_LT(map_index, map.NumEntries());
+ CHECK_EQ(map.GetDexPc(map_index), i);
+ CHECK_EQ(map.GetBitMap(map_index), reg_bitmap);
+ map_index++;
+ verifier::RegisterLine* line = method_verifier->GetRegLine(i);
+ for (size_t j = 0; j < code_item->registers_size_; j++) {
+ if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
+ CHECK_LT(j / 8, map.RegWidth());
+ CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
+ } else if ((j / 8) < map.RegWidth()) {
+ CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
+ } else {
+ // If a register doesn't contain a reference then the bitmap may be shorter than the line
+ }
+ }
+ } else {
+ CHECK(reg_bitmap == NULL);
+ }
+ }
+}
+
+void VerifiedMethodsData::ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
+ size_t* gc_points, size_t* ref_bitmap_bits,
+ size_t* log2_max_gc_pc) {
+ size_t local_gc_points = 0;
+ size_t max_insn = 0;
+ size_t max_ref_reg = -1;
+ const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+ for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
+ if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
+ local_gc_points++;
+ max_insn = i;
+ verifier::RegisterLine* line = method_verifier->GetRegLine(i);
+ max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
+ }
+ }
+ *gc_points = local_gc_points;
+ *ref_bitmap_bits = max_ref_reg + 1; // if max register is 0 we need 1 bit to encode (ie +1)
+ size_t i = 0;
+ while ((1U << i) <= max_insn) {
+ i++;
+ }
+ *log2_max_gc_pc = i;
+}
+
+void VerifiedMethodsData::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* gc_map) {
+ DCHECK(Runtime::Current()->IsCompiler());
+ {
+ WriterMutexLock mu(Thread::Current(), dex_gc_maps_lock_);
+ DexGcMapTable::iterator it = dex_gc_maps_.find(ref);
+ if (it != dex_gc_maps_.end()) {
+ delete it->second;
+ dex_gc_maps_.erase(it);
+ }
+ dex_gc_maps_.Put(ref, gc_map);
+ }
+ DCHECK(GetDexGcMap(ref) != NULL);
+}
+
+VerifiedMethodsData::MethodSafeCastSet* VerifiedMethodsData::GenerateSafeCastSet(
+ verifier::MethodVerifier* method_verifier) {
+ /*
+ * Walks over the method code and adds any cast instructions in which
+ * the type cast is implicit to a set, which is used in the code generation
+ * to elide these casts.
+ */
+ if (method_verifier->HasFailures()) {
+ return NULL;
+ }
+ UniquePtr<MethodSafeCastSet> mscs;
+ const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+ const Instruction* inst = Instruction::At(code_item->insns_);
+ const Instruction* end = Instruction::At(code_item->insns_ +
+ code_item->insns_size_in_code_units_);
+
+ for (; inst < end; inst = inst->Next()) {
+ Instruction::Code code = inst->Opcode();
+ if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
+ uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
+ const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
+ bool is_safe_cast = false;
+ if (code == Instruction::CHECK_CAST) {
+ const verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
+ const verifier::RegType& cast_type =
+ method_verifier->ResolveCheckedClass(inst->VRegB_21c());
+ is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
+ } else {
+ const verifier::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 verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
+ const verifier::RegType& component_type = method_verifier->GetRegTypeCache()
+ ->GetComponentType(array_type, method_verifier->GetClassLoader());
+ is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
+ }
+ }
+ if (is_safe_cast) {
+ if (mscs.get() == nullptr) {
+ mscs.reset(new MethodSafeCastSet());
+ } else {
+ DCHECK_LT(mscs->back(), dex_pc); // Verify ordering for push_back() to the sorted vector.
+ }
+ mscs->push_back(dex_pc);
+ }
+ }
+ }
+ return mscs.release();
+}
+
+void VerifiedMethodsData::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
+ WriterMutexLock mu(Thread::Current(), safecast_map_lock_);
+ SafeCastMap::iterator it = safecast_map_.find(ref);
+ if (it != safecast_map_.end()) {
+ delete it->second;
+ safecast_map_.erase(it);
+ }
+ safecast_map_.Put(ref, cast_set);
+ DCHECK(safecast_map_.find(ref) != safecast_map_.end());
+}
+
+VerifiedMethodsData::PcToConcreteMethodMap* VerifiedMethodsData::GenerateDevirtMap(
+ verifier::MethodVerifier* method_verifier) {
+ // It is risky to rely on reg_types for sharpening in cases of soft
+ // verification, we might end up sharpening to a wrong implementation. Just abort.
+ if (method_verifier->HasFailures()) {
+ return NULL;
+ }
+
+ UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map;
+ const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+ const uint16_t* insns = code_item->insns_;
+ const Instruction* inst = Instruction::At(insns);
+ const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
+
+ for (; inst < end; inst = inst->Next()) {
+ bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
+ (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
+ bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
+ (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
+
+ if (!is_interface && !is_virtual) {
+ continue;
+ }
+ // Get reg type for register holding the reference to the object that will be dispatched upon.
+ uint32_t dex_pc = inst->GetDexPc(insns);
+ verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
+ bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
+ (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
+ const verifier::RegType&
+ reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
+
+ if (!reg_type.HasClass()) {
+ // We will compute devirtualization information only when we know the Class of the reg type.
+ continue;
+ }
+ mirror::Class* reg_class = reg_type.GetClass();
+ if (reg_class->IsInterface()) {
+ // We can't devirtualize when the known type of the register is an interface.
+ continue;
+ }
+ if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
+ // We can't devirtualize abstract classes except on arrays of abstract classes.
+ continue;
+ }
+ mirror::ArtMethod* abstract_method = method_verifier->GetDexCache()->GetResolvedMethod(
+ is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
+ if (abstract_method == NULL) {
+ // If the method is not found in the cache this means that it was never found
+ // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
+ continue;
+ }
+ // Find the concrete method.
+ mirror::ArtMethod* concrete_method = NULL;
+ if (is_interface) {
+ concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
+ }
+ if (is_virtual) {
+ concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
+ }
+ if (concrete_method == NULL || concrete_method->IsAbstract()) {
+ // In cases where concrete_method is not found, or is abstract, continue to the next invoke.
+ continue;
+ }
+ if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
+ concrete_method->GetDeclaringClass()->IsFinal()) {
+ // If we knew exactly the class being dispatched upon, or if the target method cannot be
+ // overridden record the target to be used in the compiler driver.
+ if (pc_to_concrete_method_map.get() == NULL) {
+ pc_to_concrete_method_map.reset(new PcToConcreteMethodMap());
+ }
+ MethodReference concrete_ref(
+ concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
+ concrete_method->GetDexMethodIndex());
+ pc_to_concrete_method_map->Put(dex_pc, concrete_ref);
+ }
+ }
+ return pc_to_concrete_method_map.release();
+}
+
+void VerifiedMethodsData::SetDevirtMap(MethodReference ref,
+ const PcToConcreteMethodMap* devirt_map) {
+ WriterMutexLock mu(Thread::Current(), devirt_maps_lock_);
+ DevirtualizationMapTable::iterator it = devirt_maps_.find(ref);
+ if (it != devirt_maps_.end()) {
+ delete it->second;
+ devirt_maps_.erase(it);
+ }
+
+ devirt_maps_.Put(ref, devirt_map);
+ DCHECK(devirt_maps_.find(ref) != devirt_maps_.end());
+}
+
+} // namespace art
diff --git a/compiler/dex/verified_methods_data.h b/compiler/dex/verified_methods_data.h
new file mode 100644
index 0000000..d495dff
--- /dev/null
+++ b/compiler/dex/verified_methods_data.h
@@ -0,0 +1,117 @@
+/*
+ * 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_COMPILER_DEX_VERIFIED_METHODS_DATA_H_
+#define ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_
+
+#include <stdint.h>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "class_reference.h"
+#include "method_reference.h"
+#include "safe_map.h"
+
+namespace art {
+
+namespace verifier {
+class MethodVerifier;
+} // namespace verifier
+
+class VerifiedMethodsData {
+ public:
+ VerifiedMethodsData();
+ ~VerifiedMethodsData();
+
+ bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(dex_gc_maps_lock_, devirt_maps_lock_, safecast_map_lock_);
+
+ const std::vector<uint8_t>* GetDexGcMap(MethodReference ref)
+ LOCKS_EXCLUDED(dex_gc_maps_lock_);
+
+ const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc)
+ LOCKS_EXCLUDED(devirt_maps_lock_);
+
+ // Returns true if the cast can statically be verified to be redundant
+ // by using the check-cast elision peephole optimization in the verifier
+ bool IsSafeCast(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_);
+
+ void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
+ bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
+
+ static bool IsCandidateForCompilation(MethodReference& method_ref,
+ const uint32_t access_flags);
+
+ private:
+ /*
+ * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
+ * verification). For type-precise determination we have all the data we need, so we just need to
+ * encode it in some clever fashion.
+ * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+ */
+ const std::vector<uint8_t>* GenerateGcMap(verifier::MethodVerifier* method_verifier);
+
+ // Verify that the GC map associated with method_ is well formed
+ void VerifyGcMap(verifier::MethodVerifier* method_verifier, const std::vector<uint8_t>& data);
+
+ // Compute sizes for GC map data
+ void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
+ size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
+
+ // All the GC maps that the verifier has created
+ typedef SafeMap<const MethodReference, const std::vector<uint8_t>*,
+ MethodReferenceComparator> DexGcMapTable;
+ ReaderWriterMutex dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ DexGcMapTable dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_);
+ void SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* dex_gc_map)
+ LOCKS_EXCLUDED(dex_gc_maps_lock_);
+
+ // Cast elision types.
+ // Since we're adding the dex PCs to the set in increasing order, a sorted vector
+ // is better for performance (not just memory usage), especially for large sets.
+ typedef std::vector<uint32_t> MethodSafeCastSet;
+ typedef SafeMap<MethodReference, const MethodSafeCastSet*,
+ MethodReferenceComparator> SafeCastMap;
+ MethodSafeCastSet* GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs)
+ LOCKS_EXCLUDED(safecast_map_lock_);
+ ReaderWriterMutex safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ SafeCastMap safecast_map_ GUARDED_BY(safecast_map_lock_);
+
+ // Devirtualization map.
+ typedef SafeMap<uint32_t, MethodReference> PcToConcreteMethodMap;
+ typedef SafeMap<MethodReference, const PcToConcreteMethodMap*,
+ MethodReferenceComparator> DevirtualizationMapTable;
+ PcToConcreteMethodMap* GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ReaderWriterMutex devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ DevirtualizationMapTable devirt_maps_ GUARDED_BY(devirt_maps_lock_);
+ void SetDevirtMap(MethodReference ref, const PcToConcreteMethodMap* pc_method_map)
+ LOCKS_EXCLUDED(devirt_maps_lock_);
+
+ // Rejected classes
+ typedef std::set<ClassReference> RejectedClassesTable;
+ ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ RejectedClassesTable rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 309af1d..1124541 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -27,6 +27,7 @@
#include "class_linker.h"
#include "dex_compilation_unit.h"
#include "dex_file-inl.h"
+#include "dex/verified_methods_data.h"
#include "jni_internal.h"
#include "object_utils.h"
#include "runtime.h"
@@ -284,10 +285,10 @@
};
extern "C" void ArtInitCompilerContext(art::CompilerDriver& driver);
-extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler);
+extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver);
extern "C" void ArtUnInitCompilerContext(art::CompilerDriver& driver);
-extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler);
+extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver);
extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver,
const art::DexFile::CodeItem* code_item,
@@ -335,11 +336,15 @@
extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver,
std::string const& filename);
-CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data,
+ DexFileToMethodInlinerMap* method_inliner_map,
+ 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),
+ : verified_methods_data_(verified_methods_data),
+ method_inliner_map_(method_inliner_map),
+ compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
instruction_set_features_(instruction_set_features),
freezing_constructor_lock_("freezing constructor lock"),
@@ -1253,7 +1258,7 @@
// 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 =
- verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc);
+ verified_methods_data_->GetDevirtMap(caller_method, dex_pc);
if (devirt_map_target != NULL) {
SirtRef<mirror::DexCache> target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file));
SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
@@ -1301,7 +1306,7 @@
}
bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) {
- bool result = verifier::MethodVerifier::IsSafeCast(mr, dex_pc);
+ bool result = verified_methods_data_->IsSafeCast(mr, dex_pc);
if (result) {
stats_->SafeCast();
} else {
@@ -2240,7 +2245,7 @@
}
ClassReference ref(&dex_file, class_def_index);
// Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (verifier::MethodVerifier::IsClassRejected(ref)) {
+ if (manager->GetCompiler()->verified_methods_data_->IsClassRejected(ref)) {
return;
}
const byte* class_data = dex_file.GetClassData(class_def);
@@ -2323,7 +2328,7 @@
} else if ((access_flags & kAccAbstract) != 0) {
} else {
MethodReference method_ref(&dex_file, method_idx);
- bool compile = verifier::MethodVerifier::IsCandidateForCompilation(method_ref, access_flags);
+ bool compile = VerifiedMethodsData::IsCandidateForCompilation(method_ref, access_flags);
if (compile) {
CompilerFn compiler = compiler_;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 7e81849..f4cc84d 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -41,8 +41,10 @@
class AOTCompilationStats;
class ParallelCompilationManager;
class DexCompilationUnit;
+class DexFileToMethodInlinerMap;
class OatWriter;
class TimingLogger;
+class VerifiedMethodsData;
enum CompilerBackend {
kQuick,
@@ -90,7 +92,9 @@
// enabled. "image_classes" lets the compiler know what classes it
// can assume will be in the image, with NULL implying all available
// classes.
- explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+ explicit CompilerDriver(VerifiedMethodsData* verified_methods_data,
+ DexFileToMethodInlinerMap* method_inliner_map,
+ CompilerBackend compiler_backend, InstructionSet instruction_set,
InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes,
size_t thread_count, bool dump_stats);
@@ -105,6 +109,14 @@
void CompileOne(const mirror::ArtMethod* method, TimingLogger& timings)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ VerifiedMethodsData* GetVerifiedMethodsData() const {
+ return verified_methods_data_;
+ }
+
+ DexFileToMethodInlinerMap* GetMethodInlinerMap() const {
+ return method_inliner_map_;
+ }
+
const InstructionSet& GetInstructionSet() const {
return instruction_set_;
}
@@ -390,6 +402,9 @@
std::vector<const PatchInformation*> code_to_patch_;
std::vector<const PatchInformation*> methods_to_patch_;
+ VerifiedMethodsData* verified_methods_data_;
+ DexFileToMethodInlinerMap* method_inliner_map_;
+
CompilerBackend compiler_backend_;
const InstructionSet instruction_set_;
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
index d59afd4..35d1ecd 100644
--- a/compiler/llvm/compiler_llvm.cc
+++ b/compiler/llvm/compiler_llvm.cc
@@ -20,6 +20,7 @@
#include "base/stl_util.h"
#include "class_linker.h"
#include "compiled_method.h"
+#include "dex/verified_methods_data.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "globals.h"
@@ -155,7 +156,8 @@
MethodReference mref(dex_compilation_unit->GetDexFile(),
dex_compilation_unit->GetDexMethodIndex());
return new CompiledMethod(*compiler_driver_, compiler_driver_->GetInstructionSet(),
- cunit->GetElfObject(), *verifier::MethodVerifier::GetDexGcMap(mref),
+ cunit->GetElfObject(),
+ *compiler_driver_->GetVerifiedMethodsData()->GetDexGcMap(mref),
cunit->GetDexCompilationUnit()->GetSymbol());
}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index fd0a69d..2434262 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -79,7 +79,12 @@
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
InstructionSetFeatures insn_features;
- compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set,
+ verified_methods_data_.reset(new VerifiedMethodsData);
+ method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr);
+ callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get());
+ compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(),
+ method_inliner_map_.get(),
+ compiler_backend, insn_set,
insn_features, false, NULL, 2, true));
jobject class_loader = NULL;
if (kCompile) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 8382469..199a2b8 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -23,6 +23,7 @@
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "dex_file-inl.h"
+#include "dex/verified_methods_data.h"
#include "gc/space/space.h"
#include "mirror/art_method-inl.h"
#include "mirror/array.h"
@@ -217,7 +218,7 @@
mirror::Class::Status status;
if (compiled_class != NULL) {
status = compiled_class->GetStatus();
- } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) {
+ } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) {
status = mirror::Class::kStatusError;
} else {
status = mirror::Class::kStatusNotReady;
@@ -432,7 +433,7 @@
mirror::Class::Status status;
if (compiled_class != NULL) {
status = compiled_class->GetStatus();
- } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) {
+ } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) {
status = mirror::Class::kStatusError;
} else {
status = mirror::Class::kStatusNotReady;
diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc
index 3512911..6c779c8 100644
--- a/compiler/sea_ir/frontend.cc
+++ b/compiler/sea_ir/frontend.cc
@@ -59,7 +59,7 @@
std::string llvm_code = llvm_data->GetElf(compiler.GetInstructionSet());
CompiledMethod* compiled_method =
new CompiledMethod(compiler, compiler.GetInstructionSet(), llvm_code,
- *verifier::MethodVerifier::GetDexGcMap(mref), symbol);
+ *compiler.GetVerifiedMethodsData()->GetDexGcMap(mref), symbol);
LOG(INFO) << "Compiled SEA IR method " << PrettyMethod(method_idx, dex_file) << ".";
return compiled_method;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 28d6649..5662f36 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -30,7 +30,9 @@
#include "base/timing_logger.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
+#include "compiler_callbacks.h"
#include "dex_file-inl.h"
+#include "dex/verified_methods_data.h"
#include "driver/compiler_driver.h"
#include "elf_fixup.h"
#include "elf_stripper.h"
@@ -51,9 +53,13 @@
#include "scoped_thread_state_change.h"
#include "sirt_ref.h"
#include "vector_output_stream.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
#include "well_known_classes.h"
#include "zip_archive.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+
namespace art {
static void UsageErrorV(const char* fmt, va_list ap) {
@@ -162,12 +168,13 @@
InstructionSetFeatures instruction_set_features,
size_t thread_count)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
- if (!CreateRuntime(options, instruction_set)) {
+ UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(compiler_backend, instruction_set,
+ instruction_set_features, thread_count));
+ if (!dex2oat->CreateRuntime(options, instruction_set)) {
*p_dex2oat = NULL;
return false;
}
- *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set,
- instruction_set_features, thread_count);
+ *p_dex2oat = dex2oat.release();
return true;
}
@@ -261,7 +268,9 @@
Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
}
- UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_backend_,
+ UniquePtr<CompilerDriver> driver(new CompilerDriver(verified_methods_data_.get(),
+ method_inliner_map_.get(),
+ compiler_backend_,
instruction_set_,
instruction_set_features_,
image,
@@ -337,21 +346,52 @@
}
private:
- explicit Dex2Oat(Runtime* runtime,
- CompilerBackend compiler_backend,
+ class Dex2OatCompilerCallbacks : public CompilerCallbacks {
+ public:
+ Dex2OatCompilerCallbacks(VerifiedMethodsData* verified_methods_data,
+ DexFileToMethodInlinerMap* method_inliner_map)
+ : verified_methods_data_(verified_methods_data),
+ method_inliner_map_(method_inliner_map) { }
+ virtual ~Dex2OatCompilerCallbacks() { }
+
+ virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ bool result = verified_methods_data_->ProcessVerifiedMethod(verifier);
+ if (result && method_inliner_map_ != nullptr) {
+ MethodReference ref = verifier->GetMethodReference();
+ method_inliner_map_->GetMethodInliner(ref.dex_file)
+ ->AnalyseMethodCode(ref.dex_method_index, verifier->CodeItem());
+ }
+ return result;
+ }
+ virtual void ClassRejected(ClassReference ref) {
+ verified_methods_data_->AddRejectedClass(ref);
+ }
+
+ private:
+ VerifiedMethodsData* verified_methods_data_;
+ DexFileToMethodInlinerMap* method_inliner_map_;
+ };
+
+ explicit Dex2Oat(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),
+ verified_methods_data_(new VerifiedMethodsData),
+ method_inliner_map_(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr),
+ callbacks_(verified_methods_data_.get(), method_inliner_map_.get()),
+ runtime_(nullptr),
thread_count_(thread_count),
start_ns_(NanoTime()) {
}
- static bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
+ bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
+ options.push_back(
+ std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
if (!Runtime::Create(options, false)) {
LOG(ERROR) << "Failed to create runtime";
return false;
@@ -364,6 +404,7 @@
}
}
runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod());
+ runtime_ = runtime;
return true;
}
@@ -405,6 +446,9 @@
const InstructionSet instruction_set_;
const InstructionSetFeatures instruction_set_features_;
+ UniquePtr<VerifiedMethodsData> verified_methods_data_;
+ UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
+ Dex2OatCompilerCallbacks callbacks_;
Runtime* runtime_;
size_t thread_count_;
uint64_t start_ns_;
@@ -899,7 +943,6 @@
}
Runtime::Options options;
- options.push_back(std::make_pair("compiler", reinterpret_cast<void*>(NULL)));
std::vector<const DexFile*> boot_class_path;
if (boot_image_option.empty()) {
size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index bdadc51..2aa6716 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -36,4 +36,6 @@
GENERATE_ALLOC_ENTRYPOINTS _instrumented, Instrumented
GENERATE_ALLOC_ENTRYPOINTS _bump_pointer, BumpPointer
GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented
+GENERATE_ALLOC_ENTRYPOINTS _tlab, TLAB
+GENERATE_ALLOC_ENTRYPOINTS _tlab_instrumented, TLABInstrumented
.endm
diff --git a/runtime/arch/quick_alloc_entrypoints.cc b/runtime/arch/quick_alloc_entrypoints.cc
index 192b124..4cdb3f2 100644
--- a/runtime/arch/quick_alloc_entrypoints.cc
+++ b/runtime/arch/quick_alloc_entrypoints.cc
@@ -53,6 +53,7 @@
// Generate the entrypoint functions.
GENERATE_ENTRYPOINTS();
GENERATE_ENTRYPOINTS(_bump_pointer);
+GENERATE_ENTRYPOINTS(_tlab);
static bool entry_points_instrumented = false;
static gc::AllocatorType entry_points_allocator = kMovingCollector ?
@@ -76,6 +77,10 @@
SetQuickAllocEntryPoints_bump_pointer(qpoints, entry_points_instrumented);
break;
}
+ case gc::kAllocatorTypeTLAB: {
+ SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented);
+ break;
+ }
default: {
LOG(FATAL) << "Unimplemented";
}
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index b1d031a..589c7d9 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -176,7 +176,7 @@
// points->pCmplDouble = NULL; // Not needed on x86.
// points->pCmplFloat = NULL; // Not needed on x86.
qpoints->pFmod = art_quick_fmod;
- // qpoints->pSqrt = NULL; // Not needed on x86.
+ // qpoints->pSqrt = NULL; // Not needed on x86.
qpoints->pL2d = art_quick_l2d;
qpoints->pFmodf = art_quick_fmodf;
qpoints->pL2f = art_quick_l2f;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 643c183..fbb47bd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -32,6 +32,7 @@
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
+#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
@@ -2490,7 +2491,9 @@
self->GetException(nullptr)->SetCause(cause.get());
}
ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
- verifier::MethodVerifier::AddRejectedClass(ref);
+ if (Runtime::Current()->IsCompiler()) {
+ Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+ }
klass->SetStatus(mirror::Class::kStatusError, self);
return;
}
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 57cf71a..a3cbde3 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -25,12 +25,15 @@
#include <fstream>
#include "../../external/icu4c/common/unicode/uvernum.h"
+#include "../compiler/dex/quick/dex_file_to_method_inliner_map.h"
+#include "../compiler/dex/verified_methods_data.h"
+#include "../compiler/driver/compiler_driver.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
-#include "compiler/driver/compiler_driver.h"
+#include "compiler_callbacks.h"
#include "dex_file-inl.h"
#include "entrypoints/entrypoint_utils.h"
#include "gc/heap.h"
@@ -46,6 +49,8 @@
#include "ScopedLocalRef.h"
#include "thread.h"
#include "UniquePtr.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
#include "well_known_classes.h"
namespace art {
@@ -158,7 +163,7 @@
#include <signal.h>
#include <asm/sigcontext.h>
-#include <asm/ucontext.h>
+#include <asm-generic/ucontext.h>
// A signal handler called when have an illegal instruction. We record the fact in
@@ -401,8 +406,18 @@
std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
+ // TODO: make selectable
+#if defined(ART_USE_PORTABLE_COMPILER)
+ CompilerBackend compiler_backend = kPortable;
+#else
+ CompilerBackend compiler_backend = kQuick;
+#endif
+
+ verified_methods_data_.reset(new VerifiedMethodsData);
+ method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr);
+ callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get());
Runtime::Options options;
- options.push_back(std::make_pair("compiler", reinterpret_cast<void*>(NULL)));
+ options.push_back(std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast<void*>(NULL)));
@@ -439,13 +454,6 @@
instruction_set = kX86;
#endif
- // TODO: make selectable
-#if defined(ART_USE_PORTABLE_COMPILER)
- CompilerBackend compiler_backend = kPortable;
-#else
- CompilerBackend compiler_backend = kQuick;
-#endif
-
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
if (!runtime_->HasCalleeSaveMethod(type)) {
@@ -454,7 +462,9 @@
}
}
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
- compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set,
+ compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(),
+ method_inliner_map_.get(),
+ compiler_backend, instruction_set,
instruction_set_features,
true, new CompilerDriver::DescriptorSet,
2, true));
@@ -502,6 +512,9 @@
(*icu_cleanup_fn)();
compiler_driver_.reset();
+ callbacks_.Reset(nullptr, nullptr);
+ method_inliner_map_.reset();
+ verified_methods_data_.reset();
STLDeleteElements(&opened_dex_files_);
Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
@@ -627,6 +640,36 @@
image_reservation_.reset();
}
+ class TestCompilerCallbacks : public CompilerCallbacks {
+ public:
+ TestCompilerCallbacks() : verified_methods_data_(nullptr), method_inliner_map_(nullptr) { }
+
+ void Reset(VerifiedMethodsData* verified_methods_data,
+ DexFileToMethodInlinerMap* method_inliner_map) {
+ verified_methods_data_ = verified_methods_data;
+ method_inliner_map_ = method_inliner_map;
+ }
+
+ virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(verified_methods_data_);
+ bool result = verified_methods_data_->ProcessVerifiedMethod(verifier);
+ if (result && method_inliner_map_ != nullptr) {
+ MethodReference ref = verifier->GetMethodReference();
+ method_inliner_map_->GetMethodInliner(ref.dex_file)
+ ->AnalyseMethodCode(ref.dex_method_index, verifier->CodeItem());
+ }
+ return result;
+ }
+ virtual void ClassRejected(ClassReference ref) {
+ verified_methods_data_->AddRejectedClass(ref);
+ }
+
+ private:
+ VerifiedMethodsData* verified_methods_data_;
+ DexFileToMethodInlinerMap* method_inliner_map_;
+ };
+
std::string android_data_;
std::string dalvik_cache_;
const DexFile* java_lang_dex_file_; // owned by runtime_
@@ -634,6 +677,9 @@
UniquePtr<Runtime> runtime_;
// Owned by the runtime
ClassLinker* class_linker_;
+ UniquePtr<VerifiedMethodsData> verified_methods_data_;
+ UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
+ TestCompilerCallbacks callbacks_;
UniquePtr<CompilerDriver> compiler_driver_;
private:
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
new file mode 100644
index 0000000..9045f3a
--- /dev/null
+++ b/runtime/compiler_callbacks.h
@@ -0,0 +1,44 @@
+/*
+ * 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_COMPILER_CALLBACKS_H_
+#define ART_RUNTIME_COMPILER_CALLBACKS_H_
+
+#include "class_reference.h"
+
+namespace art {
+
+namespace verifier {
+
+class MethodVerifier;
+
+} // namespace verifier
+
+class CompilerCallbacks {
+ public:
+ virtual ~CompilerCallbacks() { }
+
+ virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+ virtual void ClassRejected(ClassReference ref) = 0;
+
+ protected:
+ CompilerCallbacks() { }
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_COMPILER_CALLBACKS_H_
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 9155088..1ae39ab 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -81,5 +81,6 @@
GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(, gc::kAllocatorTypeFreeList)
GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(BumpPointer, gc::kAllocatorTypeBumpPointer)
+GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(TLAB, gc::kAllocatorTypeTLAB)
} // namespace art
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 45ab214..7bd53df 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -29,14 +29,15 @@
static inline bool byte_cas(byte old_value, byte new_value, byte* address) {
// Little endian means most significant byte is on the left.
- const size_t shift = reinterpret_cast<uintptr_t>(address) % sizeof(uintptr_t);
+ const size_t shift_in_bytes = reinterpret_cast<uintptr_t>(address) % sizeof(uintptr_t);
// Align the address down.
- address -= shift;
+ address -= shift_in_bytes;
+ const size_t shift_in_bits = shift_in_bytes * kBitsPerByte;
int32_t* word_address = reinterpret_cast<int32_t*>(address);
// Word with the byte we are trying to cas cleared.
- const int32_t cur_word = *word_address & ~(0xFF << shift);
- const int32_t old_word = cur_word | (static_cast<int32_t>(old_value) << shift);
- const int32_t new_word = cur_word | (static_cast<int32_t>(new_value) << shift);
+ const int32_t cur_word = *word_address & ~(0xFF << shift_in_bits);
+ const int32_t old_word = cur_word | (static_cast<int32_t>(old_value) << shift_in_bits);
+ const int32_t new_word = cur_word | (static_cast<int32_t>(new_value) << shift_in_bits);
bool success = android_atomic_cas(old_word, new_word, word_address) == 0;
return success;
}
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 469b098..8ae61a3 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -37,12 +37,16 @@
size_t RosAlloc::threadLocalFreeBitMapOffsets[kNumOfSizeBrackets];
bool RosAlloc::initialized_ = false;
-RosAlloc::RosAlloc(void* base, size_t capacity)
+RosAlloc::RosAlloc(void* base, size_t capacity,
+ PageReleaseMode page_release_mode, size_t page_release_size_threshold)
: base_(reinterpret_cast<byte*>(base)), footprint_(capacity),
capacity_(capacity),
lock_("rosalloc global lock", kRosAllocGlobalLock),
- bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock) {
+ bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock),
+ page_release_mode_(page_release_mode),
+ page_release_size_threshold_(page_release_size_threshold) {
DCHECK(RoundUp(capacity, kPageSize) == capacity);
+ CHECK(IsAligned<kPageSize>(page_release_size_threshold_));
if (!initialized_) {
Initialize();
}
@@ -65,7 +69,9 @@
}
free_pages->SetByteSize(this, capacity_);
DCHECK_EQ(capacity_ % kPageSize, static_cast<size_t>(0));
+ DCHECK(free_pages->IsFree());
free_pages->ReleasePages(this);
+ DCHECK(free_pages->IsFree());
free_page_runs_.insert(free_pages);
if (kTraceRosAlloc) {
LOG(INFO) << "RosAlloc::RosAlloc() : Inserted run 0x" << std::hex
@@ -387,7 +393,9 @@
// Insert it.
DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast<size_t>(0));
DCHECK(free_page_runs_.find(fpr) == free_page_runs_.end());
+ DCHECK(fpr->IsFree());
fpr->ReleasePages(this);
+ DCHECK(fpr->IsFree());
free_page_runs_.insert(fpr);
DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end());
if (kTraceRosAlloc) {
@@ -404,20 +412,26 @@
MutexLock mu(self, lock_);
r = AllocPages(self, num_pages, kPageMapLargeObject);
}
+ if (UNLIKELY(r == nullptr)) {
+ if (kTraceRosAlloc) {
+ LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL";
+ }
+ return nullptr;
+ }
if (bytes_allocated != NULL) {
*bytes_allocated = num_pages * kPageSize;
}
if (kTraceRosAlloc) {
- if (r != NULL) {
- LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast<intptr_t>(r)
- << "-0x" << (reinterpret_cast<intptr_t>(r) + num_pages * kPageSize)
- << "(" << std::dec << (num_pages * kPageSize) << ")";
- } else {
- LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL";
- }
+ LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast<intptr_t>(r)
+ << "-0x" << (reinterpret_cast<intptr_t>(r) + num_pages * kPageSize)
+ << "(" << std::dec << (num_pages * kPageSize) << ")";
+ }
+ if (!DoesReleaseAllPages()) {
+ // If it does not release all pages, pages may not be zeroed out.
+ memset(r, 0, size);
}
// Check if the returned memory is really all zero.
- if (kCheckZeroMemory && r != NULL) {
+ if (kCheckZeroMemory) {
byte* bytes = reinterpret_cast<byte*>(r);
for (size_t i = 0; i < size; ++i) {
DCHECK_EQ(bytes[i], 0);
@@ -1366,7 +1380,12 @@
size_t fpr_size = fpr->ByteSize(this);
DCHECK(IsAligned<kPageSize>(fpr_size));
void* start = fpr;
- void* end = reinterpret_cast<byte*>(start) + fpr_size;
+ if (kIsDebugBuild) {
+ // In the debug build, the first page of a free page run
+ // contains a magic number for debugging. Exclude it.
+ start = reinterpret_cast<byte*>(fpr) + kPageSize;
+ }
+ void* end = reinterpret_cast<byte*>(fpr) + fpr_size;
handler(start, end, 0, arg);
size_t num_pages = fpr_size / kPageSize;
if (kIsDebugBuild) {
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index d5b6de1..4eb13315 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -91,18 +91,50 @@
byte* end = fpr_base + ByteSize(rosalloc);
return end;
}
+ bool IsLargerThanPageReleaseThreshold(RosAlloc* rosalloc)
+ EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+ return ByteSize(rosalloc) >= rosalloc->page_release_size_threshold_;
+ }
+ bool IsAtEndOfSpace(RosAlloc* rosalloc)
+ EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+ return reinterpret_cast<byte*>(this) + ByteSize(rosalloc) == rosalloc->base_ + rosalloc->footprint_;
+ }
+ bool ShouldReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+ switch (rosalloc->page_release_mode_) {
+ case kPageReleaseModeNone:
+ return false;
+ case kPageReleaseModeEnd:
+ return IsAtEndOfSpace(rosalloc);
+ case kPageReleaseModeSize:
+ return IsLargerThanPageReleaseThreshold(rosalloc);
+ case kPageReleaseModeSizeAndEnd:
+ return IsLargerThanPageReleaseThreshold(rosalloc) && IsAtEndOfSpace(rosalloc);
+ case kPageReleaseModeAll:
+ return true;
+ default:
+ LOG(FATAL) << "Unexpected page release mode ";
+ return false;
+ }
+ }
void ReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+ byte* start = reinterpret_cast<byte*>(this);
size_t byte_size = ByteSize(rosalloc);
DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0));
+ bool release_pages = ShouldReleasePages(rosalloc);
if (kIsDebugBuild) {
// Exclude the first page that stores the magic number.
DCHECK_GE(byte_size, static_cast<size_t>(kPageSize));
+ start += kPageSize;
byte_size -= kPageSize;
if (byte_size > 0) {
- madvise(reinterpret_cast<byte*>(this) + kPageSize, byte_size, MADV_DONTNEED);
+ if (release_pages) {
+ madvise(start, byte_size, MADV_DONTNEED);
+ }
}
} else {
- madvise(this, byte_size, MADV_DONTNEED);
+ if (release_pages) {
+ madvise(start, byte_size, MADV_DONTNEED);
+ }
}
}
};
@@ -363,6 +395,21 @@
}
};
+ public:
+ // Different page release modes.
+ enum PageReleaseMode {
+ kPageReleaseModeNone, // Release no empty pages.
+ kPageReleaseModeEnd, // Release empty pages at the end of the space.
+ kPageReleaseModeSize, // Release empty pages that are larger than the threshold.
+ kPageReleaseModeSizeAndEnd, // Release empty pages that are larger than the threshold or
+ // at the end of the space.
+ kPageReleaseModeAll, // Release all empty pages.
+ };
+
+ // The default value for page_release_size_threshold_.
+ static constexpr size_t kDefaultPageReleaseSizeThreshold = 4 * MB;
+
+ private:
// The base address of the memory region that's managed by this allocator.
byte* base_;
@@ -412,6 +459,12 @@
// allowing multiple individual frees at the same time.
ReaderWriterMutex bulk_free_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // The page release mode.
+ const PageReleaseMode page_release_mode_;
+ // Under kPageReleaseModeSize(AndEnd), if the free page run size is
+ // greater than or equal to this value, release pages.
+ const size_t page_release_size_threshold_;
+
// The base address of the memory region that's managed by this allocator.
byte* Begin() { return base_; }
// The end address of the memory region that's managed by this allocator.
@@ -439,7 +492,9 @@
void* AllocLargeObject(Thread* self, size_t size, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_);
public:
- RosAlloc(void* base, size_t capacity);
+ RosAlloc(void* base, size_t capacity,
+ PageReleaseMode page_release_mode,
+ size_t page_release_size_threshold = kDefaultPageReleaseSizeThreshold);
void* Alloc(Thread* self, size_t size, size_t* bytes_allocated)
LOCKS_EXCLUDED(lock_);
void Free(Thread* self, void* ptr)
@@ -480,6 +535,10 @@
// allocated and objects allocated, respectively.
static void BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
static void ObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+
+ bool DoesReleaseAllPages() const {
+ return page_release_mode_ == kPageReleaseModeAll;
+ }
};
} // namespace allocator
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index cf301fe..6baee54 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -83,9 +83,9 @@
uint64_t pause_start = NanoTime();
ATRACE_BEGIN("Application threads suspended");
thread_list->SuspendAll();
+ GetHeap()->RevokeAllThreadLocalBuffers();
MarkingPhase();
ReclaimPhase();
- GetHeap()->RevokeAllThreadLocalBuffers();
thread_list->ResumeAll();
ATRACE_END();
RegisterPause(NanoTime() - pause_start);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 923560e..f29eadb 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -14,22 +14,6 @@
* limitations under the License.
*/
-/*
- * 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 "semi_space.h"
#include <functional>
@@ -337,7 +321,7 @@
if (forward_address == nullptr) {
// Otherwise, we need to move the object and add it to the markstack for processing.
size_t object_size = obj->SizeOf();
- size_t dummy = 0;
+ size_t bytes_allocated = 0;
if (kEnableSimplePromo && reinterpret_cast<byte*>(obj) < last_gc_to_space_end_) {
// If it's allocated before the last GC (older), move (pseudo-promote) it to
// the non-moving space (as sort of an old generation.)
@@ -346,7 +330,7 @@
forward_address = non_moving_space->Alloc(self_, object_size, &bytes_promoted);
if (forward_address == nullptr) {
// If out of space, fall back to the to-space.
- forward_address = to_space_->Alloc(self_, object_size, &dummy);
+ forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated);
} else {
GetHeap()->num_bytes_allocated_.fetch_add(bytes_promoted);
bytes_promoted_ += bytes_promoted;
@@ -364,7 +348,7 @@
DCHECK(forward_address != nullptr);
} else {
// If it's allocated after the last GC (younger), copy it to the to-space.
- forward_address = to_space_->Alloc(self_, object_size, &dummy);
+ forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated);
}
// Copy over the object and add it to the mark stack since we still need to update it's
// references.
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 99f084a..9fb5760 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -32,7 +32,7 @@
namespace art {
namespace gc {
-template <bool kInstrumented, typename PreFenceVisitor>
+template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* klass,
size_t byte_count, AllocatorType allocator,
const PreFenceVisitor& pre_fence_visitor) {
@@ -43,13 +43,13 @@
self->AssertThreadSuspensionIsAllowable();
// Need to check that we arent the large object allocator since the large object allocation code
// path this function. If we didn't check we would have an infinite loop.
- if (allocator != kAllocatorTypeLOS && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
+ if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
return AllocLargeObject<kInstrumented, PreFenceVisitor>(self, klass, byte_count,
pre_fence_visitor);
}
mirror::Object* obj;
- size_t bytes_allocated;
AllocationTimer alloc_timer(this, &obj);
+ size_t bytes_allocated;
obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated);
if (UNLIKELY(obj == nullptr)) {
obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &klass);
@@ -89,7 +89,11 @@
} else {
DCHECK(!Dbg::IsAllocTrackingEnabled());
}
- if (concurrent_gc_) {
+ // concurrent_gc_ isn't known at compile time so we can optimize by not checking it for
+ // the BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be
+ // optimized out. And for the other allocators, AllocatorMayHaveConcurrentGC is a constant since
+ // the allocator_type should be constant propagated.
+ if (AllocatorMayHaveConcurrentGC(allocator) && concurrent_gc_) {
CheckConcurrentGC(self, new_num_bytes_allocated, obj);
}
if (kIsDebugBuild) {
@@ -105,15 +109,15 @@
inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class* klass,
size_t byte_count,
const PreFenceVisitor& pre_fence_visitor) {
- return AllocObjectWithAllocator<kInstrumented, PreFenceVisitor>(self, klass, byte_count,
- kAllocatorTypeLOS,
- pre_fence_visitor);
+ return AllocObjectWithAllocator<kInstrumented, false, PreFenceVisitor>(self, klass, byte_count,
+ kAllocatorTypeLOS,
+ pre_fence_visitor);
}
template <const bool kInstrumented, const bool kGrow>
inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type,
size_t alloc_size, size_t* bytes_allocated) {
- if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(alloc_size))) {
+ if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
return nullptr;
}
if (kInstrumented) {
@@ -153,6 +157,21 @@
DCHECK(ret == nullptr || large_object_space_->Contains(ret));
break;
}
+ case kAllocatorTypeTLAB: {
+ alloc_size = RoundUp(alloc_size, space::BumpPointerSpace::kAlignment);
+ if (UNLIKELY(self->TLABSize() < alloc_size)) {
+ // Try allocating a new thread local buffer, if the allocaiton fails the space must be
+ // full so return nullptr.
+ if (!bump_pointer_space_->AllocNewTLAB(self, alloc_size + kDefaultTLABSize)) {
+ return nullptr;
+ }
+ }
+ // The allocation can't fail.
+ ret = self->AllocTLAB(alloc_size);
+ DCHECK(ret != nullptr);
+ *bytes_allocated = alloc_size;
+ break;
+ }
default: {
LOG(FATAL) << "Invalid allocator type";
ret = nullptr;
@@ -194,14 +213,14 @@
return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray();
}
-template <const bool kGrow>
-inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size) {
+template <bool kGrow>
+inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size) {
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 (!AllocatorMayHaveConcurrentGC(allocator_type) || !concurrent_gc_) {
if (!kGrow) {
return true;
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 11acd33..61c66e7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -77,7 +77,7 @@
double target_utilization, size_t capacity, const std::string& image_file_name,
CollectorType post_zygote_collector_type, 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)
+ size_t long_gc_log_threshold, bool ignore_max_footprint, bool use_tlab)
: non_moving_space_(nullptr),
concurrent_gc_(false),
collector_type_(kCollectorTypeNone),
@@ -103,11 +103,6 @@
native_footprint_gc_watermark_(initial_size),
native_footprint_limit_(2 * initial_size),
native_need_to_run_finalization_(false),
- activity_thread_class_(NULL),
- application_thread_class_(NULL),
- activity_thread_(NULL),
- application_thread_(NULL),
- last_process_state_id_(NULL),
// Initially assume we perceive jank in case the process state is never updated.
process_state_(kProcessStateJankPerceptible),
concurrent_start_bytes_(std::numeric_limits<size_t>::max()),
@@ -148,7 +143,8 @@
total_allocation_time_(0),
verify_object_mode_(kHeapVerificationNotPermitted),
gc_disable_count_(0),
- running_on_valgrind_(RUNNING_ON_VALGRIND) {
+ running_on_valgrind_(RUNNING_ON_VALGRIND),
+ use_tlab_(use_tlab) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
@@ -184,7 +180,7 @@
requested_alloc_space_begin);
} else {
non_moving_space_ = space::RosAllocSpace::Create(name, initial_size, growth_limit, capacity,
- requested_alloc_space_begin);
+ requested_alloc_space_begin, low_memory_mode_);
}
if (kMovingCollector) {
// TODO: Place bump-pointer spaces somewhere to minimize size of card table.
@@ -337,36 +333,21 @@
}
void Heap::VisitObjects(ObjectVisitorCallback callback, void* arg) {
- // Visit objects in bump pointer space.
Thread* self = Thread::Current();
- // TODO: Use reference block.
- std::vector<SirtRef<mirror::Object>*> saved_refs;
+ // GCs can move objects, so don't allow this.
+ const char* old_cause = self->StartAssertNoThreadSuspension("Visiting objects");
if (bump_pointer_space_ != nullptr) {
- // Need to put all these in sirts since the callback may trigger a GC. TODO: Use a better data
- // structure.
- mirror::Object* obj = reinterpret_cast<mirror::Object*>(bump_pointer_space_->Begin());
- const mirror::Object* end = reinterpret_cast<const mirror::Object*>(
- bump_pointer_space_->End());
- while (obj < end) {
- saved_refs.push_back(new SirtRef<mirror::Object>(self, obj));
- obj = space::BumpPointerSpace::GetNextObject(obj);
- }
+ // Visit objects in bump pointer space.
+ bump_pointer_space_->Walk(callback, arg);
}
// TODO: Switch to standard begin and end to use ranged a based loop.
for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End();
it < end; ++it) {
mirror::Object* obj = *it;
- // Objects in the allocation stack might be in a movable space.
- saved_refs.push_back(new SirtRef<mirror::Object>(self, obj));
+ callback(obj, arg);
}
GetLiveBitmap()->Walk(callback, arg);
- for (const auto& ref : saved_refs) {
- callback(ref->get(), arg);
- }
- // Need to free the sirts in reverse order they were allocated.
- for (size_t i = saved_refs.size(); i != 0; --i) {
- delete saved_refs[i - 1];
- }
+ self->EndAssertNoThreadSuspension(old_cause);
}
void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -471,8 +452,6 @@
}
}
uint64_t allocation_time = static_cast<uint64_t>(total_allocation_time_) * kTimeAdjust;
- size_t total_objects_allocated = GetObjectsAllocatedEver();
- size_t total_bytes_allocated = GetBytesAllocatedEver();
if (total_duration != 0) {
const double total_seconds = static_cast<double>(total_duration / 1000) / 1000000.0;
os << "Total time spent in GC: " << PrettyDuration(total_duration) << "\n";
@@ -481,7 +460,9 @@
os << "Mean GC object throughput: "
<< (GetObjectsFreedEver() / total_seconds) << " objects/s\n";
}
+ size_t total_objects_allocated = GetObjectsAllocatedEver();
os << "Total number of allocations: " << total_objects_allocated << "\n";
+ size_t total_bytes_allocated = GetBytesAllocatedEver();
os << "Total bytes allocated " << PrettySize(total_bytes_allocated) << "\n";
if (kMeasureAllocationTime) {
os << "Total time spent allocating: " << PrettyDuration(allocation_time) << "\n";
@@ -698,7 +679,7 @@
}
}
total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated() -
- bump_pointer_space_->GetBytesAllocated();
+ bump_pointer_space_->Size();
const float managed_utilization = static_cast<float>(total_alloc_space_allocated) /
static_cast<float>(total_alloc_space_size);
uint64_t gc_heap_end_ns = NanoTime();
@@ -867,12 +848,10 @@
void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) {
DCHECK_LE(freed_bytes, static_cast<size_t>(num_bytes_allocated_));
num_bytes_allocated_.fetch_sub(freed_bytes);
-
if (Runtime::Current()->HasStatsEnabled()) {
RuntimeStats* thread_stats = Thread::Current()->GetStats();
thread_stats->freed_objects += freed_objects;
thread_stats->freed_bytes += freed_bytes;
-
// TODO: Do this concurrently.
RuntimeStats* global_stats = Runtime::Current()->GetStats();
global_stats->freed_objects += freed_objects;
@@ -945,19 +924,11 @@
}
size_t Heap::GetObjectsAllocatedEver() const {
- size_t total = 0;
- for (space::AllocSpace* space : alloc_spaces_) {
- total += space->GetTotalObjectsAllocated();
- }
- return total;
+ return GetObjectsFreedEver() + GetObjectsAllocated();
}
size_t Heap::GetBytesAllocatedEver() const {
- size_t total = 0;
- for (space::AllocSpace* space : alloc_spaces_) {
- total += space->GetTotalBytesAllocated();
- }
- return total;
+ return GetBytesFreedEver() + GetBytesAllocated();
}
class InstanceCounter {
@@ -1102,7 +1073,11 @@
case kCollectorTypeSS: {
concurrent_gc_ = false;
gc_plan_.push_back(collector::kGcTypeFull);
- ChangeAllocator(kAllocatorTypeBumpPointer);
+ if (use_tlab_) {
+ ChangeAllocator(kAllocatorTypeTLAB);
+ } else {
+ ChangeAllocator(kAllocatorTypeBumpPointer);
+ }
break;
}
case kCollectorTypeMS: {
@@ -1134,6 +1109,10 @@
}
}
+static void MarkInBitmapCallback(mirror::Object* obj, void* arg) {
+ reinterpret_cast<accounting::SpaceBitmap*>(arg)->Set(obj);
+}
+
void Heap::PreZygoteFork() {
static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock);
Thread* self = Thread::Current();
@@ -1158,7 +1137,7 @@
// Compact the bump pointer space to a new zygote bump pointer space.
temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
Compact(&target_space, bump_pointer_space_);
- CHECK_EQ(temp_space_->GetBytesAllocated(), 0U);
+ CHECK(temp_space_->IsEmpty());
total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects();
total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes();
// Update the end and write out image.
@@ -1167,17 +1146,12 @@
accounting::SpaceBitmap* bitmap = non_moving_space_->GetLiveBitmap();
// Record the allocations in the bitmap.
VLOG(heap) << "Recording zygote allocations";
- mirror::Object* obj = reinterpret_cast<mirror::Object*>(target_space.Begin());
- const mirror::Object* end = reinterpret_cast<const mirror::Object*>(target_space.End());
- while (obj < end) {
- bitmap->Set(obj);
- obj = space::BumpPointerSpace::GetNextObject(obj);
- }
+ target_space.Walk(MarkInBitmapCallback, bitmap);
}
// Turn the current alloc space into a zygote space and obtain the new alloc space composed of
// the remaining available heap memory.
space::MallocSpace* zygote_space = non_moving_space_;
- non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space");
+ non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space", low_memory_mode_);
non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
// Change the GC retention policy of the zygote space to only collect when full.
zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect);
@@ -1305,9 +1279,11 @@
collector::GarbageCollector* collector = nullptr;
// TODO: Clean this up.
- if (current_allocator_ == kAllocatorTypeBumpPointer) {
+ if (collector_type_ == kCollectorTypeSS) {
+ DCHECK(current_allocator_ == kAllocatorTypeBumpPointer ||
+ current_allocator_ == kAllocatorTypeTLAB);
gc_type = semi_space_collector_->GetGcType();
- CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U);
+ CHECK(temp_space_->IsEmpty());
semi_space_collector_->SetFromSpace(bump_pointer_space_);
semi_space_collector_->SetToSpace(temp_space_);
mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE);
@@ -2070,10 +2046,16 @@
void Heap::RevokeThreadLocalBuffers(Thread* thread) {
non_moving_space_->RevokeThreadLocalBuffers(thread);
+ if (bump_pointer_space_ != nullptr) {
+ bump_pointer_space_->RevokeThreadLocalBuffers(thread);
+ }
}
void Heap::RevokeAllThreadLocalBuffers() {
non_moving_space_->RevokeAllThreadLocalBuffers();
+ if (bump_pointer_space_ != nullptr) {
+ bump_pointer_space_->RevokeAllThreadLocalBuffers();
+ }
}
bool Heap::IsGCRequestPending() const {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9788064..832d5ec 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -91,6 +91,7 @@
// Different types of allocators.
enum AllocatorType {
kAllocatorTypeBumpPointer,
+ kAllocatorTypeTLAB,
kAllocatorTypeFreeList, // ROSAlloc / dlmalloc
kAllocatorTypeLOS, // Large object space.
};
@@ -139,6 +140,7 @@
static constexpr size_t kDefaultMinFree = kDefaultMaxFree / 4;
static constexpr size_t kDefaultLongPauseLogThreshold = MsToNs(5);
static constexpr size_t kDefaultLongGCLogThreshold = MsToNs(100);
+ static constexpr size_t kDefaultTLABSize = 256 * KB;
// Default target utilization.
static constexpr double kDefaultTargetUtilization = 0.5;
@@ -154,24 +156,25 @@
const std::string& original_image_file_name, CollectorType collector_type_,
size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
size_t long_pause_threshold, size_t long_gc_threshold,
- bool ignore_max_footprint);
+ bool ignore_max_footprint, bool use_tlab);
~Heap();
// Allocates and initializes storage for an object instance.
- template <const bool kInstrumented>
+ template <bool kInstrumented>
inline mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return AllocObjectWithAllocator<kInstrumented>(self, klass, num_bytes, GetCurrentAllocator());
+ return AllocObjectWithAllocator<kInstrumented, true>(self, klass, num_bytes,
+ GetCurrentAllocator());
}
- template <const bool kInstrumented>
+ template <bool kInstrumented>
inline mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass,
size_t num_bytes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return AllocObjectWithAllocator<kInstrumented>(self, klass, num_bytes,
- GetCurrentNonMovingAllocator());
+ return AllocObjectWithAllocator<kInstrumented, true>(self, klass, num_bytes,
+ GetCurrentNonMovingAllocator());
}
- template <bool kInstrumented, typename PreFenceVisitor = VoidFunctor>
+ template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor = VoidFunctor>
ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(
Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator,
const PreFenceVisitor& pre_fence_visitor = VoidFunctor())
@@ -507,17 +510,19 @@
void Compact(space::ContinuousMemMapAllocSpace* target_space,
space::ContinuousMemMapAllocSpace* source_space);
- static bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
- return allocator_type != kAllocatorTypeBumpPointer;
+ static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
+ return
+ allocator_type != kAllocatorTypeBumpPointer &&
+ allocator_type != kAllocatorTypeTLAB;
}
- static bool AllocatorHasConcurrentGC(AllocatorType allocator_type) {
- return allocator_type != kAllocatorTypeBumpPointer;
+ static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) {
+ return AllocatorHasAllocationStack(allocator_type);
}
bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const;
ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated,
mirror::Object* obj);
- // We don't force this to be inline since it is a slow path.
+ // We don't force this to be inlined since it is a slow path.
template <bool kInstrumented, typename PreFenceVisitor>
mirror::Object* AllocLargeObject(Thread* self, mirror::Class* klass, size_t byte_count,
const PreFenceVisitor& pre_fence_visitor)
@@ -544,8 +549,9 @@
void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template <const bool kGrow>
- bool IsOutOfMemoryOnAllocation(size_t alloc_size);
+
+ template <bool kGrow>
+ bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
// Pushes a list of cleared references out to the managed heap.
void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent)
@@ -721,13 +727,6 @@
// Whether or not we need to run finalizers in the next native allocation.
bool native_need_to_run_finalization_;
- // Activity manager members.
- jclass activity_thread_class_;
- jclass application_thread_class_;
- jobject activity_thread_;
- jobject application_thread_;
- jfieldID last_process_state_id_;
-
// Whether or not we currently care about pause times.
ProcessState process_state_;
@@ -845,6 +844,7 @@
collector::SemiSpace* semi_space_collector_;
const bool running_on_valgrind_;
+ const bool use_tlab_;
friend class collector::MarkSweep;
friend class collector::SemiSpace;
diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h
index 85ef2f4..82e96a4 100644
--- a/runtime/gc/space/bump_pointer_space-inl.h
+++ b/runtime/gc/space/bump_pointer_space-inl.h
@@ -23,8 +23,8 @@
namespace gc {
namespace space {
-inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) {
- num_bytes = RoundUp(num_bytes, kAlignment);
+inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t num_bytes) {
+ DCHECK(IsAligned<kAlignment>(num_bytes));
byte* old_end;
byte* new_end;
do {
@@ -38,13 +38,18 @@
} while (android_atomic_cas(reinterpret_cast<int32_t>(old_end),
reinterpret_cast<int32_t>(new_end),
reinterpret_cast<volatile int32_t*>(&end_)) != 0);
- // TODO: Less statistics?
- total_bytes_allocated_.fetch_add(num_bytes);
- num_objects_allocated_.fetch_add(1);
- total_objects_allocated_.fetch_add(1);
return reinterpret_cast<mirror::Object*>(old_end);
}
+inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) {
+ mirror::Object* ret = AllocNonvirtualWithoutAccounting(num_bytes);
+ if (ret != nullptr) {
+ objects_allocated_.fetch_add(1);
+ bytes_allocated_.fetch_add(num_bytes);
+ }
+ return ret;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 06ba57e..7ea202c 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -18,6 +18,7 @@
#include "bump_pointer_space-inl.h"
#include "mirror/object-inl.h"
#include "mirror/class-inl.h"
+#include "thread_list.h"
namespace art {
namespace gc {
@@ -40,18 +41,27 @@
BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit)
: ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit,
kGcRetentionPolicyAlwaysCollect),
- num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0),
- growth_end_(limit) {
+ growth_end_(limit),
+ objects_allocated_(0), bytes_allocated_(0),
+ block_lock_("Block lock"),
+ num_blocks_(0) {
+ CHECK_GE(Capacity(), sizeof(BlockHeader));
+ end_ += sizeof(BlockHeader);
}
BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map)
: ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(),
kGcRetentionPolicyAlwaysCollect),
- num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0),
- growth_end_(mem_map->End()) {
+ growth_end_(mem_map->End()),
+ objects_allocated_(0), bytes_allocated_(0),
+ block_lock_("Block lock"),
+ num_blocks_(0) {
+ CHECK_GE(Capacity(), sizeof(BlockHeader));
+ end_ += sizeof(BlockHeader);
}
mirror::Object* BumpPointerSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated) {
+ num_bytes = RoundUp(num_bytes, kAlignment);
mirror::Object* ret = AllocNonvirtual(num_bytes);
if (LIKELY(ret != nullptr)) {
*bytes_allocated = num_bytes;
@@ -68,9 +78,14 @@
CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed";
// Reset the end of the space back to the beginning, we move the end forward as we allocate
// objects.
- SetEnd(Begin());
+ SetEnd(Begin() + sizeof(BlockHeader));
+ objects_allocated_ = 0;
+ bytes_allocated_ = 0;
growth_end_ = Limit();
- num_objects_allocated_ = 0;
+ {
+ MutexLock mu(Thread::Current(), block_lock_);
+ num_blocks_ = 0;
+ }
}
void BumpPointerSpace::Dump(std::ostream& os) const {
@@ -83,6 +98,131 @@
return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
}
+void BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) {
+ MutexLock mu(Thread::Current(), block_lock_);
+ RevokeThreadLocalBuffersLocked(thread);
+}
+
+void BumpPointerSpace::RevokeAllThreadLocalBuffers() {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ // TODO: Not do a copy of the thread list?
+ std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+ for (Thread* thread : thread_list) {
+ RevokeThreadLocalBuffers(thread);
+ }
+}
+
+void BumpPointerSpace::UpdateMainBlock() {
+ BlockHeader* header = reinterpret_cast<BlockHeader*>(Begin());
+ header->size_ = Size() - sizeof(BlockHeader);
+ DCHECK_EQ(num_blocks_, 0U);
+}
+
+// Returns the start of the storage.
+byte* BumpPointerSpace::AllocBlock(size_t bytes) {
+ bytes = RoundUp(bytes, kAlignment);
+ if (!num_blocks_) {
+ UpdateMainBlock();
+ }
+ byte* storage = reinterpret_cast<byte*>(
+ AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
+ if (LIKELY(storage != nullptr)) {
+ BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
+ header->size_ = bytes; // Write out the block header.
+ storage += sizeof(BlockHeader);
+ ++num_blocks_;
+ }
+ return storage;
+}
+
+void BumpPointerSpace::Walk(ObjectVisitorCallback callback, void* arg) {
+ byte* pos = Begin();
+
+ {
+ MutexLock mu(Thread::Current(), block_lock_);
+ // If we have 0 blocks then we need to update the main header since we have bump pointer style
+ // allocation into an unbounded region (actually bounded by Capacity()).
+ if (num_blocks_ == 0) {
+ UpdateMainBlock();
+ }
+ }
+
+ while (pos < End()) {
+ BlockHeader* header = reinterpret_cast<BlockHeader*>(pos);
+ size_t block_size = header->size_;
+ pos += sizeof(BlockHeader); // Skip the header so that we know where the objects
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+ const mirror::Object* end = reinterpret_cast<const mirror::Object*>(pos + block_size);
+ CHECK_LE(reinterpret_cast<const byte*>(end), End());
+ // We don't know how many objects are allocated in the current block. When we hit a null class
+ // assume its the end. TODO: Have a thread update the header when it flushes the block?
+ while (obj < end && obj->GetClass() != nullptr) {
+ callback(obj, arg);
+ obj = GetNextObject(obj);
+ }
+ pos += block_size;
+ }
+}
+
+bool BumpPointerSpace::IsEmpty() const {
+ return Size() == sizeof(BlockHeader);
+}
+
+uint64_t BumpPointerSpace::GetBytesAllocated() {
+ // Start out pre-determined amount (blocks which are not being allocated into).
+ uint64_t total = static_cast<uint64_t>(bytes_allocated_.load());
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+ MutexLock mu3(Thread::Current(), block_lock_);
+ // If we don't have any blocks, we don't have any thread local buffers. This check is required
+ // since there can exist multiple bump pointer spaces which exist at the same time.
+ if (num_blocks_ > 0) {
+ for (Thread* thread : thread_list) {
+ total += thread->thread_local_pos_ - thread->thread_local_start_;
+ }
+ }
+ return total;
+}
+
+uint64_t BumpPointerSpace::GetObjectsAllocated() {
+ // Start out pre-determined amount (blocks which are not being allocated into).
+ uint64_t total = static_cast<uint64_t>(objects_allocated_.load());
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+ MutexLock mu3(Thread::Current(), block_lock_);
+ // If we don't have any blocks, we don't have any thread local buffers. This check is required
+ // since there can exist multiple bump pointer spaces which exist at the same time.
+ if (num_blocks_ > 0) {
+ for (Thread* thread : thread_list) {
+ total += thread->thread_local_objects_;
+ }
+ }
+ return total;
+}
+
+void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
+ objects_allocated_.fetch_add(thread->thread_local_objects_);
+ bytes_allocated_.fetch_add(thread->thread_local_pos_ - thread->thread_local_start_);
+ thread->SetTLAB(nullptr, nullptr);
+}
+
+bool BumpPointerSpace::AllocNewTLAB(Thread* self, size_t bytes) {
+ MutexLock mu(Thread::Current(), block_lock_);
+ RevokeThreadLocalBuffersLocked(self);
+ byte* start = AllocBlock(bytes);
+ if (start == nullptr) {
+ return false;
+ }
+ self->SetTLAB(start, start + bytes);
+ return true;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 2edd3e2..0a4be8a 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -17,6 +17,7 @@
#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_
#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_
+#include "root_visitor.h"
#include "space.h"
namespace art {
@@ -45,12 +46,13 @@
// Allocate num_bytes, returns nullptr if the space is full.
virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated);
mirror::Object* AllocNonvirtual(size_t num_bytes);
+ mirror::Object* AllocNonvirtualWithoutAccounting(size_t num_bytes);
// Return the storage space required by obj.
virtual size_t AllocationSize(const mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Nos unless we support free lists.
+ // NOPS unless we support free lists.
virtual size_t Free(Thread*, mirror::Object*) {
return 0;
}
@@ -92,21 +94,12 @@
void Dump(std::ostream& os) const;
- uint64_t GetBytesAllocated() {
- return Size();
- }
+ void RevokeThreadLocalBuffers(Thread* thread);
+ void RevokeAllThreadLocalBuffers();
- uint64_t GetObjectsAllocated() {
- return num_objects_allocated_;
- }
-
- uint64_t GetTotalBytesAllocated() {
- return total_bytes_allocated_;
- }
-
- uint64_t GetTotalObjectsAllocated() {
- return total_objects_allocated_;
- }
+ uint64_t GetBytesAllocated() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint64_t GetObjectsAllocated() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsEmpty() const;
bool Contains(const mirror::Object* obj) const {
const byte* byte_obj = reinterpret_cast<const byte*>(obj);
@@ -120,28 +113,55 @@
static mirror::Object* GetNextObject(mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Allocate a new TLAB, returns false if the allocation failed.
+ bool AllocNewTLAB(Thread* self, size_t bytes);
+
virtual BumpPointerSpace* AsBumpPointerSpace() {
return this;
}
+ // Go through all of the blocks and visit the continuous objects.
+ void Walk(ObjectVisitorCallback callback, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Object alignment within the space.
static constexpr size_t kAlignment = 8;
protected:
BumpPointerSpace(const std::string& name, MemMap* mem_map);
+ // Allocate a raw block of bytes.
+ byte* AllocBlock(size_t bytes) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
+ void RevokeThreadLocalBuffersLocked(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
+
size_t InternalAllocationSize(const mirror::Object* obj);
mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
- // Approximate number of bytes which have been allocated into the space.
- AtomicInteger num_objects_allocated_;
- AtomicInteger total_bytes_allocated_;
- AtomicInteger total_objects_allocated_;
+ // The main block is an unbounded block where objects go when there are no other blocks. This
+ // enables us to maintain tightly packed objects when you are not using thread local buffers for
+ // allocation.
+ // The main block is also the block which starts at address 0.
+ void UpdateMainBlock() EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
byte* growth_end_;
+ AtomicInteger objects_allocated_; // Accumulated from revoked thread local regions.
+ AtomicInteger bytes_allocated_; // Accumulated from revoked thread local regions.
+ Mutex block_lock_;
+
+ // The number of blocks in the space, if it is 0 then the space has one long continuous block
+ // which doesn't have an updated header.
+ size_t num_blocks_ GUARDED_BY(block_lock_);
private:
+ struct BlockHeader {
+ size_t size_; // Size of the block in bytes, does not include the header.
+ size_t unused_; // Ensures alignment of kAlignment.
+ };
+
+ COMPILE_ASSERT(sizeof(BlockHeader) % kAlignment == 0,
+ continuous_block_must_be_kAlignment_aligned);
+
friend class collector::MarkSweep;
DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace);
};
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 10e9ed8..a4e6eda 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -38,7 +38,7 @@
DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
byte* end, byte* limit, size_t growth_limit)
: MallocSpace(name, mem_map, begin, end, limit, growth_limit),
- total_bytes_freed_(0), total_objects_freed_(0), mspace_(mspace), mspace_for_alloc_(mspace) {
+ mspace_(mspace), mspace_for_alloc_(mspace) {
CHECK(mspace != NULL);
}
@@ -148,9 +148,7 @@
CHECK(ptr != NULL);
CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this;
}
- const size_t bytes_freed = InternalAllocationSize(ptr);
- total_bytes_freed_ += bytes_freed;
- ++total_objects_freed_;
+ const size_t bytes_freed = AllocationSizeNonvirtual(ptr);
if (kRecentFreeCount > 0) {
RegisterRecentFree(ptr);
}
@@ -170,7 +168,7 @@
// The head of chunk for the allocation is sizeof(size_t) behind the allocation.
__builtin_prefetch(reinterpret_cast<char*>(ptrs[i + look_ahead]) - sizeof(size_t));
}
- bytes_freed += InternalAllocationSize(ptr);
+ bytes_freed += AllocationSizeNonvirtual(ptr);
}
if (kRecentFreeCount > 0) {
@@ -196,8 +194,6 @@
{
MutexLock mu(self, lock_);
- total_bytes_freed_ += bytes_freed;
- total_objects_freed_ += num_ptrs;
mspace_bulk_free(mspace_, reinterpret_cast<void**>(ptrs), num_ptrs);
return bytes_freed;
}
@@ -211,13 +207,8 @@
return heap->GetNonMovingSpace()->MoreCore(increment);
}
-// Virtual functions can't get inlined.
-inline size_t DlMallocSpace::InternalAllocationSize(const mirror::Object* obj) {
- return AllocationSizeNonvirtual(obj);
-}
-
size_t DlMallocSpace::AllocationSize(const mirror::Object* obj) {
- return InternalAllocationSize(obj);
+ return AllocationSizeNonvirtual(obj);
}
size_t DlMallocSpace::Trim() {
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index d18d4ad..73e65d4 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -86,12 +86,6 @@
uint64_t GetBytesAllocated();
uint64_t GetObjectsAllocated();
- uint64_t GetTotalBytesAllocated() {
- return GetBytesAllocated() + total_bytes_freed_;
- }
- uint64_t GetTotalObjectsAllocated() {
- return GetObjectsAllocated() + total_objects_freed_;
- }
// Returns the class of a recently freed object.
mirror::Class* FindRecentFreedObject(const mirror::Object* obj);
@@ -112,20 +106,14 @@
byte* limit, size_t growth_limit);
private:
- size_t InternalAllocationSize(const mirror::Object* obj);
-
mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
- void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) {
+ void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, bool /*low_memory_mode*/) {
return CreateMspace(base, morecore_start, initial_size);
}
static void* CreateMspace(void* base, size_t morecore_start, size_t initial_size);
- // Approximate number of bytes and objects which have been deallocated in the space.
- size_t total_bytes_freed_;
- size_t total_objects_freed_;
-
// The boundary tag overhead.
static const size_t kChunkOverhead = kWordSize;
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 785b5ed..46df0a1 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -176,7 +176,7 @@
DCHECK(temp_bitmap_.get() == NULL);
}
-MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name) {
+MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name, bool low_memory_mode) {
// For RosAlloc, revoke thread local runs before creating a new
// alloc space so that we won't mix thread local runs from different
// alloc spaces.
@@ -213,7 +213,7 @@
UniquePtr<MemMap> mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name,
PROT_READ | PROT_WRITE, &error_msg));
CHECK(mem_map.get() != nullptr) << error_msg;
- void* allocator = CreateAllocator(end_, starting_size, initial_size);
+ void* allocator = CreateAllocator(end_, starting_size, initial_size, low_memory_mode);
// Protect memory beyond the initial size.
byte* end = mem_map->Begin() + starting_size;
if (capacity - initial_size > 0) {
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 0f882d3..d25f9cb 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -127,13 +127,14 @@
virtual MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator,
byte* begin, byte* end, byte* limit, size_t growth_limit) = 0;
- // Turn ourself into a zygote space and return a new alloc space which has our unused memory.
- MallocSpace* CreateZygoteSpace(const char* alloc_space_name);
+ // Turn ourself into a zygote space and return a new alloc space
+ // which has our unused memory. When true, the low memory mode
+ // argument specifies that the heap wishes the created space to be
+ // more aggressive in releasing unused pages.
+ MallocSpace* CreateZygoteSpace(const char* alloc_space_name, bool low_memory_mode);
virtual uint64_t GetBytesAllocated() = 0;
virtual uint64_t GetObjectsAllocated() = 0;
- virtual uint64_t GetTotalBytesAllocated() = 0;
- virtual uint64_t GetTotalObjectsAllocated() = 0;
// Returns the old mark bitmap.
accounting::SpaceBitmap* BindLiveToMarkBitmap();
@@ -154,7 +155,11 @@
static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size,
size_t* growth_limit, size_t* capacity, byte* requested_begin);
- virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) = 0;
+ // When true the low memory mode argument specifies that the heap
+ // wishes the created allocator to be more aggressive in releasing
+ // unused pages.
+ virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size,
+ bool low_memory_mode) = 0;
void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_);
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 1f8e324..80fdb6c 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -45,7 +45,7 @@
}
RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit,
- size_t capacity, byte* requested_begin) {
+ size_t capacity, byte* requested_begin, bool low_memory_mode) {
uint64_t start_time = 0;
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
start_time = NanoTime();
@@ -68,7 +68,8 @@
<< PrettySize(capacity);
return NULL;
}
- allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size);
+ allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size,
+ low_memory_mode);
if (rosalloc == NULL) {
LOG(ERROR) << "Failed to initialize rosalloc for alloc space (" << name << ")";
return NULL;
@@ -97,13 +98,18 @@
return space;
}
-allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size) {
+allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size,
+ bool low_memory_mode) {
// clear errno to allow PLOG on error
errno = 0;
// create rosalloc using our backing storage starting at begin and
// with a footprint of morecore_start. When morecore_start bytes of
// memory is exhaused morecore will be called.
- allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc(begin, morecore_start);
+ allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc(
+ begin, morecore_start,
+ low_memory_mode ?
+ art::gc::allocator::RosAlloc::kPageReleaseModeAll :
+ art::gc::allocator::RosAlloc::kPageReleaseModeSizeAndEnd);
if (rosalloc != NULL) {
rosalloc->SetFootprintLimit(initial_size);
} else {
@@ -146,9 +152,7 @@
CHECK(ptr != NULL);
CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this;
}
- const size_t bytes_freed = InternalAllocationSize(ptr);
- total_bytes_freed_atomic_.fetch_add(bytes_freed);
- ++total_objects_freed_atomic_;
+ const size_t bytes_freed = AllocationSizeNonvirtual(ptr);
if (kRecentFreeCount > 0) {
MutexLock mu(self, lock_);
RegisterRecentFree(ptr);
@@ -168,7 +172,7 @@
if (kPrefetchDuringRosAllocFreeList && i + look_ahead < num_ptrs) {
__builtin_prefetch(reinterpret_cast<char*>(ptrs[i + look_ahead]));
}
- bytes_freed += InternalAllocationSize(ptr);
+ bytes_freed += AllocationSizeNonvirtual(ptr);
}
if (kRecentFreeCount > 0) {
@@ -193,8 +197,6 @@
}
rosalloc_->BulkFree(self, reinterpret_cast<void**>(ptrs), num_ptrs);
- total_bytes_freed_atomic_.fetch_add(bytes_freed);
- total_objects_freed_atomic_.fetch_add(num_ptrs);
return bytes_freed;
}
@@ -206,20 +208,23 @@
return heap->GetNonMovingSpace()->MoreCore(increment);
}
-// Virtual functions can't get inlined.
-inline size_t RosAllocSpace::InternalAllocationSize(const mirror::Object* obj) {
+size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) {
return AllocationSizeNonvirtual(obj);
}
-size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) {
- return InternalAllocationSize(obj);
-}
-
size_t RosAllocSpace::Trim() {
- MutexLock mu(Thread::Current(), lock_);
- // Trim to release memory at the end of the space.
- rosalloc_->Trim();
- // No inspect_all necessary here as trimming of pages is built-in.
+ {
+ MutexLock mu(Thread::Current(), lock_);
+ // Trim to release memory at the end of the space.
+ rosalloc_->Trim();
+ }
+ // Attempt to release pages if it does not release all empty pages.
+ if (!rosalloc_->DoesReleaseAllPages()) {
+ VLOG(heap) << "RosAllocSpace::Trim() ";
+ size_t reclaimed = 0;
+ InspectAllRosAlloc(DlmallocMadviseCallback, &reclaimed);
+ return reclaimed;
+ }
return 0;
}
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 6311580..b0c07fa 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -38,7 +38,7 @@
// the caller should call Begin on the returned space to confirm the
// request was granted.
static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
- size_t capacity, byte* requested_begin);
+ size_t capacity, byte* requested_begin, bool low_memory_mode);
virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes,
size_t* bytes_allocated) LOCKS_EXCLUDED(lock_);
@@ -83,12 +83,6 @@
uint64_t GetBytesAllocated();
uint64_t GetObjectsAllocated();
- uint64_t GetTotalBytesAllocated() {
- return GetBytesAllocated() + total_bytes_freed_atomic_;
- }
- uint64_t GetTotalObjectsAllocated() {
- return GetObjectsAllocated() + total_objects_freed_atomic_;
- }
void RevokeThreadLocalBuffers(Thread* thread);
void RevokeAllThreadLocalBuffers();
@@ -112,23 +106,18 @@
byte* begin, byte* end, byte* limit, size_t growth_limit);
private:
- size_t InternalAllocationSize(const mirror::Object* obj);
mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated);
- void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) {
- return CreateRosAlloc(base, morecore_start, initial_size);
+ void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, bool low_memory_mode) {
+ return CreateRosAlloc(base, morecore_start, initial_size, low_memory_mode);
}
- static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size);
-
+ static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size,
+ bool low_memory_mode);
void InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
void* arg)
LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_);
- // Approximate number of bytes and objects which have been deallocated in the space.
- AtomicInteger total_bytes_freed_atomic_;
- AtomicInteger total_objects_freed_atomic_;
-
// Underlying rosalloc.
art::gc::allocator::RosAlloc* const rosalloc_;
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index ca39175..db3aca9 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -198,10 +198,6 @@
virtual uint64_t GetBytesAllocated() = 0;
// Number of objects currently allocated.
virtual uint64_t GetObjectsAllocated() = 0;
- // Number of bytes allocated since the space was created.
- virtual uint64_t GetTotalBytesAllocated() = 0;
- // Number of objects allocated since the space was created.
- 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/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
index 60c3b1c..b1be9d8 100644
--- a/runtime/gc/space/space_test.cc
+++ b/runtime/gc/space/space_test.cc
@@ -58,7 +58,8 @@
}
static MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
size_t capacity, byte* requested_begin) {
- return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
+ return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
+ Runtime::Current()->GetHeap()->IsLowMemoryMode());
}
typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
@@ -178,7 +179,7 @@
// Make sure that the zygote space isn't directly at the start of the space.
space->Alloc(self, 1U * MB, &dummy);
- space = space->CreateZygoteSpace("alloc space");
+ space = space->CreateZygoteSpace("alloc space", Runtime::Current()->GetHeap()->IsLowMemoryMode());
// Make space findable to the heap, will also delete space when runtime is cleaned up
AddSpace(space);
diff --git a/runtime/image.cc b/runtime/image.cc
index dd9b6d7..702cc9a 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '0', '5', '\0' };
+const byte ImageHeader::kImageVersion[] = { '0', '0', '6', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/locks.h b/runtime/locks.h
index 2308e95..72d4f65 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -41,6 +41,8 @@
kRosAllocBracketLock,
kRosAllocBulkFreeLock,
kAllocSpaceLock,
+ kDexFileMethodInlinerLock,
+ kDexFileToMethodInlinerMapLock,
kMarkSweepMarkStackLock,
kDefaultMutexLevel,
kMarkSweepLargeObjectLock,
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index cf4b48c..bd81bd5 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -83,9 +83,10 @@
}
gc::Heap* heap = Runtime::Current()->GetHeap();
SetLengthVisitor visitor(component_count);
+ DCHECK(allocator_type != gc::kAllocatorTypeLOS);
return down_cast<Array*>(
- heap->AllocObjectWithAllocator<kIsInstrumented>(self, array_class, size, allocator_type,
- visitor));
+ heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
+ allocator_type, visitor));
}
template <bool kIsInstrumented>
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 3a28974..e0fab8c 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -361,8 +361,8 @@
inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) {
CheckObjectAlloc();
gc::Heap* heap = Runtime::Current()->GetHeap();
- return heap->AllocObjectWithAllocator<kIsInstrumented>(self, this, this->object_size_,
- allocator_type);
+ return heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, this, this->object_size_,
+ allocator_type);
}
inline Object* Class::AllocObject(Thread* self) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 2746e1e..bd965fa 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -139,9 +139,11 @@
// slashes (so "java.lang.String" but "[Ljava.lang.String;"). Madness.
String* Class::ComputeName() {
String* name = GetName();
- if (name != NULL) {
+ if (name != nullptr) {
return name;
}
+ Thread* self = Thread::Current();
+ SirtRef<mirror::Class> sirt_c(self, this);
std::string descriptor(ClassHelper(this).GetDescriptor());
if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
// The descriptor indicates that this is the class for
@@ -160,7 +162,7 @@
default:
LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]);
}
- name = String::AllocFromModifiedUtf8(Thread::Current(), c_name);
+ name = String::AllocFromModifiedUtf8(self, c_name);
} else {
// Convert the UTF-8 name to a java.lang.String. The name must use '.' to separate package
// components.
@@ -169,9 +171,9 @@
descriptor.erase(descriptor.size() - 1);
}
std::replace(descriptor.begin(), descriptor.end(), '/', '.');
- name = String::AllocFromModifiedUtf8(Thread::Current(), descriptor.c_str());
+ name = String::AllocFromModifiedUtf8(self, descriptor.c_str());
}
- SetName(name);
+ sirt_c->SetName(name);
return name;
}
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 67c4505..6a04c3a 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -272,6 +272,7 @@
allocSize += malloc_space->GetFootprint();
allocUsed += malloc_space->GetBytesAllocated();
} else if (space->IsBumpPointerSpace()) {
+ ScopedObjectAccess soa(env);
gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace();
allocSize += bump_pointer_space->Size();
allocUsed += bump_pointer_space->GetBytesAllocated();
diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc
index 2e3d6a6..7fa9457 100644
--- a/runtime/native/dalvik_system_Zygote.cc
+++ b/runtime/native/dalvik_system_Zygote.cc
@@ -351,13 +351,14 @@
if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
// Mount entire external storage tree for all users
- if (mount(source, target, NULL, MS_BIND, NULL) == -1) {
+ if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) {
PLOG(WARNING) << "Failed to mount " << source << " to " << target;
return false;
}
} else {
// Only mount user-specific external storage
- if (mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL) == -1) {
+ if (TEMP_FAILURE_RETRY(
+ mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL)) == -1) {
PLOG(WARNING) << "Failed to mount " << source_user << " to " << target_user;
return false;
}
@@ -368,7 +369,8 @@
}
// Finally, mount user-specific path into place for legacy users
- if (mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL) == -1) {
+ if (TEMP_FAILURE_RETRY(
+ mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
PLOG(WARNING) << "Failed to mount " << target_user << " to " << legacy;
return false;
}
@@ -407,7 +409,8 @@
jint debug_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
- jstring java_se_info, jstring java_se_name, bool is_system_server) {
+ jstring java_se_info, jstring java_se_name,
+ bool is_system_server) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote";
if (!runtime->PreZygoteFork()) {
@@ -527,14 +530,16 @@
}
static jint Zygote_nativeForkAndSpecialize(JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
- jint debug_flags, jobjectArray rlimits, jint mount_external,
- jstring se_info, jstring se_name) {
- return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external, se_info, se_name, false);
+ jint debug_flags, jobjectArray rlimits,
+ jint mount_external, jstring se_info, jstring se_name) {
+ return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external,
+ se_info, se_name, false);
}
static jint Zygote_nativeForkSystemServer(JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint debug_flags, jobjectArray rlimits,
- jlong permittedCapabilities, jlong effectiveCapabilities) {
+ jlong permittedCapabilities,
+ jlong effectiveCapabilities) {
pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
debug_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h
index d52f351..78c30ff 100644
--- a/runtime/root_visitor.h
+++ b/runtime/root_visitor.h
@@ -17,6 +17,9 @@
#ifndef ART_RUNTIME_ROOT_VISITOR_H_
#define ART_RUNTIME_ROOT_VISITOR_H_
+// For size_t.
+#include <stdlib.h>
+
namespace art {
namespace mirror {
class Object;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e1b4d7e..25623a1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -70,7 +70,7 @@
Runtime* Runtime::instance_ = NULL;
Runtime::Runtime()
- : is_compiler_(false),
+ : compiler_callbacks_(nullptr),
is_zygote_(false),
is_concurrent_gc_enabled_(true),
is_explicit_gc_disabled_(false),
@@ -355,7 +355,7 @@
parsed->heap_min_free_ = gc::Heap::kDefaultMinFree;
parsed->heap_max_free_ = gc::Heap::kDefaultMaxFree;
parsed->heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization;
- parsed->heap_growth_limit_ = 0; // 0 means no growth limit.
+ parsed->heap_growth_limit_ = 0; // 0 means no growth limit .
// Default to number of processors minus one since the main GC thread also does work.
parsed->parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
// Only the main GC thread, no workers.
@@ -365,8 +365,9 @@
parsed->stack_size_ = 0; // 0 means default.
parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
parsed->low_memory_mode_ = false;
+ parsed->use_tlab_ = false;
- parsed->is_compiler_ = false;
+ parsed->compiler_callbacks_ = nullptr;
parsed->is_zygote_ = false;
parsed->interpreter_only_ = false;
parsed->is_explicit_gc_disabled_ = false;
@@ -540,12 +541,15 @@
parsed->ignore_max_footprint_ = true;
} else if (option == "-XX:LowMemoryMode") {
parsed->low_memory_mode_ = true;
+ } else if (option == "-XX:UseTLAB") {
+ parsed->use_tlab_ = true;
} else if (StartsWith(option, "-D")) {
parsed->properties_.push_back(option.substr(strlen("-D")));
} else if (StartsWith(option, "-Xjnitrace:")) {
parsed->jni_trace_ = option.substr(strlen("-Xjnitrace:"));
- } else if (option == "compiler") {
- parsed->is_compiler_ = true;
+ } else if (option == "compilercallbacks") {
+ parsed->compiler_callbacks_ =
+ reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
} else if (option == "-Xzygote") {
parsed->is_zygote_ = true;
} else if (option == "-Xint") {
@@ -669,7 +673,7 @@
parsed->boot_class_path_string_.replace(core_jar_pos, core_jar.size(), "/core-libart.jar");
}
- if (!parsed->is_compiler_ && parsed->image_.empty()) {
+ if (parsed->compiler_callbacks_ == nullptr && parsed->image_.empty()) {
parsed->image_ += GetAndroidRoot();
parsed->image_ += "/framework/boot.art";
}
@@ -882,7 +886,7 @@
class_path_string_ = options->class_path_string_;
properties_ = options->properties_;
- is_compiler_ = options->is_compiler_;
+ compiler_callbacks_ = options->compiler_callbacks_;
is_zygote_ = options->is_zygote_;
is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
@@ -925,7 +929,8 @@
options->low_memory_mode_,
options->long_pause_log_threshold_,
options->long_gc_log_threshold_,
- options->ignore_max_footprint_);
+ options->ignore_max_footprint_,
+ options->use_tlab_);
dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 01a605a..7b57dda 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -56,6 +56,7 @@
class MethodVerifier;
}
class ClassLinker;
+class CompilerCallbacks;
class DexFile;
class InternTable;
struct JavaVMExt;
@@ -100,10 +101,11 @@
std::string image_;
bool check_jni_;
std::string jni_trace_;
- bool is_compiler_;
+ CompilerCallbacks* compiler_callbacks_;
bool is_zygote_;
bool interpreter_only_;
bool is_explicit_gc_disabled_;
+ bool use_tlab_;
size_t long_pause_log_threshold_;
size_t long_gc_log_threshold_;
bool dump_gc_performance_on_shutdown_;
@@ -147,7 +149,11 @@
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
bool IsCompiler() const {
- return is_compiler_;
+ return compiler_callbacks_ != nullptr;
+ }
+
+ CompilerCallbacks* GetCompilerCallbacks() {
+ return compiler_callbacks_;
}
bool IsZygote() const {
@@ -468,7 +474,7 @@
// A pointer to the active runtime or NULL.
static Runtime* instance_;
- bool is_compiler_;
+ CompilerCallbacks* compiler_callbacks_;
bool is_zygote_;
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index e47fd37..6f3c117 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -154,6 +154,18 @@
}
}
+inline size_t Thread::TLABSize() const {
+ return thread_local_end_ - thread_local_pos_;
+}
+
+inline mirror::Object* Thread::AllocTLAB(size_t bytes) {
+ DCHECK_GE(TLABSize(), bytes);
+ ++thread_local_objects_;
+ mirror::Object* ret = reinterpret_cast<mirror::Object*>(thread_local_pos_);
+ thread_local_pos_ += bytes;
+ return ret;
+}
+
} // namespace art
#endif // ART_RUNTIME_THREAD_INL_H_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2861213..bc252de 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -928,7 +928,11 @@
no_thread_suspension_(0),
last_no_thread_suspension_cause_(NULL),
checkpoint_function_(0),
- thread_exit_check_count_(0) {
+ thread_exit_check_count_(0),
+ thread_local_start_(nullptr),
+ thread_local_pos_(nullptr),
+ thread_local_end_(nullptr),
+ thread_local_objects_(0) {
CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread);
state_and_flags_.as_struct.flags = 0;
state_and_flags_.as_struct.state = kNative;
@@ -2179,6 +2183,14 @@
stack_end_ = stack_begin_;
}
+void Thread::SetTLAB(byte* start, byte* end) {
+ DCHECK_LE(start, end);
+ thread_local_start_ = start;
+ thread_local_pos_ = thread_local_start_;
+ thread_local_end_ = end;
+ thread_local_objects_ = 0;
+}
+
std::ostream& operator<<(std::ostream& os, const Thread& thread) {
thread.ShortDump(os);
return os;
diff --git a/runtime/thread.h b/runtime/thread.h
index 44b2186..b01ec94 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -577,10 +577,8 @@
~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
Locks::thread_suspend_count_lock_);
void Destroy();
- friend class ThreadList; // For ~Thread and Destroy.
void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
- friend class Runtime; // For CreatePeer.
// Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and
// Dbg::Disconnected.
@@ -589,8 +587,6 @@
state_and_flags_.as_struct.state = new_state;
return old_state;
}
- friend class SignalCatcher; // For SetStateUnsafe.
- friend class Dbg; // F or SetStateUnsafe.
void VerifyStackImpl() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -731,9 +727,6 @@
// If we're blocked in MonitorEnter, this is the object we're trying to lock.
mirror::Object* monitor_enter_object_;
- friend class Monitor;
- friend class MonitorInfo;
-
// Top of linked list of stack indirect reference tables or NULL for none
StackIndirectReferenceTable* top_sirt_;
@@ -799,13 +792,20 @@
PortableEntryPoints portable_entrypoints_;
QuickEntryPoints quick_entrypoints_;
- private:
// How many times has our pthread key's destructor been called?
uint32_t thread_exit_check_count_;
- friend class ScopedThreadStateChange;
+ // Thread-local allocation pointer.
+ byte* thread_local_start_;
+ byte* thread_local_pos_;
+ byte* thread_local_end_;
+ size_t thread_local_objects_;
+ // Returns the remaining space in the TLAB.
+ size_t TLABSize() const;
+ // Doesn't check that there is room.
+ mirror::Object* AllocTLAB(size_t bytes);
+ void SetTLAB(byte* start, byte* end);
- public:
// Thread-local rosalloc runs. There are 34 size brackets in rosalloc
// runs (RosAlloc::kNumOfSizeBrackets). We can't refer to the
// RosAlloc class due to a header file circular dependency issue.
@@ -814,6 +814,15 @@
static const size_t kRosAllocNumOfSizeBrackets = 34;
void* rosalloc_runs_[kRosAllocNumOfSizeBrackets];
+ private:
+ friend class Dbg; // F or SetStateUnsafe.
+ friend class Monitor;
+ friend class MonitorInfo;
+ friend class Runtime; // For CreatePeer.
+ friend class ScopedThreadStateChange;
+ friend class SignalCatcher; // For SetStateUnsafe.
+ friend class ThreadList; // For ~Thread and Destroy.
+
DISALLOW_COPY_AND_ASSIGN(Thread);
};
diff --git a/runtime/verifier/dex_gc_map.h b/runtime/verifier/dex_gc_map.h
index 4570ae8..d77ea65 100644
--- a/runtime/verifier/dex_gc_map.h
+++ b/runtime/verifier/dex_gc_map.h
@@ -110,8 +110,6 @@
return data_;
}
- friend class MethodVerifier;
-
static const int kRegMapFormatShift = 5;
static const uint8_t kRegMapFormatMask = 0x7;
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
new file mode 100644
index 0000000..5cf234d
--- /dev/null
+++ b/runtime/verifier/method_verifier-inl.h
@@ -0,0 +1,78 @@
+/*
+ * 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_VERIFIER_METHOD_VERIFIER_INL_H_
+#define ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
+
+#include "base/logging.h"
+#include "method_verifier.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache.h"
+
+namespace art {
+namespace verifier {
+
+const DexFile::CodeItem* MethodVerifier::CodeItem() const {
+ return code_item_;
+}
+
+RegisterLine* MethodVerifier::GetRegLine(uint32_t dex_pc) {
+ return reg_table_.GetLine(dex_pc);
+}
+
+const InstructionFlags& MethodVerifier::GetInstructionFlags(size_t index) const {
+ return insn_flags_[index];
+}
+
+mirror::ClassLoader* MethodVerifier::GetClassLoader() {
+ return class_loader_->get();
+}
+
+mirror::DexCache* MethodVerifier::GetDexCache() {
+ return dex_cache_->get();
+}
+
+MethodReference MethodVerifier::GetMethodReference() const {
+ return MethodReference(dex_file_, dex_method_idx_);
+}
+
+uint32_t MethodVerifier::GetAccessFlags() const {
+ return method_access_flags_;
+}
+
+bool MethodVerifier::HasCheckCasts() const {
+ return has_check_casts_;
+}
+
+bool MethodVerifier::HasVirtualOrInterfaceInvokes() const {
+ return has_virtual_or_interface_invokes_;
+}
+
+bool MethodVerifier::HasFailures() const {
+ return !failure_messages_.empty();
+}
+
+const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
+ DCHECK(!HasFailures());
+ const RegType& result = ResolveClassAndCheckAccess(class_idx);
+ DCHECK(!HasFailures());
+ return result;
+}
+
+} // namespace verifier
+} // namespace art
+
+#endif // ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 9183b5f..f03cdcd 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -22,6 +22,7 @@
#include "base/mutex-inl.h"
#include "base/stringpiece.h"
#include "class_linker.h"
+#include "compiler_callbacks.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "dex_instruction_visitor.h"
@@ -90,28 +91,28 @@
if (klass->IsVerified()) {
return kNoFailure;
}
- mirror::Class* super = klass->GetSuperClass();
- if (super == NULL && strcmp("Ljava/lang/Object;", ClassHelper(klass).GetDescriptor()) != 0) {
- *error = "Verifier rejected class ";
- *error += PrettyDescriptor(klass);
- *error += " that has no super class";
- return kHardFailure;
- }
- if (super != NULL && super->IsFinal()) {
- *error = "Verifier rejected class ";
- *error += PrettyDescriptor(klass);
- *error += " that attempts to sub-class final class ";
- *error += PrettyDescriptor(super);
- return kHardFailure;
- }
+ bool early_failure = false;
+ std::string failure_message;
ClassHelper kh(klass);
const DexFile& dex_file = kh.GetDexFile();
const DexFile::ClassDef* class_def = kh.GetClassDef();
- if (class_def == NULL) {
- *error = "Verifier rejected class ";
- *error += PrettyDescriptor(klass);
- *error += " that isn't present in dex file ";
- *error += dex_file.GetLocation();
+ mirror::Class* super = klass->GetSuperClass();
+ if (super == NULL && strcmp("Ljava/lang/Object;", kh.GetDescriptor()) != 0) {
+ early_failure = true;
+ failure_message = " that has no super class";
+ } else if (super != NULL && super->IsFinal()) {
+ early_failure = true;
+ failure_message = " that attempts to sub-class final class " + PrettyDescriptor(super);
+ } else if (class_def == NULL) {
+ early_failure = true;
+ failure_message = " that isn't present in dex file " + dex_file.GetLocation();
+ }
+ if (early_failure) {
+ *error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message;
+ if (Runtime::Current()->IsCompiler()) {
+ ClassReference ref(&dex_file, klass->GetDexClassDefIndex());
+ Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+ }
return kHardFailure;
}
Thread* self = Thread::Current();
@@ -441,7 +442,12 @@
// Perform static instruction verification.
result = result && VerifyInstructions();
// Perform code-flow analysis and return.
- return result && VerifyCodeFlow();
+ result = result && VerifyCodeFlow();
+ // Compute information for compiler.
+ if (result && Runtime::Current()->IsCompiler()) {
+ result = Runtime::Current()->GetCompilerCallbacks()->MethodVerified(this);
+ }
+ return result;
}
std::ostream& MethodVerifier::Fail(VerifyError error) {
@@ -480,7 +486,7 @@
case VERIFY_ERROR_BAD_CLASS_HARD: {
if (Runtime::Current()->IsCompiler()) {
ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_));
- AddRejectedClass(ref);
+ Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
}
have_pending_hard_failure_ = true;
break;
@@ -1063,38 +1069,6 @@
DCHECK_NE(failures_.size(), 0U);
return false;
}
-
- // Compute information for compiler.
- if (Runtime::Current()->IsCompiler()) {
- MethodReference ref(dex_file_, dex_method_idx_);
- bool compile = IsCandidateForCompilation(ref, method_access_flags_);
- if (compile) {
- /* Generate a register map and add it to the method. */
- const std::vector<uint8_t>* dex_gc_map = GenerateGcMap();
- if (dex_gc_map == NULL) {
- DCHECK_NE(failures_.size(), 0U);
- return false; // Not a real failure, but a failure to encode
- }
- if (kIsDebugBuild) {
- VerifyGcMap(*dex_gc_map);
- }
- verifier::MethodVerifier::SetDexGcMap(ref, dex_gc_map);
- }
-
- if (has_check_casts_) {
- MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
- if (method_to_safe_casts != NULL) {
- SetSafeCastMap(ref, method_to_safe_casts);
- }
- }
-
- if (has_virtual_or_interface_invokes_) {
- MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
- if (pc_to_concrete_method != NULL) {
- SetDevirtMap(ref, pc_to_concrete_method);
- }
- }
- }
return true;
}
@@ -3910,325 +3884,6 @@
return *declaring_class_;
}
-void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits,
- size_t* log2_max_gc_pc) {
- size_t local_gc_points = 0;
- size_t max_insn = 0;
- size_t max_ref_reg = -1;
- for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
- if (insn_flags_[i].IsCompileTimeInfoPoint()) {
- local_gc_points++;
- max_insn = i;
- RegisterLine* line = reg_table_.GetLine(i);
- max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
- }
- }
- *gc_points = local_gc_points;
- *ref_bitmap_bits = max_ref_reg + 1; // if max register is 0 we need 1 bit to encode (ie +1)
- size_t i = 0;
- while ((1U << i) <= max_insn) {
- i++;
- }
- *log2_max_gc_pc = i;
-}
-
-MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() {
- /*
- * Walks over the method code and adds any cast instructions in which
- * the type cast is implicit to a set, which is used in the code generation
- * to elide these casts.
- */
- if (!failure_messages_.empty()) {
- return NULL;
- }
- UniquePtr<MethodSafeCastSet> mscs;
- const Instruction* inst = Instruction::At(code_item_->insns_);
- const Instruction* end = Instruction::At(code_item_->insns_ +
- code_item_->insns_size_in_code_units_);
-
- for (; inst < end; inst = inst->Next()) {
- 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_->get()));
- is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
- }
- }
- if (is_safe_cast) {
- if (mscs.get() == NULL) {
- mscs.reset(new MethodSafeCastSet());
- }
- mscs->insert(dex_pc);
- }
- }
- }
- return mscs.release();
-}
-
-MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() {
- // It is risky to rely on reg_types for sharpening in cases of soft
- // verification, we might end up sharpening to a wrong implementation. Just abort.
- if (!failure_messages_.empty()) {
- return NULL;
- }
-
- UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map;
- const uint16_t* insns = code_item_->insns_;
- const Instruction* inst = Instruction::At(insns);
- const Instruction* end = Instruction::At(insns + code_item_->insns_size_in_code_units_);
-
- for (; inst < end; inst = inst->Next()) {
- bool is_virtual = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
- (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE);
- bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
-
- if (!is_interface && !is_virtual) {
- continue;
- }
- // Get reg type for register holding the reference to the object that will be dispatched upon.
- uint32_t dex_pc = inst->GetDexPc(insns);
- RegisterLine* line = reg_table_.GetLine(dex_pc);
- bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
- const RegType&
- reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
-
- if (!reg_type.HasClass()) {
- // We will compute devirtualization information only when we know the Class of the reg type.
- continue;
- }
- mirror::Class* reg_class = reg_type.GetClass();
- if (reg_class->IsInterface()) {
- // We can't devirtualize when the known type of the register is an interface.
- continue;
- }
- if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
- // We can't devirtualize abstract classes except on arrays of abstract classes.
- continue;
- }
- mirror::ArtMethod* abstract_method = (*dex_cache_)->GetResolvedMethod(
- is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
- if (abstract_method == NULL) {
- // If the method is not found in the cache this means that it was never found
- // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
- continue;
- }
- // Find the concrete method.
- mirror::ArtMethod* concrete_method = NULL;
- if (is_interface) {
- concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
- }
- if (is_virtual) {
- concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
- }
- if (concrete_method == NULL || concrete_method->IsAbstract()) {
- // In cases where concrete_method is not found, or is abstract, continue to the next invoke.
- continue;
- }
- if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
- concrete_method->GetDeclaringClass()->IsFinal()) {
- // If we knew exactly the class being dispatched upon, or if the target method cannot be
- // overridden record the target to be used in the compiler driver.
- if (pc_to_concrete_method_map.get() == NULL) {
- pc_to_concrete_method_map.reset(new PcToConcreteMethodMap());
- }
- MethodReference concrete_ref(
- concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
- concrete_method->GetDexMethodIndex());
- pc_to_concrete_method_map->Put(dex_pc, concrete_ref);
- }
- }
- return pc_to_concrete_method_map.release();
-}
-
-const std::vector<uint8_t>* MethodVerifier::GenerateGcMap() {
- size_t num_entries, ref_bitmap_bits, pc_bits;
- ComputeGcMapSizes(&num_entries, &ref_bitmap_bits, &pc_bits);
- // There's a single byte to encode the size of each bitmap
- if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
- // TODO: either a better GC map format or per method failures
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
- << ref_bitmap_bits << " registers";
- return NULL;
- }
- size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
- // There are 2 bytes to encode the number of entries
- if (num_entries >= 65536) {
- // TODO: either a better GC map format or per method failures
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
- << num_entries << " entries";
- return NULL;
- }
- size_t pc_bytes;
- RegisterMapFormat format;
- if (pc_bits <= 8) {
- format = kRegMapFormatCompact8;
- pc_bytes = 1;
- } else if (pc_bits <= 16) {
- format = kRegMapFormatCompact16;
- pc_bytes = 2;
- } else {
- // TODO: either a better GC map format or per method failures
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
- << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
- return NULL;
- }
- size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
- std::vector<uint8_t>* table = new std::vector<uint8_t>;
- if (table == NULL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")";
- return NULL;
- }
- table->reserve(table_size);
- // Write table header
- table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) &
- ~DexPcToReferenceMap::kRegMapFormatMask));
- table->push_back(ref_bitmap_bytes & 0xFF);
- table->push_back(num_entries & 0xFF);
- table->push_back((num_entries >> 8) & 0xFF);
- // Write table data
- for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
- if (insn_flags_[i].IsCompileTimeInfoPoint()) {
- table->push_back(i & 0xFF);
- if (pc_bytes == 2) {
- table->push_back((i >> 8) & 0xFF);
- }
- RegisterLine* line = reg_table_.GetLine(i);
- line->WriteReferenceBitMap(*table, ref_bitmap_bytes);
- }
- }
- DCHECK_EQ(table->size(), table_size);
- return table;
-}
-
-void MethodVerifier::VerifyGcMap(const std::vector<uint8_t>& data) {
- // Check that for every GC point there is a map entry, there aren't entries for non-GC points,
- // that the table data is well formed and all references are marked (or not) in the bitmap
- DexPcToReferenceMap map(&data[0]);
- DCHECK_EQ(data.size(), map.RawSize());
- size_t map_index = 0;
- for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
- const uint8_t* reg_bitmap = map.FindBitMap(i, false);
- if (insn_flags_[i].IsCompileTimeInfoPoint()) {
- CHECK_LT(map_index, map.NumEntries());
- CHECK_EQ(map.GetDexPc(map_index), i);
- CHECK_EQ(map.GetBitMap(map_index), reg_bitmap);
- map_index++;
- RegisterLine* line = reg_table_.GetLine(i);
- for (size_t j = 0; j < code_item_->registers_size_; j++) {
- if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
- CHECK_LT(j / 8, map.RegWidth());
- CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
- } else if ((j / 8) < map.RegWidth()) {
- CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
- } else {
- // If a register doesn't contain a reference then the bitmap may be shorter than the line
- }
- }
- } else {
- CHECK(reg_bitmap == NULL);
- }
- }
-}
-
-void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* gc_map) {
- DCHECK(Runtime::Current()->IsCompiler());
- {
- WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
- DexGcMapTable::iterator it = dex_gc_maps_->find(ref);
- if (it != dex_gc_maps_->end()) {
- delete it->second;
- dex_gc_maps_->erase(it);
- }
- dex_gc_maps_->Put(ref, gc_map);
- }
- DCHECK(GetDexGcMap(ref) != NULL);
-}
-
-
-void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
- DCHECK(Runtime::Current()->IsCompiler());
- WriterMutexLock mu(Thread::Current(), *safecast_map_lock_);
- SafeCastMap::iterator it = safecast_map_->find(ref);
- if (it != safecast_map_->end()) {
- delete it->second;
- safecast_map_->erase(it);
- }
- safecast_map_->Put(ref, cast_set);
- DCHECK(safecast_map_->find(ref) != safecast_map_->end());
-}
-
-bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) {
- DCHECK(Runtime::Current()->IsCompiler());
- ReaderMutexLock mu(Thread::Current(), *safecast_map_lock_);
- SafeCastMap::const_iterator it = safecast_map_->find(ref);
- if (it == safecast_map_->end()) {
- return false;
- }
-
- // Look up the cast address in the set of safe casts
- MethodVerifier::MethodSafeCastSet::const_iterator cast_it = it->second->find(pc);
- return cast_it != it->second->end();
-}
-
-const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(MethodReference ref) {
- DCHECK(Runtime::Current()->IsCompiler());
- ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
- DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref);
- CHECK(it != dex_gc_maps_->end())
- << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file);
- CHECK(it->second != NULL);
- return it->second;
-}
-
-void MethodVerifier::SetDevirtMap(MethodReference ref,
- const PcToConcreteMethodMap* devirt_map) {
- DCHECK(Runtime::Current()->IsCompiler());
- WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_);
- DevirtualizationMapTable::iterator it = devirt_maps_->find(ref);
- if (it != devirt_maps_->end()) {
- delete it->second;
- devirt_maps_->erase(it);
- }
-
- devirt_maps_->Put(ref, devirt_map);
- DCHECK(devirt_maps_->find(ref) != devirt_maps_->end());
-}
-
-const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref,
- uint32_t dex_pc) {
- DCHECK(Runtime::Current()->IsCompiler());
- ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_);
- DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref);
- if (it == devirt_maps_->end()) {
- return NULL;
- }
-
- // Look up the PC in the map, get the concrete method to execute and return its reference.
- MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method
- = it->second->find(dex_pc);
- if (pc_to_concrete_method != it->second->end()) {
- return &(pc_to_concrete_method->second);
- } else {
- return NULL;
- }
-}
-
std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_pc) {
RegisterLine* line = reg_table_.GetLine(dex_pc);
std::vector<int32_t> result;
@@ -4273,120 +3928,14 @@
return result;
}
-bool MethodVerifier::IsCandidateForCompilation(MethodReference& method_ref,
- const uint32_t access_flags) {
-#ifdef ART_SEA_IR_MODE
- bool use_sea = Runtime::Current()->IsSeaIRMode();
- use_sea = use_sea && (std::string::npos != PrettyMethod(
- method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
- if (use_sea) return true;
-#endif
- // Don't compile class initializers, ever.
- if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
- return false;
- }
- return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
-}
-
-ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
-MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::safecast_map_lock_ = NULL;
-MethodVerifier::SafeCastMap* MethodVerifier::safecast_map_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::devirt_maps_lock_ = NULL;
-MethodVerifier::DevirtualizationMapTable* MethodVerifier::devirt_maps_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::rejected_classes_lock_ = NULL;
-MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL;
-
void MethodVerifier::Init() {
- if (Runtime::Current()->IsCompiler()) {
- dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
- }
-
- safecast_map_lock_ = new ReaderWriterMutex("verifier Cast Elision lock");
- {
- WriterMutexLock mu(self, *safecast_map_lock_);
- safecast_map_ = new MethodVerifier::SafeCastMap();
- }
-
- devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
-
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
- }
-
- rejected_classes_lock_ = new ReaderWriterMutex("verifier rejected classes lock");
- {
- WriterMutexLock mu(self, *rejected_classes_lock_);
- rejected_classes_ = new MethodVerifier::RejectedClassesTable;
- }
- }
art::verifier::RegTypeCache::Init();
}
void MethodVerifier::Shutdown() {
- if (Runtime::Current()->IsCompiler()) {
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- STLDeleteValues(dex_gc_maps_);
- delete dex_gc_maps_;
- dex_gc_maps_ = NULL;
- }
- delete dex_gc_maps_lock_;
- dex_gc_maps_lock_ = NULL;
-
- {
- WriterMutexLock mu(self, *safecast_map_lock_);
- STLDeleteValues(safecast_map_);
- delete safecast_map_;
- safecast_map_ = NULL;
- }
- delete safecast_map_lock_;
- safecast_map_lock_ = NULL;
-
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- STLDeleteValues(devirt_maps_);
- delete devirt_maps_;
- devirt_maps_ = NULL;
- }
- delete devirt_maps_lock_;
- devirt_maps_lock_ = NULL;
-
- {
- WriterMutexLock mu(self, *rejected_classes_lock_);
- delete rejected_classes_;
- rejected_classes_ = NULL;
- }
- delete rejected_classes_lock_;
- rejected_classes_lock_ = NULL;
- }
verifier::RegTypeCache::ShutDown();
}
-void MethodVerifier::AddRejectedClass(ClassReference ref) {
- DCHECK(Runtime::Current()->IsCompiler());
- {
- WriterMutexLock mu(Thread::Current(), *rejected_classes_lock_);
- rejected_classes_->insert(ref);
- }
- CHECK(IsClassRejected(ref));
-}
-
-bool MethodVerifier::IsClassRejected(ClassReference ref) {
- DCHECK(Runtime::Current()->IsCompiler());
- ReaderMutexLock mu(Thread::Current(), *rejected_classes_lock_);
- return (rejected_classes_->find(ref) != rejected_classes_->end());
-}
-
void MethodVerifier::VisitRoots(RootVisitor* visitor, void* arg) {
reg_types_.VisitRoots(visitor, arg);
}
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index dffda96..ac36a7e 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -183,16 +183,6 @@
// information
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static const std::vector<uint8_t>* GetDexGcMap(MethodReference ref)
- LOCKS_EXCLUDED(dex_gc_maps_lock_);
-
- static const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc)
- LOCKS_EXCLUDED(devirt_maps_lock_);
-
- // Returns true if the cast can statically be verified to be redundant
- // by using the check-cast elision peephole optimization in the verifier
- static bool IsSafeCast(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_);
-
// Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
// to the locks held at 'dex_pc' in method 'm'.
static void FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc,
@@ -212,11 +202,6 @@
static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void Shutdown();
- static void AddRejectedClass(ClassReference ref)
- LOCKS_EXCLUDED(rejected_classes_lock_);
- static bool IsClassRejected(ClassReference ref)
- LOCKS_EXCLUDED(rejected_classes_lock_);
-
bool CanLoadClasses() const {
return can_load_classes_;
}
@@ -236,11 +221,22 @@
// Describe VRegs at the given dex pc.
std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
- static bool IsCandidateForCompilation(MethodReference& method_ref,
- const uint32_t access_flags);
-
void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Accessors used by the compiler via CompilerCallback
+ const DexFile::CodeItem* CodeItem() const;
+ RegisterLine* GetRegLine(uint32_t dex_pc);
+ const InstructionFlags& GetInstructionFlags(size_t index) const;
+ mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ MethodReference GetMethodReference() const;
+ uint32_t GetAccessFlags() const;
+ bool HasCheckCasts() const;
+ bool HasVirtualOrInterfaceInvokes() const;
+ bool HasFailures() const;
+ const RegType& ResolveCheckedClass(uint32_t class_idx)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
// Adds the given string to the beginning of the last failure message.
void PrependToLastFailMessage(std::string);
@@ -612,57 +608,8 @@
// Get a type representing the declaring class of the method.
const RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- /*
- * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
- * verification). For type-precise determination we have all the data we need, so we just need to
- * encode it in some clever fashion.
- * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
- */
- const std::vector<uint8_t>* GenerateGcMap();
-
- // Verify that the GC map associated with method_ is well formed
- void VerifyGcMap(const std::vector<uint8_t>& data);
-
- // Compute sizes for GC map data
- void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
-
InstructionFlags* CurrentInsnFlags();
- // All the GC maps that the verifier has created
- typedef SafeMap<const MethodReference, const std::vector<uint8_t>*,
- MethodReferenceComparator> DexGcMapTable;
- static ReaderWriterMutex* dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- static DexGcMapTable* dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_);
- static void SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* dex_gc_map)
- LOCKS_EXCLUDED(dex_gc_maps_lock_);
-
-
- // Cast elision types.
- typedef std::set<uint32_t> MethodSafeCastSet;
- typedef SafeMap<const MethodReference, const MethodSafeCastSet*,
- MethodReferenceComparator> SafeCastMap;
- MethodVerifier::MethodSafeCastSet* GenerateSafeCastSet()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs);
- LOCKS_EXCLUDED(safecast_map_lock_);
- static ReaderWriterMutex* safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- static SafeCastMap* safecast_map_ GUARDED_BY(safecast_map_lock_);
-
- // Devirtualization map.
- typedef SafeMap<const uint32_t, MethodReference> PcToConcreteMethodMap;
- typedef SafeMap<const MethodReference, const PcToConcreteMethodMap*,
- MethodReferenceComparator> DevirtualizationMapTable;
- MethodVerifier::PcToConcreteMethodMap* GenerateDevirtMap()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static ReaderWriterMutex* devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- static DevirtualizationMapTable* devirt_maps_ GUARDED_BY(devirt_maps_lock_);
- static void SetDevirtMap(MethodReference ref,
- const PcToConcreteMethodMap* pc_method_map)
- LOCKS_EXCLUDED(devirt_maps_lock_);
- typedef std::set<ClassReference> RejectedClassesTable;
- static ReaderWriterMutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_);
RegTypeCache reg_types_;
diff --git a/test/Android.mk b/test/Android.mk
index 1bf6074..5187724 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -71,7 +71,6 @@
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_MODULE_PATH := $(3)
LOCAL_DEX_PREOPT_IMAGE := $(TARGET_CORE_IMG_OUT)
- LOCAL_DEX_PREOPT := false
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
include $(BUILD_JAVA_LIBRARY)