Merge "ART: checkpoint mechanism optimization"
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 1f040d6..0f756ef 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -20,6 +20,21 @@
ART_TARGET_SUPPORTED_ARCH := arm arm64 mips mips64 x86 x86_64
ART_HOST_SUPPORTED_ARCH := x86 x86_64
+ART_COVERAGE := false
+
+ifeq ($(ART_COVERAGE),true)
+# https://gcc.gnu.org/onlinedocs/gcc/Cross-profiling.html
+GCOV_PREFIX := /data/local/tmp/gcov
+# GCOV_PREFIX_STRIP is an integer that defines how many levels should be
+# stripped off the beginning of the path. We want the paths in $GCOV_PREFIX to
+# be relative to $ANDROID_BUILD_TOP so we can just adb pull from the top and not
+# have to worry about placing things ourselves.
+GCOV_PREFIX_STRIP := $(shell echo $(ANDROID_BUILD_TOP) | grep -o / | wc -l)
+GCOV_ENV := GCOV_PREFIX=$(GCOV_PREFIX) GCOV_PREFIX_STRIP=$(GCOV_PREFIX_STRIP)
+else
+GCOV_ENV :=
+endif
+
ifeq (,$(filter $(TARGET_ARCH),$(ART_TARGET_SUPPORTED_ARCH)))
$(warning unsupported TARGET_ARCH=$(TARGET_ARCH))
endif
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index ad4c987..3000cdf 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -238,10 +238,14 @@
ifeq ($(HOST_OS),linux)
# Larger frame-size for host clang builds today
- ifndef SANITIZE_HOST
- art_host_non_debug_cflags += -Wframe-larger-than=2700
+ ifneq ($(ART_COVERAGE),true)
+ ifneq ($(NATIVE_COVERAGE),true)
+ ifndef SANITIZE_HOST
+ art_host_non_debug_cflags += -Wframe-larger-than=2700
+ endif
+ art_target_non_debug_cflags += -Wframe-larger-than=1728
+ endif
endif
- art_target_non_debug_cflags += -Wframe-larger-than=1728
endif
ifndef LIBART_IMG_HOST_BASE_ADDRESS
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 4bb2cb1..dfea6e1 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -129,6 +129,8 @@
endif
endif
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
ifeq ($$(art_target_or_host),target)
include $(BUILD_EXECUTABLE)
ART_TARGET_EXECUTABLES := $(ART_TARGET_EXECUTABLES) $$(foreach name,$$(art_out_binary_name),$(TARGET_OUT_EXECUTABLES)/$$(name))
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a5cfcba..b4eb0c0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -275,7 +275,7 @@
$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID
$(hide) adb shell chmod 755 $(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
- (adb shell "LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+ (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(3) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
$(ART_TARGET_NATIVETEST_DIR)/$(TARGET_$(2)ARCH)/$(1) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID" \
&& (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$$$$PPID /tmp/ \
&& $$(call ART_TEST_PASSED,$$@)) \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 83ab730..b87201a 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -100,8 +100,9 @@
optimizing/inliner.cc \
optimizing/instruction_simplifier.cc \
optimizing/intrinsics.cc \
- optimizing/intrinsics_x86_64.cc \
optimizing/intrinsics_arm64.cc \
+ optimizing/intrinsics_x86_64.cc \
+ optimizing/licm.cc \
optimizing/locations.cc \
optimizing/nodes.cc \
optimizing/optimization.cc \
@@ -241,6 +242,9 @@
else
LOCAL_SHARED_LIBRARIES += libvixl
endif
+
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
ifeq ($$(art_target_or_host),target)
# For atrace.
LOCAL_SHARED_LIBRARIES += libcutils
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 05414b3..8718191 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -116,13 +116,14 @@
BasicBlock* bb = *p_bb;
if (mir != NULL) {
mir = mir->next;
- if (mir == NULL) {
+ while (mir == NULL) {
bb = GetBasicBlock(bb->fall_through);
if ((bb == NULL) || Predecessors(bb) != 1) {
- mir = NULL;
+ // mir is null and we cannot proceed further.
+ break;
} else {
- *p_bb = bb;
- mir = bb->first_mir_insn;
+ *p_bb = bb;
+ mir = bb->first_mir_insn;
}
}
}
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 5538d79..13f9072 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <string>
+#include <sstream>
#include "backend_arm.h"
#include "base/logging.h"
@@ -490,6 +491,24 @@
buf += *fmt++;
}
}
+ // Dump thread offset.
+ std::string fmt_str = GetTargetInstFmt(lir->opcode);
+ if (std::string::npos != fmt_str.find(", [!1C, #!2") && rARM_SELF == lir->operands[1] &&
+ std::string::npos != buf.find(", [")) {
+ int offset = lir->operands[2];
+ if (std::string::npos != fmt_str.find("#!2d")) {
+ } else if (std::string::npos != fmt_str.find("#!2E")) {
+ offset *= 4;
+ } else if (std::string::npos != fmt_str.find("#!2F")) {
+ offset *= 2;
+ } else {
+ LOG(FATAL) << "Should not reach here";
+ }
+ std::ostringstream tmp_stream;
+ Thread::DumpThreadOffset<4>(tmp_stream, offset);
+ buf += " ; ";
+ buf += tmp_stream.str();
+ }
return buf;
}
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index d2204f5..6492442 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -400,8 +400,9 @@
}
static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) {
- // Emit relative calls anywhere in the image or within a dex file otherwise.
- return cu->compiler_driver->IsImage() || cu->dex_file == target_method.dex_file;
+ UNUSED(cu, target_method);
+ // Always emit relative calls.
+ return true;
}
/*
diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc
index 34662f2..136be94 100644
--- a/compiler/dex/quick/arm64/target_arm64.cc
+++ b/compiler/dex/quick/arm64/target_arm64.cc
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <string>
+#include <sstream>
#include "backend_arm64.h"
#include "base/logging.h"
@@ -522,6 +523,24 @@
buf += *fmt++;
}
}
+ // Dump thread offset.
+ std::string fmt_str = GetTargetInstFmt(lir->opcode);
+ if (std::string::npos != fmt_str.find(", [!1X, #!2") && rxSELF == lir->operands[1] &&
+ std::string::npos != buf.find(", [")) {
+ int offset = lir->operands[2];
+ if (std::string::npos != fmt_str.find("#!2d")) {
+ } else if (std::string::npos != fmt_str.find("#!2D")) {
+ offset *= (IS_WIDE(lir->opcode)) ? 8 : 4;
+ } else if (std::string::npos != fmt_str.find("#!2F")) {
+ offset *= 2;
+ } else {
+ LOG(FATAL) << "Should not reach here";
+ }
+ std::ostringstream tmp_stream;
+ Thread::DumpThreadOffset<8>(tmp_stream, offset);
+ buf += " ; ";
+ buf += tmp_stream.str();
+ }
return buf;
}
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 0337096..43ebf55 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -2303,9 +2303,9 @@
StoreFinalValue(rl_dest, rl_result);
} else {
int displacement = SRegOffset(rl_result.s_reg_low);
+ ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
LIR *l = NewLIR4(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg(),
extract_index);
- AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */);
AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */);
}
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index cc59a2f..7451bd5 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1267,14 +1267,14 @@
}
// TODO: support patching on all architectures.
use_dex_cache = use_dex_cache || (force_relocations && !support_boot_image_fixup_);
- mirror::Class* declaring_class = method->GetDeclaringClass();
- bool method_code_in_boot = (declaring_class->GetClassLoader() == nullptr);
+ bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
if (!use_dex_cache) {
if (!method_code_in_boot) {
use_dex_cache = true;
} else {
- bool has_clinit_trampoline = method->IsStatic() && !declaring_class->IsInitialized();
- if (has_clinit_trampoline && (declaring_class != referrer_class)) {
+ bool has_clinit_trampoline =
+ method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
+ if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
// Ensure we run the clinit trampoline unless we are invoking a static method in the same
// class.
use_dex_cache = true;
@@ -1285,15 +1285,7 @@
*stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
}
if (!use_dex_cache && force_relocations) {
- bool is_in_image;
- if (IsImage()) {
- is_in_image = IsImageClass(method->GetDeclaringClassDescriptor());
- } else {
- is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 &&
- Runtime::Current()->GetHeap()->FindSpaceFromObject(declaring_class,
- false)->IsImageSpace();
- }
- if (!is_in_image) {
+ if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
// We can only branch directly to Methods that are resolved in the DexCache.
// Otherwise we won't invoke the resolution trampoline.
use_dex_cache = true;
@@ -1302,7 +1294,7 @@
// The method is defined not within this dex file. We need a dex cache slot within the current
// dex file or direct pointers.
bool must_use_direct_pointers = false;
- if (target_method->dex_file == declaring_class->GetDexCache()->GetDexFile()) {
+ if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) {
target_method->dex_method_index = method->GetDexMethodIndex();
} else {
if (no_guarantee_of_dex_cache_entry) {
@@ -1315,7 +1307,7 @@
} else {
if (force_relocations && !use_dex_cache) {
target_method->dex_method_index = method->GetDexMethodIndex();
- target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
}
must_use_direct_pointers = true;
}
@@ -1338,14 +1330,14 @@
*type = sharp_type;
*direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
*direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
- target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else if (!must_use_direct_pointers) {
// Set the code and rely on the dex cache for the method.
*type = sharp_type;
if (force_relocations) {
*direct_code = -1;
- target_method->dex_file = declaring_class->GetDexCache()->GetDexFile();
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else {
*direct_code = compiler_->GetEntryPointOf(method);
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 7516811..3c36ffa 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1103,18 +1103,10 @@
if (UNLIKELY(target_offset == 0)) {
mirror::ArtMethod* target = GetTargetMethod(patch);
DCHECK(target != nullptr);
- size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet());
- const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size);
- if (oat_code_offset != 0) {
- DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
- DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
- DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset));
- target_offset = PointerToLowMemUInt32(oat_code_offset);
- } else {
- target_offset = target->IsNative()
- ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
- : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
- }
+ DCHECK_EQ(target->GetQuickOatCodeOffset(), 0u);
+ target_offset = target->IsNative()
+ ? writer_->oat_header_->GetQuickGenericJniTrampolineOffset()
+ : writer_->oat_header_->GetQuickToInterpreterBridgeOffset();
}
return target_offset;
}
@@ -1146,9 +1138,10 @@
void PatchCodeAddress(std::vector<uint8_t>* code, uint32_t offset, uint32_t target_offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uint32_t address = writer_->image_writer_ == nullptr ? target_offset :
- PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
- writer_->oat_data_offset_ + target_offset);
+ // NOTE: Direct calls across oat files don't use linker patches.
+ DCHECK(writer_->image_writer_ != nullptr);
+ uint32_t address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() +
+ writer_->oat_data_offset_ + target_offset);
DCHECK_LE(offset + 4, code->size());
uint8_t* data = &(*code)[offset];
data[0] = address & 0xffu;
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index b6aaab5..3dcb08d 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -652,7 +652,7 @@
graph->AddBlock(block);
entry->AddSuccessor(block);
HInstruction* new_array = new (allocator)
- HNewArray(constant_10, 0, Primitive::kPrimInt);
+ HNewArray(constant_10, 0, Primitive::kPrimInt, kQuickAllocArray);
block->AddInstruction(new_array);
block->AddInstruction(new (allocator) HGoto());
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 955deaa..c509606 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -190,37 +190,37 @@
template<typename T>
void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
int32_t target_offset = instruction.GetTargetOffset();
- PotentiallyAddSuspendCheck(target_offset, dex_pc);
+ HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+ HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+ DCHECK(branch_target != nullptr);
+ DCHECK(fallthrough_target != nullptr);
+ PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
T* comparison = new (arena_) T(first, second);
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
- HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
- target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
+ current_block_->AddSuccessor(branch_target);
+ current_block_->AddSuccessor(fallthrough_target);
current_block_ = nullptr;
}
template<typename T>
void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
int32_t target_offset = instruction.GetTargetOffset();
- PotentiallyAddSuspendCheck(target_offset, dex_pc);
+ HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
+ HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
+ DCHECK(branch_target != nullptr);
+ DCHECK(fallthrough_target != nullptr);
+ PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
T* comparison = new (arena_) T(value, GetIntConstant(0));
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
- HBasicBlock* target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
- target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(target != nullptr);
- current_block_->AddSuccessor(target);
+ current_block_->AddSuccessor(branch_target);
+ current_block_->AddSuccessor(fallthrough_target);
current_block_ = nullptr;
}
@@ -847,7 +847,10 @@
uint32_t* args,
uint32_t register_index) {
HInstruction* length = GetIntConstant(number_of_vreg_arguments);
- HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index);
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
+ HInstruction* object = new (arena_) HNewArray(length, dex_pc, type_index, entrypoint);
current_block_->AddInstruction(object);
const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -987,6 +990,11 @@
return true;
}
+bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index) const {
+ return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index);
+}
+
void HGraphBuilder::BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc) {
SwitchTable table(instruction, dex_pc, false);
@@ -1026,7 +1034,9 @@
bool is_last_case, const SwitchTable& table,
HInstruction* value, int32_t case_value_int,
int32_t target_offset, uint32_t dex_pc) {
- PotentiallyAddSuspendCheck(target_offset, dex_pc);
+ HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
+ DCHECK(case_target != nullptr);
+ PotentiallyAddSuspendCheck(case_target, dex_pc);
// The current case's value.
HInstruction* this_case_value = GetIntConstant(case_value_int);
@@ -1038,8 +1048,6 @@
current_block_->AddInstruction(ifinst);
// Case hit: use the target offset to determine where to go.
- HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(case_target != nullptr);
current_block_->AddSuccessor(case_target);
// Case miss: go to the next case (or default fall-through).
@@ -1064,10 +1072,19 @@
}
}
-void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc) {
+void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) {
+ int32_t target_offset = target->GetDexPc() - dex_pc;
if (target_offset <= 0) {
- // Unconditionnally add a suspend check to backward branches. We can remove
- // them after we recognize loops in the graph.
+ // DX generates back edges to the first encountered return. We can save
+ // time of later passes by not adding redundant suspend checks.
+ HInstruction* last_in_target = target->GetLastInstruction();
+ if (last_in_target != nullptr &&
+ (last_in_target->IsReturn() || last_in_target->IsReturnVoid())) {
+ return;
+ }
+
+ // Add a suspend check to backward branches which may potentially loop. We
+ // can remove them after we recognize loops in the graph.
current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_pc));
}
}
@@ -1189,9 +1206,9 @@
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
int32_t offset = instruction.GetTargetOffset();
- PotentiallyAddSuspendCheck(offset, dex_pc);
HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
DCHECK(target != nullptr);
+ PotentiallyAddSuspendCheck(target, dex_pc);
current_block_->AddInstruction(new (arena_) HGoto());
current_block_->AddSuccessor(target);
current_block_ = nullptr;
@@ -1772,16 +1789,24 @@
}
case Instruction::NEW_INSTANCE: {
- current_block_->AddInstruction(
- new (arena_) HNewInstance(dex_pc, instruction.VRegB_21c()));
+ uint16_t type_index = instruction.VRegB_21c();
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+ ? kQuickAllocObjectWithAccessCheck
+ : kQuickAllocObject;
+
+ current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint));
UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
break;
}
case Instruction::NEW_ARRAY: {
+ uint16_t type_index = instruction.VRegC_22c();
HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
current_block_->AddInstruction(
- new (arena_) HNewArray(length, dex_pc, instruction.VRegC_22c()));
+ new (arena_) HNewArray(length, dex_pc, type_index, entrypoint));
UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index cc5f6a0..8ee27a1 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -106,8 +106,9 @@
HLocal* GetLocalAt(int register_index) const;
void UpdateLocal(int register_index, HInstruction* instruction) const;
HInstruction* LoadLocal(int register_index, Primitive::Type type) const;
- void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_pc);
+ void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc);
void InitializeParameters(uint16_t number_of_parameters);
+ bool NeedsAccessCheck(uint32_t type_index) const;
template<typename T>
void Unop_12x(const Instruction& instruction, Primitive::Type type);
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 0a405c4..dc2446d 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -41,60 +41,57 @@
}
void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
- const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
- DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
- DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
- Initialize();
-
DCHECK_EQ(frame_size_, kUninitializedFrameSize);
+
+ Initialize();
if (!is_leaf) {
MarkNotLeaf();
}
- ComputeFrameSize(GetGraph()->GetNumberOfLocalVRegs()
- + GetGraph()->GetTemporariesVRegSlots()
- + 1 /* filler */,
- 0, /* the baseline compiler does not have live registers at slow path */
- 0, /* the baseline compiler does not have live registers at slow path */
- GetGraph()->GetMaximumNumberOfOutVRegs()
- + 1 /* current method */);
- GenerateFrameEntry();
+ InitializeCodeGeneration(GetGraph()->GetNumberOfLocalVRegs()
+ + GetGraph()->GetTemporariesVRegSlots()
+ + 1 /* filler */,
+ 0, /* the baseline compiler does not have live registers at slow path */
+ 0, /* the baseline compiler does not have live registers at slow path */
+ GetGraph()->GetMaximumNumberOfOutVRegs()
+ + 1 /* current method */,
+ GetGraph()->GetBlocks());
+ CompileInternal(allocator, /* is_baseline */ true);
+}
+void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) {
HGraphVisitor* location_builder = GetLocationBuilder();
HGraphVisitor* instruction_visitor = GetInstructionVisitor();
- for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
- HBasicBlock* block = blocks.Get(i);
+ DCHECK_EQ(current_block_index_, 0u);
+ GenerateFrameEntry();
+ for (size_t e = block_order_->Size(); current_block_index_ < e; ++current_block_index_) {
+ HBasicBlock* block = block_order_->Get(current_block_index_);
Bind(block);
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
- current->Accept(location_builder);
- InitLocations(current);
+ if (is_baseline) {
+ current->Accept(location_builder);
+ InitLocations(current);
+ }
current->Accept(instruction_visitor);
}
}
- GenerateSlowPaths();
+
+ // Generate the slow paths.
+ for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
+ slow_paths_.Get(i)->EmitNativeCode(this);
+ }
+
+ // Finalize instructions in assember;
Finalize(allocator);
}
void CodeGenerator::CompileOptimized(CodeAllocator* allocator) {
- // The frame size has already been computed during register allocation.
+ // The register allocator already called `InitializeCodeGeneration`,
+ // where the frame size has been computed.
DCHECK_NE(frame_size_, kUninitializedFrameSize);
- const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
- DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
- DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
+ DCHECK(block_order_ != nullptr);
Initialize();
-
- GenerateFrameEntry();
- HGraphVisitor* instruction_visitor = GetInstructionVisitor();
- for (size_t i = 0, e = blocks.Size(); i < e; ++i) {
- HBasicBlock* block = blocks.Get(i);
- Bind(block);
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* current = it.Current();
- current->Accept(instruction_visitor);
- }
- }
- GenerateSlowPaths();
- Finalize(allocator);
+ CompileInternal(allocator, /* is_baseline */ false);
}
void CodeGenerator::Finalize(CodeAllocator* allocator) {
@@ -105,12 +102,6 @@
GetAssembler()->FinalizeInstructions(code);
}
-void CodeGenerator::GenerateSlowPaths() {
- for (size_t i = 0, e = slow_paths_.Size(); i < e; ++i) {
- slow_paths_.Get(i)->EmitNativeCode(this);
- }
-}
-
size_t CodeGenerator::FindFreeEntry(bool* array, size_t length) {
for (size_t i = 0; i < length; ++i) {
if (!array[i]) {
@@ -136,10 +127,14 @@
return -1;
}
-void CodeGenerator::ComputeFrameSize(size_t number_of_spill_slots,
- size_t maximum_number_of_live_core_registers,
- size_t maximum_number_of_live_fp_registers,
- size_t number_of_out_slots) {
+void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
+ size_t maximum_number_of_live_core_registers,
+ size_t maximum_number_of_live_fp_registers,
+ size_t number_of_out_slots,
+ const GrowableArray<HBasicBlock*>& block_order) {
+ block_order_ = &block_order;
+ DCHECK(block_order_->Get(0) == GetGraph()->GetEntryBlock());
+ DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), block_order_->Get(1)));
ComputeSpillMask();
first_register_slot_in_slow_path_ = (number_of_out_slots + number_of_spill_slots) * kVRegSize;
@@ -326,8 +321,9 @@
}
bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
- // We currently iterate over the block in insertion order.
- return current->GetBlockId() + 1 == next->GetBlockId();
+ DCHECK_EQ(block_order_->Get(current_block_index_), current);
+ return (current_block_index_ < block_order_->Size() - 1)
+ && (block_order_->Get(current_block_index_ + 1) == next);
}
CodeGenerator* CodeGenerator::Create(HGraph* graph,
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 45f02e5..ab63b91 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -113,10 +113,11 @@
virtual size_t GetWordSize() const = 0;
virtual size_t GetFloatingPointSpillSlotSize() const = 0;
virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
- void ComputeFrameSize(size_t number_of_spill_slots,
- size_t maximum_number_of_live_core_registers,
- size_t maximum_number_of_live_fp_registers,
- size_t number_of_out_slots);
+ void InitializeCodeGeneration(size_t number_of_spill_slots,
+ size_t maximum_number_of_live_core_registers,
+ size_t maximum_number_of_live_fp_registers,
+ size_t number_of_out_slots,
+ const GrowableArray<HBasicBlock*>& block_order);
int32_t GetStackSlot(HLocal* local) const;
Location GetTemporaryLocation(HTemporary* temp) const;
@@ -181,8 +182,6 @@
slow_paths_.Add(slow_path);
}
- void GenerateSlowPaths();
-
void BuildMappingTable(std::vector<uint8_t>* vector, DefaultSrcMap* src_map) const;
void BuildVMapTable(std::vector<uint8_t>* vector) const;
void BuildNativeGCMap(
@@ -253,6 +252,8 @@
compiler_options_(compiler_options),
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
+ block_order_(nullptr),
+ current_block_index_(0),
is_leaf_(true),
stack_map_stream_(graph->GetArena()) {}
@@ -312,6 +313,7 @@
private:
void InitLocations(HInstruction* instruction);
size_t GetStackOffsetOfSavedRegister(size_t index);
+ void CompileInternal(CodeAllocator* allocator, bool is_baseline);
HGraph* const graph_;
const CompilerOptions& compiler_options_;
@@ -319,6 +321,13 @@
GrowableArray<PcInfo> pc_infos_;
GrowableArray<SlowPathCode*> slow_paths_;
+ // The order to use for code generation.
+ const GrowableArray<HBasicBlock*>* block_order_;
+
+ // The current block index in `block_order_` of the block
+ // we are generating code for.
+ size_t current_block_index_;
+
bool is_leaf_;
StackMapStream stack_map_stream_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 7731a10..b0cd7ba 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2416,8 +2416,9 @@
InvokeRuntimeCallingConvention calling_convention;
codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
- codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+ codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
}
void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
@@ -2434,8 +2435,9 @@
InvokeRuntimeCallingConvention calling_convention;
codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
- codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+ codegen_->InvokeRuntime(GetThreadOffset<kArmWordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 78ae55e..3bc23fe 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2112,7 +2112,9 @@
codegen_->LoadCurrentMethod(current_method);
__ Mov(type_index, instruction->GetTypeIndex());
codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
+ GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
void*, uint32_t, int32_t, mirror::ArtMethod*>();
}
@@ -2136,7 +2138,9 @@
codegen_->LoadCurrentMethod(current_method);
__ Mov(type_index, instruction->GetTypeIndex());
codegen_->InvokeRuntime(
- QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
+ GetThreadOffset<kArm64WordSize>(instruction->GetEntrypoint()).Int32Value(),
+ instruction,
+ instruction->GetDexPc());
CheckEntrypointTypes<kQuickAllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*>();
}
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 063550b..c840793 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -17,6 +17,7 @@
#include "code_generator_x86.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
#include "mirror/array-inl.h"
#include "mirror/art_method.h"
@@ -2555,8 +2556,7 @@
codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
- __ fs()->call(
- Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
+ __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
@@ -2577,8 +2577,7 @@
codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(2));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
- __ fs()->call(
- Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocArrayWithAccessCheck)));
+ __ fs()->call(Address::Absolute(GetThreadOffset<kX86WordSize>(instruction->GetEntrypoint())));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
DCHECK(!codegen_->IsLeafMethod());
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 90b7bda..2ff53a0 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2424,8 +2424,8 @@
codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
__ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
- __ gs()->call(Address::Absolute(
- QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
+ __ gs()->call(
+ Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2446,8 +2446,8 @@
codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(2)));
__ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
- __ gs()->call(Address::Absolute(
- QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocArrayWithAccessCheck), true));
+ __ gs()->call(
+ Address::Absolute(GetThreadOffset<kX86_64WordSize>(instruction->GetEntrypoint()), true));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 35c5269..4ebb136 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -153,8 +153,9 @@
? use->GetBlock()->GetPhis()
: use->GetBlock()->GetInstructions();
if (!list.Contains(use)) {
- AddError(StringPrintf("User %d of instruction %d is not defined "
+ AddError(StringPrintf("User %s:%d of instruction %d is not defined "
"in a basic block of the control-flow graph.",
+ use->DebugName(),
use->GetId(),
instruction->GetId()));
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index ef461d9..22a3d12 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -18,6 +18,7 @@
#include "code_generator.h"
#include "nodes.h"
+#include "optimization.h"
#include "ssa_liveness_analysis.h"
namespace art {
@@ -216,6 +217,14 @@
}
}
output_ << " (liveness: " << instruction->GetLifetimePosition() << ")";
+ } else if (pass_name_ == kLoopInvariantCodeMotionPassName) {
+ output_ << " ( loop_header:";
+ HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+ if (info == nullptr) {
+ output_ << "null )";
+ } else {
+ output_ << "B" << info->GetHeader()->GetBlockId() << " )";
+ }
}
}
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index b90d15e..8d6fe04 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -27,10 +27,6 @@
class DexCompilationUnit;
class HGraph;
-// TODO: Create an analysis/optimization abstraction.
-static const char* kLivenessPassName = "liveness";
-static const char* kRegisterAllocatorPassName = "register";
-
/**
* This class outputs the HGraph in the C1visualizer format.
* Note: Currently only works if the compiler is single threaded.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 532167c..41e5164 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -35,24 +35,26 @@
namespace art {
static constexpr int kMaxInlineCodeUnits = 100;
-static constexpr int kMaxInlineNumberOfBlocks = 3;
+static constexpr int kDepthLimit = 5;
void HInliner::Run() {
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- for (HInstructionIterator instr_it(it.Current()->GetInstructions());
- !instr_it.Done();
- instr_it.Advance()) {
- HInvokeStaticOrDirect* current = instr_it.Current()->AsInvokeStaticOrDirect();
- if (current != nullptr) {
- if (!TryInline(current, current->GetDexMethodIndex(), current->GetInvokeType())) {
+ const GrowableArray<HBasicBlock*>& blocks = graph_->GetReversePostOrder();
+ for (size_t i = 0; i < blocks.Size(); ++i) {
+ HBasicBlock* block = blocks.Get(i);
+ for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) {
+ HInstruction* next = instruction->GetNext();
+ HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
+ if (call != nullptr) {
+ if (!TryInline(call, call->GetDexMethodIndex(), call->GetInvokeType())) {
if (kIsDebugBuild) {
std::string callee_name =
- PrettyMethod(current->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
+ PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
bool should_inline = callee_name.find("$inline$") != std::string::npos;
CHECK(!should_inline) << "Could not inline " << callee_name;
}
}
}
+ instruction = next;
}
}
}
@@ -137,13 +139,6 @@
return false;
}
- if (callee_graph->GetBlocks().Size() > kMaxInlineNumberOfBlocks) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
- << " has too many blocks to be inlined: "
- << callee_graph->GetBlocks().Size();
- return false;
- }
-
if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
compiler_driver_->GetInstructionSet())) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
@@ -157,36 +152,6 @@
return false;
}
- HReversePostOrderIterator it(*callee_graph);
- it.Advance(); // Past the entry block to avoid seeing the suspend check.
- for (; !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- if (block->IsLoopHeader()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
- << " could not be inlined because it contains a loop";
- return false;
- }
-
- for (HInstructionIterator instr_it(block->GetInstructions());
- !instr_it.Done();
- instr_it.Advance()) {
- HInstruction* current = instr_it.Current();
- if (current->CanThrow()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
- << " could not be inlined because " << current->DebugName()
- << " can throw";
- return false;
- }
-
- if (current->NeedsEnvironment()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
- << " could not be inlined because " << current->DebugName()
- << " needs an environment";
- return false;
- }
- }
- }
-
// Run simple optimizations on the graph.
SsaRedundantPhiElimination redundant_phi(callee_graph);
SsaDeadPhiElimination dead_phi(callee_graph);
@@ -207,6 +172,46 @@
optimization->Run();
}
+ if (depth_ + 1 < kDepthLimit) {
+ HInliner inliner(
+ callee_graph, outer_compilation_unit_, compiler_driver_, outer_stats_, depth_ + 1);
+ inliner.Run();
+ }
+
+ HReversePostOrderIterator it(*callee_graph);
+ it.Advance(); // Past the entry block, it does not contain instructions that prevent inlining.
+ for (; !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (block->IsLoopHeader()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because it contains a loop";
+ return false;
+ }
+
+ for (HInstructionIterator instr_it(block->GetInstructions());
+ !instr_it.Done();
+ instr_it.Advance()) {
+ HInstruction* current = instr_it.Current();
+ if (current->IsSuspendCheck()) {
+ continue;
+ }
+
+ if (current->CanThrow()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " can throw";
+ return false;
+ }
+
+ if (current->NeedsEnvironment()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " needs an environment";
+ return false;
+ }
+ }
+ }
+
callee_graph->InlineInto(graph_, invoke_instruction);
// Now that we have inlined the callee, we need to update the next
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 370e33c..07d893e 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -33,11 +33,13 @@
HInliner(HGraph* outer_graph,
const DexCompilationUnit& outer_compilation_unit,
CompilerDriver* compiler_driver,
- OptimizingCompilerStats* stats)
+ OptimizingCompilerStats* stats,
+ size_t depth = 0)
: HOptimization(outer_graph, true, "inliner"),
outer_compilation_unit_(outer_compilation_unit),
compiler_driver_(compiler_driver),
- outer_stats_(stats) {}
+ outer_stats_(stats),
+ depth_(depth) {}
void Run() OVERRIDE;
@@ -47,6 +49,7 @@
const DexCompilationUnit& outer_compilation_unit_;
CompilerDriver* const compiler_driver_;
OptimizingCompilerStats* const outer_stats_;
+ const size_t depth_;
DISALLOW_COPY_AND_ASSIGN(HInliner);
};
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
new file mode 100644
index 0000000..10f24d8
--- /dev/null
+++ b/compiler/optimizing/licm.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 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 "licm.h"
+#include "side_effects_analysis.h"
+
+namespace art {
+
+static bool IsPhiOf(HInstruction* instruction, HBasicBlock* block) {
+ return instruction->IsPhi() && instruction->GetBlock() == block;
+}
+
+/**
+ * Returns whether `instruction` has all its inputs and environment defined
+ * before the loop it is in.
+ */
+static bool InputsAreDefinedBeforeLoop(HInstruction* instruction) {
+ DCHECK(instruction->IsInLoop());
+ HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
+ for (HInputIterator it(instruction); !it.Done(); it.Advance()) {
+ HLoopInformation* input_loop = it.Current()->GetBlock()->GetLoopInformation();
+ // We only need to check whether the input is defined in the loop. If it is not
+ // it is defined before the loop.
+ if (input_loop != nullptr && input_loop->IsIn(*info)) {
+ return false;
+ }
+ }
+
+ if (instruction->HasEnvironment()) {
+ HEnvironment* environment = instruction->GetEnvironment();
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* input = environment->GetInstructionAt(i);
+ if (input != nullptr) {
+ HLoopInformation* input_loop = input->GetBlock()->GetLoopInformation();
+ if (input_loop != nullptr && input_loop->IsIn(*info)) {
+ // We can move an instruction that takes a loop header phi in the environment:
+ // we will just replace that phi with its first input later in `UpdateLoopPhisIn`.
+ bool is_loop_header_phi = IsPhiOf(input, info->GetHeader());
+ if (!is_loop_header_phi) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * If `environment` has a loop header phi, we replace it with its first input.
+ */
+static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) {
+ for (size_t i = 0, e = environment->Size(); i < e; ++i) {
+ HInstruction* input = environment->GetInstructionAt(i);
+ if (input != nullptr && IsPhiOf(input, info->GetHeader())) {
+ HUseListNode<HEnvironment*>* env_use = environment->GetInstructionEnvUseAt(i);
+ input->RemoveEnvironmentUser(env_use);
+ HInstruction* incoming = input->InputAt(0);
+ environment->SetRawEnvAt(i, incoming);
+ incoming->AddEnvUseAt(environment, i);
+ }
+ }
+}
+
+void LICM::Run() {
+ DCHECK(side_effects_.HasRun());
+ // Only used during debug.
+ ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().Size(), false);
+
+ // Post order visit to visit inner loops before outer loops.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (!block->IsLoopHeader()) {
+ // Only visit the loop when we reach the header.
+ continue;
+ }
+
+ HLoopInformation* loop_info = block->GetLoopInformation();
+ SideEffects loop_effects = side_effects_.GetLoopEffects(block);
+ HBasicBlock* pre_header = loop_info->GetPreHeader();
+
+ for (HBlocksInLoopIterator it_loop(*loop_info); !it_loop.Done(); it_loop.Advance()) {
+ HBasicBlock* inner = it_loop.Current();
+ DCHECK(inner->IsInLoop());
+ if (inner->GetLoopInformation() != loop_info) {
+ // Thanks to post order visit, inner loops were already visited.
+ DCHECK(visited.IsBitSet(inner->GetBlockId()));
+ continue;
+ }
+ visited.SetBit(inner->GetBlockId());
+
+ // We can move an instruction that can throw only if it is the first
+ // throwing instruction in the loop. Note that the first potentially
+ // throwing instruction encountered that is not hoisted stops this
+ // optimization. Non-throwing instruction can still be hoisted.
+ bool found_first_non_hoisted_throwing_instruction_in_loop = !inner->IsLoopHeader();
+ for (HInstructionIterator inst_it(inner->GetInstructions());
+ !inst_it.Done();
+ inst_it.Advance()) {
+ HInstruction* instruction = inst_it.Current();
+ if (instruction->CanBeMoved()
+ && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop)
+ && !instruction->GetSideEffects().DependsOn(loop_effects)
+ && InputsAreDefinedBeforeLoop(instruction)) {
+ // We need to update the environment if the instruction has a loop header
+ // phi in it.
+ if (instruction->NeedsEnvironment()) {
+ UpdateLoopPhisIn(instruction->GetEnvironment(), loop_info);
+ }
+ instruction->MoveBefore(pre_header->GetLastInstruction());
+ } else if (instruction->CanThrow()) {
+ // If `instruction` can throw, we cannot move further instructions
+ // that can throw as well.
+ found_first_non_hoisted_throwing_instruction_in_loop = true;
+ }
+ }
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
new file mode 100644
index 0000000..4812394
--- /dev/null
+++ b/compiler/optimizing/licm.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_LICM_H_
+#define ART_COMPILER_OPTIMIZING_LICM_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class SideEffectsAnalysis;
+
+class LICM : public HOptimization {
+ public:
+ LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
+ : HOptimization(graph, true, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+
+ void Run() OVERRIDE;
+
+ private:
+ const SideEffectsAnalysis& side_effects_;
+
+ DISALLOW_COPY_AND_ASSIGN(LICM);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_LICM_H_
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fe9ce74..f1868cb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -292,6 +292,10 @@
return true;
}
+void HLoopInformation::Add(HBasicBlock* block) {
+ blocks_.SetBit(block->GetBlockId());
+}
+
void HLoopInformation::PopulateRecursive(HBasicBlock* block) {
if (blocks_.IsBitSet(block->GetBlockId())) {
return;
@@ -707,7 +711,7 @@
return os;
}
-void HInstruction::InsertBefore(HInstruction* cursor) {
+void HInstruction::MoveBefore(HInstruction* cursor) {
next_->previous_ = previous_;
if (previous_ != nullptr) {
previous_->next_ = next_;
@@ -715,6 +719,7 @@
if (block_->instructions_.first_instruction_ == this) {
block_->instructions_.first_instruction_ = next_;
}
+ DCHECK_NE(block_->instructions_.last_instruction_, this);
previous_ = cursor->previous_;
if (previous_ != nullptr) {
@@ -723,12 +728,127 @@
next_ = cursor;
cursor->previous_ = this;
block_ = cursor->block_;
+
+ if (block_->instructions_.first_instruction_ == cursor) {
+ block_->instructions_.first_instruction_ = this;
+ }
+}
+
+HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+ DCHECK(!cursor->IsControlFlow());
+ DCHECK_NE(instructions_.last_instruction_, cursor);
+ DCHECK_EQ(cursor->GetBlock(), this);
+
+ HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
+ new_block->instructions_.first_instruction_ = cursor->GetNext();
+ new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+ cursor->next_->previous_ = nullptr;
+ cursor->next_ = nullptr;
+ instructions_.last_instruction_ = cursor;
+
+ new_block->instructions_.SetBlockOfInstructions(new_block);
+ for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) {
+ HBasicBlock* successor = GetSuccessors().Get(i);
+ new_block->successors_.Add(successor);
+ successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block);
+ }
+ successors_.Reset();
+
+ for (size_t i = 0, e = GetDominatedBlocks().Size(); i < e; ++i) {
+ HBasicBlock* dominated = GetDominatedBlocks().Get(i);
+ dominated->dominator_ = new_block;
+ new_block->dominated_blocks_.Add(dominated);
+ }
+ dominated_blocks_.Reset();
+ return new_block;
+}
+
+void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
+ for (HInstruction* current = first_instruction_;
+ current != nullptr;
+ current = current->GetNext()) {
+ current->SetBlock(block);
+ }
+}
+
+void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& instruction_list) {
+ DCHECK(Contains(cursor));
+ if (!instruction_list.IsEmpty()) {
+ if (cursor == last_instruction_) {
+ last_instruction_ = instruction_list.last_instruction_;
+ } else {
+ cursor->next_->previous_ = instruction_list.last_instruction_;
+ }
+ instruction_list.last_instruction_->next_ = cursor->next_;
+ cursor->next_ = instruction_list.first_instruction_;
+ instruction_list.first_instruction_->previous_ = cursor;
+ }
+}
+
+void HInstructionList::Add(const HInstructionList& instruction_list) {
+ DCHECK(!IsEmpty());
+ AddAfter(last_instruction_, instruction_list);
+}
+
+void HBasicBlock::MergeWith(HBasicBlock* other) {
+ DCHECK(successors_.IsEmpty()) << "Unimplemented block merge scenario";
+ DCHECK(dominated_blocks_.IsEmpty()) << "Unimplemented block merge scenario";
+ DCHECK(other->GetDominator()->IsEntryBlock() && other->GetGraph() != graph_)
+ << "Unimplemented block merge scenario";
+ DCHECK(other->GetPhis().IsEmpty());
+
+ successors_.Reset();
+ dominated_blocks_.Reset();
+ instructions_.Add(other->GetInstructions());
+ other->GetInstructions().SetBlockOfInstructions(this);
+
+ while (!other->GetSuccessors().IsEmpty()) {
+ HBasicBlock* successor = other->GetSuccessors().Get(0);
+ successor->ReplacePredecessor(other, this);
+ }
+
+ for (size_t i = 0, e = other->GetDominatedBlocks().Size(); i < e; ++i) {
+ HBasicBlock* dominated = other->GetDominatedBlocks().Get(i);
+ dominated_blocks_.Add(dominated);
+ dominated->SetDominator(this);
+ }
+ other->dominated_blocks_.Reset();
+ other->dominator_ = nullptr;
+ other->graph_ = nullptr;
+}
+
+void HBasicBlock::ReplaceWith(HBasicBlock* other) {
+ while (!GetPredecessors().IsEmpty()) {
+ HBasicBlock* predecessor = GetPredecessors().Get(0);
+ predecessor->ReplaceSuccessor(this, other);
+ }
+ while (!GetSuccessors().IsEmpty()) {
+ HBasicBlock* successor = GetSuccessors().Get(0);
+ successor->ReplacePredecessor(this, other);
+ }
+ for (size_t i = 0; i < dominated_blocks_.Size(); ++i) {
+ other->AddDominatedBlock(dominated_blocks_.Get(i));
+ }
+ GetDominator()->ReplaceDominatedBlock(this, other);
+ other->SetDominator(GetDominator());
+ dominator_ = nullptr;
+ graph_ = nullptr;
+}
+
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+static void MakeRoomFor(GrowableArray<HBasicBlock*>* blocks,
+ size_t number_of_new_blocks,
+ size_t at) {
+ size_t old_size = blocks->Size();
+ size_t new_size = old_size + number_of_new_blocks;
+ blocks->SetSize(new_size);
+ for (size_t i = old_size - 1, j = new_size - 1; i > at; --i, --j) {
+ blocks->Put(j, blocks->Get(i));
+ }
}
void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
- // We currently only support graphs with one entry block, one body block, and one exit block.
- DCHECK_EQ(GetBlocks().Size(), 3u);
-
// Walk over the entry block and:
// - Move constants from the entry block to the outer_graph's entry block,
// - Replace HParameterValue instructions with their real value.
@@ -737,7 +857,7 @@
for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
if (current->IsConstant()) {
- current->InsertBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
+ current->MoveBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
} else if (current->IsParameterValue()) {
current->ReplaceWith(invoke->InputAt(parameter_index++));
} else {
@@ -746,41 +866,122 @@
}
}
- // Insert the body's instructions except the last, just after the `invoke`
- // instruction.
- HBasicBlock* body = GetBlocks().Get(1);
- DCHECK(!body->IsExitBlock());
- HInstruction* last = body->GetLastInstruction();
- HInstruction* first = body->GetFirstInstruction();
+ if (GetBlocks().Size() == 3) {
+ // Simple case: Put the first block's instruction into `invoke`'s block.
+ HBasicBlock* body = GetBlocks().Get(1);
+ DCHECK(!body->IsExitBlock());
+ HInstruction* last = body->GetLastInstruction();
- if (first != last) {
- HInstruction* antelast = last->GetPrevious();
+ invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+ body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
- // Update the instruction list of the body to only contain the last
- // instruction.
- last->previous_ = nullptr;
- body->instructions_.first_instruction_ = last;
- body->instructions_.last_instruction_ = last;
-
- // Update the instruction list of the `invoke`'s block to now contain the
- // body's instructions.
- antelast->next_ = invoke->GetNext();
- antelast->next_->previous_ = antelast;
- first->previous_ = invoke;
- invoke->next_ = first;
-
- // Update the block pointer of all instructions.
- for (HInstruction* current = antelast; current != invoke; current = current->GetPrevious()) {
- current->SetBlock(invoke->GetBlock());
+ // Replace the invoke with the return value of the inlined graph.
+ if (last->IsReturn()) {
+ invoke->ReplaceWith(last->InputAt(0));
+ } else {
+ DCHECK(last->IsReturnVoid());
}
- }
- // Replace the invoke with the return value of the inlined graph.
- if (last->IsReturn()) {
- invoke->ReplaceWith(last->InputAt(0));
- body->RemoveInstruction(last);
+ invoke->GetBlock()->RemoveInstruction(last);
} else {
- DCHECK(last->IsReturnVoid());
+ // Need to inline multiple blocks. We split `invoke`'s block
+ // into two blocks, merge the first block of the inlined graph into
+ // the first half, and replace the exit block if the inlined graph
+ // with the second half.
+ ArenaAllocator* allocator = outer_graph->GetArena();
+ HBasicBlock* at = invoke->GetBlock();
+ HBasicBlock* to = at->SplitAfter(invoke);
+
+ HBasicBlock* first = entry_block_->GetSuccessors().Get(0);
+ DCHECK(!first->IsInLoop());
+ at->MergeWith(first);
+ exit_block_->ReplaceWith(to);
+
+ // Update all predecessors of the exit block (now the `to` block)
+ // to not `HReturn` but `HGoto` instead. Also collect the return
+ // values if any, and potentially make it a phi if there are multiple
+ // predecessors.
+ HInstruction* return_value = nullptr;
+ for (size_t i = 0, e = to->GetPredecessors().Size(); i < e; ++i) {
+ HBasicBlock* predecessor = to->GetPredecessors().Get(i);
+ HInstruction* last = predecessor->GetLastInstruction();
+ if (!last->IsReturnVoid()) {
+ if (return_value != nullptr) {
+ if (!return_value->IsPhi()) {
+ HPhi* phi = new (allocator) HPhi(
+ allocator, kNoRegNumber, to->GetPredecessors().Size(), invoke->GetType());
+ return_value->AsPhi()->AddInput(return_value);
+ to->AddPhi(phi);
+ return_value = phi;
+ }
+ return_value->AsPhi()->AddInput(last->InputAt(0));
+ } else {
+ return_value = last->InputAt(0);
+ }
+ }
+ predecessor->AddInstruction(new (allocator) HGoto());
+ predecessor->RemoveInstruction(last);
+ }
+
+ if (return_value != nullptr) {
+ invoke->ReplaceWith(return_value);
+ }
+
+ // Update the meta information surrounding blocks:
+ // (1) the graph they are now in,
+ // (2) the reverse post order of that graph,
+ // (3) the potential loop information they are now in.
+
+ // We don't add the entry block, the exit block, and the first block, which
+ // has been merged with `at`.
+ static constexpr int kNumberOfSkippedBlocksInCallee = 3;
+
+ // We add the `to` block.
+ static constexpr int kNumberOfNewBlocksInCaller = 1;
+ size_t blocks_added = (reverse_post_order_.Size() - kNumberOfSkippedBlocksInCallee)
+ + kNumberOfNewBlocksInCaller;
+
+ // Find the location of `at` in the outer graph's reverse post order. The new
+ // blocks will be added after it.
+ size_t index_of_at = 0;
+ while (outer_graph->reverse_post_order_.Get(index_of_at) != at) {
+ index_of_at++;
+ }
+ MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
+
+ // Do a reverse post order of the blocks in the callee and do (1), (2),
+ // and (3) to the blocks that apply.
+ HLoopInformation* info = at->GetLoopInformation();
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ if (current != exit_block_ && current != entry_block_ && current != first) {
+ DCHECK(!current->IsInLoop());
+ DCHECK(current->GetGraph() == this);
+ current->SetGraph(outer_graph);
+ outer_graph->AddBlock(current);
+ outer_graph->reverse_post_order_.Put(++index_of_at, current);
+ if (info != nullptr) {
+ info->Add(current);
+ current->SetLoopInformation(info);
+ }
+ }
+ }
+
+ // Do (1), (2), and (3) to `to`.
+ to->SetGraph(outer_graph);
+ outer_graph->AddBlock(to);
+ outer_graph->reverse_post_order_.Put(++index_of_at, to);
+ if (info != nullptr) {
+ info->Add(to);
+ to->SetLoopInformation(info);
+ if (info->IsBackEdge(at)) {
+ // Only `at` can become a back edge, as the inlined blocks
+ // are predecessors of `at`.
+ DCHECK_EQ(1u, info->NumberOfBackEdges());
+ info->ClearBackEdges();
+ info->AddBackEdge(to);
+ }
+ }
}
// Finally remove the invoke from the caller.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 3e4028e..30d869d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_NODES_H_
#define ART_COMPILER_OPTIMIZING_NODES_H_
+#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "invoke_type.h"
#include "locations.h"
#include "offsets.h"
@@ -72,6 +73,15 @@
bool FoundBefore(const HInstruction* instruction1,
const HInstruction* instruction2) const;
+ bool IsEmpty() const { return first_instruction_ == nullptr; }
+ void Clear() { first_instruction_ = last_instruction_ = nullptr; }
+
+ // Update the block of all instructions to be `block`.
+ void SetBlockOfInstructions(HBasicBlock* block) const;
+
+ void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+ void Add(const HInstructionList& instruction_list);
+
private:
HInstruction* first_instruction_;
HInstruction* last_instruction_;
@@ -240,6 +250,10 @@
return header_;
}
+ void SetHeader(HBasicBlock* block) {
+ header_ = block;
+ }
+
HSuspendCheck* GetSuspendCheck() const { return suspend_check_; }
void SetSuspendCheck(HSuspendCheck* check) { suspend_check_ = check; }
bool HasSuspendCheck() const { return suspend_check_ != nullptr; }
@@ -287,6 +301,8 @@
const ArenaBitVector& GetBlocks() const { return blocks_; }
+ void Add(HBasicBlock* block);
+
private:
// Internal recursive implementation of `Populate`.
void PopulateRecursive(HBasicBlock* block);
@@ -350,6 +366,7 @@
}
HGraph* GetGraph() const { return graph_; }
+ void SetGraph(HGraph* graph) { graph_ = graph; }
int GetBlockId() const { return block_id_; }
void SetBlockId(int id) { block_id_ = id; }
@@ -357,6 +374,16 @@
HBasicBlock* GetDominator() const { return dominator_; }
void SetDominator(HBasicBlock* dominator) { dominator_ = dominator; }
void AddDominatedBlock(HBasicBlock* block) { dominated_blocks_.Add(block); }
+ void ReplaceDominatedBlock(HBasicBlock* existing, HBasicBlock* new_block) {
+ for (size_t i = 0, e = dominated_blocks_.Size(); i < e; ++i) {
+ if (dominated_blocks_.Get(i) == existing) {
+ dominated_blocks_.Put(i, new_block);
+ return;
+ }
+ }
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
int NumberOfBackEdges() const {
return loop_information_ == nullptr
@@ -383,10 +410,22 @@
successors_.Put(successor_index, new_block);
}
+ void ReplacePredecessor(HBasicBlock* existing, HBasicBlock* new_block) {
+ size_t predecessor_index = GetPredecessorIndexOf(existing);
+ DCHECK_NE(predecessor_index, static_cast<size_t>(-1));
+ existing->RemoveSuccessor(this);
+ new_block->successors_.Add(this);
+ predecessors_.Put(predecessor_index, new_block);
+ }
+
void RemovePredecessor(HBasicBlock* block) {
predecessors_.Delete(block);
}
+ void RemoveSuccessor(HBasicBlock* block) {
+ successors_.Delete(block);
+ }
+
void ClearAllPredecessors() {
predecessors_.Reset();
}
@@ -421,6 +460,26 @@
return -1;
}
+ // Split the block into two blocks just after `cursor`. Returns the newly
+ // created block. Note that this method just updates raw block information,
+ // like predecessors, successors, dominators, and instruction list. It does not
+ // update the graph, reverse post order, loop information, nor make sure the
+ // blocks are consistent (for example ending with a control flow instruction).
+ HBasicBlock* SplitAfter(HInstruction* cursor);
+
+ // Merge `other` at the end of `this`. Successors and dominated blocks of
+ // `other` are changed to be successors and dominated blocks of `this`. Note
+ // that this method does not update the graph, reverse post order, loop
+ // information, nor make sure the blocks are consistent (for example ending
+ // with a control flow instruction).
+ void MergeWith(HBasicBlock* other);
+
+ // Replace `this` with `other`. Predecessors, successors, and dominated blocks
+ // of `this` are moved to `other`.
+ // Note that this method does not update the graph, reverse post order, loop
+ // information, nor make sure the blocks are consistent (for example ending
+ void ReplaceWith(HBasicBlock* other);
+
void AddInstruction(HInstruction* instruction);
void RemoveInstruction(HInstruction* instruction);
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
@@ -445,8 +504,9 @@
return loop_information_;
}
- // Set the loop_information_ on this block. This method overrides the current
+ // Set the loop_information_ on this block. Overrides the current
// loop_information if it is an outer loop of the passed loop information.
+ // Note that this method is called while creating the loop information.
void SetInLoop(HLoopInformation* info) {
if (IsLoopHeader()) {
// Nothing to do. This just means `info` is an outer loop.
@@ -464,6 +524,11 @@
}
}
+ // Raw update of the loop information.
+ void SetLoopInformation(HLoopInformation* info) {
+ loop_information_ = info;
+ }
+
bool IsInLoop() const { return loop_information_ != nullptr; }
// Returns wheter this block dominates the blocked passed as parameter.
@@ -481,7 +546,7 @@
void SetIsCatchBlock() { is_catch_block_ = true; }
private:
- HGraph* const graph_;
+ HGraph* graph_;
GrowableArray<HBasicBlock*> predecessors_;
GrowableArray<HBasicBlock*> successors_;
HInstructionList instructions_;
@@ -899,8 +964,8 @@
void ReplaceWith(HInstruction* instruction);
void ReplaceInput(HInstruction* replacement, size_t index);
- // Insert `this` instruction in `cursor`'s graph, just before `cursor`.
- void InsertBefore(HInstruction* cursor);
+ // Move `this` instruction before `cursor`.
+ void MoveBefore(HInstruction* cursor);
#define INSTRUCTION_TYPE_CHECK(type, super) \
bool Is##type() const { return (As##type() != nullptr); } \
@@ -1794,10 +1859,11 @@
class HNewInstance : public HExpression<0> {
public:
- HNewInstance(uint32_t dex_pc, uint16_t type_index)
+ HNewInstance(uint32_t dex_pc, uint16_t type_index, QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
dex_pc_(dex_pc),
- type_index_(type_index) {}
+ type_index_(type_index),
+ entrypoint_(entrypoint) {}
uint32_t GetDexPc() const { return dex_pc_; }
uint16_t GetTypeIndex() const { return type_index_; }
@@ -1812,11 +1878,14 @@
bool CanBeNull() const OVERRIDE { return false; }
+ QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+
DECLARE_INSTRUCTION(NewInstance);
private:
const uint32_t dex_pc_;
const uint16_t type_index_;
+ const QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewInstance);
};
@@ -1837,10 +1906,14 @@
class HNewArray : public HExpression<1> {
public:
- HNewArray(HInstruction* length, uint32_t dex_pc, uint16_t type_index)
+ HNewArray(HInstruction* length,
+ uint32_t dex_pc,
+ uint16_t type_index,
+ QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::None()),
dex_pc_(dex_pc),
- type_index_(type_index) {
+ type_index_(type_index),
+ entrypoint_(entrypoint) {
SetRawInputAt(0, length);
}
@@ -1852,11 +1925,14 @@
bool CanBeNull() const OVERRIDE { return false; }
+ QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+
DECLARE_INSTRUCTION(NewArray);
private:
const uint32_t dex_pc_;
const uint16_t type_index_;
+ const QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewArray);
};
@@ -2168,6 +2244,8 @@
DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
};
+static constexpr uint32_t kNoRegNumber = -1;
+
class HPhi : public HInstruction {
public:
HPhi(ArenaAllocator* arena, uint32_t reg_number, size_t number_of_inputs, Primitive::Type type)
@@ -2562,6 +2640,12 @@
return MustGenerateClinitCheck() || !is_referrers_class_;
}
+ bool CanThrow() const OVERRIDE {
+ // May call runtime and and therefore can throw.
+ // TODO: finer grain decision.
+ return !is_referrers_class_;
+ }
+
DECLARE_INSTRUCTION(LoadClass);
private:
@@ -2726,12 +2810,14 @@
bool NeedsEnvironment() const OVERRIDE { return true; }
+ bool CanThrow() const OVERRIDE { return true; }
+
uint32_t GetDexPc() const { return dex_pc_; }
DECLARE_INSTRUCTION(Throw);
private:
- uint32_t dex_pc_;
+ const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HThrow);
};
@@ -3063,6 +3149,39 @@
DISALLOW_COPY_AND_ASSIGN(HPostOrderIterator);
};
+// Iterator over the blocks that art part of the loop. Includes blocks part
+// of an inner loop. The order in which the blocks are iterated is on their
+// block id.
+class HBlocksInLoopIterator : public ValueObject {
+ public:
+ explicit HBlocksInLoopIterator(const HLoopInformation& info)
+ : blocks_in_loop_(info.GetBlocks()),
+ blocks_(info.GetHeader()->GetGraph()->GetBlocks()),
+ index_(0) {
+ if (!blocks_in_loop_.IsBitSet(index_)) {
+ Advance();
+ }
+ }
+
+ bool Done() const { return index_ == blocks_.Size(); }
+ HBasicBlock* Current() const { return blocks_.Get(index_); }
+ void Advance() {
+ ++index_;
+ for (size_t e = blocks_.Size(); index_ < e; ++index_) {
+ if (blocks_in_loop_.IsBitSet(index_)) {
+ break;
+ }
+ }
+ }
+
+ private:
+ const BitVector& blocks_in_loop_;
+ const GrowableArray<HBasicBlock*>& blocks_;
+ size_t index_;
+
+ DISALLOW_COPY_AND_ASSIGN(HBlocksInLoopIterator);
+};
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index e36ef19..9315d89 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -21,6 +21,10 @@
namespace art {
+static const char* kLivenessPassName = "liveness";
+static const char* kRegisterAllocatorPassName = "register";
+static const char* kLoopInvariantCodeMotionPassName = "licm";
+
/**
* Abstraction to implement an optimization pass.
*/
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 705345b..50d7924 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -34,6 +34,7 @@
#include "inliner.h"
#include "instruction_simplifier.h"
#include "intrinsics.h"
+#include "licm.h"
#include "jni/quick/jni_compiler.h"
#include "mirror/art_method-inl.h"
#include "nodes.h"
@@ -225,6 +226,7 @@
HConstantFolding fold2(graph);
SideEffectsAnalysis side_effects(graph);
GVNOptimization gvn(graph, side_effects);
+ LICM licm(graph, side_effects);
BoundsCheckElimination bce(graph);
ReferenceTypePropagation type_propagation(graph);
InstructionSimplifier simplify2(graph, "instruction_simplifier_after_types");
@@ -242,6 +244,7 @@
&fold2,
&side_effects,
&gvn,
+ &licm,
&bce,
&type_propagation,
&simplify2
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index da6b294..a231a72 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -100,17 +100,16 @@
TEST(PrettyPrinterTest, CFG3) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 5: SuspendCheck\n"
- " 6: Goto 1\n"
+ " 4: SuspendCheck\n"
+ " 5: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
" 0: Goto 3\n"
"BasicBlock 2, pred: 3, succ: 4\n"
" 1: ReturnVoid\n"
"BasicBlock 3, pred: 1, succ: 2\n"
- " 2: SuspendCheck\n"
- " 3: Goto 2\n"
+ " 2: Goto 2\n"
"BasicBlock 4, pred: 2\n"
- " 4: Exit\n";
+ " 3: Exit\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x200,
@@ -160,15 +159,14 @@
TEST(PrettyPrinterTest, CFG5) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 4: SuspendCheck\n"
- " 5: Goto 1\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
"BasicBlock 1, pred: 0, 2, succ: 3\n"
" 0: ReturnVoid\n"
"BasicBlock 2, succ: 1\n"
- " 1: SuspendCheck\n"
- " 2: Goto 1\n"
+ " 1: Goto 1\n"
"BasicBlock 3, pred: 1\n"
- " 3: Exit\n";
+ " 2: Exit\n";
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID,
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 6f8f688..0a3f24b 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1459,9 +1459,11 @@
}
void RegisterAllocator::Resolve() {
- codegen_->ComputeFrameSize(
- spill_slots_.Size(), maximum_number_of_live_core_registers_,
- maximum_number_of_live_fp_registers_, reserved_out_slots_);
+ codegen_->InitializeCodeGeneration(spill_slots_.Size(),
+ maximum_number_of_live_core_registers_,
+ maximum_number_of_live_fp_registers_,
+ reserved_out_slots_,
+ liveness_.GetLinearOrder());
// Adjust the Out Location of instructions.
// TODO: Use pointers of Location inside LiveInterval to avoid doing another iteration.
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index 96e1c8f..ea1ca5a 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -19,6 +19,11 @@
namespace art {
void SideEffectsAnalysis::Run() {
+ // Inlining might have created more blocks, so we need to increase the size
+ // if needed.
+ block_effects_.SetSize(graph_->GetBlocks().Size());
+ loop_effects_.SetSize(graph_->GetBlocks().Size());
+
if (kIsDebugBuild) {
for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 3eccd3f..5383c28 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -25,8 +25,8 @@
namespace art {
namespace arm {
-bool Thumb2Assembler::ShifterOperandCanHold(Register rd,
- Register rn,
+bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED,
+ Register rn ATTRIBUTE_UNUSED,
Opcode opcode,
uint32_t immediate,
ShifterOperand* shifter_op) {
@@ -37,13 +37,6 @@
switch (opcode) {
case ADD:
case SUB:
- if (rn == SP) {
- if (rd == SP) {
- return immediate < (1 << 9); // 9 bits allowed.
- } else {
- return immediate < (1 << 12); // 12 bits.
- }
- }
if (immediate < (1 << 12)) { // Less than (or equal to) 12 bits can always be done.
return true;
}
@@ -713,7 +706,7 @@
}
bool can_contain_high_register = (opcode == MOV)
- || ((opcode == ADD) && (rn == rd));
+ || ((opcode == ADD) && (rn == rd) && !set_cc);
if (IsHighRegister(rd) || IsHighRegister(rn)) {
if (!can_contain_high_register) {
@@ -927,41 +920,71 @@
if (so.IsImmediate()) {
use_immediate = true;
immediate = so.GetImmediate();
+ } else {
+ // Adjust rn and rd: only two registers will be emitted.
+ switch (opcode) {
+ case AND:
+ case ORR:
+ case EOR:
+ case RSB:
+ case ADC:
+ case SBC:
+ case BIC: {
+ if (rn == rd) {
+ rn = so.GetRegister();
+ } else {
+ CHECK_EQ(rd, so.GetRegister());
+ }
+ break;
+ }
+ case CMP:
+ case CMN: {
+ CHECK_EQ(rd, 0);
+ rd = rn;
+ rn = so.GetRegister();
+ break;
+ }
+ case TST:
+ case TEQ:
+ case MVN: {
+ CHECK_EQ(rn, 0);
+ rn = so.GetRegister();
+ break;
+ }
+ default:
+ break;
+ }
}
switch (opcode) {
case AND: thumb_opcode = 0U /* 0b0000 */; break;
+ case ORR: thumb_opcode = 12U /* 0b1100 */; break;
case EOR: thumb_opcode = 1U /* 0b0001 */; break;
- case SUB: break;
case RSB: thumb_opcode = 9U /* 0b1001 */; break;
- case ADD: break;
case ADC: thumb_opcode = 5U /* 0b0101 */; break;
case SBC: thumb_opcode = 6U /* 0b0110 */; break;
- case RSC: break;
- case TST: thumb_opcode = 8U /* 0b1000 */; rn = so.GetRegister(); break;
- case TEQ: break;
- case CMP:
+ case BIC: thumb_opcode = 14U /* 0b1110 */; break;
+ case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break;
+ case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break;
+ case CMP: {
if (use_immediate) {
// T2 encoding.
- dp_opcode = 0;
- opcode_shift = 11;
- thumb_opcode = 5U /* 0b101 */;
- rd_shift = 8;
- rn_shift = 8;
+ dp_opcode = 0;
+ opcode_shift = 11;
+ thumb_opcode = 5U /* 0b101 */;
+ rd_shift = 8;
+ rn_shift = 8;
} else {
thumb_opcode = 10U /* 0b1010 */;
- rd = rn;
- rn = so.GetRegister();
}
break;
+ }
case CMN: {
+ CHECK(!use_immediate);
thumb_opcode = 11U /* 0b1011 */;
- rd = rn;
- rn = so.GetRegister();
break;
}
- case ORR: thumb_opcode = 12U /* 0b1100 */; break;
case MOV:
dp_opcode = 0;
if (use_immediate) {
@@ -984,9 +1007,11 @@
}
}
break;
- case BIC: thumb_opcode = 14U /* 0b1110 */; break;
- case MVN: thumb_opcode = 15U /* 0b1111 */; rn = so.GetRegister(); break;
+
+ case TEQ:
+ case RSC:
default:
+ LOG(FATAL) << "Invalid thumb1 opcode " << opcode;
break;
}
}
@@ -1009,7 +1034,7 @@
// ADD and SUB are complex enough to warrant their own emitter.
void Thumb2Assembler::Emit16BitAddSub(Condition cond ATTRIBUTE_UNUSED,
Opcode opcode,
- bool set_cc ATTRIBUTE_UNUSED,
+ bool set_cc,
Register rn,
Register rd,
const ShifterOperand& so) {
@@ -1031,7 +1056,7 @@
case ADD:
if (so.IsRegister()) {
Register rm = so.GetRegister();
- if (rn == rd) {
+ if (rn == rd && !set_cc) {
// Can use T2 encoding (allows 4 bit registers)
dp_opcode = 1U /* 0b01 */;
opcode_shift = 10;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 425ccd7..e571e72 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -30,11 +30,15 @@
}
std::string GetAssemblerParameters() OVERRIDE {
- return " -mthumb -mfpu=neon";
+ return " -march=armv7-a -mcpu=cortex-a15 -mfpu=neon -mthumb";
+ }
+
+ const char* GetAssemblyHeader() OVERRIDE {
+ return kThumb2AssemblyHeader;
}
std::string GetDisassembleParameters() OVERRIDE {
- return " -D -bbinary -marm --no-show-raw-insn";
+ return " -D -bbinary -marm --disassembler-options=force-thumb --no-show-raw-insn";
}
void SetUpHelpers() OVERRIDE {
@@ -76,6 +80,8 @@
private:
std::vector<arm::Register*> registers_;
+
+ static constexpr const char* kThumb2AssemblyHeader = ".syntax unified\n.thumb\n";
};
@@ -192,4 +198,21 @@
DriverStr(expected, "strexd");
}
+TEST_F(AssemblerThumb2Test, eor) {
+#define __ GetAssembler()->
+ __ eor(arm::R1, arm::R1, arm::ShifterOperand(arm::R0));
+ __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R1));
+ __ eor(arm::R1, arm::R8, arm::ShifterOperand(arm::R0));
+ __ eor(arm::R8, arm::R1, arm::ShifterOperand(arm::R0));
+ __ eor(arm::R1, arm::R0, arm::ShifterOperand(arm::R8));
+
+ const char* expected =
+ "eors r1, r0\n"
+ "eor r1, r0, r1\n"
+ "eor r1, r8, r0\n"
+ "eor r8, r1, r0\n"
+ "eor r1, r0, r8\n";
+ DriverStr(expected, "abs");
+}
+
} // namespace art
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index e3a9580..a171e59 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -309,13 +309,13 @@
// 16 bit variants.
__ add(R0, R1, ShifterOperand());
__ sub(R0, R1, ShifterOperand());
- __ and_(R0, R1, ShifterOperand());
- __ orr(R0, R1, ShifterOperand());
- __ eor(R0, R1, ShifterOperand());
- __ bic(R0, R1, ShifterOperand());
- __ adc(R0, R1, ShifterOperand());
- __ sbc(R0, R1, ShifterOperand());
- __ rsb(R0, R1, ShifterOperand());
+ __ and_(R0, R0, ShifterOperand(R1));
+ __ orr(R0, R0, ShifterOperand(R1));
+ __ eor(R0, R0, ShifterOperand(R1));
+ __ bic(R0, R0, ShifterOperand(R1));
+ __ adc(R0, R0, ShifterOperand(R1));
+ __ sbc(R0, R0, ShifterOperand(R1));
+ __ rsb(R0, R0, ShifterOperand(R1));
__ tst(R0, ShifterOperand(R1));
__ teq(R0, ShifterOperand(R1));
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 239e746..8afd443 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -35,6 +35,7 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := dalvikvm32
LOCAL_MODULE_STEM_64 := dalvikvm64
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
include $(BUILD_EXECUTABLE)
# Create symlink for the primary version target.
@@ -67,6 +68,7 @@
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := dalvikvm32
LOCAL_MODULE_STEM_64 := dalvikvm64
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
include $(BUILD_HOST_EXECUTABLE)
# Create symlink for the primary version target.
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index 735c850..c9aa8c8 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -84,6 +84,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_build.mk
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
# For disassembler_arm64.
ifeq ($$(art_ndebug_or_debug),debug)
LOCAL_SHARED_LIBRARIES += libvixld
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index bd3bebf..4ff44b4 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,7 +18,7 @@
#include <inttypes.h>
-#include <ostream>
+#include <sstream>
#include "base/logging.h"
#include "base/stringprintf.h"
@@ -27,22 +27,23 @@
namespace art {
namespace arm64 {
+// This enumeration should mirror the declarations in
+// runtime/arch/arm64/registers_arm64.h. We do not include that file to
+// avoid a dependency on libart.
+enum {
+ TR = 18,
+ ETR = 21,
+ IP0 = 16,
+ IP1 = 17,
+ FP = 29,
+ LR = 30
+};
+
void CustomDisassembler::AppendRegisterNameToOutput(
const vixl::Instruction* instr,
const vixl::CPURegister& reg) {
USE(instr);
if (reg.IsRegister()) {
- // This enumeration should mirror the declarations in
- // runtime/arch/arm64/registers_arm64.h. We do not include that file to
- // avoid a dependency on libart.
- enum {
- TR = 18,
- ETR = 21,
- IP0 = 16,
- IP1 = 17,
- FP = 29,
- LR = 30
- };
switch (reg.code()) {
case IP0: AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0"); return;
case IP1: AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1"); return;
@@ -66,16 +67,7 @@
return;
}
- char* buffer = buffer_;
- char* buffer_end = buffer_ + buffer_size_;
-
- // Find the end position in the buffer.
- while ((*buffer != 0) && (buffer < buffer_end)) {
- ++buffer;
- }
-
void* data_address = instr->LiteralAddress<void*>();
- ptrdiff_t buf_size_remaining = buffer_end - buffer;
vixl::Instr op = instr->Mask(vixl::LoadLiteralMask);
switch (op) {
@@ -84,14 +76,14 @@
case vixl::LDRSW_x_lit: {
int64_t data = op == vixl::LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address)
: *reinterpret_cast<int32_t*>(data_address);
- snprintf(buffer, buf_size_remaining, " (0x%" PRIx64 " / %" PRId64 ")", data, data);
+ AppendToOutput(" (0x%" PRIx64 " / %" PRId64 ")", data, data);
break;
}
case vixl::LDR_s_lit:
case vixl::LDR_d_lit: {
double data = (op == vixl::LDR_s_lit) ? *reinterpret_cast<float*>(data_address)
: *reinterpret_cast<double*>(data_address);
- snprintf(buffer, buf_size_remaining, " (%g)", data);
+ AppendToOutput(" (%g)", data);
break;
}
default:
@@ -99,6 +91,17 @@
}
}
+void CustomDisassembler::VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) {
+ Disassembler::VisitLoadStoreUnsignedOffset(instr);
+
+ if (instr->Rn() == TR) {
+ int64_t offset = instr->ImmLSUnsigned() << instr->SizeLS();
+ std::ostringstream tmp_stream;
+ Thread::DumpThreadOffset<8>(tmp_stream, static_cast<uint32_t>(offset));
+ AppendToOutput(" (%s)", tmp_stream.str().c_str());
+ }
+}
+
size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) {
const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin);
decoder.Decode(instr);
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index a370b8d..57f11c8 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -34,11 +34,14 @@
vixl::Disassembler(), read_literals_(read_literals) {}
// Use register aliases in the disassembly.
- virtual void AppendRegisterNameToOutput(const vixl::Instruction* instr,
- const vixl::CPURegister& reg) OVERRIDE;
+ void AppendRegisterNameToOutput(const vixl::Instruction* instr,
+ const vixl::CPURegister& reg) OVERRIDE;
// Improve the disassembly of literal load instructions.
- virtual void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+ void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE;
+
+ // Improve the disassembly of thread offset.
+ void VisitLoadStoreUnsignedOffset(const vixl::Instruction* instr) OVERRIDE;
private:
// Indicate if the disassembler should read data loaded from literal pools.
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index 1a768c8..203488d 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -119,12 +119,6 @@
DumpAddrReg(os, rex, reg_num);
}
-static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) {
- bool rex_x = (rex & REX_X) != 0;
- uint8_t reg_num = rex_x ? (reg + 8) : reg;
- DumpAddrReg(os, rex, reg_num);
-}
-
static void DumpOpcodeReg(std::ostream& os, uint8_t rex, uint8_t reg,
bool byte_operand, uint8_t size_override) {
bool rex_b = (rex & REX_B) != 0;
@@ -184,18 +178,30 @@
uint8_t index = (sib >> 3) & 7;
uint8_t base = sib & 7;
address << "[";
+
+ // REX.x is bit 3 of index.
+ if ((rex64 & REX_X) != 0) {
+ index += 8;
+ }
+
+ // Mod = 0 && base = 5 (ebp): no base (ignores REX.b).
+ bool has_base = false;
if (base != 5 || mod != 0) {
+ has_base = true;
DumpBaseReg(address, rex64, base);
- if (index != 4) {
+ }
+
+ // Index = 4 (esp/rsp) is disallowed.
+ if (index != 4) {
+ if (has_base) {
address << " + ";
}
- }
- if (index != 4) {
- DumpIndexReg(address, rex64, index);
+ DumpAddrReg(address, rex64, index);
if (scale != 0) {
address << StringPrintf(" * %d", 1 << scale);
}
}
+
if (mod == 0) {
if (base == 5) {
if (index != 4) {
diff --git a/runtime/Android.mk b/runtime/Android.mk
index a53616a..8e17191 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -300,6 +300,7 @@
gc/collector/gc_type.h \
gc/allocator_type.h \
gc/collector_type.h \
+ gc/space/region_space.h \
gc/space/space.h \
gc/heap.h \
instrumentation.h \
@@ -481,6 +482,8 @@
LOCAL_MODULE_TARGET_ARCH := $$(ART_TARGET_SUPPORTED_ARCH)
endif
+ LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
+
ifeq ($$(art_target_or_host),target)
ifneq ($$(art_ndebug_or_debug),debug)
# Leave the symbols in the shared library so that stack unwinders can
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 3430eb5..60e692b 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -756,9 +756,48 @@
GENERATE_ALL_ALLOC_ENTRYPOINTS
UNIMPLEMENTED art_quick_test_suspend
-UNIMPLEMENTED art_quick_proxy_invoke_handler
+
+ /*
+ * Called by managed code that is attempting to call a method on a proxy class. On entry
+ * r0 holds the proxy method; r1, r2 and r3 may contain arguments.
+ */
+ .extern artQuickProxyInvokeHandler
+ENTRY art_quick_proxy_invoke_handler
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ sd $a0, 0($sp) # place proxy method at bottom of frame
+ move $a2, rSELF # pass Thread::Current
+ jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP)
+ move $a3, $sp # pass $sp
+ ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
+ daddiu $sp, $sp, REFS_AND_ARGS_MINUS_REFS_SIZE # skip a0-a7 and f12-f19
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ bne $t0, $zero, 1f
+ dmtc1 $v0, $f0 # place return value to FP return value
+ jalr $zero, $ra
+ dmtc1 $v1, $f1 # place return value to FP return value
+1:
+ DELIVER_PENDING_EXCEPTION
+END art_quick_proxy_invoke_handler
+
UNIMPLEMENTED art_quick_imt_conflict_trampoline
-UNIMPLEMENTED art_quick_resolution_trampoline
+
+ .extern artQuickResolutionTrampoline
+ENTRY art_quick_resolution_trampoline
+ SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ move $a2, rSELF # pass Thread::Current
+ jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP)
+ move $a3, $sp # pass $sp
+ beq $v0, $zero, 1f
+ lwu $a0, 0($sp) # load resolved method in $a0
+ # artQuickResolutionTrampoline puts resolved method in *SP
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ move $t9, $v0 # code pointer must be in $t9 to generate the global pointer
+ jalr $zero, $t9 # tail call to method
+ nop
+1:
+ RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME
+ DELIVER_PENDING_EXCEPTION
+END art_quick_resolution_trampoline
.extern artQuickGenericJniTrampoline
.extern artQuickGenericJniEndTrampoline
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 4390180..c3e24a7 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -276,6 +276,10 @@
}
}
+#if defined(__clang__) && defined(__ARM_64BIT_STATE)
+// b/19180814 When POPCOUNT is inlined, boot up failed on arm64 devices.
+__attribute__((optnone))
+#endif
uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) {
uint32_t word_end = WordIndex(end);
uint32_t partial_word_bits = end & 0x1f;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index d89ad5e..a0e978b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4382,6 +4382,10 @@
LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c;
return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
}
+ if (c->GetClass() == nullptr) {
+ LOG(ERROR) << "Null class of class " << c << " for object " << o;
+ return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+ }
if (c->IsClassClass()) {
return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7026b21..734c935 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -819,10 +819,10 @@
void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
DCHECK(obj != nullptr);
- CHECK(collector_->heap_->GetMarkBitmap()->Test(obj)) << obj;
- CHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << obj;
+ DCHECK(collector_->heap_->GetMarkBitmap()->Test(obj)) << obj;
+ DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << obj;
obj->SetReadBarrierPointer(ReadBarrier::WhitePtr());
- CHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
+ DCHECK_EQ(obj->GetReadBarrierPointer(), ReadBarrier::WhitePtr()) << obj;
}
private:
@@ -964,10 +964,10 @@
void operator()(mirror::Object* ref) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
DCHECK(ref != nullptr);
- CHECK(collector_->region_space_bitmap_->Test(ref)) << ref;
- CHECK(collector_->region_space_->IsInUnevacFromSpace(ref)) << ref;
+ DCHECK(collector_->region_space_bitmap_->Test(ref)) << ref;
+ DCHECK(collector_->region_space_->IsInUnevacFromSpace(ref)) << ref;
if (kUseBakerReadBarrier) {
- CHECK(ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr()) << ref;
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::BlackPtr()) << ref;
// Clear the black ptr.
ref->SetReadBarrierPointer(ReadBarrier::WhitePtr());
}
@@ -1389,17 +1389,18 @@
mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
DCHECK(from_ref != nullptr);
- if (region_space_->IsInToSpace(from_ref)) {
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
// It's already marked.
return from_ref;
}
mirror::Object* to_ref;
- if (region_space_->IsInFromSpace(from_ref)) {
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
to_ref = GetFwdPtr(from_ref);
DCHECK(to_ref == nullptr || region_space_->IsInToSpace(to_ref) ||
heap_->non_moving_space_->HasAddress(to_ref))
<< "from_ref=" << from_ref << " to_ref=" << to_ref;
- } else if (region_space_->IsInUnevacFromSpace(from_ref)) {
+ } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
if (region_space_bitmap_->Test(from_ref)) {
to_ref = from_ref;
} else {
@@ -1464,12 +1465,13 @@
}
DCHECK(from_ref != nullptr);
DCHECK(heap_->collector_type_ == kCollectorTypeCC);
- if (region_space_->IsInToSpace(from_ref)) {
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
// It's already marked.
return from_ref;
}
mirror::Object* to_ref;
- if (region_space_->IsInFromSpace(from_ref)) {
+ if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
to_ref = GetFwdPtr(from_ref);
if (kUseBakerReadBarrier) {
DCHECK(to_ref != ReadBarrier::GrayPtr()) << "from_ref=" << from_ref << " to_ref=" << to_ref;
@@ -1480,7 +1482,7 @@
}
DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
<< "from_ref=" << from_ref << " to_ref=" << to_ref;
- } else if (region_space_->IsInUnevacFromSpace(from_ref)) {
+ } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
// This may or may not succeed, which is ok.
if (kUseBakerReadBarrier) {
from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index fd00739..a4ed718 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -104,7 +104,7 @@
inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size) {
- DCHECK_EQ(state_, static_cast<uint8_t>(kRegionToSpace));
+ DCHECK(IsAllocated() && IsInToSpace());
DCHECK(IsAligned<kAlignment>(num_bytes));
Atomic<uint8_t*>* atomic_top = reinterpret_cast<Atomic<uint8_t*>*>(&top_);
uint8_t* old_top;
@@ -132,7 +132,7 @@
size_t num_bytes = obj->SizeOf();
if (usable_size != nullptr) {
if (LIKELY(num_bytes <= kRegionSize)) {
- DCHECK(RefToRegion(obj)->IsNormal());
+ DCHECK(RefToRegion(obj)->IsAllocated());
*usable_size = RoundUp(num_bytes, kAlignment);
} else {
DCHECK(RefToRegion(obj)->IsLarge());
@@ -142,7 +142,7 @@
return num_bytes;
}
-template<RegionSpace::SubSpaceType kSubSpaceType>
+template<RegionSpace::RegionType kRegionType>
uint64_t RegionSpace::GetBytesAllocatedInternal() {
uint64_t bytes = 0;
MutexLock mu(Thread::Current(), region_lock_);
@@ -151,33 +151,33 @@
if (r->IsFree()) {
continue;
}
- switch (kSubSpaceType) {
- case kAllSpaces:
+ switch (kRegionType) {
+ case RegionType::kRegionTypeAll:
bytes += r->BytesAllocated();
break;
- case kFromSpace:
+ case RegionType::kRegionTypeFromSpace:
if (r->IsInFromSpace()) {
bytes += r->BytesAllocated();
}
break;
- case kUnevacFromSpace:
+ case RegionType::kRegionTypeUnevacFromSpace:
if (r->IsInUnevacFromSpace()) {
bytes += r->BytesAllocated();
}
break;
- case kToSpace:
+ case RegionType::kRegionTypeToSpace:
if (r->IsInToSpace()) {
bytes += r->BytesAllocated();
}
break;
default:
- LOG(FATAL) << "Unexpected space type : " << static_cast<int>(kSubSpaceType);
+ LOG(FATAL) << "Unexpected space type : " << kRegionType;
}
}
return bytes;
}
-template<RegionSpace::SubSpaceType kSubSpaceType>
+template<RegionSpace::RegionType kRegionType>
uint64_t RegionSpace::GetObjectsAllocatedInternal() {
uint64_t bytes = 0;
MutexLock mu(Thread::Current(), region_lock_);
@@ -186,27 +186,27 @@
if (r->IsFree()) {
continue;
}
- switch (kSubSpaceType) {
- case kAllSpaces:
+ switch (kRegionType) {
+ case RegionType::kRegionTypeAll:
bytes += r->ObjectsAllocated();
break;
- case kFromSpace:
+ case RegionType::kRegionTypeFromSpace:
if (r->IsInFromSpace()) {
bytes += r->ObjectsAllocated();
}
break;
- case kUnevacFromSpace:
+ case RegionType::kRegionTypeUnevacFromSpace:
if (r->IsInUnevacFromSpace()) {
bytes += r->ObjectsAllocated();
}
break;
- case kToSpace:
+ case RegionType::kRegionTypeToSpace:
if (r->IsInToSpace()) {
bytes += r->ObjectsAllocated();
}
break;
default:
- LOG(FATAL) << "Unexpected space type : " << static_cast<int>(kSubSpaceType);
+ LOG(FATAL) << "Unexpected space type : " << kRegionType;
}
}
return bytes;
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 2ecb79e..2c556d9 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -71,7 +71,7 @@
}
full_region_ = Region();
DCHECK(!full_region_.IsFree());
- DCHECK(full_region_.IsNormal());
+ DCHECK(full_region_.IsAllocated());
current_region_ = &full_region_;
evac_region_ = nullptr;
size_t ignored;
@@ -115,7 +115,7 @@
}
inline bool RegionSpace::Region::ShouldBeEvacuated() {
- DCHECK(state_ == kRegionToSpace || state_ == kRegionLargeToSpace);
+ DCHECK((IsAllocated() || IsLarge()) && IsInToSpace());
// if the region was allocated after the start of the
// previous GC or the live ratio is below threshold, evacuate
// it.
@@ -126,13 +126,13 @@
bool is_live_percent_valid = live_bytes_ != static_cast<size_t>(-1);
if (is_live_percent_valid) {
uint live_percent = GetLivePercent();
- if (state_ == kRegionToSpace) {
+ if (IsAllocated()) {
// Side node: live_percent == 0 does not necessarily mean
// there's no live objects due to rounding (there may be a
// few).
result = live_percent < kEvaculateLivePercentThreshold;
} else {
- DCHECK(state_ == kRegionLargeToSpace);
+ DCHECK(IsLarge());
result = live_percent == 0U;
}
} else {
@@ -155,11 +155,14 @@
bool prev_large_evacuated = false;
for (size_t i = 0; i < num_regions_; ++i) {
Region* r = ®ions_[i];
- RegionState state = static_cast<RegionState>(r->state_);
+ RegionState state = r->State();
+ RegionType type = r->Type();
if (!r->IsFree()) {
DCHECK(r->IsInToSpace());
if (LIKELY(num_expected_large_tails == 0U)) {
- DCHECK(state == kRegionToSpace || state == kRegionLargeToSpace);
+ DCHECK((state == RegionState::kRegionStateAllocated ||
+ state == RegionState::kRegionStateLarge) &&
+ type == RegionType::kRegionTypeToSpace);
bool should_evacuate = force_evacuate_all || r->ShouldBeEvacuated();
if (should_evacuate) {
r->SetAsFromSpace();
@@ -168,13 +171,15 @@
r->SetAsUnevacFromSpace();
DCHECK(r->IsInUnevacFromSpace());
}
- if (UNLIKELY(state == kRegionLargeToSpace)) {
+ if (UNLIKELY(state == RegionState::kRegionStateLarge &&
+ type == RegionType::kRegionTypeToSpace)) {
prev_large_evacuated = should_evacuate;
num_expected_large_tails = RoundUp(r->BytesAllocated(), kRegionSize) / kRegionSize - 1;
DCHECK_GT(num_expected_large_tails, 0U);
}
} else {
- DCHECK(state == kRegionLargeTailToSpace);
+ DCHECK(state == RegionState::kRegionStateLargeTail &&
+ type == RegionType::kRegionTypeToSpace);
if (prev_large_evacuated) {
r->SetAsFromSpace();
DCHECK(r->IsInFromSpace());
@@ -361,7 +366,7 @@
if (tlab_start != nullptr) {
DCHECK(IsAligned<kRegionSize>(tlab_start));
Region* r = RefToRegionLocked(reinterpret_cast<mirror::Object*>(tlab_start));
- DCHECK(r->IsNormal());
+ DCHECK(r->IsAllocated());
DCHECK_EQ(thread->GetThreadLocalBytesAllocated(), kRegionSize);
r->RecordThreadLocalAllocations(thread->GetThreadLocalObjectsAllocated(),
thread->GetThreadLocalBytesAllocated());
@@ -402,7 +407,8 @@
void RegionSpace::Region::Dump(std::ostream& os) const {
os << "Region[" << idx_ << "]=" << reinterpret_cast<void*>(begin_) << "-" << reinterpret_cast<void*>(top_)
<< "-" << reinterpret_cast<void*>(end_)
- << " state=" << static_cast<uint>(state_) << " objects_allocated=" << objects_allocated_
+ << " state=" << static_cast<uint>(state_) << " type=" << static_cast<uint>(type_)
+ << " objects_allocated=" << objects_allocated_
<< " alloc_time=" << alloc_time_ << " live_bytes=" << live_bytes_
<< " is_newly_allocated=" << is_newly_allocated_ << " is_a_tlab=" << is_a_tlab_ << " thread=" << thread_ << "\n";
}
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index b4a043f..4160547 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -17,9 +17,10 @@
#ifndef ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
#define ART_RUNTIME_GC_SPACE_REGION_SPACE_H_
+#include "gc/accounting/read_barrier_table.h"
#include "object_callbacks.h"
#include "space.h"
-#include "gc/accounting/read_barrier_table.h"
+#include "thread.h"
namespace art {
namespace gc {
@@ -94,32 +95,40 @@
void AssertAllThreadLocalBuffersAreRevoked() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_,
Locks::thread_list_lock_);
- enum SubSpaceType {
- kAllSpaces, // All spaces.
- kFromSpace, // From-space. To be evacuated.
- kUnevacFromSpace, // Unevacuated from-space. Not to be evacuated.
- kToSpace, // To-space.
+ enum class RegionType : uint8_t {
+ kRegionTypeAll, // All types.
+ kRegionTypeFromSpace, // From-space. To be evacuated.
+ kRegionTypeUnevacFromSpace, // Unevacuated from-space. Not to be evacuated.
+ kRegionTypeToSpace, // To-space.
+ kRegionTypeNone, // None.
};
- template<SubSpaceType kSubSpaceType> uint64_t GetBytesAllocatedInternal();
- template<SubSpaceType kSubSpaceType> uint64_t GetObjectsAllocatedInternal();
+ enum class RegionState : uint8_t {
+ kRegionStateFree, // Free region.
+ kRegionStateAllocated, // Allocated region.
+ kRegionStateLarge, // Large allocated (allocation larger than the region size).
+ kRegionStateLargeTail, // Large tail (non-first regions of a large allocation).
+ };
+
+ template<RegionType kRegionType> uint64_t GetBytesAllocatedInternal();
+ template<RegionType kRegionType> uint64_t GetObjectsAllocatedInternal();
uint64_t GetBytesAllocated() {
- return GetBytesAllocatedInternal<kAllSpaces>();
+ return GetBytesAllocatedInternal<RegionType::kRegionTypeAll>();
}
uint64_t GetObjectsAllocated() {
- return GetObjectsAllocatedInternal<kAllSpaces>();
+ return GetObjectsAllocatedInternal<RegionType::kRegionTypeAll>();
}
uint64_t GetBytesAllocatedInFromSpace() {
- return GetBytesAllocatedInternal<kFromSpace>();
+ return GetBytesAllocatedInternal<RegionType::kRegionTypeFromSpace>();
}
uint64_t GetObjectsAllocatedInFromSpace() {
- return GetObjectsAllocatedInternal<kFromSpace>();
+ return GetObjectsAllocatedInternal<RegionType::kRegionTypeFromSpace>();
}
uint64_t GetBytesAllocatedInUnevacFromSpace() {
- return GetBytesAllocatedInternal<kUnevacFromSpace>();
+ return GetBytesAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
}
uint64_t GetObjectsAllocatedInUnevacFromSpace() {
- return GetObjectsAllocatedInternal<kUnevacFromSpace>();
+ return GetObjectsAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>();
}
bool CanMoveObjects() const OVERRIDE {
@@ -181,6 +190,14 @@
return false;
}
+ RegionType GetRegionType(mirror::Object* ref) {
+ if (HasAddress(ref)) {
+ Region* r = RefToRegionUnlocked(ref);
+ return r->Type();
+ }
+ return RegionType::kRegionTypeNone;
+ }
+
void SetFromSpace(accounting::ReadBarrierTable* rb_table, bool force_evacuate_all)
LOCKS_EXCLUDED(region_lock_);
@@ -190,7 +207,7 @@
void ClearFromSpace();
void AddLiveBytes(mirror::Object* ref, size_t alloc_size) {
- Region* reg = RefToRegion(ref);
+ Region* reg = RefToRegionUnlocked(ref);
reg->AddLiveBytes(alloc_size);
}
@@ -209,38 +226,36 @@
template<bool kToSpaceOnly>
void WalkInternal(ObjectCallback* callback, void* arg) NO_THREAD_SAFETY_ANALYSIS;
- enum RegionState {
- kRegionFree, // Free region.
- kRegionToSpace, // To-space region.
- kRegionFromSpace, // From-space region. To be evacuated.
- kRegionUnevacFromSpace, // Unevacuated from-space region. Not to be evacuated.
- kRegionLargeToSpace, // Large (allocation larger than the region size) to-space.
- kRegionLargeFromSpace, // Large from-space. To be evacuated.
- kRegionLargeUnevacFromSpace, // Large unevacuated from-space.
- kRegionLargeTailToSpace, // Large tail (non-first regions of a large allocation).
- kRegionLargeTailFromSpace, // Large tail from-space.
- kRegionLargeTailUnevacFromSpace, // Large tail unevacuated from-space.
- };
-
class Region {
public:
Region()
: idx_(static_cast<size_t>(-1)),
- begin_(nullptr), top_(nullptr), end_(nullptr), state_(kRegionToSpace),
+ begin_(nullptr), top_(nullptr), end_(nullptr),
+ state_(RegionState::kRegionStateAllocated), type_(RegionType::kRegionTypeToSpace),
objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {}
Region(size_t idx, uint8_t* begin, uint8_t* end)
- : idx_(idx), begin_(begin), top_(begin), end_(end), state_(kRegionFree),
+ : idx_(idx), begin_(begin), top_(begin), end_(end),
+ state_(RegionState::kRegionStateFree), type_(RegionType::kRegionTypeNone),
objects_allocated_(0), alloc_time_(0), live_bytes_(static_cast<size_t>(-1)),
is_newly_allocated_(false), is_a_tlab_(false), thread_(nullptr) {
DCHECK_LT(begin, end);
DCHECK_EQ(static_cast<size_t>(end - begin), kRegionSize);
}
+ RegionState State() const {
+ return state_;
+ }
+
+ RegionType Type() const {
+ return type_;
+ }
+
void Clear() {
top_ = begin_;
- state_ = kRegionFree;
+ state_ = RegionState::kRegionStateFree;
+ type_ = RegionType::kRegionTypeNone;
objects_allocated_ = 0;
alloc_time_ = 0;
live_bytes_ = static_cast<size_t>(-1);
@@ -257,8 +272,9 @@
size_t* usable_size);
bool IsFree() const {
- bool is_free = state_ == kRegionFree;
+ bool is_free = state_ == RegionState::kRegionStateFree;
if (is_free) {
+ DCHECK(IsInNoSpace());
DCHECK_EQ(begin_, top_);
DCHECK_EQ(objects_allocated_, 0U);
}
@@ -268,19 +284,22 @@
// Given a free region, declare it non-free (allocated).
void Unfree(uint32_t alloc_time) {
DCHECK(IsFree());
- state_ = kRegionToSpace;
+ state_ = RegionState::kRegionStateAllocated;
+ type_ = RegionType::kRegionTypeToSpace;
alloc_time_ = alloc_time;
}
void UnfreeLarge(uint32_t alloc_time) {
DCHECK(IsFree());
- state_ = kRegionLargeToSpace;
+ state_ = RegionState::kRegionStateLarge;
+ type_ = RegionType::kRegionTypeToSpace;
alloc_time_ = alloc_time;
}
void UnfreeLargeTail(uint32_t alloc_time) {
DCHECK(IsFree());
- state_ = kRegionLargeTailToSpace;
+ state_ = RegionState::kRegionStateLargeTail;
+ type_ = RegionType::kRegionTypeToSpace;
alloc_time_ = alloc_time;
}
@@ -288,25 +307,23 @@
is_newly_allocated_ = true;
}
- // Non-large, non-large-tail.
- bool IsNormal() const {
- return state_ == kRegionToSpace || state_ == kRegionFromSpace ||
- state_ == kRegionUnevacFromSpace;
+ // Non-large, non-large-tail allocated.
+ bool IsAllocated() const {
+ return state_ == RegionState::kRegionStateAllocated;
}
+ // Large allocated.
bool IsLarge() const {
- bool is_large = state_ == kRegionLargeToSpace || state_ == kRegionLargeFromSpace ||
- state_ == kRegionLargeUnevacFromSpace;
+ bool is_large = state_ == RegionState::kRegionStateLarge;
if (is_large) {
DCHECK_LT(begin_ + 1 * MB, top_);
}
return is_large;
}
+ // Large-tail allocated.
bool IsLargeTail() const {
- bool is_large_tail = state_ == kRegionLargeTailToSpace ||
- state_ == kRegionLargeTailFromSpace ||
- state_ == kRegionLargeTailUnevacFromSpace;
+ bool is_large_tail = state_ == RegionState::kRegionStateLargeTail;
if (is_large_tail) {
DCHECK_EQ(begin_, top_);
}
@@ -318,71 +335,36 @@
}
bool IsInFromSpace() const {
- return state_ == kRegionFromSpace || state_ == kRegionLargeFromSpace ||
- state_ == kRegionLargeTailFromSpace;
+ return type_ == RegionType::kRegionTypeFromSpace;
}
bool IsInToSpace() const {
- return state_ == kRegionToSpace || state_ == kRegionLargeToSpace ||
- state_ == kRegionLargeTailToSpace;
+ return type_ == RegionType::kRegionTypeToSpace;
}
bool IsInUnevacFromSpace() const {
- return state_ == kRegionUnevacFromSpace || state_ == kRegionLargeUnevacFromSpace ||
- state_ == kRegionLargeTailUnevacFromSpace;
+ return type_ == RegionType::kRegionTypeUnevacFromSpace;
+ }
+
+ bool IsInNoSpace() const {
+ return type_ == RegionType::kRegionTypeNone;
}
void SetAsFromSpace() {
- switch (state_) {
- case kRegionToSpace:
- state_ = kRegionFromSpace;
- break;
- case kRegionLargeToSpace:
- state_ = kRegionLargeFromSpace;
- break;
- case kRegionLargeTailToSpace:
- state_ = kRegionLargeTailFromSpace;
- break;
- default:
- LOG(FATAL) << "Unexpected region state : " << static_cast<uint>(state_)
- << " idx=" << idx_;
- }
+ DCHECK(!IsFree() && IsInToSpace());
+ type_ = RegionType::kRegionTypeFromSpace;
live_bytes_ = static_cast<size_t>(-1);
}
void SetAsUnevacFromSpace() {
- switch (state_) {
- case kRegionToSpace:
- state_ = kRegionUnevacFromSpace;
- break;
- case kRegionLargeToSpace:
- state_ = kRegionLargeUnevacFromSpace;
- break;
- case kRegionLargeTailToSpace:
- state_ = kRegionLargeTailUnevacFromSpace;
- break;
- default:
- LOG(FATAL) << "Unexpected region state : " << static_cast<uint>(state_)
- << " idx=" << idx_;
- }
+ DCHECK(!IsFree() && IsInToSpace());
+ type_ = RegionType::kRegionTypeUnevacFromSpace;
live_bytes_ = 0U;
}
void SetUnevacFromSpaceAsToSpace() {
- switch (state_) {
- case kRegionUnevacFromSpace:
- state_ = kRegionToSpace;
- break;
- case kRegionLargeUnevacFromSpace:
- state_ = kRegionLargeToSpace;
- break;
- case kRegionLargeTailUnevacFromSpace:
- state_ = kRegionLargeTailToSpace;
- break;
- default:
- LOG(FATAL) << "Unexpected region state : " << static_cast<uint>(state_)
- << " idx=" << idx_;
- }
+ DCHECK(!IsFree() && IsInUnevacFromSpace());
+ type_ = RegionType::kRegionTypeToSpace;
}
ALWAYS_INLINE bool ShouldBeEvacuated();
@@ -419,7 +401,7 @@
DCHECK_EQ(begin_, top_);
return 0;
} else {
- DCHECK(IsNormal()) << static_cast<uint>(state_);
+ DCHECK(IsAllocated()) << static_cast<uint>(state_);
DCHECK_LE(begin_, top_);
size_t bytes = static_cast<size_t>(top_ - begin_);
DCHECK_LE(bytes, kRegionSize);
@@ -437,7 +419,7 @@
DCHECK_EQ(objects_allocated_, 0U);
return 0;
} else {
- DCHECK(IsNormal()) << static_cast<uint>(state_);
+ DCHECK(IsAllocated()) << static_cast<uint>(state_);
return objects_allocated_;
}
}
@@ -465,7 +447,7 @@
void Dump(std::ostream& os) const;
void RecordThreadLocalAllocations(size_t num_objects, size_t num_bytes) {
- DCHECK(IsNormal());
+ DCHECK(IsAllocated());
DCHECK_EQ(objects_allocated_, 0U);
DCHECK_EQ(top_, end_);
objects_allocated_ = num_objects;
@@ -479,7 +461,8 @@
// Can't use Atomic<uint8_t*> as Atomic's copy operator is implicitly deleted.
uint8_t* top_; // The current position of the allocation.
uint8_t* end_; // The end address of the region.
- uint8_t state_; // The region state (see RegionState).
+ RegionState state_; // The region state (see RegionState).
+ RegionType type_; // The region type (see RegionType).
uint64_t objects_allocated_; // The number of objects allocated.
uint32_t alloc_time_; // The allocation time of the region.
size_t live_bytes_; // The live bytes. Used to compute the live percent.
@@ -534,6 +517,9 @@
DISALLOW_COPY_AND_ASSIGN(RegionSpace);
};
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionState& value);
+std::ostream& operator<<(std::ostream& os, const RegionSpace::RegionType& value);
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 4551d5b..96430a0 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -18,10 +18,6 @@
#include <sstream>
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
#include "base/stringpiece.h"
#include "debugger.h"
#include "gc/heap.h"
diff --git a/runtime/profiler.cc b/runtime/profiler.cc
index b3da134..c3bdcb1 100644
--- a/runtime/profiler.cc
+++ b/runtime/profiler.cc
@@ -40,10 +40,6 @@
#include "thread.h"
#include "thread_list.h"
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
#include "entrypoints/quick/quick_entrypoints.h"
namespace art {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 57a849a..3acac3a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -33,6 +33,8 @@
#include <vector>
#include <fcntl.h>
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
#include "arch/arm/quick_method_frame_info_arm.h"
#include "arch/arm/registers_arm.h"
#include "arch/arm64/quick_method_frame_info_arm64.h"
@@ -59,19 +61,19 @@
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "gc/space/space.h"
+#include "handle_scope-inl.h"
#include "image.h"
#include "instrumentation.h"
#include "intern_table.h"
#include "jni_internal.h"
+#include "mirror/array.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
-#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/stack_trace_element.h"
#include "mirror/throwable.h"
#include "monitor.h"
-#include "native_bridge_art_interface.h"
#include "native/dalvik_system_DexFile.h"
#include "native/dalvik_system_VMDebug.h"
#include "native/dalvik_system_VMRuntime.h"
@@ -80,48 +82,41 @@
#include "native/java_lang_Class.h"
#include "native/java_lang_DexCache.h"
#include "native/java_lang_Object.h"
-#include "native/java_lang_ref_FinalizerReference.h"
-#include "native/java_lang_reflect_Array.h"
-#include "native/java_lang_reflect_Constructor.h"
-#include "native/java_lang_reflect_Field.h"
-#include "native/java_lang_reflect_Method.h"
-#include "native/java_lang_reflect_Proxy.h"
-#include "native/java_lang_ref_Reference.h"
#include "native/java_lang_Runtime.h"
#include "native/java_lang_String.h"
#include "native/java_lang_System.h"
#include "native/java_lang_Thread.h"
#include "native/java_lang_Throwable.h"
#include "native/java_lang_VMClassLoader.h"
+#include "native/java_lang_ref_FinalizerReference.h"
+#include "native/java_lang_ref_Reference.h"
+#include "native/java_lang_reflect_Array.h"
+#include "native/java_lang_reflect_Constructor.h"
+#include "native/java_lang_reflect_Field.h"
+#include "native/java_lang_reflect_Method.h"
+#include "native/java_lang_reflect_Proxy.h"
#include "native/java_util_concurrent_atomic_AtomicLong.h"
#include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
#include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
#include "native/sun_misc_Unsafe.h"
-#include "parsed_options.h"
+#include "native_bridge_art_interface.h"
#include "oat_file.h"
#include "os.h"
+#include "parsed_options.h"
+#include "profiler.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
-#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "sigchain.h"
#include "signal_catcher.h"
#include "signal_set.h"
-#include "handle_scope-inl.h"
#include "thread.h"
#include "thread_list.h"
#include "trace.h"
#include "transaction.h"
-#include "profiler.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
-#include "JniConstants.h" // Last to avoid LOG redefinition in ics-mr1-plus-art.
-
-#ifdef HAVE_ANDROID_OS
-#include "cutils/properties.h"
-#endif
-
namespace art {
// If a signal isn't handled properly, enable a handler that attempts to dump the Java stack.
diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk
index e0f1769..e1aae11 100644
--- a/sigchainlib/Android.mk
+++ b/sigchainlib/Android.mk
@@ -28,6 +28,7 @@
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
$(eval $(call set-target-local-clang-vars))
include $(BUILD_SHARED_LIBRARY)
@@ -56,6 +57,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
LOCAL_LDLIBS = -ldl
LOCAL_MULTILIB := both
+LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
include $(BUILD_HOST_SHARED_LIBRARY)
include $(CLEAR_VARS)
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 32fbf2d..238e73a 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -292,8 +292,8 @@
break;
}
- strong[depth] = funStr;
weak[depth] = new WeakReference(funStr);
+ strong[depth] = funStr;
if (depth+1 < MAX_DEPTH)
dive(depth+1, iteration+1);
else
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index 7f24b1b..4d781c3 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -28,14 +28,22 @@
testMethodTracing();
}
- private static void testMethodTracing() throws Exception {
- File tempFile;
+ private static File createTempFile() throws Exception {
try {
- tempFile = File.createTempFile("test", ".trace");
+ return File.createTempFile("test", ".trace");
} catch (IOException e) {
- System.setProperty("java.io.tmpdir", "/sdcard");
- tempFile = File.createTempFile("test", ".trace");
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile("test", ".trace");
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile("test", ".trace");
+ }
}
+ }
+
+ private static void testMethodTracing() throws Exception {
+ File tempFile = createTempFile();
tempFile.deleteOnExit();
String tempFileName = tempFile.getPath();
diff --git a/test/445-checker-licm/expected.txt b/test/445-checker-licm/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/445-checker-licm/expected.txt
diff --git a/test/445-checker-licm/info.txt b/test/445-checker-licm/info.txt
new file mode 100644
index 0000000..e09d958
--- /dev/null
+++ b/test/445-checker-licm/info.txt
@@ -0,0 +1 @@
+Checker test for testing loop invariant code motion.
diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java
new file mode 100644
index 0000000..91ac2ed
--- /dev/null
+++ b/test/445-checker-licm/src/Main.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.div() licm (before)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.div() licm (after)
+ // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.div() licm (after)
+ // CHECK-DAG: Div ( loop_header:null )
+
+ public static int div() {
+ int result = 0;
+ for (int i = 0; i < 10; ++i) {
+ result += staticField / 42;
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.innerDiv() licm (before)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.innerDiv() licm (after)
+ // CHECK-NOT: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.innerDiv() licm (after)
+ // CHECK-DAG: Div ( loop_header:null )
+
+ public static int innerDiv() {
+ int result = 0;
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 10; ++j) {
+ result += staticField / 42;
+ }
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.innerDiv2() licm (before)
+ // CHECK-DAG: Mul ( loop_header:{{B4}} )
+
+ // CHECK-START: int Main.innerDiv2() licm (after)
+ // CHECK-DAG: Mul ( loop_header:{{B2}} )
+
+ public static int innerDiv2() {
+ int result = 0;
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 0; j < 10; ++j) {
+ // The operation has been hoisted out of the inner loop.
+ // Note that we depend on the compiler's block numbering to
+ // check if it has been moved.
+ result += staticField * i;
+ }
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.innerDiv3(int, int) licm (before)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.innerDiv3(int, int) licm (after)
+ // CHECK-DAG: Div ( loop_header:{{B\d+}} )
+
+ public static int innerDiv3(int a, int b) {
+ int result = 0;
+ while (b < 5) {
+ // a might be null, so we can't hoist the operation.
+ result += staticField / a;
+ b++;
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.arrayLength(int[]) licm (before)
+ // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:{{B\d+}} )
+ // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.arrayLength(int[]) licm (after)
+ // CHECK-NOT: NullCheck ( loop_header:{{B\d+}} )
+ // CHECK-NOT: ArrayLength ( loop_header:{{B\d+}} )
+
+ // CHECK-START: int Main.arrayLength(int[]) licm (after)
+ // CHECK-DAG: [[NullCheck:l\d+]] NullCheck ( loop_header:null )
+ // CHECK-DAG: ArrayLength [ [[NullCheck]] ] ( loop_header:null )
+
+ public static int arrayLength(int[] array) {
+ int result = 0;
+ for (int i = 0; i < array.length; ++i) {
+ result += array[i];
+ }
+ return result;
+ }
+
+ public static int staticField = 42;
+
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) {
+ assertEquals(10, div());
+ assertEquals(100, innerDiv());
+ assertEquals(12, arrayLength(new int[] { 4, 8 }));
+ }
+}
diff --git a/test/446-checker-inliner2/expected.txt b/test/446-checker-inliner2/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/446-checker-inliner2/expected.txt
diff --git a/test/446-checker-inliner2/info.txt b/test/446-checker-inliner2/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/446-checker-inliner2/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/446-checker-inliner2/src/Main.java b/test/446-checker-inliner2/src/Main.java
new file mode 100644
index 0000000..ecf071e
--- /dev/null
+++ b/test/446-checker-inliner2/src/Main.java
@@ -0,0 +1,72 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.inlineInstanceCall(Main) inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineInstanceCall(Main) inliner (after)
+ // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
+ // CHECK-DAG: Return [ [[Field]] ]
+
+ public static int inlineInstanceCall(Main m) {
+ return m.foo();
+ }
+
+ private int foo() {
+ return field;
+ }
+
+ int field = 42;
+
+ // CHECK-START: int Main.inlineNestedCall() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineNestedCall() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineNestedCall() inliner (after)
+ // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
+ // CHECK-DAG: Return [ [[Const38]] ]
+
+ public static int inlineNestedCall() {
+ return nestedCall();
+ }
+
+ public static int nestedCall() {
+ return bar();
+ }
+
+ public static int bar() {
+ return 38;
+ }
+
+ public static void main(String[] args) {
+ if (inlineInstanceCall(new Main()) != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (inlineNestedCall() != 38) {
+ throw new Error("Expected 38");
+ }
+ }
+}
diff --git a/test/447-checker-inliner3/expected.txt b/test/447-checker-inliner3/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/447-checker-inliner3/expected.txt
diff --git a/test/447-checker-inliner3/info.txt b/test/447-checker-inliner3/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/447-checker-inliner3/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/test/447-checker-inliner3/src/Main.java b/test/447-checker-inliner3/src/Main.java
new file mode 100644
index 0000000..db4b236
--- /dev/null
+++ b/test/447-checker-inliner3/src/Main.java
@@ -0,0 +1,77 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class Main {
+
+ // CHECK-START: int Main.inlineIfThenElse() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineIfThenElse() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int inlineIfThenElse() {
+ return foo(true);
+ }
+
+ private static int foo(boolean value) {
+ if (value) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // CHECK-START: int Main.inlineInLoop() inliner (before)
+ // CHECK-DAG: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineInLoop() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int inlineInLoop() {
+ int result = 0;
+ for (int i = 0; i < 32; ++i) {
+ result += foo(i % 2 == 0);
+ }
+ return result;
+ }
+
+ // CHECK-START: int Main.inlineInLoopHeader() inliner (before)
+ // CHECK-DAG: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineInLoopHeader() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static int inlineInLoopHeader() {
+ int result = 0;
+ for (int i = 0; i < foo(i % 2 == 0); ++i) {
+ result += 42;
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ if (inlineIfThenElse() != 1) {
+ throw new Error("Expected 1");
+ }
+ if (inlineInLoop() != 16) {
+ throw new Error("Expected 16");
+ }
+ if (inlineInLoopHeader() != 42) {
+ throw new Error("Expected 16");
+ }
+ }
+}
diff --git a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
index a88d31b..9c41abf 100644
--- a/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
+++ b/test/802-deoptimization/src/CatchHandlerOnEntryHelper.java
@@ -21,10 +21,10 @@
public static void throwExceptionDuringDeopt(int i) {
if (i == 0) {
- DeoptimizationController.startDeoptomization();
+ DeoptimizationController.startDeoptimization();
throw new RuntimeException("Test exception");
} else {
- DeoptimizationController.stopDeoptomization();
+ DeoptimizationController.stopDeoptimization();
}
}
}
diff --git a/test/802-deoptimization/src/DeoptimizationController.java b/test/802-deoptimization/src/DeoptimizationController.java
index c031c07..c926669 100644
--- a/test/802-deoptimization/src/DeoptimizationController.java
+++ b/test/802-deoptimization/src/DeoptimizationController.java
@@ -22,15 +22,23 @@
* Controls deoptimization using dalvik.system.VMDebug class.
*/
public class DeoptimizationController {
- public static void startDeoptomization() {
+ private static File createTempFile() throws Exception {
try {
- File tempFile;
+ return File.createTempFile("test", ".trace");
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
try {
- tempFile = File.createTempFile("test", ".trace");
- } catch (IOException e) {
+ return File.createTempFile("test", ".trace");
+ } catch (IOException e2) {
System.setProperty("java.io.tmpdir", "/sdcard");
- tempFile = File.createTempFile("test", ".trace");
+ return File.createTempFile("test", ".trace");
}
+ }
+ }
+
+ public static void startDeoptimization() {
+ try {
+ File tempFile = createTempFile();
tempFile.deleteOnExit();
String tempFileName = tempFile.getPath();
@@ -43,7 +51,7 @@
}
}
- public static void stopDeoptomization() {
+ public static void stopDeoptimization() {
try {
VMDebug.stopMethodTracing();
if (VMDebug.getMethodTracingMode() != 0) {