Merge "Initialize pDeoptimize quick entry pointer for Mips."
diff --git a/Android.mk b/Android.mk
index a050c58..c01464a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -79,6 +79,7 @@
include $(art_path)/runtime/Android.mk
include $(art_path)/compiler/Android.mk
include $(art_path)/dexdump/Android.mk
+include $(art_path)/dexlist/Android.mk
include $(art_path)/dex2oat/Android.mk
include $(art_path)/disassembler/Android.mk
include $(art_path)/oatdump/Android.mk
@@ -237,10 +238,10 @@
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
-# Dexdump regression test.
+# Dexdump/list regression test.
.PHONY: test-art-host-dexdump
-test-art-host-dexdump: dexdump2
- art/test/dexdump/run-all-tests
+test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist2)
+ ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests
# Valgrind. Currently only 32b gtests.
.PHONY: valgrind-test-art-host
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index 77f39c4..ee0cb09 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -40,7 +40,7 @@
ART_BUILD_HOST_STATIC := false
endif
-ifneq ($$(HOST_OS),linux)
+ifneq ($(HOST_OS),linux)
ART_BUILD_HOST_STATIC := false
endif
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 183f4e3..a561c5f 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -88,4 +88,8 @@
HOST_CORE_DEX_FILES := $(foreach jar,$(HOST_CORE_JARS), $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar)
TARGET_CORE_DEX_FILES := $(foreach jar,$(TARGET_CORE_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar), ,COMMON)/javalib.jar)
+
+# Classpath for Jack compilation: we only need core-libart.
+HOST_JACK_CLASSPATH := $(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart-hostdex,t,COMMON)/classes.jack)
+TARGET_JACK_CLASSPATH := $(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart, ,COMMON)/classes.jack)
endif # ART_ANDROID_COMMON_PATH_MK
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 273ae0e..0958c64 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -104,6 +104,17 @@
$(TARGET_CORE_IMAGE_default_no-pic_32) \
dexdump2
+# The dexlist test requires an image and the dexlist utility.
+# TODO: rename into dexlist when migration completes
+ART_GTEST_dexlist_test_HOST_DEPS := \
+ $(HOST_CORE_IMAGE_default_no-pic_64) \
+ $(HOST_CORE_IMAGE_default_no-pic_32) \
+ $(HOST_OUT_EXECUTABLES)/dexlist2
+ART_GTEST_dexlist_test_TARGET_DEPS := \
+ $(TARGET_CORE_IMAGE_default_no-pic_64) \
+ $(TARGET_CORE_IMAGE_default_no-pic_32) \
+ dexlist2
+
# The imgdiag test has dependencies on core.oat since it needs to load it during the test.
# For the host, also add the installed tool (in the base size, that should suffice). For the
# target, just the module is fine, the sync will happen late enough.
@@ -132,6 +143,7 @@
RUNTIME_GTEST_COMMON_SRC_FILES := \
cmdline/cmdline_parser_test.cc \
dexdump/dexdump_test.cc \
+ dexlist/dexlist_test.cc \
imgdiag/imgdiag_test.cc \
oatdump/oatdump_test.cc \
runtime/arch/arch_test.cc \
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index d993d93..d1fe167 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -1336,9 +1336,24 @@
}
OpRegReg(kOpRev, rl_result.reg.GetLow(), rl_i.reg.GetHigh());
OpRegReg(kOpRev, rl_result.reg.GetHigh(), r_i_low);
+ // Free up at least one input register if it was a temp. Otherwise we may be in the bad
+ // situation of not having a temp available for SwapBits. Make sure it's not overlapping
+ // with the output, though.
if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+ // There's definitely a free temp after this.
FreeTemp(r_i_low);
+ } else {
+ // We opportunistically release both here. That saves duplication of the register state
+ // lookup (to see if it's actually a temp).
+ if (rl_i.reg.GetLowReg() != rl_result.reg.GetHighReg()) {
+ FreeTemp(rl_i.reg.GetLow());
+ }
+ if (rl_i.reg.GetHighReg() != rl_result.reg.GetLowReg() &&
+ rl_i.reg.GetHighReg() != rl_result.reg.GetHighReg()) {
+ FreeTemp(rl_i.reg.GetHigh());
+ }
}
+
SwapBits(rl_result.reg.GetLow(), 1, 0x55555555);
SwapBits(rl_result.reg.GetLow(), 2, 0x33333333);
SwapBits(rl_result.reg.GetLow(), 4, 0x0f0f0f0f);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 73e121f..fdfeb48 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -989,6 +989,8 @@
CHECK_EQ(image_objects_offset_begin_ + bin_slot_previous_sizes_[kBinArtMethodClean],
methods_section->Offset());
cur_pos = methods_section->End();
+ // Round up to the alignment the string table expects. See HashSet::WriteToMemory.
+ cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
// Calculate the size of the interned strings.
auto* interned_strings_section = §ions[ImageHeader::kSectionInternedStrings];
*interned_strings_section = ImageSection(cur_pos, intern_table_bytes_);
@@ -1417,9 +1419,6 @@
if (UNLIKELY(orig->IsAbstract())) {
copy->SetEntryPointFromQuickCompiledCodePtrSize(
GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_);
- copy->SetEntryPointFromInterpreterPtrSize(
- reinterpret_cast<EntryPointFromInterpreter*>(const_cast<uint8_t*>(
- GetOatAddress(interpreter_to_interpreter_bridge_offset_))), target_ptr_size_);
} else {
bool quick_is_interpreted;
const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted);
@@ -1432,16 +1431,6 @@
copy->SetEntryPointFromJniPtrSize(
GetOatAddress(jni_dlsym_lookup_offset_), target_ptr_size_);
}
-
- // Interpreter entrypoint:
- // Set the interpreter entrypoint depending on whether there is compiled code or not.
- uint32_t interpreter_code = (quick_is_interpreted)
- ? interpreter_to_interpreter_bridge_offset_
- : interpreter_to_compiled_code_bridge_offset_;
- EntryPointFromInterpreter* interpreter_entrypoint =
- reinterpret_cast<EntryPointFromInterpreter*>(
- const_cast<uint8_t*>(GetOatAddress(interpreter_code)));
- copy->SetEntryPointFromInterpreterPtrSize(interpreter_entrypoint, target_ptr_size_);
}
}
}
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index daf7d67..329112a 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -119,6 +119,14 @@
// Check if the selection negates/preserves the value of the condition and
// if so, generate a suitable replacement instruction.
HInstruction* if_condition = if_instruction->InputAt(0);
+
+ // Don't change FP compares. The definition of compares involving NaNs forces
+ // the compares to be done as written by the user.
+ if (if_condition->IsCondition() &&
+ Primitive::IsFloatingPointType(if_condition->InputAt(0)->GetType())) {
+ return;
+ }
+
HInstruction* replacement;
if (NegatesCondition(true_value, false_value)) {
replacement = GetOppositeCondition(if_condition);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 732630d..fe52c44 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -259,14 +259,20 @@
return false;
}
-bool HGraphBuilder::IsBlockInPcRange(HBasicBlock* block,
- uint32_t dex_pc_start,
- uint32_t dex_pc_end) {
- uint32_t dex_pc = block->GetDexPc();
- return block != entry_block_
- && block != exit_block_
- && dex_pc >= dex_pc_start
- && dex_pc < dex_pc_end;
+static const DexFile::TryItem* GetTryItem(HBasicBlock* block,
+ const DexFile::CodeItem& code_item,
+ const ArenaBitVector& can_block_throw) {
+ DCHECK(!block->IsSingleTryBoundary());
+
+ // Block does not contain throwing instructions. Even if it is covered by
+ // a TryItem, we will consider it not in a try block.
+ if (!can_block_throw.IsBitSet(block->GetBlockId())) {
+ return nullptr;
+ }
+
+ // Instructions in the block may throw. Find a TryItem covering this block.
+ int32_t try_item_idx = DexFile::FindTryItem(code_item, block->GetDexPc());
+ return (try_item_idx == -1) ? nullptr : DexFile::GetTryItems(code_item, try_item_idx);
}
void HGraphBuilder::CreateBlocksForTryCatch(const DexFile::CodeItem& code_item) {
@@ -327,80 +333,90 @@
return;
}
+ // Bit vector stores information on which blocks contain throwing instructions.
+ // Must be expandable because catch blocks may be split into two.
+ ArenaBitVector can_block_throw(arena_, graph_->GetBlocks().Size(), /* expandable */ true);
+
+ // Scan blocks and mark those which contain throwing instructions.
+ for (size_t block_id = 0, e = graph_->GetBlocks().Size(); block_id < e; ++block_id) {
+ HBasicBlock* block = graph_->GetBlocks().Get(block_id);
+ bool can_throw = false;
+ for (HInstructionIterator insn(block->GetInstructions()); !insn.Done(); insn.Advance()) {
+ if (insn.Current()->CanThrow()) {
+ can_throw = true;
+ break;
+ }
+ }
+
+ if (can_throw) {
+ if (block->IsCatchBlock()) {
+ // Catch blocks are always considered an entry point into the TryItem in
+ // order to avoid splitting exceptional edges. We split the block after
+ // the move-exception (if present) and mark the first part non-throwing.
+ // Later on, a TryBoundary will be inserted between the two blocks.
+ HInstruction* first_insn = block->GetFirstInstruction();
+ if (first_insn->IsLoadException()) {
+ // Catch block starts with a LoadException. Split the block after the
+ // StoreLocal that must come after the load.
+ DCHECK(first_insn->GetNext()->IsStoreLocal());
+ block = block->SplitBefore(first_insn->GetNext()->GetNext());
+ } else {
+ // Catch block does not load the exception. Split at the beginning to
+ // create an empty catch block.
+ block = block->SplitBefore(first_insn);
+ }
+ }
+ can_block_throw.SetBit(block->GetBlockId());
+ }
+ }
+
// Iterate over all blocks, find those covered by some TryItem and:
// (a) split edges which enter/exit the try range,
// (b) create TryBoundary instructions in the new blocks,
// (c) link the new blocks to corresponding exception handlers.
// We cannot iterate only over blocks in `branch_targets_` because switch-case
// blocks share the same dex_pc.
- for (size_t block_id = 1, e = graph_->GetBlocks().Size(); block_id < e; ++block_id) {
+ for (size_t block_id = 0, e = graph_->GetBlocks().Size(); block_id < e; ++block_id) {
HBasicBlock* try_block = graph_->GetBlocks().Get(block_id);
- // Iteration starts from 1 to skip the entry block.
- DCHECK_NE(try_block, entry_block_);
- // Exit block has not yet been added to the graph at this point.
- DCHECK_NE(try_block, exit_block_);
// TryBoundary blocks are added at the end of the list and not iterated over.
DCHECK(!try_block->IsSingleTryBoundary());
// Find the TryItem for this block.
- int32_t try_item_idx = DexFile::FindTryItem(code_item, try_block->GetDexPc());
- if (try_item_idx == -1) {
+ const DexFile::TryItem* try_item = GetTryItem(try_block, code_item, can_block_throw);
+ if (try_item == nullptr) {
continue;
}
- const DexFile::TryItem& try_item = *DexFile::GetTryItems(code_item, try_item_idx);
- uint32_t try_start = try_item.start_addr_;
- uint32_t try_end = try_start + try_item.insn_count_;
- if (try_block->IsCatchBlock()) {
- // Catch blocks are always considered an entry point into the TryItem in
- // order to avoid splitting exceptional edges (they might not have been
- // created yet). We separate the move-exception (if present) from the
- // rest of the block and insert a TryBoundary after it, creating a
- // landing pad for the exceptional edges.
- HInstruction* first_insn = try_block->GetFirstInstruction();
- HInstruction* split_position = nullptr;
- if (first_insn->IsLoadException()) {
- // Catch block starts with a LoadException. Split the block after the
- // StoreLocal that must come after the load.
- DCHECK(first_insn->GetNext()->IsStoreLocal());
- split_position = first_insn->GetNext()->GetNext();
- } else {
- // Catch block does not obtain the exception. Split at the beginning
- // to create an empty catch block.
- split_position = first_insn;
- }
- DCHECK(split_position != nullptr);
- HBasicBlock* catch_block = try_block;
- try_block = catch_block->SplitBefore(split_position);
- SplitTryBoundaryEdge(catch_block, try_block, HTryBoundary::kEntry, code_item, try_item);
- } else {
- // For non-catch blocks, find predecessors which are not covered by the
- // same TryItem range. Such edges enter the try block and will have
- // a TryBoundary inserted.
- for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
- HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
- if (predecessor->IsSingleTryBoundary()) {
- // The edge was already split because of an exit from a neighbouring
- // TryItem. We split it again and insert an entry point.
- if (kIsDebugBuild) {
- HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
- DCHECK(!last_insn->IsEntry());
- DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
- DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
- DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end));
- }
- } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) {
- // This is an entry point into the TryItem and the edge has not been
- // split yet. That means that `predecessor` is not in a TryItem, or
- // it is in a different TryItem and we happened to iterate over this
- // block first. We split the edge and insert an entry point.
- } else {
- // Not an edge on the boundary of the try block.
- continue;
+ // Catch blocks were split earlier and cannot throw.
+ DCHECK(!try_block->IsCatchBlock());
+
+ // Find predecessors which are not covered by the same TryItem range. Such
+ // edges enter the try block and will have a TryBoundary inserted.
+ for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
+ HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
+ if (predecessor->IsSingleTryBoundary()) {
+ // The edge was already split because of an exit from a neighbouring
+ // TryItem. We split it again and insert an entry point.
+ if (kIsDebugBuild) {
+ HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
+ const DexFile::TryItem* predecessor_try_item =
+ GetTryItem(predecessor->GetSinglePredecessor(), code_item, can_block_throw);
+ DCHECK(!last_insn->IsEntry());
+ DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
+ DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
+ DCHECK_NE(try_item, predecessor_try_item);
}
- SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, try_item);
+ } else if (GetTryItem(predecessor, code_item, can_block_throw) != try_item) {
+ // This is an entry point into the TryItem and the edge has not been
+ // split yet. That means that `predecessor` is not in a TryItem, or
+ // it is in a different TryItem and we happened to iterate over this
+ // block first. We split the edge and insert an entry point.
+ } else {
+ // Not an edge on the boundary of the try block.
+ continue;
}
+ SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, *try_item);
}
// Find successors which are not covered by the same TryItem range. Such
@@ -416,11 +432,13 @@
// TryItem. We split it again and insert an exit.
if (kIsDebugBuild) {
HTryBoundary* last_insn = successor->GetLastInstruction()->AsTryBoundary();
+ const DexFile::TryItem* successor_try_item =
+ GetTryItem(last_insn->GetNormalFlowSuccessor(), code_item, can_block_throw);
DCHECK_EQ(try_block, successor->GetSinglePredecessor());
DCHECK(last_insn->IsEntry());
- DCHECK(!IsBlockInPcRange(last_insn->GetNormalFlowSuccessor(), try_start, try_end));
+ DCHECK_NE(try_item, successor_try_item);
}
- } else if (!IsBlockInPcRange(successor, try_start, try_end)) {
+ } else if (GetTryItem(successor, code_item, can_block_throw) != try_item) {
// This is an exit out of the TryItem and the edge has not been split
// yet. That means that either `successor` is not in a TryItem, or it
// is in a different TryItem and we happened to iterate over this
@@ -437,7 +455,7 @@
// Not an edge on the boundary of the try block.
continue;
}
- SplitTryBoundaryEdge(try_block, successor, HTryBoundary::kExit, code_item, try_item);
+ SplitTryBoundaryEdge(try_block, successor, HTryBoundary::kExit, code_item, *try_item);
}
}
}
@@ -496,14 +514,14 @@
// Add the suspend check to the entry block.
entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
entry_block_->AddInstruction(new (arena_) HGoto());
+ // Add the exit block at the end.
+ graph_->AddBlock(exit_block_);
// Iterate over blocks covered by TryItems and insert TryBoundaries at entry
// and exit points. This requires all control-flow instructions and
// non-exceptional edges to have been created.
InsertTryBoundaryBlocks(code_item);
- // Add the exit block at the end to give it the highest id.
- graph_->AddBlock(exit_block_);
return true;
}
@@ -657,7 +675,7 @@
void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction,
Primitive::Type type,
- HCompare::Bias bias,
+ ComparisonBias bias,
uint32_t dex_pc) {
HInstruction* first = LoadLocal(instruction.VRegB(), type);
HInstruction* second = LoadLocal(instruction.VRegC(), type);
@@ -2311,27 +2329,27 @@
}
case Instruction::CMP_LONG: {
- Binop_23x_cmp(instruction, Primitive::kPrimLong, HCompare::kNoBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimLong, kNoBias, dex_pc);
break;
}
case Instruction::CMPG_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kGtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, kGtBias, dex_pc);
break;
}
case Instruction::CMPG_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kGtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, kGtBias, dex_pc);
break;
}
case Instruction::CMPL_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, HCompare::kLtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, kLtBias, dex_pc);
break;
}
case Instruction::CMPL_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, HCompare::kLtBias, dex_pc);
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, kLtBias, dex_pc);
break;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index cae762b..7098eb8 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -98,9 +98,6 @@
HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
- // Returns whether the dex_pc of `block` lies within the given range.
- bool IsBlockInPcRange(HBasicBlock* block, uint32_t dex_pc_start, uint32_t dex_pc_end);
-
// Adds new blocks to `branch_targets_` starting at the limits of TryItems and
// their exception handlers.
void CreateBlocksForTryCatch(const DexFile::CodeItem& code_item);
@@ -139,7 +136,7 @@
void Binop_23x_cmp(const Instruction& instruction,
Primitive::Type type,
- HCompare::Bias bias,
+ ComparisonBias bias,
uint32_t dex_pc);
template<typename T>
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 262b234..be71443 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -889,6 +889,180 @@
UNUSED(exit);
}
+void InstructionCodeGeneratorX86::GenerateFPJumps(HCondition* cond,
+ Label* true_label,
+ Label* false_label) {
+ bool gt_bias = cond->IsGtBias();
+ IfCondition if_cond = cond->GetCondition();
+ Condition ccode = X86Condition(if_cond);
+ switch (if_cond) {
+ case kCondEQ:
+ if (!gt_bias) {
+ __ j(kParityEven, false_label);
+ }
+ break;
+ case kCondNE:
+ if (!gt_bias) {
+ __ j(kParityEven, true_label);
+ }
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ j(kParityEven, false_label);
+ }
+ ccode = kBelow;
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ j(kParityEven, false_label);
+ }
+ ccode = kBelowEqual;
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ j(kParityEven, true_label);
+ }
+ ccode = kAbove;
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ j(kParityEven, true_label);
+ }
+ ccode = kAboveEqual;
+ break;
+ }
+ __ j(ccode, true_label);
+}
+
+void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond,
+ Label* true_label,
+ Label* false_label) {
+ LocationSummary* locations = cond->GetLocations();
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+ IfCondition if_cond = cond->GetCondition();
+
+ Register left_low = left.AsRegisterPairLow<Register>();
+ Register left_high = left.AsRegisterPairHigh<Register>();
+ IfCondition true_high_cond = if_cond;
+ IfCondition false_high_cond = cond->GetOppositeCondition();
+ Condition final_condition = X86Condition(if_cond);
+
+ // Set the conditions for the test, remembering that == needs to be
+ // decided using the low words.
+ switch (if_cond) {
+ case kCondEQ:
+ false_high_cond = kCondNE;
+ break;
+ case kCondNE:
+ false_high_cond = kCondEQ;
+ break;
+ case kCondLT:
+ false_high_cond = kCondGT;
+ final_condition = kBelow;
+ break;
+ case kCondLE:
+ true_high_cond = kCondLT;
+ final_condition = kBelowEqual;
+ break;
+ case kCondGT:
+ false_high_cond = kCondLT;
+ final_condition = kAbove;
+ break;
+ case kCondGE:
+ true_high_cond = kCondGT;
+ final_condition = kAboveEqual;
+ break;
+ }
+
+ if (right.IsConstant()) {
+ int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+ int32_t val_low = Low32Bits(value);
+ int32_t val_high = High32Bits(value);
+
+ if (val_high == 0) {
+ __ testl(left_high, left_high);
+ } else {
+ __ cmpl(left_high, Immediate(val_high));
+ }
+ if (if_cond == kCondNE) {
+ __ j(X86Condition(true_high_cond), true_label);
+ } else if (if_cond == kCondEQ) {
+ __ j(X86Condition(false_high_cond), false_label);
+ } else {
+ __ j(X86Condition(true_high_cond), true_label);
+ __ j(X86Condition(false_high_cond), false_label);
+ }
+ // Must be equal high, so compare the lows.
+ if (val_low == 0) {
+ __ testl(left_low, left_low);
+ } else {
+ __ cmpl(left_low, Immediate(val_low));
+ }
+ } else {
+ Register right_low = right.AsRegisterPairLow<Register>();
+ Register right_high = right.AsRegisterPairHigh<Register>();
+
+ __ cmpl(left_high, right_high);
+ if (if_cond == kCondNE) {
+ __ j(X86Condition(true_high_cond), true_label);
+ } else if (if_cond == kCondEQ) {
+ __ j(X86Condition(false_high_cond), false_label);
+ } else {
+ __ j(X86Condition(true_high_cond), true_label);
+ __ j(X86Condition(false_high_cond), false_label);
+ }
+ // Must be equal high, so compare the lows.
+ __ cmpl(left_low, right_low);
+ }
+ // The last comparison might be unsigned.
+ __ j(final_condition, true_label);
+}
+
+void InstructionCodeGeneratorX86::GenerateCompareTestAndBranch(HIf* if_instr,
+ HCondition* condition,
+ Label* true_target,
+ Label* false_target,
+ Label* always_true_target) {
+ LocationSummary* locations = condition->GetLocations();
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ // We don't want true_target as a nullptr.
+ if (true_target == nullptr) {
+ true_target = always_true_target;
+ }
+ bool falls_through = (false_target == nullptr);
+
+ // FP compares don't like null false_targets.
+ if (false_target == nullptr) {
+ false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor());
+ }
+
+ Primitive::Type type = condition->InputAt(0)->GetType();
+ switch (type) {
+ case Primitive::kPrimLong:
+ GenerateLongComparesAndJumps(condition, true_target, false_target);
+ break;
+ case Primitive::kPrimFloat:
+ DCHECK(right.IsFpuRegister());
+ __ ucomiss(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ case Primitive::kPrimDouble:
+ DCHECK(right.IsFpuRegister());
+ __ ucomisd(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected compare type " << type;
+ }
+
+ if (!falls_through) {
+ __ jmp(false_target);
+ }
+}
+
void InstructionCodeGeneratorX86::GenerateTestAndBranch(HInstruction* instruction,
Label* true_target,
Label* false_target,
@@ -910,9 +1084,12 @@
!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
// Moves do not affect the eflags register, so if the condition is
// evaluated just before the if, we don't need to evaluate it
- // again.
+ // again. We can't use the eflags on long/FP conditions if they are
+ // materialized due to the complex branching.
+ Primitive::Type type = cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
bool eflags_set = cond->IsCondition()
- && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction);
+ && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction)
+ && type == Primitive::kPrimInt;
if (materialized) {
if (!eflags_set) {
// Materialized condition, compare against 0.
@@ -927,6 +1104,16 @@
__ j(X86Condition(cond->AsCondition()->GetCondition()), true_target);
}
} else {
+ // Is this a long or FP comparison that has been folded into the HCondition?
+ if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
+ // Generate the comparison directly.
+ GenerateCompareTestAndBranch(instruction->AsIf(),
+ cond->AsCondition(),
+ true_target,
+ false_target,
+ always_true_target);
+ return;
+ }
Location lhs = cond->GetLocations()->InAt(0);
Location rhs = cond->GetLocations()->InAt(1);
// LHS is guaranteed to be in a register (see
@@ -1041,36 +1228,94 @@
void LocationsBuilderX86::VisitCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::Any());
- if (cond->NeedsMaterialization()) {
- // We need a byte register.
- locations->SetOut(Location::RegisterLocation(ECX));
+ // Handle the long/FP comparisons made in instruction simplification.
+ switch (cond->InputAt(0)->GetType()) {
+ case Primitive::kPrimLong: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+ if (cond->NeedsMaterialization()) {
+ locations->SetOut(Location::RequiresRegister());
+ }
+ break;
+ }
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble: {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ if (cond->NeedsMaterialization()) {
+ locations->SetOut(Location::RequiresRegister());
+ }
+ break;
+ }
+ default:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ if (cond->NeedsMaterialization()) {
+ // We need a byte register.
+ locations->SetOut(Location::RegisterLocation(ECX));
+ }
+ break;
}
}
void InstructionCodeGeneratorX86::VisitCondition(HCondition* cond) {
- if (cond->NeedsMaterialization()) {
- LocationSummary* locations = cond->GetLocations();
- Register reg = locations->Out().AsRegister<Register>();
- // Clear register: setcc only sets the low byte.
- __ xorl(reg, reg);
- Location lhs = locations->InAt(0);
- Location rhs = locations->InAt(1);
- if (rhs.IsRegister()) {
- __ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>());
- } else if (rhs.IsConstant()) {
- int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- if (constant == 0) {
- __ testl(lhs.AsRegister<Register>(), lhs.AsRegister<Register>());
- } else {
- __ cmpl(lhs.AsRegister<Register>(), Immediate(constant));
- }
- } else {
- __ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
- }
- __ setb(X86Condition(cond->GetCondition()), reg);
+ if (!cond->NeedsMaterialization()) {
+ return;
}
+
+ LocationSummary* locations = cond->GetLocations();
+ Location lhs = locations->InAt(0);
+ Location rhs = locations->InAt(1);
+ Register reg = locations->Out().AsRegister<Register>();
+ Label true_label, false_label;
+
+ switch (cond->InputAt(0)->GetType()) {
+ default: {
+ // Integer case.
+
+ // Clear output register: setcc only sets the low byte.
+ __ xorl(reg, reg);
+
+ if (rhs.IsRegister()) {
+ __ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>());
+ } else if (rhs.IsConstant()) {
+ int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
+ if (constant == 0) {
+ __ testl(lhs.AsRegister<Register>(), lhs.AsRegister<Register>());
+ } else {
+ __ cmpl(lhs.AsRegister<Register>(), Immediate(constant));
+ }
+ } else {
+ __ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
+ }
+ __ setb(X86Condition(cond->GetCondition()), reg);
+ return;
+ }
+ case Primitive::kPrimLong:
+ GenerateLongComparesAndJumps(cond, &true_label, &false_label);
+ break;
+ case Primitive::kPrimFloat:
+ __ ucomiss(lhs.AsFpuRegister<XmmRegister>(), rhs.AsFpuRegister<XmmRegister>());
+ GenerateFPJumps(cond, &true_label, &false_label);
+ break;
+ case Primitive::kPrimDouble:
+ __ ucomisd(lhs.AsFpuRegister<XmmRegister>(), rhs.AsFpuRegister<XmmRegister>());
+ GenerateFPJumps(cond, &true_label, &false_label);
+ break;
+ }
+
+ // Convert the jumps into the result.
+ Label done_label;
+
+ // false case: result = 0;
+ __ Bind(&false_label);
+ __ xorl(reg, reg);
+ __ jmp(&done_label);
+
+ // True case: result = 1
+ __ Bind(&true_label);
+ __ movl(reg, Immediate(1));
+ __ Bind(&done_label);
}
void LocationsBuilderX86::VisitEqual(HEqual* comp) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 623e832..65d6e0a 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -177,7 +177,7 @@
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
void DivByPowerOfTwo(HDiv* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
- void GenerateRemFP(HRem *rem);
+ void GenerateRemFP(HRem* rem);
void HandleShift(HBinaryOperation* instruction);
void GenerateShlLong(const Location& loc, Register shifter);
void GenerateShrLong(const Location& loc, Register shifter);
@@ -201,6 +201,13 @@
Label* true_target,
Label* false_target,
Label* always_true_target);
+ void GenerateCompareTestAndBranch(HIf* if_inst,
+ HCondition* condition,
+ Label* true_target,
+ Label* false_target,
+ Label* always_true_target);
+ void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label);
+ void GenerateLongComparesAndJumps(HCondition* cond, Label* true_label, Label* false_label);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
X86Assembler* const assembler_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c9d19c8..ddaa60d 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -833,6 +833,134 @@
UNUSED(exit);
}
+void InstructionCodeGeneratorX86_64::GenerateFPJumps(HCondition* cond,
+ Label* true_label,
+ Label* false_label) {
+ bool gt_bias = cond->IsGtBias();
+ IfCondition if_cond = cond->GetCondition();
+ Condition ccode = X86_64Condition(if_cond);
+ switch (if_cond) {
+ case kCondEQ:
+ if (!gt_bias) {
+ __ j(kParityEven, false_label);
+ }
+ break;
+ case kCondNE:
+ if (!gt_bias) {
+ __ j(kParityEven, true_label);
+ }
+ break;
+ case kCondLT:
+ if (gt_bias) {
+ __ j(kParityEven, false_label);
+ }
+ ccode = kBelow;
+ break;
+ case kCondLE:
+ if (gt_bias) {
+ __ j(kParityEven, false_label);
+ }
+ ccode = kBelowEqual;
+ break;
+ case kCondGT:
+ if (gt_bias) {
+ __ j(kParityEven, true_label);
+ }
+ ccode = kAbove;
+ break;
+ case kCondGE:
+ if (gt_bias) {
+ __ j(kParityEven, true_label);
+ }
+ ccode = kAboveEqual;
+ break;
+ }
+ __ j(ccode, true_label);
+}
+
+void InstructionCodeGeneratorX86_64::GenerateCompareTestAndBranch(HIf* if_instr,
+ HCondition* condition,
+ Label* true_target,
+ Label* false_target,
+ Label* always_true_target) {
+ LocationSummary* locations = condition->GetLocations();
+ Location left = locations->InAt(0);
+ Location right = locations->InAt(1);
+
+ // We don't want true_target as a nullptr.
+ if (true_target == nullptr) {
+ true_target = always_true_target;
+ }
+ bool falls_through = (false_target == nullptr);
+
+ // FP compares don't like null false_targets.
+ if (false_target == nullptr) {
+ false_target = codegen_->GetLabelOf(if_instr->IfFalseSuccessor());
+ }
+
+ Primitive::Type type = condition->InputAt(0)->GetType();
+ switch (type) {
+ case Primitive::kPrimLong: {
+ CpuRegister left_reg = left.AsRegister<CpuRegister>();
+ if (right.IsConstant()) {
+ int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+ if (IsInt<32>(value)) {
+ if (value == 0) {
+ __ testq(left_reg, left_reg);
+ } else {
+ __ cmpq(left_reg, Immediate(static_cast<int32_t>(value)));
+ }
+ } else {
+ // Value won't fit in an 32-bit integer.
+ __ cmpq(left_reg, codegen_->LiteralInt64Address(value));
+ }
+ } else if (right.IsDoubleStackSlot()) {
+ __ cmpq(left_reg, Address(CpuRegister(RSP), right.GetStackIndex()));
+ } else {
+ __ cmpq(left_reg, right.AsRegister<CpuRegister>());
+ }
+ __ j(X86_64Condition(condition->GetCondition()), true_target);
+ break;
+ }
+ case Primitive::kPrimFloat: {
+ if (right.IsFpuRegister()) {
+ __ ucomiss(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ } else if (right.IsConstant()) {
+ __ ucomiss(left.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralFloatAddress(
+ right.GetConstant()->AsFloatConstant()->GetValue()));
+ } else {
+ DCHECK(right.IsStackSlot());
+ __ ucomiss(left.AsFpuRegister<XmmRegister>(),
+ Address(CpuRegister(RSP), right.GetStackIndex()));
+ }
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ }
+ case Primitive::kPrimDouble: {
+ if (right.IsFpuRegister()) {
+ __ ucomisd(left.AsFpuRegister<XmmRegister>(), right.AsFpuRegister<XmmRegister>());
+ } else if (right.IsConstant()) {
+ __ ucomisd(left.AsFpuRegister<XmmRegister>(),
+ codegen_->LiteralDoubleAddress(
+ right.GetConstant()->AsDoubleConstant()->GetValue()));
+ } else {
+ DCHECK(right.IsDoubleStackSlot());
+ __ ucomisd(left.AsFpuRegister<XmmRegister>(),
+ Address(CpuRegister(RSP), right.GetStackIndex()));
+ }
+ GenerateFPJumps(condition, true_target, false_target);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unexpected condition type " << type;
+ }
+
+ if (!falls_through) {
+ __ jmp(false_target);
+ }
+}
+
void InstructionCodeGeneratorX86_64::GenerateTestAndBranch(HInstruction* instruction,
Label* true_target,
Label* false_target,
@@ -854,9 +982,13 @@
!cond->IsCondition() || cond->AsCondition()->NeedsMaterialization();
// Moves do not affect the eflags register, so if the condition is
// evaluated just before the if, we don't need to evaluate it
- // again.
+ // again. We can't use the eflags on FP conditions if they are
+ // materialized due to the complex branching.
+ Primitive::Type type = cond->IsCondition() ? cond->InputAt(0)->GetType() : Primitive::kPrimInt;
bool eflags_set = cond->IsCondition()
- && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction);
+ && cond->AsCondition()->IsBeforeWhenDisregardMoves(instruction)
+ && !Primitive::IsFloatingPointType(type);
+
if (materialized) {
if (!eflags_set) {
// Materialized condition, compare against 0.
@@ -872,6 +1004,13 @@
__ j(X86_64Condition(cond->AsCondition()->GetCondition()), true_target);
}
} else {
+ // Is this a long or FP comparison that has been folded into the HCondition?
+ if (type == Primitive::kPrimLong || Primitive::IsFloatingPointType(type)) {
+ // Generate the comparison directly
+ GenerateCompareTestAndBranch(instruction->AsIf(), cond->AsCondition(),
+ true_target, false_target, always_true_target);
+ return;
+ }
Location lhs = cond->GetLocations()->InAt(0);
Location rhs = cond->GetLocations()->InAt(1);
if (rhs.IsRegister()) {
@@ -985,35 +1124,122 @@
void LocationsBuilderX86_64::VisitCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::Any());
+ // Handle the long/FP comparisons made in instruction simplification.
+ switch (cond->InputAt(0)->GetType()) {
+ case Primitive::kPrimLong:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ break;
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
+ break;
+ default:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ break;
+ }
if (cond->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* cond) {
- if (cond->NeedsMaterialization()) {
- LocationSummary* locations = cond->GetLocations();
- CpuRegister reg = locations->Out().AsRegister<CpuRegister>();
- // Clear register: setcc only sets the low byte.
- __ xorl(reg, reg);
- Location lhs = locations->InAt(0);
- Location rhs = locations->InAt(1);
- if (rhs.IsRegister()) {
- __ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
- } else if (rhs.IsConstant()) {
- int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- if (constant == 0) {
- __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
- } else {
- __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
- }
- } else {
- __ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
- }
- __ setcc(X86_64Condition(cond->GetCondition()), reg);
+ if (!cond->NeedsMaterialization()) {
+ return;
}
+
+ LocationSummary* locations = cond->GetLocations();
+ Location lhs = locations->InAt(0);
+ Location rhs = locations->InAt(1);
+ CpuRegister reg = locations->Out().AsRegister<CpuRegister>();
+ Label true_label, false_label;
+
+ switch (cond->InputAt(0)->GetType()) {
+ default:
+ // Integer case.
+
+ // Clear output register: setcc only sets the low byte.
+ __ xorl(reg, reg);
+
+ if (rhs.IsRegister()) {
+ __ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
+ } else if (rhs.IsConstant()) {
+ int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
+ if (constant == 0) {
+ __ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
+ } else {
+ __ cmpl(lhs.AsRegister<CpuRegister>(), Immediate(constant));
+ }
+ } else {
+ __ cmpl(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
+ }
+ __ setcc(X86_64Condition(cond->GetCondition()), reg);
+ return;
+ case Primitive::kPrimLong:
+ // Clear output register: setcc only sets the low byte.
+ __ xorl(reg, reg);
+
+ if (rhs.IsRegister()) {
+ __ cmpq(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
+ } else if (rhs.IsConstant()) {
+ int64_t value = rhs.GetConstant()->AsLongConstant()->GetValue();
+ if (IsInt<32>(value)) {
+ if (value == 0) {
+ __ testq(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
+ } else {
+ __ cmpq(lhs.AsRegister<CpuRegister>(), Immediate(static_cast<int32_t>(value)));
+ }
+ } else {
+ // Value won't fit in an int.
+ __ cmpq(lhs.AsRegister<CpuRegister>(), codegen_->LiteralInt64Address(value));
+ }
+ } else {
+ __ cmpq(lhs.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
+ }
+ __ setcc(X86_64Condition(cond->GetCondition()), reg);
+ return;
+ case Primitive::kPrimFloat: {
+ XmmRegister lhs_reg = lhs.AsFpuRegister<XmmRegister>();
+ if (rhs.IsConstant()) {
+ float value = rhs.GetConstant()->AsFloatConstant()->GetValue();
+ __ ucomiss(lhs_reg, codegen_->LiteralFloatAddress(value));
+ } else if (rhs.IsStackSlot()) {
+ __ ucomiss(lhs_reg, Address(CpuRegister(RSP), rhs.GetStackIndex()));
+ } else {
+ __ ucomiss(lhs_reg, rhs.AsFpuRegister<XmmRegister>());
+ }
+ GenerateFPJumps(cond, &true_label, &false_label);
+ break;
+ }
+ case Primitive::kPrimDouble: {
+ XmmRegister lhs_reg = lhs.AsFpuRegister<XmmRegister>();
+ if (rhs.IsConstant()) {
+ double value = rhs.GetConstant()->AsDoubleConstant()->GetValue();
+ __ ucomisd(lhs_reg, codegen_->LiteralDoubleAddress(value));
+ } else if (rhs.IsDoubleStackSlot()) {
+ __ ucomisd(lhs_reg, Address(CpuRegister(RSP), rhs.GetStackIndex()));
+ } else {
+ __ ucomisd(lhs_reg, rhs.AsFpuRegister<XmmRegister>());
+ }
+ GenerateFPJumps(cond, &true_label, &false_label);
+ break;
+ }
+ }
+
+ // Convert the jumps into the result.
+ Label done_label;
+
+ // false case: result = 0;
+ __ Bind(&false_label);
+ __ xorl(reg, reg);
+ __ jmp(&done_label);
+
+ // True case: result = 1
+ __ Bind(&true_label);
+ __ movl(reg, Immediate(1));
+ __ Bind(&done_label);
}
void LocationsBuilderX86_64::VisitEqual(HEqual* comp) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index c2aa56b..4b90381 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -183,7 +183,7 @@
void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor);
void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg);
void HandleBitwiseOperation(HBinaryOperation* operation);
- void GenerateRemFP(HRem *rem);
+ void GenerateRemFP(HRem* rem);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
void DivByPowerOfTwo(HDiv* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
@@ -202,6 +202,12 @@
Label* true_target,
Label* false_target,
Label* always_true_target);
+ void GenerateCompareTestAndBranch(HIf* if_inst,
+ HCondition* condition,
+ Label* true_target,
+ Label* false_target,
+ Label* always_true_target);
+ void GenerateFPJumps(HCondition* cond, Label* true_label, Label* false_label);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
X86_64Assembler* const assembler_;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 504c141..37c060c 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -357,6 +357,10 @@
StartAttributeStream("kind") << barrier->GetBarrierKind();
}
+ void VisitMonitorOperation(HMonitorOperation* monitor) OVERRIDE {
+ StartAttributeStream("kind") << (monitor->IsEnter() ? "enter" : "exit");
+ }
+
void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
StartAttributeStream("gen_clinit_check") << std::boolalpha
<< load_class->MustGenerateClinitCheck() << std::noboolalpha;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 62f90c2..337cf5b 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -54,6 +54,11 @@
void VisitCheckCast(HCheckCast* instruction) OVERRIDE;
void VisitAdd(HAdd* instruction) OVERRIDE;
void VisitAnd(HAnd* instruction) OVERRIDE;
+ void VisitCondition(HCondition* instruction) OVERRIDE;
+ void VisitGreaterThan(HGreaterThan* condition) OVERRIDE;
+ void VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) OVERRIDE;
+ void VisitLessThan(HLessThan* condition) OVERRIDE;
+ void VisitLessThanOrEqual(HLessThanOrEqual* condition) OVERRIDE;
void VisitDiv(HDiv* instruction) OVERRIDE;
void VisitMul(HMul* instruction) OVERRIDE;
void VisitNeg(HNeg* instruction) OVERRIDE;
@@ -330,7 +335,11 @@
block->RemoveInstruction(equal);
RecordSimplification();
}
+ } else {
+ VisitCondition(equal);
}
+ } else {
+ VisitCondition(equal);
}
}
@@ -358,7 +367,11 @@
block->RemoveInstruction(not_equal);
RecordSimplification();
}
+ } else {
+ VisitCondition(not_equal);
}
+ } else {
+ VisitCondition(not_equal);
}
}
@@ -485,6 +498,76 @@
}
}
+void InstructionSimplifierVisitor::VisitGreaterThan(HGreaterThan* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitLessThan(HLessThan* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitLessThanOrEqual(HLessThanOrEqual* condition) {
+ VisitCondition(condition);
+}
+
+void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
+ // Try to fold an HCompare into this HCondition.
+
+ // This simplification is currently only supported on x86 and x86_64.
+ // TODO: Implement it for ARM, ARM64 and MIPS64.
+ InstructionSet instruction_set = GetGraph()->GetInstructionSet();
+ if (instruction_set != kX86 && instruction_set != kX86_64) {
+ return;
+ }
+
+ HInstruction* left = condition->GetLeft();
+ HInstruction* right = condition->GetRight();
+ // We can only replace an HCondition which compares a Compare to 0.
+ // Both 'dx' and 'jack' generate a compare to 0 when compiling a
+ // condition with a long, float or double comparison as input.
+ if (!left->IsCompare() || !right->IsConstant() || right->AsIntConstant()->GetValue() != 0) {
+ // Conversion is not possible.
+ return;
+ }
+
+ // Is the Compare only used for this purpose?
+ if (!left->GetUses().HasOnlyOneUse()) {
+ // Someone else also wants the result of the compare.
+ return;
+ }
+
+ if (!left->GetEnvUses().IsEmpty()) {
+ // There is a reference to the compare result in an environment. Do we really need it?
+ if (GetGraph()->IsDebuggable()) {
+ return;
+ }
+
+ // We have to ensure that there are no deopt points in the sequence.
+ if (left->HasAnyEnvironmentUseBefore(condition)) {
+ return;
+ }
+ }
+
+ // Clean up any environment uses from the HCompare, if any.
+ left->RemoveEnvironmentUsers();
+
+ // We have decided to fold the HCompare into the HCondition. Transfer the information.
+ condition->SetBias(left->AsCompare()->GetBias());
+
+ // Replace the operands of the HCondition.
+ condition->ReplaceInput(left->InputAt(0), 0);
+ condition->ReplaceInput(left->InputAt(1), 1);
+
+ // Remove the HCompare.
+ left->GetBlock()->RemoveInstruction(left);
+
+ RecordSimplification();
+}
+
void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
HInstruction* input_other = instruction->GetLeastConstantLeft();
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index b82e37c..588ab70 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -940,6 +940,9 @@
GetRight()->AsLongConstant()->GetValue());
if (GetResultType() == Primitive::kPrimLong) {
return GetBlock()->GetGraph()->GetLongConstant(value);
+ } else if (GetResultType() == Primitive::kPrimBoolean) {
+ // This can be the result of an HCondition evaluation.
+ return GetBlock()->GetGraph()->GetIntConstant(static_cast<int32_t>(value));
} else {
DCHECK_EQ(GetResultType(), Primitive::kPrimInt);
return GetBlock()->GetGraph()->GetIntConstant(static_cast<int32_t>(value));
@@ -1647,4 +1650,38 @@
return os;
}
+bool HInstruction::HasAnyEnvironmentUseBefore(HInstruction* other) {
+ // For now, assume that instructions in different blocks may use the
+ // environment.
+ // TODO: Use the control flow to decide if this is true.
+ if (GetBlock() != other->GetBlock()) {
+ return true;
+ }
+
+ // We know that we are in the same block. Walk from 'this' to 'other',
+ // checking to see if there is any instruction with an environment.
+ HInstruction* current = this;
+ for (; current != other && current != nullptr; current = current->GetNext()) {
+ // This is a conservative check, as the instruction result may not be in
+ // the referenced environment.
+ if (current->HasEnvironment()) {
+ return true;
+ }
+ }
+
+ // We should have been called with 'this' before 'other' in the block.
+ // Just confirm this.
+ DCHECK(current != nullptr);
+ return false;
+}
+
+void HInstruction::RemoveEnvironmentUsers() {
+ for (HUseIterator<HEnvironment*> use_it(GetEnvUses()); !use_it.Done(); use_it.Advance()) {
+ HUseListNode<HEnvironment*>* user_node = use_it.Current();
+ HEnvironment* user = user_node->GetUser();
+ user->SetRawEnvAt(user_node->GetIndex(), nullptr);
+ }
+ env_uses_.Clear();
+}
+
} // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 2cffe02..59255d1 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -325,6 +325,10 @@
return invoke_type_;
}
+ InstructionSet GetInstructionSet() const {
+ return instruction_set_;
+ }
+
private:
void VisitBlockForDominatorTree(HBasicBlock* block,
HBasicBlock* predecessor,
@@ -1659,6 +1663,14 @@
virtual bool NeedsDexCache() const { return false; }
+ // Does this instruction have any use in an environment before
+ // control flow hits 'other'?
+ bool HasAnyEnvironmentUseBefore(HInstruction* other);
+
+ // Remove all references to environment uses of this instruction.
+ // The caller must ensure that this is safe to do.
+ void RemoveEnvironmentUsers();
+
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
@@ -2135,11 +2147,20 @@
DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
};
+// The comparison bias applies for floating point operations and indicates how NaN
+// comparisons are treated:
+enum ComparisonBias {
+ kNoBias, // bias is not applicable (i.e. for long operation)
+ kGtBias, // return 1 for NaN comparisons
+ kLtBias, // return -1 for NaN comparisons
+};
+
class HCondition : public HBinaryOperation {
public:
HCondition(HInstruction* first, HInstruction* second)
: HBinaryOperation(Primitive::kPrimBoolean, first, second),
- needs_materialization_(true) {}
+ needs_materialization_(true),
+ bias_(kNoBias) {}
bool NeedsMaterialization() const { return needs_materialization_; }
void ClearNeedsMaterialization() { needs_materialization_ = false; }
@@ -2152,11 +2173,24 @@
virtual IfCondition GetCondition() const = 0;
+ virtual IfCondition GetOppositeCondition() const = 0;
+
+ bool IsGtBias() { return bias_ == kGtBias; }
+
+ void SetBias(ComparisonBias bias) { bias_ = bias; }
+
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ return bias_ == other->AsCondition()->bias_;
+ }
+
private:
// For register allocation purposes, returns whether this instruction needs to be
// materialized (that is, not just be in the processor flags).
bool needs_materialization_;
+ // Needed if we merge a HCompare into a HCondition.
+ ComparisonBias bias_;
+
DISALLOW_COPY_AND_ASSIGN(HCondition);
};
@@ -2181,6 +2215,10 @@
return kCondEQ;
}
+ IfCondition GetOppositeCondition() const OVERRIDE {
+ return kCondNE;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(HEqual);
};
@@ -2205,6 +2243,10 @@
return kCondNE;
}
+ IfCondition GetOppositeCondition() const OVERRIDE {
+ return kCondEQ;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(HNotEqual);
};
@@ -2227,6 +2269,10 @@
return kCondLT;
}
+ IfCondition GetOppositeCondition() const OVERRIDE {
+ return kCondGE;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(HLessThan);
};
@@ -2249,6 +2295,10 @@
return kCondLE;
}
+ IfCondition GetOppositeCondition() const OVERRIDE {
+ return kCondGT;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual);
};
@@ -2271,6 +2321,10 @@
return kCondGT;
}
+ IfCondition GetOppositeCondition() const OVERRIDE {
+ return kCondLE;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(HGreaterThan);
};
@@ -2293,6 +2347,10 @@
return kCondGE;
}
+ IfCondition GetOppositeCondition() const OVERRIDE {
+ return kCondLT;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual);
};
@@ -2302,18 +2360,10 @@
// Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1.
class HCompare : public HBinaryOperation {
public:
- // The bias applies for floating point operations and indicates how NaN
- // comparisons are treated:
- enum Bias {
- kNoBias, // bias is not applicable (i.e. for long operation)
- kGtBias, // return 1 for NaN comparisons
- kLtBias, // return -1 for NaN comparisons
- };
-
HCompare(Primitive::Type type,
HInstruction* first,
HInstruction* second,
- Bias bias,
+ ComparisonBias bias,
uint32_t dex_pc)
: HBinaryOperation(Primitive::kPrimInt, first, second), bias_(bias), dex_pc_(dex_pc) {
DCHECK_EQ(type, first->GetType());
@@ -2338,6 +2388,8 @@
return bias_ == other->AsCompare()->bias_;
}
+ ComparisonBias GetBias() const { return bias_; }
+
bool IsGtBias() { return bias_ == kGtBias; }
uint32_t GetDexPc() const { return dex_pc_; }
@@ -2345,7 +2397,7 @@
DECLARE_INSTRUCTION(Compare);
private:
- const Bias bias_;
+ const ComparisonBias bias_;
const uint32_t dex_pc_;
DISALLOW_COPY_AND_ASSIGN(HCompare);
@@ -4079,13 +4131,19 @@
};
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None()), kind_(kind), dex_pc_(dex_pc) {
+ : HTemplateInstruction(SideEffects::ChangesSomething()), kind_(kind), dex_pc_(dex_pc) {
SetRawInputAt(0, object);
}
// Instruction may throw a Java exception, so we need an environment.
- bool NeedsEnvironment() const OVERRIDE { return true; }
- bool CanThrow() const OVERRIDE { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return CanThrow(); }
+
+ bool CanThrow() const OVERRIDE {
+ // Verifier guarantees that monitor-exit cannot throw.
+ // This is important because it allows the HGraphBuilder to remove
+ // a dead throw-catch loop generated for `synchronized` blocks/methods.
+ return IsEnter();
+ }
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index b86bc85..6da5c35 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -953,6 +953,48 @@
DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd");
}
+TEST_F(AssemblerX86_64Test, UcomissAddress) {
+ GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
+ GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM1), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
+ GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM2), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
+ GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM3), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), 0));
+ GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM4), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0));
+ const char* expected =
+ "ucomiss 0xc(%RDI,%RBX,4), %xmm0\n"
+ "ucomiss 0xc(%RDI,%R9,4), %xmm1\n"
+ "ucomiss 0xc(%RDI,%R9,4), %xmm2\n"
+ "ucomiss (%R13), %xmm3\n"
+ "ucomiss (%R13,%R9,1), %xmm4\n";
+
+ DriverStr(expected, "ucomiss_address");
+}
+
+TEST_F(AssemblerX86_64Test, UcomisdAddress) {
+ GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
+ GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM1), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
+ GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM2), x86_64::Address(
+ x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12));
+ GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM3), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), 0));
+ GetAssembler()->ucomisd(x86_64::XmmRegister(x86_64::XMM4), x86_64::Address(
+ x86_64::CpuRegister(x86_64::R13), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_1, 0));
+ const char* expected =
+ "ucomisd 0xc(%RDI,%RBX,4), %xmm0\n"
+ "ucomisd 0xc(%RDI,%R9,4), %xmm1\n"
+ "ucomisd 0xc(%RDI,%R9,4), %xmm2\n"
+ "ucomisd (%R13), %xmm3\n"
+ "ucomisd (%R13,%R9,1), %xmm4\n";
+
+ DriverStr(expected, "ucomisd_address");
+}
+
// X87
std::string x87_fn(AssemblerX86_64Test::Base* assembler_test ATTRIBUTE_UNUSED,
diff --git a/dexlist/Android.mk b/dexlist/Android.mk
new file mode 100755
index 0000000..988fe03
--- /dev/null
+++ b/dexlist/Android.mk
@@ -0,0 +1,54 @@
+# 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.
+
+# TODO(ajcbik): Art-i-fy this makefile
+
+# TODO(ajcbik): rename dexlist2 into dexlist when Dalvik version is removed
+
+LOCAL_PATH:= $(call my-dir)
+
+dexlist_src_files := dexlist.cc
+dexlist_c_includes := art/runtime
+dexlist_libraries := libart
+
+##
+## Build the device command line tool dexlist.
+##
+
+ifneq ($(SDK_ONLY),true) # SDK_only doesn't need device version
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := cc
+LOCAL_SRC_FILES := $(dexlist_src_files)
+LOCAL_C_INCLUDES := $(dexlist_c_includes)
+LOCAL_CFLAGS += -Wall
+LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
+LOCAL_MODULE := dexlist2
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+include $(BUILD_EXECUTABLE)
+endif # !SDK_ONLY
+
+##
+## Build the host command line tool dexlist.
+##
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := cc
+LOCAL_SRC_FILES := $(dexlist_src_files)
+LOCAL_C_INCLUDES := $(dexlist_c_includes)
+LOCAL_CFLAGS += -Wall
+LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
+LOCAL_MODULE := dexlist2
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
new file mode 100644
index 0000000..d7c0e4c
--- /dev/null
+++ b/dexlist/dexlist.cc
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ *
+ * Implementation file of the dexlist utility.
+ *
+ * This is a re-implementation of the original dexlist utility that was
+ * based on Dalvik functions in libdex into a new dexlist that is now
+ * based on Art functions in libart instead. The output is identical to
+ * the original for correct DEX files. Error messages may differ, however.
+ *
+ * List all methods in all concrete classes in one or more DEX files.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "dex_file-inl.h"
+#include "mem_map.h"
+#include "runtime.h"
+
+namespace art {
+
+static const char* gProgName = "dexlist";
+
+/* Command-line options. */
+static struct {
+ char* argCopy;
+ const char* classToFind;
+ const char* methodToFind;
+ const char* outputFileName;
+} gOptions;
+
+/*
+ * Output file. Defaults to stdout.
+ */
+static FILE* gOutFile = stdout;
+
+/*
+ * Data types that match the definitions in the VM specification.
+ */
+typedef uint8_t u1;
+typedef uint16_t u2;
+typedef uint32_t u4;
+typedef uint64_t u8;
+typedef int32_t s4;
+typedef int64_t s8;
+
+/*
+ * Returns a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ */
+static char* descriptorToDot(const char* str) {
+ size_t at = strlen(str);
+ if (str[0] == 'L') {
+ at -= 2; // Two fewer chars to copy.
+ str++;
+ }
+ char* newStr = reinterpret_cast<char*>(malloc(at + 1));
+ newStr[at] = '\0';
+ while (at > 0) {
+ at--;
+ newStr[at] = (str[at] == '/') ? '.' : str[at];
+ }
+ return newStr;
+}
+
+/*
+ * Positions table callback; we just want to catch the number of the
+ * first line in the method, which *should* correspond to the first
+ * entry from the table. (Could also use "min" here.)
+ */
+static bool positionsCb(void* context, u4 /*address*/, u4 lineNum) {
+ int* pFirstLine = reinterpret_cast<int *>(context);
+ if (*pFirstLine == -1) {
+ *pFirstLine = lineNum;
+ }
+ return 0;
+}
+
+/*
+ * Dumps a method.
+ */
+static void dumpMethod(const DexFile* pDexFile,
+ const char* fileName, u4 idx, u4 flags,
+ const DexFile::CodeItem* pCode, u4 codeOffset) {
+ // Abstract and native methods don't get listed.
+ if (pCode == nullptr || codeOffset == 0) {
+ return;
+ }
+
+ // Method information.
+ const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx);
+ const char* methodName = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+ const char* classDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
+ char* className = descriptorToDot(classDescriptor);
+ const u4 insnsOff = codeOffset + 0x10;
+
+ // Don't list methods that do not match a particular query.
+ if (gOptions.methodToFind != nullptr &&
+ (strcmp(gOptions.classToFind, className) != 0 ||
+ strcmp(gOptions.methodToFind, methodName) != 0)) {
+ free(className);
+ return;
+ }
+
+ // If the filename is empty, then set it to something printable.
+ if (fileName == nullptr || fileName[0] == 0) {
+ fileName = "(none)";
+ }
+
+ // Find the first line.
+ int firstLine = -1;
+ bool is_static = (flags & kAccStatic) != 0;
+ pDexFile->DecodeDebugInfo(
+ pCode, is_static, idx, positionsCb, nullptr, &firstLine);
+
+ // Method signature.
+ const Signature signature = pDexFile->GetMethodSignature(pMethodId);
+ char* typeDesc = strdup(signature.ToString().c_str());
+
+ // Dump actual method information.
+ fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n",
+ insnsOff, pCode->insns_size_in_code_units_ * 2,
+ className, methodName, typeDesc, fileName, firstLine);
+
+ free(typeDesc);
+ free(className);
+}
+
+/*
+ * Runs through all direct and virtual methods in the class.
+ */
+void dumpClass(const DexFile* pDexFile, u4 idx) {
+ const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
+
+ const char* fileName;
+ if (pClassDef.source_file_idx_ == DexFile::kDexNoIndex) {
+ fileName = nullptr;
+ } else {
+ fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_);
+ }
+
+ const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
+ if (pEncodedData != nullptr) {
+ ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
+ // Skip the fields.
+ for (; pClassData.HasNextStaticField(); pClassData.Next()) {}
+ for (; pClassData.HasNextInstanceField(); pClassData.Next()) {}
+ // Direct methods.
+ for (; pClassData.HasNextDirectMethod(); pClassData.Next()) {
+ dumpMethod(pDexFile, fileName,
+ pClassData.GetMemberIndex(),
+ pClassData.GetRawMemberAccessFlags(),
+ pClassData.GetMethodCodeItem(),
+ pClassData.GetMethodCodeItemOffset());
+ }
+ // Virtual methods.
+ for (; pClassData.HasNextVirtualMethod(); pClassData.Next()) {
+ dumpMethod(pDexFile, fileName,
+ pClassData.GetMemberIndex(),
+ pClassData.GetRawMemberAccessFlags(),
+ pClassData.GetMethodCodeItem(),
+ pClassData.GetMethodCodeItemOffset());
+ }
+ }
+}
+
+/*
+ * Processes a single file (either direct .dex or indirect .zip/.jar/.apk).
+ */
+static int processFile(const char* fileName) {
+ // If the file is not a .dex file, the function tries .zip/.jar/.apk files,
+ // all of which are Zip archives with "classes.dex" inside.
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) {
+ fputs(error_msg.c_str(), stderr);
+ fputc('\n', stderr);
+ return -1;
+ }
+
+ // Determine if opening file yielded a single dex file.
+ //
+ // TODO(ajcbik): this restriction is not really needed, but kept
+ // for now to stay close to original dexlist; we can
+ // later relax this!
+ //
+ if (dex_files.size() != 1) {
+ fprintf(stderr, "ERROR: DEX parse failed\n");
+ return -1;
+ }
+ const DexFile* pDexFile = dex_files[0].get();
+
+ // Success. Iterate over all classes.
+ fprintf(gOutFile, "#%s\n", fileName);
+ const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
+ for (u4 idx = 0; idx < classDefsSize; idx++) {
+ dumpClass(pDexFile, idx);
+ }
+ return 0;
+}
+
+/*
+ * Shows usage.
+ */
+static void usage(void) {
+ fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+ fprintf(stderr, "%s: [-m p.c.m] [-o outfile] dexfile...\n", gProgName);
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Main driver of the dexlist utility.
+ */
+int dexlistDriver(int argc, char** argv) {
+ // Art specific set up.
+ InitLogging(argv);
+ MemMap::Init();
+
+ // Reset options.
+ bool wantUsage = false;
+ memset(&gOptions, 0, sizeof(gOptions));
+
+ // Parse all arguments.
+ while (1) {
+ const int ic = getopt(argc, argv, "o:m:");
+ if (ic < 0) {
+ break; // done
+ }
+ switch (ic) {
+ case 'o': // output file
+ gOptions.outputFileName = optarg;
+ break;
+ case 'm':
+ // If -m X.Y.Z is given, then find all instances of the
+ // fully-qualified method name. This isn't really what
+ // dexlist is for, but it's easy to do it here.
+ {
+ gOptions.argCopy = strdup(optarg);
+ char* meth = strrchr(gOptions.argCopy, '.');
+ if (meth == nullptr) {
+ fprintf(stderr, "Expected: package.Class.method\n");
+ wantUsage = true;
+ } else {
+ *meth = '\0';
+ gOptions.classToFind = gOptions.argCopy;
+ gOptions.methodToFind = meth + 1;
+ }
+ }
+ break;
+ default:
+ wantUsage = true;
+ break;
+ } // switch
+ } // while
+
+ // Detect early problems.
+ if (optind == argc) {
+ fprintf(stderr, "%s: no file specified\n", gProgName);
+ wantUsage = true;
+ }
+ if (wantUsage) {
+ usage();
+ free(gOptions.argCopy);
+ return 2;
+ }
+
+ // Open alternative output file.
+ if (gOptions.outputFileName) {
+ gOutFile = fopen(gOptions.outputFileName, "w");
+ if (!gOutFile) {
+ fprintf(stderr, "Can't open %s\n", gOptions.outputFileName);
+ free(gOptions.argCopy);
+ return 1;
+ }
+ }
+
+ // Process all files supplied on command line. If one of them fails we
+ // continue on, only returning a failure at the end.
+ int result = 0;
+ while (optind < argc) {
+ result |= processFile(argv[optind++]);
+ } // while
+
+ free(gOptions.argCopy);
+ return result != 0;
+}
+
+} // namespace art
+
+int main(int argc, char** argv) {
+ return art::dexlistDriver(argc, argv);
+}
+
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
new file mode 100644
index 0000000..7b1b63d
--- /dev/null
+++ b/dexlist/dexlist_test.cc
@@ -0,0 +1,88 @@
+/*
+ * 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 <string>
+#include <vector>
+#include <sstream>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/stringprintf.h"
+#include "common_runtime_test.h"
+#include "runtime/arch/instruction_set.h"
+#include "runtime/gc/heap.h"
+#include "runtime/gc/space/image_space.h"
+#include "runtime/os.h"
+#include "runtime/utils.h"
+#include "utils.h"
+
+namespace art {
+
+class DexListTest : public CommonRuntimeTest {
+ protected:
+ virtual void SetUp() {
+ CommonRuntimeTest::SetUp();
+ // Dogfood our own lib core dex file.
+ dex_file_ = GetLibCoreDexFileName();
+ }
+
+ // Runs test with given arguments.
+ bool Exec(const std::vector<std::string>& args, std::string* error_msg) {
+ // TODO(ajcbik): dexlist2 -> dexlist
+ std::string file_path = GetTestAndroidRoot();
+ if (IsHost()) {
+ file_path += "/bin/dexlist2";
+ } else {
+ file_path += "/xbin/dexlist2";
+ }
+ EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ std::vector<std::string> exec_argv = { file_path };
+ exec_argv.insert(exec_argv.end(), args.begin(), args.end());
+ return ::art::Exec(exec_argv, error_msg);
+ }
+
+ std::string dex_file_;
+};
+
+
+TEST_F(DexListTest, NoInputFileGiven) {
+ std::string error_msg;
+ ASSERT_FALSE(Exec({}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, CantOpenOutput) {
+ std::string error_msg;
+ ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, IllFormedMethod) {
+ std::string error_msg;
+ ASSERT_FALSE(Exec({"-m", "joho", dex_file_}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, FullOutput) {
+ std::string error_msg;
+ ASSERT_TRUE(Exec({"-o", "/dev/null", dex_file_}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, MethodOutput) {
+ std::string error_msg;
+ ASSERT_TRUE(Exec({"-o", "/dev/null", "-m", "java.lang.Object.toString",
+ dex_file_}, &error_msg)) << error_msg;
+}
+
+} // namespace art
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index f324881..dce5206 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -548,10 +548,6 @@
os << " entryPointFromJni: "
<< reinterpret_cast<const void*>(
art_method->GetEntryPointFromJniPtrSize(pointer_size)) << ", ";
- os << " entryPointFromInterpreter: "
- << reinterpret_cast<const void*>(
- art_method->GetEntryPointFromInterpreterPtrSize(pointer_size))
- << ", ";
os << " entryPointFromQuickCompiledCode: "
<< reinterpret_cast<const void*>(
art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
@@ -631,10 +627,6 @@
os << " entryPointFromJni: "
<< reinterpret_cast<const void*>(
art_method->GetEntryPointFromJniPtrSize(pointer_size)) << ", ";
- os << " entryPointFromInterpreter: "
- << reinterpret_cast<const void*>(
- art_method->GetEntryPointFromInterpreterPtrSize(pointer_size))
- << ", ";
os << " entryPointFromQuickCompiledCode: "
<< reinterpret_cast<const void*>(
art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index cf4f822..8dde547 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1618,7 +1618,8 @@
stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
- stats_.art_method_bytes += method_section.Size();
+ // RoundUp to 8 bytes to match the intern table alignment expectation.
+ stats_.art_method_bytes += RoundUp(method_section.Size(), sizeof(uint64_t));
stats_.interned_strings_bytes += intern_section.Size();
stats_.Dump(os);
os << "\n";
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 3a155be..dbd1d23 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -651,8 +651,6 @@
copy->SetDexCacheResolvedTypes(RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes()));
copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
- copy->SetEntryPointFromInterpreterPtrSize(RelocatedAddressOfPointer(
- object->GetEntryPointFromInterpreterPtrSize(pointer_size)), pointer_size);
copy->SetEntryPointFromJniPtrSize(RelocatedAddressOfPointer(
object->GetEntryPointFromJniPtrSize(pointer_size)), pointer_size);
}
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 4a1e2c4..e8c47d9 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -43,9 +43,6 @@
class PointerArray;
} // namespace mirror
-typedef void (EntryPointFromInterpreter)(Thread* self, const DexFile::CodeItem* code_item,
- ShadowFrame* shadow_frame, JValue* result);
-
class ArtMethod FINAL {
public:
ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
@@ -272,23 +269,6 @@
void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- EntryPointFromInterpreter* GetEntryPointFromInterpreter() {
- return GetEntryPointFromInterpreterPtrSize(sizeof(void*));
- }
- EntryPointFromInterpreter* GetEntryPointFromInterpreterPtrSize(size_t pointer_size) {
- return GetEntryPoint<EntryPointFromInterpreter*>(
- EntryPointFromInterpreterOffset(pointer_size), pointer_size);
- }
-
- void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) {
- SetEntryPointFromInterpreterPtrSize(entry_point_from_interpreter, sizeof(void*));
- }
- void SetEntryPointFromInterpreterPtrSize(EntryPointFromInterpreter* entry_point_from_interpreter,
- size_t pointer_size) {
- SetEntryPoint(EntryPointFromInterpreterOffset(pointer_size), entry_point_from_interpreter,
- pointer_size);
- }
-
const void* GetEntryPointFromQuickCompiledCode() {
return GetEntryPointFromQuickCompiledCodePtrSize(sizeof(void*));
}
@@ -398,11 +378,6 @@
void UnregisterNative() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static MemberOffset EntryPointFromInterpreterOffset(size_t pointer_size) {
- return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
- PtrSizedFields, entry_point_from_interpreter_) / sizeof(void*) * pointer_size);
- }
-
static MemberOffset EntryPointFromJniOffset(size_t pointer_size) {
return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
PtrSizedFields, entry_point_from_jni_) / sizeof(void*) * pointer_size);
@@ -573,10 +548,6 @@
// PACKED(4) is necessary for the correctness of
// RoundUp(OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_), pointer_size).
struct PACKED(4) PtrSizedFields {
- // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
- // compiled code.
- void* entry_point_from_interpreter_;
-
// Pointer to JNI function registered to this method, or a function to resolve the JNI function.
void* entry_point_from_jni_;
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 10ed0f4..20d75f3 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -189,11 +189,11 @@
ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET,
art::ArtMethod::DexCacheResolvedTypesOffset().Int32Value())
-#define ART_METHOD_QUICK_CODE_OFFSET_32 36
+#define ART_METHOD_QUICK_CODE_OFFSET_32 32
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
-#define ART_METHOD_QUICK_CODE_OFFSET_64 48
+#define ART_METHOD_QUICK_CODE_OFFSET_64 40
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_64,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value())
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 7972158..6f45dc8 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -137,7 +137,7 @@
}
template<int n, typename T>
-static inline bool IsAligned(T x) {
+static constexpr bool IsAligned(T x) {
static_assert((n & (n - 1)) == 0, "n is not a power of two");
return (x & (n - 1)) == 0;
}
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 35b50d1..93d4edc 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_BASE_LOGGING_H_
#define ART_RUNTIME_BASE_LOGGING_H_
-#include <memory>
#include <ostream>
#include "base/macros.h"
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 23c5942..0694227 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1195,13 +1195,9 @@
if (kIsDebugBuild && !method->IsRuntimeMethod()) {
CHECK(method->GetDeclaringClass() != nullptr);
}
- if (!method->IsNative()) {
- method->SetEntryPointFromInterpreterPtrSize(
- artInterpreterToInterpreterBridge, image_pointer_size_);
- if (!method->IsRuntimeMethod() && method != runtime->GetResolutionMethod()) {
- method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
- image_pointer_size_);
- }
+ if (!method->IsNative() && !method->IsRuntimeMethod() && !method->IsResolutionMethod()) {
+ method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
+ image_pointer_size_);
}
}
}
@@ -2206,11 +2202,6 @@
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode());
- if (enter_interpreter && !method->IsNative()) {
- method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
- } else {
- method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
- }
if (method->IsAbstract()) {
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
@@ -3516,7 +3507,6 @@
// At runtime the method looks like a reference and argument saving method, clone the code
// related parameters from this method.
out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
- out->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) const {
@@ -5647,18 +5637,15 @@
const void* method_code) const {
OatFile::OatMethod oat_method = CreateOatMethod(method_code);
oat_method.LinkMethod(method);
- method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
void ClassLinker::SetEntryPointsToInterpreter(ArtMethod* method) const {
if (!method->IsNative()) {
- method->SetEntryPointFromInterpreter(artInterpreterToInterpreterBridge);
method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
} else {
const void* quick_method_code = GetQuickGenericJniStub();
OatFile::OatMethod oat_method = CreateOatMethod(quick_method_code);
oat_method.LinkMethod(method);
- method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index d9935cb..e4f7b7a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_CLASS_LINKER_H_
#define ART_RUNTIME_CLASS_LINKER_H_
-#include <deque>
#include <string>
#include <utility>
#include <vector>
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index de46b35..97d170e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4836,6 +4836,9 @@
CHECK(!Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
records = &dummy;
}
+ // We don't need to wait on the condition variable records->new_record_condition_, because this
+ // function only reads the class objects, which are already marked so it doesn't change their
+ // reachability.
//
// Part 1: generate string tables.
@@ -4850,7 +4853,7 @@
count > 0 && it != end; count--, it++) {
const gc::AllocRecord* record = it->second;
std::string temp;
- class_names.Add(record->GetClass()->GetDescriptor(&temp));
+ class_names.Add(record->GetClassDescriptor(&temp));
for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
ArtMethod* m = record->StackElement(i).GetMethod();
class_names.Add(m->GetDeclaringClassDescriptor());
@@ -4902,7 +4905,7 @@
const gc::AllocRecord* record = it->second;
size_t stack_depth = record->GetDepth();
size_t allocated_object_class_name_index =
- class_names.IndexOf(record->GetClass()->GetDescriptor(&temp));
+ class_names.IndexOf(record->GetClassDescriptor(&temp));
JDWP::Append4BE(bytes, record->ByteCount());
JDWP::Append2BE(bytes, static_cast<uint16_t>(record->GetTid()));
JDWP::Append2BE(bytes, allocated_object_class_name_index);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 7ac264a..3a15f1a 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -786,7 +786,10 @@
// Get the pointer to the start of the debugging data
const uint8_t* GetDebugInfoStream(const CodeItem* code_item) const {
- if (code_item->debug_info_off_ == 0) {
+ // Check that the offset is in bounds.
+ // Note that although the specification says that 0 should be used if there
+ // is no debug information, some applications incorrectly use 0xFFFFFFFF.
+ if (code_item->debug_info_off_ == 0 || code_item->debug_info_off_ >= size_) {
return nullptr;
} else {
return begin_ + code_item->debug_info_off_;
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 762f061..5f91566 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -320,7 +320,7 @@
return false;
}
- ArtMethod* method_obj = 0;
+ ArtMethod* method_obj = nullptr;
uintptr_t return_pc = 0;
uintptr_t sp = 0;
@@ -331,7 +331,9 @@
// If we don't have a potential method, we're outta here.
VLOG(signals) << "potential method: " << method_obj;
// TODO: Check linear alloc and image.
- if (method_obj == 0 || !IsAligned<kObjectAlignment>(method_obj)) {
+ DCHECK(IsAligned<sizeof(void*)>(ArtMethod::ObjectSize(sizeof(void*))))
+ << "ArtMethod is not pointer aligned";
+ if (method_obj == nullptr || !IsAligned<sizeof(void*)>(method_obj)) {
VLOG(signals) << "no method";
return false;
}
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 11921f4..6537ed2 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -32,6 +32,15 @@
return method_->GetLineNumFromDexPC(dex_pc_);
}
+const char* AllocRecord::GetClassDescriptor(std::string* storage) const {
+ // klass_ could contain null only if we implement class unloading.
+ if (UNLIKELY(klass_.IsNull())) {
+ return "null";
+ } else {
+ return klass_.Read()->GetDescriptor(storage);
+ }
+}
+
void AllocRecordObjectMap::SetProperties() {
#ifdef HAVE_ANDROID_OS
// Check whether there's a system property overriding the max number of records.
@@ -97,7 +106,7 @@
// Only visit the last recent_record_max_ number of allocation records in entries_ and mark the
// klass_ fields as strong roots.
for (auto it = entries_.rbegin(), end = entries_.rend(); count > 0 && it != end; count--, ++it) {
- buffered_visitor.VisitRoot(it->second->GetClassGcRoot());
+ buffered_visitor.VisitRootIfNonNull(it->second->GetClassGcRoot());
}
}
@@ -107,6 +116,8 @@
GcRoot<mirror::Class>& klass = record->GetClassGcRoot();
// This does not need a read barrier because this is called by GC.
mirror::Object* old_object = klass.Read<kWithoutReadBarrier>();
+ // The class object can become null if we implement class unloading.
+ // In that case we might still want to keep the class name string (not implemented).
mirror::Object* new_object = UNLIKELY(old_object == nullptr) ?
nullptr : callback(old_object, arg);
if (UNLIKELY(old_object != new_object)) {
@@ -163,11 +174,6 @@
allow_new_record_ = false;
}
-void AllocRecordObjectMap::EnsureNewAllocationRecordsDisallowed() {
- CHECK(!allow_new_record_);
-}
-
-
struct AllocRecordStackVisitor : public StackVisitor {
AllocRecordStackVisitor(Thread* thread, AllocRecordStackTrace* trace_in, size_t max)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index f567153..06721c8 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -188,7 +188,10 @@
return klass_.Read();
}
- GcRoot<mirror::Class>& GetClassGcRoot() {
+ const char* GetClassDescriptor(std::string* storage) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ GcRoot<mirror::Class>& GetClassGcRoot() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return klass_;
}
@@ -262,15 +265,18 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+ // Allocation tracking could be enabled by user in between DisallowNewAllocationRecords() and
+ // AllowNewAllocationRecords(), in which case new allocation records can be added although they
+ // should be disallowed. However, this is GC-safe because new objects are not processed in this GC
+ // cycle. The only downside of not handling this case is that such new allocation records can be
+ // swept from the list. But missing the first few records is acceptable for using the button to
+ // enable allocation tracking.
void DisallowNewAllocationRecords()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
void AllowNewAllocationRecords()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
- void EnsureNewAllocationRecordsDisallowed()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
// TODO: Is there a better way to hide the entries_'s type?
EntryList::iterator Begin()
diff --git a/runtime/gc/allocator_type.h b/runtime/gc/allocator_type.h
index f9a2ff6..185a9b7 100644
--- a/runtime/gc/allocator_type.h
+++ b/runtime/gc/allocator_type.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_ALLOCATOR_TYPE_H_
#define ART_RUNTIME_GC_ALLOCATOR_TYPE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index c7d2e9f..ccf5154 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -19,6 +19,7 @@
#include "art_field-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/reference_processor.h"
#include "gc/space/image_space.h"
#include "gc/space/space.h"
#include "intern_table.h"
@@ -334,6 +335,8 @@
}
}
}
+ // TODO: Other garbage collectors uses Runtime::VisitConcurrentRoots(), refactor this part
+ // to also use the same function.
{
TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
Runtime::Current()->VisitConstantRoots(this);
@@ -351,6 +354,7 @@
TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
Runtime::Current()->VisitNonThreadRoots(this);
}
+ Runtime::Current()->GetHeap()->VisitAllocationRecords(this);
// Immune spaces.
for (auto& space : heap_->GetContinuousSpaces()) {
diff --git a/runtime/gc/collector/gc_type.h b/runtime/gc/collector/gc_type.h
index f18e40f..401444a 100644
--- a/runtime/gc/collector/gc_type.h
+++ b/runtime/gc/collector/gc_type.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_
#define ART_RUNTIME_GC_COLLECTOR_GC_TYPE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 9275e6d..95ba380 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_COLLECTOR_TYPE_H_
#define ART_RUNTIME_GC_COLLECTOR_TYPE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index 1f2643a..0536f32d 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -17,7 +17,7 @@
#ifndef ART_RUNTIME_GC_GC_CAUSE_H_
#define ART_RUNTIME_GC_GC_CAUSE_H_
-#include <ostream>
+#include <iosfwd>
namespace art {
namespace gc {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 1b45ea1..26a45d3 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -37,13 +37,12 @@
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
-#include "gc/accounting/mod_union_table.h"
#include "gc/accounting/mod_union_table-inl.h"
#include "gc/accounting/remembered_set.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/collector/concurrent_copying.h"
#include "gc/collector/mark_compact.h"
-#include "gc/collector/mark_sweep-inl.h"
+#include "gc/collector/mark_sweep.h"
#include "gc/collector/partial_mark_sweep.h"
#include "gc/collector/semi_space.h"
#include "gc/collector/sticky_mark_sweep.h"
@@ -62,7 +61,6 @@
#include "image.h"
#include "intern_table.h"
#include "mirror/class-inl.h"
-#include "mirror/object.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/reference-inl.h"
@@ -467,6 +465,7 @@
gc_complete_cond_.reset(new ConditionVariable("GC complete condition variable",
*gc_complete_lock_));
task_processor_.reset(new TaskProcessor());
+ reference_processor_.reset(new ReferenceProcessor());
pending_task_lock_ = new Mutex("Pending task lock");
if (ignore_max_footprint_) {
SetIdealFootprint(std::numeric_limits<size_t>::max());
@@ -1865,7 +1864,7 @@
static_cast<double>(space_size_before_compaction);
tl->ResumeAll();
// Finish GC.
- reference_processor_.EnqueueClearedReferences(self);
+ reference_processor_->EnqueueClearedReferences(self);
GrowForUtilization(semi_space_collector_);
LogGC(kGcCauseHomogeneousSpaceCompact, collector);
FinishGC(self, collector::kGcTypeFull);
@@ -1998,7 +1997,7 @@
ChangeCollector(collector_type);
tl->ResumeAll();
// Can't call into java code with all threads suspended.
- reference_processor_.EnqueueClearedReferences(self);
+ reference_processor_->EnqueueClearedReferences(self);
uint64_t duration = NanoTime() - start_time;
GrowForUtilization(semi_space_collector_);
DCHECK(collector != nullptr);
@@ -2472,7 +2471,7 @@
total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes();
RequestTrim(self);
// Enqueue cleared references.
- reference_processor_.EnqueueClearedReferences(self);
+ reference_processor_->EnqueueClearedReferences(self);
// Grow the heap so that we know when to perform the next GC.
GrowForUtilization(collector, bytes_allocated_before_gc);
LogGC(gc_cause, collector);
@@ -3734,17 +3733,6 @@
}
}
-void Heap::EnsureNewAllocationRecordsDisallowed() const {
- if (IsAllocTrackingEnabled()) {
- // Lock and unlock once to ensure that no threads are still in the
- // middle of adding new allocation records.
- MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
- if (IsAllocTrackingEnabled()) {
- GetAllocationRecords()->EnsureNewAllocationRecordsDisallowed();
- }
- }
-}
-
// Based on debug malloc logic from libc/bionic/debug_stacktrace.cpp.
class StackCrawlState {
public:
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 1c75bd0..d0040f2 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -26,22 +26,17 @@
#include "arch/instruction_set.h"
#include "atomic.h"
#include "base/time_utils.h"
-#include "base/timing_logger.h"
#include "gc/accounting/atomic_stack.h"
#include "gc/accounting/card_table.h"
#include "gc/accounting/read_barrier_table.h"
#include "gc/gc_cause.h"
-#include "gc/collector/garbage_collector.h"
#include "gc/collector/gc_type.h"
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "globals.h"
-#include "jni.h"
#include "object_callbacks.h"
#include "offsets.h"
-#include "reference_processor.h"
#include "safe_map.h"
-#include "thread_pool.h"
#include "verify_object.h"
namespace art {
@@ -50,6 +45,7 @@
class Mutex;
class StackVisitor;
class Thread;
+class ThreadPool;
class TimingLogger;
namespace mirror {
@@ -631,7 +627,7 @@
bool HasImageSpace() const;
ReferenceProcessor* GetReferenceProcessor() {
- return &reference_processor_;
+ return reference_processor_.get();
}
TaskProcessor* GetTaskProcessor() {
return task_processor_.get();
@@ -721,10 +717,6 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
- void EnsureNewAllocationRecordsDisallowed() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
-
private:
class ConcurrentGCTask;
class CollectorTransitionTask;
@@ -1019,7 +1011,7 @@
std::unique_ptr<ConditionVariable> gc_complete_cond_ GUARDED_BY(gc_complete_lock_);
// Reference processor;
- ReferenceProcessor reference_processor_;
+ std::unique_ptr<ReferenceProcessor> reference_processor_;
// Task processor, proxies heap trim requests to the daemon threads.
std::unique_ptr<TaskProcessor> task_processor_;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 52192e2..2b567fe 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -16,7 +16,9 @@
#include "large_object_space.h"
+#include <valgrind.h>
#include <memory>
+#include <memcheck/memcheck.h>
#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/accounting/space_bitmap-inl.h"
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 5f3a1db..9495864 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -20,8 +20,6 @@
#include "space.h"
#include <ostream>
-#include <valgrind.h>
-#include <memcheck/memcheck.h>
namespace art {
namespace gc {
diff --git a/runtime/gc/space/rosalloc_space-inl.h b/runtime/gc/space/rosalloc_space-inl.h
index 25d4445..f94ec23 100644
--- a/runtime/gc/space/rosalloc_space-inl.h
+++ b/runtime/gc/space/rosalloc_space-inl.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_
#define ART_RUNTIME_GC_SPACE_ROSALLOC_SPACE_INL_H_
+#include <valgrind.h>
+
#include "gc/allocator/rosalloc-inl.h"
#include "gc/space/valgrind_settings.h"
#include "rosalloc_space.h"
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index d37ddcb..abe9dc2 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -99,19 +99,6 @@
}
}
method->SetEntryPointFromQuickCompiledCode(quick_code);
- if (!method->IsResolutionMethod()) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsQuickToInterpreterBridge(quick_code) ||
- (class_linker->IsQuickResolutionStub(quick_code) &&
- Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() &&
- !method->IsNative() && !method->IsProxyMethod())) {
- DCHECK(!method->IsNative()) << PrettyMethod(method);
- DCHECK(!method->IsProxyMethod()) << PrettyMethod(method);
- method->SetEntryPointFromInterpreter(art::artInterpreterToInterpreterBridge);
- } else {
- method->SetEntryPointFromInterpreter(art::artInterpreterToCompiledCodeBridge);
- }
- }
}
void Instrumentation::InstallStubsForMethod(ArtMethod* method) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index fa103b1..0980ea1 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -19,6 +19,7 @@
#include <cmath>
#include "debugger.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
#include "mirror/array-inl.h"
#include "unstarted_runtime.h"
#include "verifier/method_verifier.h"
@@ -490,6 +491,23 @@
uint32_t arg[Instruction::kMaxVarArgRegs],
uint32_t vregC) ALWAYS_INLINE;
+SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) ALWAYS_INLINE;
+
+static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) {
+ ArtMethod* target = new_shadow_frame->GetMethod();
+ if (UNLIKELY(target->IsNative() || target->IsProxyMethod())) {
+ return false;
+ }
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ return runtime->GetInstrumentation()->IsForcedInterpretOnly() ||
+ // Doing this check avoids doing compiled/interpreter transitions.
+ class_linker->IsQuickToInterpreterBridge(target->GetEntryPointFromQuickCompiledCode()) ||
+ // Force the use of interpreter when it is required by the debugger.
+ Dbg::IsForcedInterpreterNeededForCalling(self, target);
+}
+
template<bool is_range, bool do_assignability_check>
static inline bool DoCallCommon(ArtMethod* called_method,
Thread* self,
@@ -660,28 +678,11 @@
// Do the call now.
if (LIKELY(Runtime::Current()->IsStarted())) {
- if (kIsDebugBuild && new_shadow_frame->GetMethod()->GetEntryPointFromInterpreter() == nullptr) {
- LOG(FATAL) << "Attempt to invoke non-executable method: "
- << PrettyMethod(new_shadow_frame->GetMethod());
- UNREACHABLE();
- }
- if (kIsDebugBuild && Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly() &&
- !new_shadow_frame->GetMethod()->IsNative() &&
- !new_shadow_frame->GetMethod()->IsProxyMethod() &&
- new_shadow_frame->GetMethod()->GetEntryPointFromInterpreter()
- == artInterpreterToCompiledCodeBridge) {
- LOG(FATAL) << "Attempt to call compiled code when -Xint: "
- << PrettyMethod(new_shadow_frame->GetMethod());
- UNREACHABLE();
- }
- // Force the use of interpreter when it is required by the debugger.
- EntryPointFromInterpreter* entry;
- if (UNLIKELY(Dbg::IsForcedInterpreterNeededForCalling(self, new_shadow_frame->GetMethod()))) {
- entry = &art::artInterpreterToInterpreterBridge;
+ if (NeedsInterpreter(self, new_shadow_frame)) {
+ artInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
} else {
- entry = new_shadow_frame->GetMethod()->GetEntryPointFromInterpreter();
+ artInterpreterToCompiledCodeBridge(self, code_item, new_shadow_frame, result);
}
- entry(self, code_item, new_shadow_frame, result);
} else {
UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index bc9545b..fda97db 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -132,11 +132,7 @@
VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to breakpoint";
return false;
}
- const bool result = jit_compile_method_(jit_compiler_handle_, method, self);
- if (result) {
- method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
- }
- return result;
+ return jit_compile_method_(jit_compiler_handle_, method, self);
}
void Jit::CreateThreadPool() {
diff --git a/runtime/oat.h b/runtime/oat.h
index 000ae8e..5706c4e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '6', '4', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '6', '5', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 8e99dbb..df34ce7 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -17,13 +17,6 @@
#ifndef ART_RUNTIME_OBJECT_CALLBACKS_H_
#define ART_RUNTIME_OBJECT_CALLBACKS_H_
-// For ostream.
-#include <ostream>
-// For uint32_t.
-#include <stdint.h>
-// For size_t.
-#include <stdlib.h>
-
#include "base/macros.h"
namespace art {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 20e4149..5067b0d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1510,7 +1510,6 @@
monitor_list_->EnsureNewMonitorsDisallowed();
intern_table_->EnsureNewInternsDisallowed();
java_vm_->EnsureNewWeakGlobalsDisallowed();
- heap_->EnsureNewAllocationRecordsDisallowed();
}
void Runtime::SetInstructionSet(InstructionSet instruction_set) {
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 11c94db..6f3b0a3 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -857,7 +857,6 @@
<< " native=" << method->IsNative()
<< " entrypoints=" << method->GetEntryPointFromQuickCompiledCode()
<< "," << method->GetEntryPointFromJni()
- << "," << method->GetEntryPointFromInterpreter()
<< " next=" << *cur_quick_frame_;
}
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index f7ef894..5f965f1 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -25,6 +25,7 @@
#include "base/mutex-inl.h"
#include "gc/heap.h"
#include "jni_env_ext.h"
+#include "thread_pool.h"
namespace art {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index b697b43..7e8128f 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -28,7 +28,6 @@
#include <sstream>
#include "base/histogram-inl.h"
-#include "base/mutex.h"
#include "base/mutex-inl.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 1e84c9d..d8f80fa 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -201,112 +201,4 @@
return tasks_.size();
}
-WorkStealingWorker::WorkStealingWorker(ThreadPool* thread_pool, const std::string& name,
- size_t stack_size)
- : ThreadPoolWorker(thread_pool, name, stack_size), task_(nullptr) {}
-
-void WorkStealingWorker::Run() {
- Thread* self = Thread::Current();
- Task* task = nullptr;
- WorkStealingThreadPool* thread_pool = down_cast<WorkStealingThreadPool*>(thread_pool_);
- while ((task = thread_pool_->GetTask(self)) != nullptr) {
- WorkStealingTask* stealing_task = down_cast<WorkStealingTask*>(task);
-
- {
- CHECK(task_ == nullptr);
- MutexLock mu(self, thread_pool->work_steal_lock_);
- // Register that we are running the task
- ++stealing_task->ref_count_;
- task_ = stealing_task;
- }
- stealing_task->Run(self);
- // Mark ourselves as not running a task so that nobody tries to steal from us.
- // There is a race condition that someone starts stealing from us at this point. This is okay
- // due to the reference counting.
- task_ = nullptr;
-
- bool finalize;
-
- // Steal work from tasks until there is none left to steal. Note: There is a race, but
- // all that happens when the race occurs is that we steal some work instead of processing a
- // task from the queue.
- while (thread_pool->GetTaskCount(self) == 0) {
- WorkStealingTask* steal_from_task = nullptr;
-
- {
- MutexLock mu(self, thread_pool->work_steal_lock_);
- // Try finding a task to steal from.
- steal_from_task = thread_pool->FindTaskToStealFrom();
- if (steal_from_task != nullptr) {
- CHECK_NE(stealing_task, steal_from_task)
- << "Attempting to steal from completed self task";
- steal_from_task->ref_count_++;
- } else {
- break;
- }
- }
-
- if (steal_from_task != nullptr) {
- // Task which completed earlier is going to steal some work.
- stealing_task->StealFrom(self, steal_from_task);
-
- {
- // We are done stealing from the task, lets decrement its reference count.
- MutexLock mu(self, thread_pool->work_steal_lock_);
- finalize = !--steal_from_task->ref_count_;
- }
-
- if (finalize) {
- steal_from_task->Finalize();
- }
- }
- }
-
- {
- MutexLock mu(self, thread_pool->work_steal_lock_);
- // If nobody is still referencing task_ we can finalize it.
- finalize = !--stealing_task->ref_count_;
- }
-
- if (finalize) {
- stealing_task->Finalize();
- }
- }
-}
-
-WorkStealingWorker::~WorkStealingWorker() {}
-
-WorkStealingThreadPool::WorkStealingThreadPool(const char* name, size_t num_threads)
- : ThreadPool(name, 0),
- work_steal_lock_("work stealing lock"),
- steal_index_(0) {
- while (GetThreadCount() < num_threads) {
- const std::string worker_name = StringPrintf("Work stealing worker %zu", GetThreadCount());
- threads_.push_back(new WorkStealingWorker(this, worker_name,
- ThreadPoolWorker::kDefaultStackSize));
- }
-}
-
-WorkStealingTask* WorkStealingThreadPool::FindTaskToStealFrom() {
- const size_t thread_count = GetThreadCount();
- for (size_t i = 0; i < thread_count; ++i) {
- // TODO: Use CAS instead of lock.
- ++steal_index_;
- if (steal_index_ >= thread_count) {
- steal_index_-= thread_count;
- }
-
- WorkStealingWorker* worker = down_cast<WorkStealingWorker*>(threads_[steal_index_]);
- WorkStealingTask* task = worker->task_;
- if (task) {
- // Not null, we can probably steal from this worker.
- return task;
- }
- }
- // Couldn't find something to steal.
- return nullptr;
-}
-
-WorkStealingThreadPool::~WorkStealingThreadPool() {}
-
} // namespace art
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index 0557708..88700e6 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -144,58 +144,6 @@
DISALLOW_COPY_AND_ASSIGN(ThreadPool);
};
-class WorkStealingTask : public Task {
- public:
- WorkStealingTask() : ref_count_(0) {}
-
- size_t GetRefCount() const {
- return ref_count_;
- }
-
- virtual void StealFrom(Thread* self, WorkStealingTask* source) = 0;
-
- private:
- // How many people are referencing this task.
- size_t ref_count_;
-
- friend class WorkStealingWorker;
-};
-
-class WorkStealingWorker : public ThreadPoolWorker {
- public:
- virtual ~WorkStealingWorker();
-
- bool IsRunningTask() const {
- return task_ != nullptr;
- }
-
- protected:
- WorkStealingTask* task_;
-
- WorkStealingWorker(ThreadPool* thread_pool, const std::string& name, size_t stack_size);
- virtual void Run();
-
- private:
- friend class WorkStealingThreadPool;
- DISALLOW_COPY_AND_ASSIGN(WorkStealingWorker);
-};
-
-class WorkStealingThreadPool : public ThreadPool {
- public:
- explicit WorkStealingThreadPool(const char* name, size_t num_threads);
- virtual ~WorkStealingThreadPool();
-
- private:
- Mutex work_steal_lock_;
- // Which thread we are stealing from (round robin).
- size_t steal_index_;
-
- // Find a task to steal from
- WorkStealingTask* FindTaskToStealFrom() EXCLUSIVE_LOCKS_REQUIRED(work_steal_lock_);
-
- friend class WorkStealingWorker;
-};
-
} // namespace art
#endif // ART_RUNTIME_THREAD_POOL_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 92c9eb8..09db7cd 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4133,7 +4133,9 @@
<< " to be compatible with type '" << insn_type
<< "' but found type '" << *field_type
<< "' in get-object";
- work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
+ if (error != VERIFY_ERROR_BAD_CLASS_HARD) {
+ work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
+ }
return;
}
}
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index c8aa4fd..1435607 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -585,7 +585,15 @@
DCHECK(!Equals(incoming_type)); // Trivial equality handled by caller
// Perform pointer equality tests for conflict to avoid virtual method dispatch.
const ConflictType& conflict = reg_types->Conflict();
- if (this == &conflict) {
+ if (IsUndefined() || incoming_type.IsUndefined()) {
+ // There is a difference between undefined and conflict. Conflicts may be copied around, but
+ // not used. Undefined registers must not be copied. So any merge with undefined should return
+ // undefined.
+ if (IsUndefined()) {
+ return *this;
+ }
+ return incoming_type;
+ } else if (this == &conflict) {
DCHECK(IsConflict());
return *this; // Conflict MERGE * => Conflict
} else if (&incoming_type == &conflict) {
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 244deed..9cd2bdf 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -38,10 +38,9 @@
verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
<< new_type << "'";
return false;
- } else if (new_type.IsConflict()) { // should only be set during a merge
- verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
- return false;
} else {
+ // Note: previously we failed when asked to set a conflict. However, conflicts are OK as long
+ // as they are not accessed, and our backends can handle this nowadays.
line_[vdst] = new_type.GetId();
}
// Clear the monitor entry bits for this register.
@@ -93,8 +92,9 @@
if (!SetRegisterType(verifier, vdst, type)) {
return;
}
- if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
- (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
+ if (!type.IsConflict() && // Allow conflicts to be copied around.
+ ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
+ (cat == kTypeCategoryRef && !type.IsReferenceTypes()))) {
verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
<< " cat=" << static_cast<int>(cat);
} else if (cat == kTypeCategoryRef) {
diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build
index f909fb2..faa2983 100644
--- a/test/003-omnibus-opcodes/build
+++ b/test/003-omnibus-opcodes/build
@@ -22,5 +22,10 @@
rm classes/UnresClass.class
${JAVAC} -d classes `find src2 -name '*.java'`
-${DX} -JXmx256m --debug --dex --output=classes.dex classes
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ ${JACK} --import classes.jack --output-dex .
+else
+ ${DX} -JXmx256m --debug --dex --output=classes.dex classes
+ fi
zip $TEST_NAME.jar classes.dex
diff --git a/test/004-ReferenceMap/build b/test/004-ReferenceMap/build
new file mode 100644
index 0000000..08987b5
--- /dev/null
+++ b/test/004-ReferenceMap/build
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# The test relies on DEX file produced by javac+dx so keep building with them for now
+# (see b/19467889)
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/004-StackWalk/build b/test/004-StackWalk/build
new file mode 100644
index 0000000..08987b5
--- /dev/null
+++ b/test/004-StackWalk/build
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# The test relies on DEX file produced by javac+dx so keep building with them for now
+# (see b/19467889)
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/005-annotations/build b/test/005-annotations/build
index 2474055..3f00a1a 100644
--- a/test/005-annotations/build
+++ b/test/005-annotations/build
@@ -25,4 +25,12 @@
# ...but not at run time.
rm 'classes/android/test/anno/MissingAnnotation.class'
rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
-${DX} -JXmx256m --debug --dex --output=$TEST_NAME.jar classes
+
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ ${JACK} --import classes.jack --output-dex .
+else
+ ${DX} -JXmx256m --debug --dex --output=classes.dex classes
+fi
+
+zip $TEST_NAME.jar classes.dex
diff --git a/test/022-interface/build b/test/022-interface/build
index c86b1dc..3f8915c 100644
--- a/test/022-interface/build
+++ b/test/022-interface/build
@@ -19,5 +19,11 @@
# Use classes that are compiled with ecj that exposes an invokeinterface
# issue when interfaces override methods in Object
-${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ ${JACK} --import classes.jack --output-dex .
+else
+ ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+fi
+
zip $TEST_NAME.jar classes.dex
diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build
index ad42a2d..3bb6747 100644
--- a/test/023-many-interfaces/build
+++ b/test/023-many-interfaces/build
@@ -21,8 +21,14 @@
gcc -Wall -Werror -o iface-gen iface-gen.c
./iface-gen
-mkdir classes
-${JAVAC} -d classes src/*.java
+if [ ${USE_JACK} = "true" ]; then
+ # Use the default Jack commands
+ ./default-build
+else
+ mkdir classes
+ ${JAVAC} -d classes src/*.java
-${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
-zip $TEST_NAME.jar classes.dex
+ # dx needs more memory for that test so do not pass Xmx option here.
+ ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+ zip $TEST_NAME.jar classes.dex
+fi
diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java
index 8c7c27d..0de56f9 100644
--- a/test/036-finalizer/src/Main.java
+++ b/test/036-finalizer/src/Main.java
@@ -68,14 +68,17 @@
return s[0];
}
+ private static void printWeakReference(WeakReference<FinalizerTest> wimp) {
+ // Reference ft so we are sure the WeakReference cannot be cleared.
+ FinalizerTest keepLive = wimp.get();
+ System.out.println("wimp: " + wimpString(wimp));
+ }
+
public static void main(String[] args) {
WeakReference<FinalizerTest> wimp = makeRef();
- FinalizerTest keepLive = wimp.get();
-
- System.out.println("wimp: " + wimpString(wimp));
+ printWeakReference(wimp);
/* this will try to collect and finalize ft */
- keepLive = null;
System.out.println("gc");
Runtime.getRuntime().gc();
diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build
index ef286d1..ae42519 100644
--- a/test/056-const-string-jumbo/build
+++ b/test/056-const-string-jumbo/build
@@ -39,8 +39,13 @@
printf("}\n") > fileName;
}'
-mkdir classes
-${JAVAC} -d classes src/*.java
+if [ ${USE_JACK} = "true" ]; then
+ ${JACK} --output-dex . src
+else
+ mkdir classes
+ ${JAVAC} -d classes src/*.java
-${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --output=classes.dex classes
+ ${DX} -JXmx500m --debug --dex --no-optimize --positions=none --no-locals --output=classes.dex classes
+fi
+
zip $TEST_NAME.jar classes.dex
diff --git a/test/074-gc-thrash/src/Main.java b/test/074-gc-thrash/src/Main.java
index 238e73a..f947d0b 100644
--- a/test/074-gc-thrash/src/Main.java
+++ b/test/074-gc-thrash/src/Main.java
@@ -218,17 +218,7 @@
return;
}
- /*
- * Check the results of the last trip through. Everything in
- * "weak" should be matched in "strong", and the two should be
- * equivalent (object-wise, not just string-equality-wise).
- */
- for (int i = 0; i < MAX_DEPTH; i++) {
- if (strong[i] != weak[i].get()) {
- System.err.println("Deep: " + i + " strong=" + strong[i] +
- ", weak=" + weak[i].get());
- }
- }
+ checkStringReferences();
/*
* Wipe "strong", do a GC, see if "weak" got collected.
@@ -248,6 +238,26 @@
System.out.println("Deep: iters=" + iter / MAX_DEPTH);
}
+
+ /**
+ * Check the results of the last trip through. Everything in
+ * "weak" should be matched in "strong", and the two should be
+ * equivalent (object-wise, not just string-equality-wise).
+ *
+ * We do that check in a separate method to avoid retaining these
+ * String references in local DEX registers. In interpreter mode,
+ * they would retain these references until the end of the method
+ * or until they are updated to another value.
+ */
+ private static void checkStringReferences() {
+ for (int i = 0; i < MAX_DEPTH; i++) {
+ if (strong[i] != weak[i].get()) {
+ System.err.println("Deep: " + i + " strong=" + strong[i] +
+ ", weak=" + weak[i].get());
+ }
+ }
+ }
+
/**
* Recursively dive down, setting one or more local variables.
*
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 4dfa73c..177c5a4 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -1000,6 +1000,45 @@
Assert.assertEquals(Long.reverse(0x8765432187654321L), 0x84c2a6e184c2a6e1L);
Assert.assertEquals(Long.reverse(Long.MAX_VALUE), 0xfffffffffffffffeL);
Assert.assertEquals(Long.reverse(Long.MIN_VALUE), 1L);
+
+ Assert.assertEquals(test_Long_reverse_b22324327(0xaaaaaaaaaaaaaaaaL, 0x5555555555555555L),
+ 157472205507277347L);
+ }
+
+ // A bit more complicated than the above. Use local variables to stress register allocation.
+ private static long test_Long_reverse_b22324327(long l1, long l2) {
+ // A couple of local integers. Use them in a loop, so they get promoted.
+ int i1 = 0, i2 = 1, i3 = 2, i4 = 3, i5 = 4, i6 = 5, i7 = 6, i8 = 7;
+ for (int k = 0; k < 10; k++) {
+ i1 += 1;
+ i2 += 2;
+ i3 += 3;
+ i4 += 4;
+ i5 += 5;
+ i6 += 6;
+ i7 += 7;
+ i8 += 8;
+ }
+
+ // Do the Long.reverse() calls, save the results.
+ long r1 = Long.reverse(l1);
+ long r2 = Long.reverse(l2);
+
+ // Some more looping with the ints.
+ for (int k = 0; k < 10; k++) {
+ i1 += 1;
+ i2 += 2;
+ i3 += 3;
+ i4 += 4;
+ i5 += 5;
+ i6 += 6;
+ i7 += 7;
+ i8 += 8;
+ }
+
+ // Include everything in the result, so things are kept live. Try to be a little bit clever to
+ // avoid things being folded somewhere.
+ return (r1 / i1) + (r2 / i2) + i3 + i4 + i5 + i6 + i7 + i8;
}
static Object runtime;
diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build
index 963d6b3..6f50a76 100644
--- a/test/085-old-style-inner-class/build
+++ b/test/085-old-style-inner-class/build
@@ -22,7 +22,12 @@
mkdir classes
${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'`
-# Suppress stderr to keep the inner class warnings out of the expected output.
-${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ ${JACK} --import classes.jack --output-dex .
+else
+ # Suppress stderr to keep the inner class warnings out of the expected output.
+ ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null
+fi
zip $TEST_NAME.jar classes.dex
diff --git a/test/089-many-methods/build b/test/089-many-methods/build
index 7ede759..ff77c60 100644
--- a/test/089-many-methods/build
+++ b/test/089-many-methods/build
@@ -43,7 +43,8 @@
printf("}\n") > fileName;
}'
+# The test relies on the error message produced by dx, not jack, so keep building with dx for now
+# (b/19467889).
mkdir classes
${JAVAC} -d classes `find src -name '*.java'`
${DX} -JXmx1024m --dex --no-optimize classes
-
diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build
index 6576779..a855873 100644
--- a/test/097-duplicate-method/build
+++ b/test/097-duplicate-method/build
@@ -18,8 +18,19 @@
set -e
mkdir classes
-${JAVAC} -d classes src/*.java
-${JASMIN} -d classes src/*.j
-${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+if [ ${USE_JACK} = "true" ]; then
+ ${JACK} --output-jack src.jack src
+
+ ${JASMIN} -d classes src/*.j
+ ${JILL} classes --output jasmin.jack
+
+ # We set jack.import.type.policy=keep-first to consider class definitions from jasmin first.
+ ${JACK} --import jasmin.jack --import src.jack -D jack.import.type.policy=keep-first --output-dex .
+else
+ ${JAVAC} -d classes src/*.java
+ ${JASMIN} -d classes src/*.j
+
+ ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+fi
zip $TEST_NAME.jar classes.dex
diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build
index c21a9ef..e772fb8 100644
--- a/test/111-unresolvable-exception/build
+++ b/test/111-unresolvable-exception/build
@@ -21,5 +21,10 @@
${JAVAC} -d classes `find src -name '*.java'`
rm classes/TestException.class
-${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ ${JACK} --import classes.jack --output-dex .
+else
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+fi
zip $TEST_NAME.jar classes.dex
diff --git a/test/113-multidex/build b/test/113-multidex/build
index ec8706e..8ef5c0e 100644
--- a/test/113-multidex/build
+++ b/test/113-multidex/build
@@ -17,16 +17,32 @@
# Stop if something fails.
set -e
-mkdir classes
-
# All except Main
+mkdir classes
${JAVAC} -d classes `find src -name '*.java'`
rm classes/Main.class
-${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
# Only Main
-${JAVAC} -d classes `find src -name '*.java'`
-rm classes/Second.class classes/FillerA.class classes/FillerB.class classes/Inf*.class
-${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes
+mkdir classes2
+${JAVAC} -d classes2 `find src -name '*.java'`
+rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class
+if [ ${USE_JACK} = "true" ]; then
+ # Create .jack files from classes generated with javac.
+ ${JILL} classes --output classes.jack
+ ${JILL} classes2 --output classes2.jack
+
+ # Create DEX files from .jack files.
+ ${JACK} --import classes.jack --output-dex .
+ mv classes.dex classes-1.dex
+ ${JACK} --import classes2.jack --output-dex .
+ mv classes.dex classes2.dex
+ mv classes-1.dex classes.dex
+else
+ # All except Main
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+
+ # Only Main
+ ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2
+fi
zip $TEST_NAME.jar classes.dex classes2.dex
diff --git a/test/114-ParallelGC/src/Main.java b/test/114-ParallelGC/src/Main.java
index 46029cf..159dd5c 100644
--- a/test/114-ParallelGC/src/Main.java
+++ b/test/114-ParallelGC/src/Main.java
@@ -53,20 +53,21 @@
}
// Allocate objects to definitely run GC before quitting.
- ArrayList<Object> l = new ArrayList<Object>();
- try {
- for (int i = 0; i < 100000; i++) {
- l.add(new ArrayList<Object>(i));
- }
- } catch (OutOfMemoryError oom) {
- }
- // Make the (outer) ArrayList unreachable. Note it may still
- // be reachable under an interpreter or a compiler without a
- // liveness analysis.
- l = null;
+ allocateObjectsToRunGc();
+
new ArrayList<Object>(50);
}
+ private static void allocateObjectsToRunGc() {
+ ArrayList<Object> l = new ArrayList<Object>();
+ try {
+ for (int i = 0; i < 100000; i++) {
+ l.add(new ArrayList<Object>(i));
+ }
+ } catch (OutOfMemoryError oom) {
+ }
+ }
+
private Main(CyclicBarrier startBarrier) {
this.startBarrier = startBarrier;
}
diff --git a/test/121-modifiers/build b/test/121-modifiers/build
index d73be86..85b69e9 100644
--- a/test/121-modifiers/build
+++ b/test/121-modifiers/build
@@ -30,5 +30,11 @@
# mv NonInf.out classes/NonInf.class
# mv Main.class A.class A\$B.class A\$C.class classes/
-${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers.
+ ${JACK} --sanity-checks off --import classes.jack --output-dex .
+else
+ ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
+fi
zip $TEST_NAME.jar classes.dex
diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build
index 62e57c8..b92ecf9 100644
--- a/test/124-missing-classes/build
+++ b/test/124-missing-classes/build
@@ -25,4 +25,11 @@
# ...but not at run time.
rm 'classes/MissingClass.class'
rm 'classes/Main$MissingInnerClass.class'
-${DX} -JXmx256m --debug --dex --output=$TEST_NAME.jar classes
+
+if [ ${USE_JACK} = "true" ]; then
+ ${JILL} classes --output classes.jack
+ ${JACK} --import classes.jack --output-dex .
+else
+ ${DX} -JXmx256m --debug --dex --output=classes.dex classes
+fi
+zip $TEST_NAME.jar classes.dex
diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build
index 4c30f3f..b7f2118 100644
--- a/test/126-miranda-multidex/build
+++ b/test/126-miranda-multidex/build
@@ -17,16 +17,32 @@
# Stop if something fails.
set -e
+# All except MirandaInterface
mkdir classes
-
-# All except Main
${JAVAC} -d classes `find src -name '*.java'`
rm classes/MirandaInterface.class
-${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
-# Only Main
-${JAVAC} -d classes `find src -name '*.java'`
-rm classes/Main.class classes/MirandaAbstract.class classes/MirandaClass*.class classes/MirandaInterface2*.class
-${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes
+# Only MirandaInterface
+mkdir classes2
+${JAVAC} -d classes2 `find src -name '*.java'`
+rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class
+if [ ${USE_JACK} = "true" ]; then
+ # Create .jack files from classes generated with javac.
+ ${JILL} classes --output classes.jack
+ ${JILL} classes2 --output classes2.jack
+
+ # Create DEX files from .jack files.
+ ${JACK} --import classes.jack --output-dex .
+ mv classes.dex classes-1.dex
+ ${JACK} --import classes2.jack --output-dex .
+ mv classes.dex classes2.dex
+ mv classes-1.dex classes.dex
+else
+ # All except Main
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes
+
+ # Only Main
+ ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex classes2
+fi
zip $TEST_NAME.jar classes.dex classes2.dex
diff --git a/test/127-secondarydex/build b/test/127-secondarydex/build
index 712774f..0d9f4d6 100755
--- a/test/127-secondarydex/build
+++ b/test/127-secondarydex/build
@@ -23,9 +23,21 @@
mkdir classes-ex
mv classes/Super.class classes-ex
-if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+if [ ${USE_JACK} = "true" ]; then
+ # Create .jack files from classes generated with javac.
+ ${JILL} classes --output classes.jack
+ ${JILL} classes-ex --output classes-ex.jack
+
+ # Create DEX files from .jack files.
+ ${JACK} --import classes.jack --output-dex .
zip $TEST_NAME.jar classes.dex
- ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ ${JACK} --import classes-ex.jack --output-dex .
zip ${TEST_NAME}-ex.jar classes.dex
+else
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+ fi
fi
diff --git a/test/131-structural-change/build b/test/131-structural-change/build
index 7ddc81d..ff0da20 100755
--- a/test/131-structural-change/build
+++ b/test/131-structural-change/build
@@ -17,15 +17,23 @@
# Stop if something fails.
set -e
-mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
-
-mkdir classes-ex
-${JAVAC} -d classes-ex `find src-ex -name '*.java'`
-
-if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+if [ ${USE_JACK} = "true" ]; then
+ ${JACK} --output-dex . src
zip $TEST_NAME.jar classes.dex
- ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+
+ ${JACK} --output-dex . src-ex
zip ${TEST_NAME}-ex.jar classes.dex
+else
+ mkdir classes
+ ${JAVAC} -d classes `find src -name '*.java'`
+
+ mkdir classes-ex
+ ${JAVAC} -d classes-ex `find src-ex -name '*.java'`
+
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+ zip $TEST_NAME.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+ fi
fi
diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build
index 789d38e..5ff73ec 100644
--- a/test/303-verification-stress/build
+++ b/test/303-verification-stress/build
@@ -21,8 +21,14 @@
gcc -Wall -Werror -o classes-gen classes-gen.c
./classes-gen
-mkdir classes
-${JAVAC} -d classes src/*.java
+if [ ${USE_JACK} = "true" ]; then
+ # Use the default Jack commands
+ ./default-build
+else
+ mkdir classes
+ ${JAVAC} -d classes src/*.java
-${DX} --debug --dex --output=classes.dex classes
-zip $TEST_NAME.jar classes.dex
+ # dx needs more memory for that test so do not pass Xmx option here.
+ ${DX} --debug --dex --output=classes.dex classes
+ zip $TEST_NAME.jar classes.dex
+fi
diff --git a/test/454-get-vreg/build b/test/454-get-vreg/build
new file mode 100644
index 0000000..08987b5
--- /dev/null
+++ b/test/454-get-vreg/build
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# The test relies on DEX file produced by javac+dx so keep building with them for now
+# (see b/19467889)
+mkdir classes
+${JAVAC} -d classes `find src -name '*.java'`
+${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java
index a4280de..18125fa 100644
--- a/test/482-checker-loop-back-edge-use/src/Main.java
+++ b/test/482-checker-loop-back-edge-use/src/Main.java
@@ -36,11 +36,11 @@
}
/// CHECK-START: void Main.loop3(boolean) liveness (after)
- /// CHECK: ParameterValue liveness:4 ranges:{[4,64)} uses:[60,64]
- /// CHECK: Goto liveness:62
+ /// CHECK: ParameterValue liveness:4 ranges:{[4,60)} uses:[56,60]
+ /// CHECK: Goto liveness:58
- /// CHECK-START: void Main.loop3(boolean) liveness (after)
- /// CHECK-NOT: Goto liveness:56
+ // CHECK-START: void Main.loop3(boolean) liveness (after)
+ // CHECK-NOT: Goto liveness:50
public static void loop3(boolean incoming) {
// 'incoming' only needs a use at the outer loop's back edge.
while (System.currentTimeMillis() != 42) {
@@ -49,11 +49,11 @@
}
}
- /// CHECK-START: void Main.loop4(boolean) liveness (after)
- /// CHECK: ParameterValue liveness:4 ranges:{[4,24)} uses:[24]
+ // CHECK-START: void Main.loop4(boolean) liveness (after)
+ // CHECK: ParameterValue liveness:4 ranges:{[4,22)} uses:[22]
- /// CHECK-START: void Main.loop4(boolean) liveness (after)
- /// CHECK-NOT: Goto liveness:22
+ // CHECK-START: void Main.loop4(boolean) liveness (after)
+ // CHECK-NOT: Goto liveness:18
public static void loop4(boolean incoming) {
// 'incoming' has no loop use, so should not have back edge uses.
System.out.println(incoming);
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index 4ea7b61..2274ba4 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -713,20 +713,20 @@
## CHECK-START: int Builder.testSwitchTryExit(int, int, int, int) builder (after)
## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
+## CHECK: successors "<<BEnterTry1:B\d+>>"
## CHECK: name "<<BPSwitch0:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>"
-## CHECK: successors "<<BTry2:B\d+>>" "<<BPSwitch1:B\d+>>"
+## CHECK: predecessors "<<BEnterTry1>>"
+## CHECK: successors "<<BTry2:B\d+>>" "<<BExitTry1:B\d+>>"
## CHECK: If
-## CHECK: name "<<BPSwitch1>>"
-## CHECK: predecessors "<<BPSwitch0>>"
-## CHECK: successors "<<BExitTry1:B\d+>>" "<<BTry1:B\d+>>"
+## CHECK: name "<<BPSwitch1:B\d+>>"
+## CHECK: predecessors "<<BExitTry1>>"
+## CHECK: successors "<<BOutside:B\d+>>" "<<BEnterTry2:B\d+>>"
## CHECK: If
-## CHECK: name "<<BTry1>>"
-## CHECK: predecessors "<<BPSwitch1>>"
+## CHECK: name "<<BTry1:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2>>"
## CHECK: successors "<<BTry2>>"
## CHECK: Div
@@ -735,28 +735,34 @@
## CHECK: successors "<<BExitTry2:B\d+>>"
## CHECK: Div
-## CHECK: name "<<BOutside:B\d+>>"
-## CHECK: predecessors "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: name "<<BOutside>>"
+## CHECK: predecessors "<<BPSwitch1>>" "<<BExitTry2>>"
## CHECK: successors "<<BCatchReturn:B\d+>>"
## CHECK: Div
## CHECK: name "<<BCatchReturn>>"
-## CHECK: predecessors "<<BOutside>>" "<<BEnterTry>>" "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>"
## CHECK: flags "catch_block"
## CHECK: Return
-## CHECK: name "<<BEnterTry>>"
+## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BPSwitch0>>"
## CHECK: xhandlers "<<BCatchReturn>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
-## CHECK: predecessors "<<BPSwitch1>>"
-## CHECK: successors "<<BOutside>>"
+## CHECK: predecessors "<<BPSwitch0>>"
+## CHECK: successors "<<BPSwitch1>>"
## CHECK: xhandlers "<<BCatchReturn>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BEnterTry2>>"
+## CHECK: predecessors "<<BPSwitch1>>"
+## CHECK: successors "<<BTry1>>"
+## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: TryBoundary kind:entry
+
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
## CHECK: successors "<<BOutside>>"
@@ -767,6 +773,7 @@
.registers 4
:try_start
+ div-int/2addr p0, p1
packed-switch p0, :pswitch_data
div-int/2addr p0, p1
@@ -809,6 +816,10 @@
## CHECK: flags "catch_block"
## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: name "<<BExit>>"
+## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>"
+## CHECK: Exit
+
## CHECK: name "<<BEnterTry>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BTry>>"
@@ -821,10 +832,6 @@
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
-## CHECK: name "<<BExit>>"
-## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>"
-## CHECK: Exit
-
.method public static testThrow(Ljava/lang/Exception;)I
.registers 2
@@ -852,6 +859,9 @@
## CHECK: name "<<BReturn:B\d+>>"
## CHECK: predecessors "<<BExitTry>>"
+## CHECK: successors "<<BExit:B\d+>>"
+
+## CHECK: name "<<BExit>>"
## CHECK: name "<<BTry:B\d+>>"
## CHECK: predecessors "<<BEnterTry>>"
@@ -904,6 +914,14 @@
## CHECK: name "<<BReturn:B\d+>>"
## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: name "{{B\d+}}"
+## CHECK: Exit
+
+## CHECK: name "<<BTry2:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: successors "<<BExitTry2>>"
+## CHECK: Div
+
## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BTry1>>"
@@ -916,11 +934,6 @@
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2>>"
-## CHECK: successors "<<BExitTry2>>"
-## CHECK: Div
-
## CHECK: name "<<BEnterTry2>>"
## CHECK: predecessors "<<BCatch>>"
## CHECK: successors "<<BTry2>>"
@@ -956,48 +969,51 @@
## CHECK: successors "<<BCatch1:B\d+>>"
## CHECK: name "<<BCatch1>>"
-## CHECK: predecessors "B0" "<<BEnter2:B\d+>>" "<<BExit2:B\d+>>"
-## CHECK: successors "<<BEnter1:B\d+>>"
+## CHECK: predecessors "B0" "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>"
+## CHECK: successors "<<BEnterTry1:B\d+>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BCatch2:B\d+>>"
-## CHECK: predecessors "<<BExit1:B\d+>>" "<<BEnter1>>" "<<BExit1>>"
-## CHECK: successors "<<BEnter2>>"
+## CHECK: predecessors "<<BExitTry1:B\d+>>" "<<BEnterTry1>>" "<<BExitTry1>>"
+## CHECK: successors "<<BEnterTry2>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit2>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BExit:B\d+>>"
## CHECK: Return
+## CHECK: name "<<BExit>>"
+
## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnter1>>"
-## CHECK: successors "<<BExit1>>"
+## CHECK: predecessors "<<BEnterTry1>>"
+## CHECK: successors "<<BExitTry1>>"
## CHECK: Div
-## CHECK: name "<<BEnter1>>"
+## CHECK: name "<<BTry2:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: successors "<<BExitTry2>>"
+## CHECK: Div
+
+## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "<<BCatch1>>"
## CHECK: successors "<<BTry1>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:entry
-## CHECK: name "<<BExit1>>"
+## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BTry1>>"
## CHECK: successors "<<BCatch2>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:exit
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnter2>>"
-## CHECK: successors "<<BExit2>>"
-## CHECK: Div
-
-## CHECK: name "<<BEnter2>>"
+## CHECK: name "<<BEnterTry2>>"
## CHECK: predecessors "<<BCatch2>>"
## CHECK: successors "<<BTry2>>"
## CHECK: xhandlers "<<BCatch1>>"
## CHECK: TryBoundary kind:entry
-## CHECK: name "<<BExit2>>"
+## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: xhandlers "<<BCatch1>>"
@@ -1131,3 +1147,29 @@
:try_end
.catchall {:try_start .. :try_end} :catch_all
.end method
+
+## CHECK-START: int Builder.testSynchronized(java.lang.Object) builder (after)
+## CHECK: flags "catch_block"
+## CHECK-NOT: end_block
+## CHECK: MonitorOperation kind:exit
+
+.method public static testSynchronized(Ljava/lang/Object;)I
+ .registers 2
+
+ monitor-enter p0
+
+ :try_start_9
+ invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I
+ move-result v0
+
+ monitor-exit p0
+ return v0
+
+ :catchall_11
+ move-exception v0
+ monitor-exit p0
+ :try_end_15
+ .catchall {:try_start_9 .. :try_end_15} :catchall_11
+
+ throw v0
+.end method
diff --git a/test/522-checker-regression-monitor-exit/expected.txt b/test/522-checker-regression-monitor-exit/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/522-checker-regression-monitor-exit/expected.txt
diff --git a/test/522-checker-regression-monitor-exit/info.txt b/test/522-checker-regression-monitor-exit/info.txt
new file mode 100644
index 0000000..7cfc963
--- /dev/null
+++ b/test/522-checker-regression-monitor-exit/info.txt
@@ -0,0 +1,3 @@
+Regression test for removal of monitor-exit due to lack of specified side-effects.
+The test invokes a synchronized version of Object.hashCode in multiple threads.
+If monitor-exit is removed, the following threads will get stuck and timeout.
\ No newline at end of file
diff --git a/test/522-checker-regression-monitor-exit/smali/Test.smali b/test/522-checker-regression-monitor-exit/smali/Test.smali
new file mode 100644
index 0000000..c8e9198
--- /dev/null
+++ b/test/522-checker-regression-monitor-exit/smali/Test.smali
@@ -0,0 +1,40 @@
+#
+# 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.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+## CHECK-START: int Test.synchronizedHashCode(java.lang.Object) dead_code_elimination (before)
+## CHECK: MonitorOperation [<<Param:l\d+>>] kind:enter
+## CHECK: MonitorOperation [<<Param>>] kind:exit
+
+## CHECK-START: int Test.synchronizedHashCode(java.lang.Object) dead_code_elimination (after)
+## CHECK: MonitorOperation [<<Param:l\d+>>] kind:enter
+## CHECK: MonitorOperation [<<Param>>] kind:exit
+
+.method public static synchronizedHashCode(Ljava/lang/Object;)I
+ .registers 2
+
+ monitor-enter p0
+ invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I
+ move-result v0
+
+ # Must not get removed by DCE.
+ monitor-exit p0
+
+ return v0
+
+.end method
diff --git a/test/522-checker-regression-monitor-exit/src/Main.java b/test/522-checker-regression-monitor-exit/src/Main.java
new file mode 100644
index 0000000..c85ac96
--- /dev/null
+++ b/test/522-checker-regression-monitor-exit/src/Main.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeoutException;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ private static class HashCodeQuery implements Callable<Integer> {
+ public HashCodeQuery(Object obj) {
+ m_obj = obj;
+ }
+
+ public Integer call() {
+ Integer result;
+ try {
+ Class<?> c = Class.forName("Test");
+ Method m = c.getMethod("synchronizedHashCode", new Class[] { Object.class });
+ result = (Integer) m.invoke(null, m_obj);
+ } catch (Exception e) {
+ System.err.println("Hash code query exception");
+ e.printStackTrace();
+ result = -1;
+ }
+ return result;
+ }
+
+ private Object m_obj;
+ private int m_index;
+ }
+
+ public static void main(String args[]) throws Exception {
+ Object obj = new Object();
+ int numThreads = 10;
+
+ ExecutorService pool = Executors.newFixedThreadPool(numThreads);
+
+ List<HashCodeQuery> queries = new ArrayList<HashCodeQuery>(numThreads);
+ for (int i = 0; i < numThreads; ++i) {
+ queries.add(new HashCodeQuery(obj));
+ }
+
+ try {
+ List<Future<Integer>> results = pool.invokeAll(queries, 5, TimeUnit.SECONDS);
+
+ int hash = obj.hashCode();
+ for (int i = 0; i < numThreads; ++i) {
+ int result = results.get(i).get();
+ if (hash != result) {
+ throw new Error("Query #" + i + " wrong. Expected " + hash + ", got " + result);
+ }
+ }
+ pool.shutdown();
+ } catch (CancellationException ex) {
+ System.err.println("Job timeout");
+ System.exit(1);
+ }
+ }
+}
diff --git a/test/523-checker-can-throw-regression/expected.txt b/test/523-checker-can-throw-regression/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/523-checker-can-throw-regression/expected.txt
diff --git a/test/523-checker-can-throw-regression/info.txt b/test/523-checker-can-throw-regression/info.txt
new file mode 100644
index 0000000..720dc85
--- /dev/null
+++ b/test/523-checker-can-throw-regression/info.txt
@@ -0,0 +1,2 @@
+Regression test for the HGraphBuilder which would split a throwing catch block
+but would not update information about which blocks throw.
\ No newline at end of file
diff --git a/test/523-checker-can-throw-regression/smali/Test.smali b/test/523-checker-can-throw-regression/smali/Test.smali
new file mode 100644
index 0000000..87192ea
--- /dev/null
+++ b/test/523-checker-can-throw-regression/smali/Test.smali
@@ -0,0 +1,53 @@
+#
+# 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.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+## CHECK-START: int Test.testCase(int, int, int) builder (after)
+## CHECK: TryBoundary kind:entry
+## CHECK: TryBoundary kind:entry
+## CHECK-NOT: TryBoundary kind:entry
+
+## CHECK-START: int Test.testCase(int, int, int) builder (after)
+## CHECK: TryBoundary kind:exit
+## CHECK: TryBoundary kind:exit
+## CHECK-NOT: TryBoundary kind:exit
+
+.method public static testCase(III)I
+ .registers 4
+
+ :try_start_1
+ div-int/2addr p0, p1
+ return p0
+ :try_end_1
+ .catchall {:try_start_1 .. :try_end_1} :catchall
+
+ :catchall
+ :try_start_2
+ move-exception v0
+ # Block would be split here but second part not marked as throwing.
+ div-int/2addr p0, p1
+ if-eqz p2, :else
+
+ div-int/2addr p0, p1
+ :else
+ div-int/2addr p0, p2
+ return p0
+ :try_end_2
+ .catchall {:try_start_2 .. :try_end_2} :catchall
+
+.end method
diff --git a/test/523-checker-can-throw-regression/src/Main.java b/test/523-checker-can-throw-regression/src/Main.java
new file mode 100644
index 0000000..3ff48f3
--- /dev/null
+++ b/test/523-checker-can-throw-regression/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeoutException;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String args[]) {}
+}
diff --git a/test/701-easy-div-rem/build b/test/701-easy-div-rem/build
index 1dc8452..666fe89 100644
--- a/test/701-easy-div-rem/build
+++ b/test/701-easy-div-rem/build
@@ -23,6 +23,10 @@
# Increase the file size limitation for classes.lst as the machine generated
# source file contains a lot of methods and is quite large.
-ulimit -S 4096
+
+# Jack generates big temp files so only apply ulimit for dx.
+if [ ${USE_JACK} = "false" ]; then
+ ulimit -S 4096
+fi
./default-build
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index fe68c5b..4c17240 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -28,4 +28,7 @@
b/22080519
b/21645819
b/22244733
+b/22331663
+b/22331663 (pass)
+b/22331663 (fail)
Done!
diff --git a/test/800-smali/smali/b_22331663.smali b/test/800-smali/smali/b_22331663.smali
new file mode 100644
index 0000000..057fc7f
--- /dev/null
+++ b/test/800-smali/smali/b_22331663.smali
@@ -0,0 +1,38 @@
+.class public LB22331663;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ # Make v4 defined, just use null.
+ const v4, 0
+
+ if-eqz v5, :Label2
+
+:Label1
+ # Construct a java.lang.Object completely, and throw a new exception.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+ new-instance v3, Ljava/lang/RuntimeException;
+ invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+ throw v3
+
+:Label2
+ # Allocate a java.lang.Object (do not initialize), and throw a new exception.
+ new-instance v4, Ljava/lang/Object;
+
+ new-instance v3, Ljava/lang/RuntimeException;
+ invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+ throw v3
+
+:Label3
+ # Catch handler. Here we had to merge the uninitialized with the initialized reference,
+ # which creates a conflict. Copy the conflict, and then return. This should not make the
+ # verifier fail the method.
+ move-object v0, v4
+
+ return-void
+
+.catchall {:Label1 .. :Label3} :Label3
+.end method
diff --git a/test/800-smali/smali/b_22331663_fail.smali b/test/800-smali/smali/b_22331663_fail.smali
new file mode 100644
index 0000000..0c25e30
--- /dev/null
+++ b/test/800-smali/smali/b_22331663_fail.smali
@@ -0,0 +1,20 @@
+.class public LB22331663Fail;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ if-eqz v5, :Label1
+
+ # Construct a java.lang.Object completely. This makes v4 of reference type.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+:Label1
+ # At this point, v4 is the merge of Undefined and ReferenceType. The verifier should
+ # reject any use of this, even a copy. Previously this was a conflict. Conflicts must
+ # be movable now, so ensure that we do not get a conflict (and then allow the move).
+ move-object v0, v4
+
+ return-void
+.end method
diff --git a/test/800-smali/smali/b_22331663_pass.smali b/test/800-smali/smali/b_22331663_pass.smali
new file mode 100644
index 0000000..1b54180
--- /dev/null
+++ b/test/800-smali/smali/b_22331663_pass.smali
@@ -0,0 +1,22 @@
+.class public LB22331663Pass;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ if-eqz v5, :Label1
+
+ # Construct a java.lang.Object completely. This makes v4 of reference type.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+:Label1
+ # At this point, v4 is the merge of Undefined and ReferenceType. The verifier should not
+ # reject this if it is unused.
+
+ # Do an allocation here. This will force heap checking in gcstress mode.
+ new-instance v0, Ljava/lang/Object;
+ invoke-direct {v0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 61f0d7b..8be6418 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -103,6 +103,12 @@
null, null));
testCases.add(new TestCase("b/22244733", "B22244733", "run", new Object[] { "abc" },
null, "abc"));
+ testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
+ null, null));
+ testCases.add(new TestCase("b/22331663 (pass)", "B22331663Pass", "run",
+ new Object[] { false }, null, null));
+ testCases.add(new TestCase("b/22331663 (fail)", "B22331663Fail", "run",
+ new Object[] { false }, new VerifyError(), null));
}
public void runTests() {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c4111f6..c18bb5c 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -33,22 +33,46 @@
TEST_ART_RUN_TEST_BUILD_RULES :=
# Dependencies for actually running a run-test.
-TEST_ART_RUN_TEST_DEPENDENCIES := $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger
+TEST_ART_RUN_TEST_DEPENDENCIES := \
+ $(DX) \
+ $(HOST_OUT_EXECUTABLES)/jasmin \
+ $(HOST_OUT_EXECUTABLES)/smali \
+ $(HOST_OUT_EXECUTABLES)/dexmerger
+
+ifeq ($(ANDROID_COMPILE_WITH_JACK),true)
+ TEST_ART_RUN_TEST_DEPENDENCIES += \
+ $(JACK_JAR) \
+ $(JACK_LAUNCHER_JAR) \
+ $(JILL_JAR)
+endif
# Helper to create individual build targets for tests. Must be called with $(eval).
# $(1): the test number
define define-build-art-run-test
dmart_target := $(art_run_tests_dir)/art-run-tests/$(1)/touch
+ run_test_options = --build-only
+ ifeq ($(ANDROID_COMPILE_WITH_JACK),true)
+ run_test_options += --build-with-jack
+ else
+ run_test_options += --build-with-javac-dx
+ endif
+$$(dmart_target): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
$$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES)
$(hide) rm -rf $$(dir $$@) && mkdir -p $$(dir $$@)
$(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
- $(LOCAL_PATH)/run-test --build-only --output-path $$(abspath $$(dir $$@)) $(1)
+ JACK=$(abspath $(JACK)) \
+ JACK_VM_COMMAND="$(JACK_VM) $(DEFAULT_JACK_VM_ARGS) $(JAVA_TMPDIR_ARG) -jar $(abspath $(JACK_LAUNCHER_JAR)) " \
+ JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \
+ JACK_JAR=$(abspath $(JACK_JAR)) \
+ JILL_JAR=$(abspath $(JILL_JAR)) \
+ $(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1)
$(hide) touch $$@
TEST_ART_RUN_TEST_BUILD_RULES += $$(dmart_target)
dmart_target :=
+ run_test_options :=
endef
$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-build-art-run-test,$(test))))
@@ -599,6 +623,12 @@
prereq_rule :=
test_groups :=
uc_host_or_target :=
+ jack_classpath :=
+ ifeq ($(ANDROID_COMPILE_WITH_JACK),true)
+ run_test_options += --build-with-jack
+ else
+ run_test_options += --build-with-javac-dx
+ endif
ifeq ($(ART_TEST_RUN_TEST_ALWAYS_CLEAN),true)
run_test_options += --always-clean
endif
@@ -607,11 +637,13 @@
test_groups := ART_RUN_TEST_HOST_RULES
run_test_options += --host
prereq_rule := $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES)
+ jack_classpath := $(HOST_JACK_CLASSPATH)
else
ifeq ($(1),target)
uc_host_or_target := TARGET
test_groups := ART_RUN_TEST_TARGET_RULES
prereq_rule := test-art-target-sync
+ jack_classpath := $(TARGET_JACK_CLASSPATH)
else
$$(error found $(1) expected $(TARGET_TYPES))
endif
@@ -806,12 +838,19 @@
run_test_options := --android-root $(ART_TEST_ANDROID_ROOT) $$(run_test_options)
endif
$$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
+$$(run_test_rule_name): PRIVATE_JACK_CLASSPATH := $$(jack_classpath)
.PHONY: $$(run_test_rule_name)
-$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $(HOST_OUT_EXECUTABLES)/hprof-conv $$(prereq_rule)
+$$(run_test_rule_name): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(HOST_OUT_EXECUTABLES)/hprof-conv $$(prereq_rule)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
- DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
+ DX=$(abspath $(DX)) \
+ JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \
SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \
DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \
+ JACK=$(abspath $(JACK)) \
+ JACK_VM_COMMAND="$(JACK_VM) $(DEFAULT_JACK_VM_ARGS) $(JAVA_TMPDIR_ARG) -jar $(abspath $(JACK_LAUNCHER_JAR)) " \
+ JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \
+ JACK_JAR=$(abspath $(JACK_JAR)) \
+ JILL_JAR=$(abspath $(JILL_JAR)) \
art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \
&& $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@)
$$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
@@ -826,6 +865,7 @@
run_test_options :=
run_test_rule_name :=
prereq_rule :=
+ jack_classpath :=
endef # define-test-art-run-test
$(foreach target, $(TARGET_TYPES), \
diff --git a/test/dexdump/bytecodes.lst b/test/dexdump/bytecodes.lst
new file mode 100644
index 0000000..aeda7b4
--- /dev/null
+++ b/test/dexdump/bytecodes.lst
@@ -0,0 +1,20 @@
+#bytecodes.dex
+0x000009a0 8 com.google.android.test.BuildConfig <init> ()V BuildConfig.java 4
+0x000009b8 8 com.google.android.test.R$attr <init> ()V R.java 11
+0x000009d0 8 com.google.android.test.R$drawable <init> ()V R.java 13
+0x000009e8 8 com.google.android.test.R <init> ()V R.java 10
+0x00000a00 148 com.google.android.test.Test <clinit> ()V Test.java 7
+0x00000aa4 468 com.google.android.test.Test <init> ()V Test.java 43
+0x00000ca4 478 com.google.android.test.Test add (Ljava/lang/Object;)Ljava/lang/Object; Test.java 179
+0x00000ea0 236 com.google.android.test.Test adds (Ljava/lang/Object;)Ljava/lang/Object; Test.java 201
+0x00000f9c 342 com.google.android.test.Test copies ()V Test.java 216
+0x00001104 156 com.google.android.test.Test doit (I)V Test.java 98
+0x000011b0 146 com.google.android.test.Test geta ()Z Test.java 72
+0x00001254 38 com.google.android.test.Test p (I)V Test.java 120
+0x0000128c 636 com.google.android.test.Test params (BCSIJFDLjava/lang/Object;[I)J Test.java 232
+0x00001518 170 com.google.android.test.Test q (II)V Test.java 127
+0x000015d4 186 com.google.android.test.Test r (II)I Test.java 139
+0x000016a0 388 com.google.android.test.Test s (JJ)J Test.java 159
+0x00001834 96 com.google.android.test.Test seta ()V Test.java 60
+0x000018a4 14 com.google.android.test.Test onStart ()V Test.java 86
+0x000018c4 18 com.google.android.test.Test run ()V Test.java 92
diff --git a/test/dexdump/checkers.lst b/test/dexdump/checkers.lst
new file mode 100644
index 0000000..daef138
--- /dev/null
+++ b/test/dexdump/checkers.lst
@@ -0,0 +1,82 @@
+#checkers.dex
+0x0000149c 8 com.google.android.checkers.Checkers <init> ()V (none) -1
+0x000014b4 66 com.google.android.checkers.Checkers a (Z)V (none) -1
+0x00001508 8 com.google.android.checkers.Checkers onConfigurationChanged (Landroid/content/res/Configuration;)V (none) -1
+0x00001520 118 com.google.android.checkers.Checkers onCreate (Landroid/os/Bundle;)V (none) -1
+0x000015a8 432 com.google.android.checkers.Checkers onCreateOptionsMenu (Landroid/view/Menu;)Z (none) -1
+0x00001768 116 com.google.android.checkers.Checkers onKeyDown (ILandroid/view/KeyEvent;)Z (none) -1
+0x000017ec 316 com.google.android.checkers.Checkers onOptionsItemSelected (Landroid/view/MenuItem;)Z (none) -1
+0x00001938 42 com.google.android.checkers.Checkers onPause ()V (none) -1
+0x00001974 16 com.google.android.checkers.Checkers onStop ()V (none) -1
+0x00001994 88 com.google.android.checkers.Checkers onTrackballEvent (Landroid/view/MotionEvent;)Z (none) -1
+0x000019fc 1324 com.google.android.checkers.CheckersView <init> (Landroid/content/Context;Landroid/content/SharedPreferences;)V (none) -1
+0x00001f48 62 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences$Editor;Ljava/lang/String;[I)V (none) -1
+0x00001f98 66 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences;Ljava/lang/String;[I)V (none) -1
+0x00001fec 126 com.google.android.checkers.CheckersView a (Landroid/graphics/Canvas;IIII)V (none) -1
+0x0000207c 162 com.google.android.checkers.CheckersView a (Landroid/graphics/Canvas;IIIILandroid/graphics/Paint;Landroid/graphics/Paint;Z)V (none) -1
+0x00002130 8 com.google.android.checkers.CheckersView a (Lcom/google/android/checkers/CheckersView;I)V (none) -1
+0x00002148 588 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences;)Z (none) -1
+0x000023d0 22 com.google.android.checkers.CheckersView a (Lcom/google/android/checkers/CheckersView;)Z (none) -1
+0x000023f8 1290 com.google.android.checkers.CheckersView a (ZIIII)Z (none) -1
+0x00002930 204 com.google.android.checkers.CheckersView b (FF)I (none) -1
+0x00002a20 36 com.google.android.checkers.CheckersView b (I)V (none) -1
+0x00002a60 198 com.google.android.checkers.CheckersView b (Landroid/graphics/Canvas;IIII)V (none) -1
+0x00002b38 524 com.google.android.checkers.CheckersView c (I)V (none) -1
+0x00002d54 176 com.google.android.checkers.CheckersView d ()V (none) -1
+0x00002e14 20 com.google.android.checkers.CheckersView e ()Z (none) -1
+0x00002e38 128 com.google.android.checkers.CheckersView a ()V (none) -1
+0x00002ec8 226 com.google.android.checkers.CheckersView a (FF)V (none) -1
+0x00002fd8 32 com.google.android.checkers.CheckersView a (IIII)V (none) -1
+0x00003008 340 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences$Editor;)V (none) -1
+0x00003178 34 com.google.android.checkers.CheckersView a (I)Z (none) -1
+0x000031ac 44 com.google.android.checkers.CheckersView a (Z)Z (none) -1
+0x000031f4 60 com.google.android.checkers.CheckersView b ()V (none) -1
+0x0000324c 138 com.google.android.checkers.CheckersView b (Z)Z (none) -1
+0x000032f4 16 com.google.android.checkers.CheckersView c ()I (none) -1
+0x00003320 68 com.google.android.checkers.CheckersView c (Z)Z (none) -1
+0x00003380 38 com.google.android.checkers.CheckersView d (Z)Z (none) -1
+0x000033c4 2528 com.google.android.checkers.CheckersView draw (Landroid/graphics/Canvas;)V (none) -1
+0x00003dd0 38 com.google.android.checkers.CheckersView e (Z)Z (none) -1
+0x00003e14 104 com.google.android.checkers.CheckersView onSizeChanged (IIII)V (none) -1
+0x00003e98 82 com.google.android.checkers.CheckersView onTouchEvent (Landroid/view/MotionEvent;)Z (none) -1
+0x00003efc 128 com.google.android.checkers.CheckersView setLevel (I)V (none) -1
+0x00003f98 2780 com.google.android.checkers.a <clinit> ()V (none) -1
+0x00004a84 188 com.google.android.checkers.a <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x00004b5c 28 com.google.android.checkers.a a (II)I (none) -1
+0x00004b88 2592 com.google.android.checkers.a a (IIIIIZ)I (none) -1
+0x000055b8 110 com.google.android.checkers.a a (IZ)I (none) -1
+0x00005638 196 com.google.android.checkers.a a (Z)I (none) -1
+0x0000570c 112 com.google.android.checkers.a a (ZII)I (none) -1
+0x0000578c 88 com.google.android.checkers.a a (ZIIIZ)I (none) -1
+0x000057f4 68 com.google.android.checkers.a a (ZIIZ)I (none) -1
+0x00005848 152 com.google.android.checkers.a a (IIII)V (none) -1
+0x000058f0 78 com.google.android.checkers.a a (IIIII)V (none) -1
+0x00005950 198 com.google.android.checkers.a a (IIIIIIII)V (none) -1
+0x00005a28 1750 com.google.android.checkers.a a (IZI)Z (none) -1
+0x00006110 92 com.google.android.checkers.a b (ZIIIZ)I (none) -1
+0x0000617c 112 com.google.android.checkers.a b (ZIIZ)I (none) -1
+0x000061fc 38 com.google.android.checkers.a b ()V (none) -1
+0x0000624c 736 com.google.android.checkers.a b (I)V (none) -1
+0x0000653c 198 com.google.android.checkers.a b (IIIIIIII)V (none) -1
+0x00006614 922 com.google.android.checkers.a b (IZI)Z (none) -1
+0x000069c0 108 com.google.android.checkers.a c (ZIIZ)I (none) -1
+0x00006a3c 16 com.google.android.checkers.a c ()V (none) -1
+0x00006a68 406 com.google.android.checkers.a c (IIIIIIII)V (none) -1
+0x00006c10 112 com.google.android.checkers.a d (ZIIZ)I (none) -1
+0x00006c90 16 com.google.android.checkers.a a (ZZ)I (none) -1
+0x00006cb0 90 com.google.android.checkers.a a ()V (none) -1
+0x00006d1c 8 com.google.android.checkers.a a (I)V (none) -1
+0x00006d34 74 com.google.android.checkers.a a (IIIIZ)V (none) -1
+0x00006d90 32 com.google.android.checkers.a b (ZZ)V (none) -1
+0x00006dcc 1052 com.google.android.checkers.a run ()V (none) -1
+0x000071f8 12 com.google.android.checkers.b <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x00007214 28 com.google.android.checkers.b onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x00007240 12 com.google.android.checkers.c <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x0000725c 2 com.google.android.checkers.c onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x00007270 12 com.google.android.checkers.d <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x0000728c 2 com.google.android.checkers.d onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x000072a0 12 com.google.android.checkers.e <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x000072bc 14 com.google.android.checkers.e onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x000072dc 12 com.google.android.checkers.f <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x000072f8 12 com.google.android.checkers.f onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x00007314 58 com.google.android.checkers.g a ([B)Z (none) -1
diff --git a/test/dexdump/run-all-tests b/test/dexdump/run-all-tests
index f5d8cd6..d9f1e96 100755
--- a/test/dexdump/run-all-tests
+++ b/test/dexdump/run-all-tests
@@ -37,10 +37,14 @@
tmpdir=/tmp/test-$$
mkdir ${tmpdir}
-# Set up binary and flags to test.
+# Set up dexdump binary and flags to test.
DEXD="${ANDROID_HOST_OUT}/bin/dexdump2"
-FLAGS1="-dfh"
-FLAGS2="-l xml"
+DEXDFLAGS1="-dfh"
+DEXDFLAGS2="-l xml"
+
+# Set up dexlist binary and flags to test.
+DEXL="${ANDROID_HOST_OUT}/bin/dexlist2"
+DEXLFLAGS=""
# Run the tests.
passed=0
@@ -50,18 +54,28 @@
basenm=`basename "${i}" .dex`
txtfile=${basenm}.txt
xmlfile=${basenm}.xml
- genfile1=${tmpdir}/${txtfile}
- genfile2=${tmpdir}/${xmlfile}
- ${DEXD} ${FLAGS1} ${i} > ${genfile1}
- cmp ${txtfile} ${genfile1}
+ lstfile=${basenm}.lst
+ gentxtfile=${tmpdir}/${txtfile}
+ genxmlfile=${tmpdir}/${xmlfile}
+ genlstfile=${tmpdir}/${lstfile}
+ ${DEXD} ${DEXDFLAGS1} ${i} > ${gentxtfile}
+ cmp ${txtfile} ${gentxtfile}
if [ "$?" = "0" ]; then
((passed += 1))
else
((failed += 1))
echo failed: ${i}
fi
- ${DEXD} ${FLAGS2} ${i} > ${genfile2}
- cmp ${xmlfile} ${genfile2}
+ ${DEXD} ${DEXDFLAGS2} ${i} > ${genxmlfile}
+ cmp ${xmlfile} ${genxmlfile}
+ if [ "$?" = "0" ]; then
+ ((passed += 1))
+ else
+ ((failed += 1))
+ echo failed: ${i}
+ fi
+ ${DEXL} ${DEXLFLAGS} ${i} > ${genlstfile}
+ cmp ${lstfile} ${genlstfile}
if [ "$?" = "0" ]; then
((passed += 1))
else
@@ -77,7 +91,7 @@
echo
# Clean up, cd back to original dir.
-#rm -rf ${tmpdir}
+rm -rf ${tmpdir}
cd ${oldwd}
# Return status.
diff --git a/test/etc/default-build b/test/etc/default-build
index 92954a9..c281bca 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -39,24 +39,57 @@
exit 0
fi
-if [ -d src ]; then
- mkdir classes
- ${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
-fi
-
-if [ -d src2 ]; then
- mkdir -p classes
- ${JAVAC} -d classes `find src2 -name '*.java'`
-fi
-
if ! [ -d src ] && ! [ -d src2 ]; then
# No src directory? Then forget about trying to run dx.
SKIP_DX_MERGER="true"
fi
-if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
- --dump-width=1000 ${DX_FLAGS} classes
+if [ -d src-multidex ]; then
+ # Jack does not support this configuration unless we specify how to partition the DEX file
+ # with a .jpp file.
+ USE_JACK="false"
+fi
+
+if [ ${USE_JACK} = "true" ]; then
+ # Jack toolchain
+ if [ -d src ]; then
+ ${JACK} --output-jack src.jack src
+ imported_jack_files="--import src.jack"
+ fi
+
+ if [ -d src2 ]; then
+ ${JACK} --output-jack src2.jack src2
+ imported_jack_files="--import src2.jack ${imported_jack_files}"
+ fi
+
+ # Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider
+ # class definitions from src2 first.
+ ${JACK} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex .
+else
+ # Legacy toolchain with javac+dx
+ if [ -d src ]; then
+ mkdir classes
+ ${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
+ fi
+
+ if [ -d src-multidex ]; then
+ mkdir classes2
+ ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
+ --dump-width=1000 ${DX_FLAGS} classes2
+ fi
+ fi
+
+ if [ -d src2 ]; then
+ mkdir -p classes
+ ${JAVAC} -d classes `find src2 -name '*.java'`
+ fi
+
+ if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
+ fi
fi
if [ -d smali ]; then
@@ -72,30 +105,34 @@
fi
if [ -d src-ex ]; then
- mkdir classes-ex
- ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
- if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
- --dump-width=1000 ${DX_FLAGS} classes-ex
+ if [ ${USE_JACK} = "true" ]; then
+ # Rename previous "classes.dex" so it is not overwritten.
+ mv classes.dex classes-1.dex
+ #TODO find another way to append src.jack to the jack classpath
+ ${JACK}:src.jack --output-dex . src-ex
+ zip $TEST_NAME-ex.jar classes.dex
+ # Restore previous "classes.dex" so it can be zipped.
+ mv classes-1.dex classes.dex
+ else
+ mkdir classes-ex
+ ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
+ --dump-width=1000 ${DX_FLAGS} classes-ex
- # quick shuffle so that the stored name is "classes.dex"
- mv classes.dex classes-1.dex
- mv classes-ex.dex classes.dex
- zip $TEST_NAME-ex.jar classes.dex
- mv classes.dex classes-ex.dex
- mv classes-1.dex classes.dex
+ # quick shuffle so that the stored name is "classes.dex"
+ mv classes.dex classes-1.dex
+ mv classes-ex.dex classes.dex
+ zip $TEST_NAME-ex.jar classes.dex
+ mv classes.dex classes-ex.dex
+ mv classes-1.dex classes.dex
+ fi
fi
fi
# Create a single jar with two dex files for multidex.
if [ -d src-multidex ]; then
- mkdir classes2
- ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
- if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
- --dump-width=1000 ${DX_FLAGS} classes2
- zip $TEST_NAME.jar classes.dex classes2.dex
- fi
+ zip $TEST_NAME.jar classes.dex classes2.dex
elif [ ${NEED_DEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex
fi
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index cf6be83..842d87e 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -458,7 +458,9 @@
# When running under gdb, we cannot do piping and grepping...
$cmdline "$@"
else
- $cmdline "$@" 2>&1
+ trap 'kill -INT -$pid' INT
+ $cmdline "$@" 2>&1 & pid=$!
+ wait $pid
# Add extra detail if time out is enabled.
if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "y" ]; then
echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
diff --git a/test/run-test b/test/run-test
index ffa25eb..f5fff09a 100755
--- a/test/run-test
+++ b/test/run-test
@@ -46,6 +46,7 @@
export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
+export USE_JACK="false"
# If dx was not set by the environment variable, assume it is in the path.
if [ -z "$DX" ]; then
@@ -67,6 +68,46 @@
export DXMERGER="dexmerger"
fi
+# If jack was not set by the environment variable, assume it is in the path.
+if [ -z "$JACK" ]; then
+ export JACK="jack"
+fi
+
+# If the tree is compiled with Jack, build test with Jack by default.
+if [ "$ANDROID_COMPILE_WITH_JACK" = "true" ]; then
+ USE_JACK="true"
+fi
+
+# ANDROID_BUILD_TOP is not set in a build environment.
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ export ANDROID_BUILD_TOP=$oldwd
+fi
+
+# If JACK_VM_COMMAND is not set, assume it launches the prebuilt jack-launcher.
+if [ -z "$JACK_VM_COMMAND" ]; then
+ if [ ! -z "$TMPDIR" ]; then
+ jack_temp_dir="-Djava.io.tmpdir=$TMPDIR"
+ fi
+ export JACK_VM_COMMAND="java -Dfile.encoding=UTF-8 -Xms2560m -XX:+TieredCompilation $jack_temp_dir -jar $ANDROID_BUILD_TOP/prebuilts/sdk/tools/jack-launcher.jar"
+fi
+
+# If JACK_CLASSPATH is not set, assume it only contains core-libart.
+if [ -z "$JACK_CLASSPATH" ]; then
+ export JACK_CLASSPATH="$ANDROID_BUILD_TOP/out/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack"
+fi
+
+# If JACK_JAR is not set, assume it is located in the prebuilts directory.
+if [ -z "$JACK_JAR" ]; then
+ export JACK_JAR="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/jack.jar"
+fi
+
+# If JILL_JAR is not set, assume it is located in the prebuilts directory.
+if [ -z "$JILL_JAR" ]; then
+ export JILL_JAR="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/jill.jar"
+fi
+
+export JACK="$JACK -g -cp $JACK_CLASSPATH"
+export JILL="java -jar $JILL_JAR"
info="info.txt"
build="build"
@@ -116,6 +157,7 @@
runtime="jvm"
prebuild_mode="no"
NEED_DEX="false"
+ USE_JACK="false"
run_args="${run_args} --jvm"
shift
elif [ "x$1" = "x-O" ]; then
@@ -237,6 +279,12 @@
elif [ "x$1" = "x--build-only" ]; then
build_only="yes"
shift
+ elif [ "x$1" = "x--build-with-javac-dx" ]; then
+ USE_JACK="false"
+ shift
+ elif [ "x$1" = "x--build-with-jack" ]; then
+ USE_JACK="true"
+ shift
elif [ "x$1" = "x--output-path" ]; then
shift
tmp_dir=$1
@@ -369,10 +417,7 @@
fi
elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
- # ANDROID_BUILD_TOP and ANDROID_HOST_OUT are not set in a build environment.
- if [ -z "$ANDROID_BUILD_TOP" ]; then
- export ANDROID_BUILD_TOP=$oldwd
- fi
+ # ANDROID_HOST_OUT is not set in a build environment.
if [ -z "$ANDROID_HOST_OUT" ]; then
export ANDROID_HOST_OUT=$ANDROID_BUILD_TOP/out/host/linux-x86
fi
@@ -462,6 +507,8 @@
echo " --debuggable Whether to compile Java code for a debugger."
echo " --gdb Run under gdb; incompatible with some tests."
echo " --build-only Build test files only (off by default)."
+ echo " --build-with-javac-dx Build test files with javac and dx (on by default)."
+ echo " --build-with-jack Build test files with jack and jill (off by default)."
echo " --interpreter Enable interpreter only mode (off by default)."
echo " --jit Enable jit (off by default)."
echo " --optimizing Enable optimizing compiler (default)."
@@ -556,6 +603,10 @@
# if Checker is not invoked and the test only runs the program.
build_args="${build_args} --dx-option --no-optimize"
+ # Jack does not necessarily generate the same DEX output than dx. Because these tests depend
+ # on a particular DEX output, keep building them with dx for now (b/19467889).
+ USE_JACK="false"
+
if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$target_mode" = "no" -a "$debuggable" = "no" ]; then
run_checker="yes"
run_args="${run_args} -Xcompiler-option --dump-cfg=$tmp_dir/$cfg_output \
@@ -564,14 +615,20 @@
fi
# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB.
-file_size_limit=2048
+build_file_size_limit=2048
+run_file_size_limit=2048
if echo "$test_dir" | grep 089; then
- file_size_limit=5120
+ build_file_size_limit=5120
+ run_file_size_limit=5120
elif echo "$test_dir" | grep 083; then
- file_size_limit=5120
+ build_file_size_limit=5120
+ run_file_size_limit=5120
fi
-if ! ulimit -S "$file_size_limit"; then
- echo "ulimit file size setting failed"
+if [ ${USE_JACK} = "false" ]; then
+ # Set ulimit if we build with dx only, Jack can generate big temp files.
+ if ! ulimit -S "$build_file_size_limit"; then
+ echo "ulimit file size setting failed"
+ fi
fi
good="no"
@@ -582,6 +639,9 @@
build_exit="$?"
echo "build exit status: $build_exit" 1>&2
if [ "$build_exit" = '0' ]; then
+ if ! ulimit -S "$run_file_size_limit"; then
+ echo "ulimit file size setting failed"
+ fi
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" 2>&1
run_exit="$?"
@@ -604,6 +664,9 @@
"./${build}" $build_args >"$build_output" 2>&1
build_exit="$?"
if [ "$build_exit" = '0' ]; then
+ if ! ulimit -S "$run_file_size_limit"; then
+ echo "ulimit file size setting failed"
+ fi
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" >"$output" 2>&1
if [ "$run_checker" = "yes" ]; then
@@ -635,6 +698,9 @@
"./${build}" $build_args >"$build_output" 2>&1
build_exit="$?"
if [ "$build_exit" = '0' ]; then
+ if ! ulimit -S "$run_file_size_limit"; then
+ echo "ulimit file size setting failed"
+ fi
echo "${test_dir}: running..." 1>&2
"./${run}" $run_args "$@" >"$output" 2>&1
run_exit="$?"
diff --git a/tools/symbolize.sh b/tools/symbolize.sh
index 7365a9b..f5686e6 100755
--- a/tools/symbolize.sh
+++ b/tools/symbolize.sh
@@ -22,6 +22,7 @@
INTERACTIVE="no"
if [ "x$1" = "x--interactive" ] ; then
INTERACTIVE="yes"
+ shift
fi
# Pull the file from the device and symbolize it.
@@ -57,4 +58,15 @@
done
}
-all
+if [ "x$1" = "x" ] ; then
+ # No further arguments, iterate over all oat files on device.
+ all
+else
+ # Take the parameters as a list of paths on device.
+ while (($#)); do
+ DIR=$(dirname $1)
+ NAME=$(basename $1)
+ one $DIR $NAME
+ shift
+ done
+fi