summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator.cc3
-rw-r--r--compiler/optimizing/code_generator.h9
-rw-r--r--compiler/optimizing/code_generator_arm.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc7
-rw-r--r--compiler/optimizing/code_generator_arm64.h21
-rw-r--r--compiler/optimizing/code_generator_mips.h2
-rw-r--r--compiler/optimizing/code_generator_mips64.h2
-rw-r--r--compiler/optimizing/code_generator_x86.h2
-rw-r--r--compiler/optimizing/code_generator_x86_64.h2
-rw-r--r--compiler/optimizing/instruction_builder.cc8
-rw-r--r--compiler/optimizing/licm.cc15
-rw-r--r--compiler/optimizing/licm_test.cc12
-rw-r--r--compiler/optimizing/load_store_elimination.cc23
-rw-r--r--compiler/optimizing/nodes.h69
-rw-r--r--compiler/optimizing/side_effects_test.cc34
-rw-r--r--compiler/optimizing/ssa_builder.cc3
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.h56
-rw-r--r--compiler/utils/arm64/assembler_arm64.cc9
-rw-r--r--compiler/utils/arm64/assembler_arm64.h42
-rw-r--r--compiler/utils/assembler.h6
-rw-r--r--runtime/interpreter/mterp/mterp.cc2
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86.S2
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86_64.S460
-rw-r--r--runtime/interpreter/mterp/x86/footer.S2
-rw-r--r--runtime/interpreter/mterp/x86_64/bincmp.S12
-rw-r--r--runtime/interpreter/mterp/x86_64/entry.S6
-rw-r--r--runtime/interpreter/mterp/x86_64/footer.S134
-rw-r--r--runtime/interpreter/mterp/x86_64/header.S55
-rw-r--r--runtime/interpreter/mterp/x86_64/op_aget_object.S3
-rw-r--r--runtime/interpreter/mterp/x86_64/op_goto.S8
-rw-r--r--runtime/interpreter/mterp/x86_64/op_goto_16.S8
-rw-r--r--runtime/interpreter/mterp/x86_64/op_goto_32.S8
-rw-r--r--runtime/interpreter/mterp/x86_64/op_iget.S3
-rw-r--r--runtime/interpreter/mterp/x86_64/op_iget_object_quick.S3
-rw-r--r--runtime/interpreter/mterp/x86_64/op_instance_of.S3
-rw-r--r--runtime/interpreter/mterp/x86_64/op_move_exception.S5
-rw-r--r--runtime/interpreter/mterp/x86_64/op_packed_switch.S8
-rw-r--r--runtime/interpreter/mterp/x86_64/op_return.S4
-rw-r--r--runtime/interpreter/mterp/x86_64/op_return_void.S4
-rw-r--r--runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S4
-rw-r--r--runtime/interpreter/mterp/x86_64/op_return_wide.S4
-rw-r--r--runtime/interpreter/mterp/x86_64/op_sget.S3
-rw-r--r--runtime/interpreter/mterp/x86_64/op_throw.S3
-rw-r--r--runtime/interpreter/mterp/x86_64/zcmp.S12
-rw-r--r--runtime/interpreter/unstarted_runtime.cc95
-rw-r--r--runtime/interpreter/unstarted_runtime_list.h2
-rw-r--r--runtime/jit/profiling_info.h1
-rw-r--r--runtime/mirror/string-inl.h4
-rw-r--r--runtime/oat_file_assistant.cc17
-rw-r--r--runtime/oat_file_manager.cc2
-rw-r--r--runtime/openjdkjvm/OpenjdkJvm.cc13
-rw-r--r--runtime/verifier/method_verifier.cc14
-rw-r--r--test/098-ddmc/src/Main.java7
-rw-r--r--test/100-reflect2/expected.txt4
-rw-r--r--test/147-stripped-dex-fallback/expected.txt1
-rw-r--r--test/147-stripped-dex-fallback/info.txt2
-rwxr-xr-xtest/147-stripped-dex-fallback/run24
-rw-r--r--test/147-stripped-dex-fallback/src/Main.java21
-rw-r--r--test/525-checker-arrays-and-fields/info.txt1
-rw-r--r--test/525-checker-arrays-fields1/expected.txt1
-rw-r--r--test/525-checker-arrays-fields1/info.txt1
-rw-r--r--test/525-checker-arrays-fields1/src/Main.java711
-rw-r--r--test/525-checker-arrays-fields2/expected.txt1
-rw-r--r--test/525-checker-arrays-fields2/info.txt1
-rw-r--r--test/525-checker-arrays-fields2/src/Main.java (renamed from test/525-checker-arrays-and-fields/src/Main.java)638
-rw-r--r--test/530-checker-lse/src/Main.java65
-rw-r--r--test/594-checker-array-alias/expected.txt1
-rw-r--r--test/594-checker-array-alias/info.txt1
-rw-r--r--test/594-checker-array-alias/src/Main.java255
-rw-r--r--test/594-checker-irreducible-linorder/expected.txt (renamed from test/525-checker-arrays-and-fields/expected.txt)0
-rw-r--r--test/594-checker-irreducible-linorder/info.txt2
-rw-r--r--test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali64
-rw-r--r--test/594-checker-irreducible-linorder/src/Main.java25
-rw-r--r--test/800-smali/expected.txt1
-rw-r--r--test/800-smali/smali/b_28187158.smali12
-rw-r--r--test/800-smali/src/Main.java2
-rw-r--r--test/Android.run-test.mk6
-rwxr-xr-xtest/etc/run-test-jar19
-rwxr-xr-xtest/run-test6
79 files changed, 2133 insertions, 970 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a771cc1567..e7fa4e472b 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -187,7 +187,8 @@ class DisassemblyScope {
void CodeGenerator::GenerateSlowPaths() {
size_t code_start = 0;
- for (SlowPathCode* slow_path : slow_paths_) {
+ for (const std::unique_ptr<SlowPathCode>& slow_path_unique_ptr : slow_paths_) {
+ SlowPathCode* slow_path = slow_path_unique_ptr.get();
current_slow_path_ = slow_path;
if (disasm_info_ != nullptr) {
code_start = GetAssembler()->CodeSize();
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 87832a2d9f..d69c41055b 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -67,7 +67,7 @@ class CodeAllocator {
DISALLOW_COPY_AND_ASSIGN(CodeAllocator);
};
-class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> {
+class SlowPathCode : public DeletableArenaObject<kArenaAllocSlowPaths> {
public:
explicit SlowPathCode(HInstruction* instruction) : instruction_(instruction) {
for (size_t i = 0; i < kMaximumNumberOfExpectedRegisters; ++i) {
@@ -205,7 +205,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
virtual const Assembler& GetAssembler() const = 0;
virtual size_t GetWordSize() const = 0;
virtual size_t GetFloatingPointSpillSlotSize() const = 0;
- virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0;
+ virtual uintptr_t GetAddressOf(HBasicBlock* block) = 0;
void InitializeCodeGeneration(size_t number_of_spill_slots,
size_t maximum_number_of_live_core_registers,
size_t maximum_number_of_live_fpu_registers,
@@ -298,8 +298,9 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
// save live registers, which may be needed by the runtime to set catch phis.
bool IsImplicitNullCheckAllowed(HNullCheck* null_check) const;
+ // TODO: Avoid creating the `std::unique_ptr` here.
void AddSlowPath(SlowPathCode* slow_path) {
- slow_paths_.push_back(slow_path);
+ slow_paths_.push_back(std::unique_ptr<SlowPathCode>(slow_path));
}
void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item);
@@ -617,7 +618,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
HGraph* const graph_;
const CompilerOptions& compiler_options_;
- ArenaVector<SlowPathCode*> slow_paths_;
+ ArenaVector<std::unique_ptr<SlowPathCode>> slow_paths_;
// The current slow-path that we're generating code for.
SlowPathCode* current_slow_path_;
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 144d58d85a..0020f7b4f4 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -339,7 +339,7 @@ class CodeGeneratorARM : public CodeGenerator {
return assembler_;
}
- uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
return GetLabelOf(block)->Position();
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index efe4c06d3f..e8e6b68975 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -899,7 +899,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
callee_saved_fp_registers.list(),
compiler_options,
stats),
- block_labels_(nullptr),
+ block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
location_builder_(graph, this),
instruction_visitor_(graph, this),
@@ -928,7 +928,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
#define __ GetVIXLAssembler()->
void CodeGeneratorARM64::EmitJumpTables() {
- for (auto jump_table : jump_tables_) {
+ for (auto&& jump_table : jump_tables_) {
jump_table->EmitTable(this);
}
}
@@ -4784,8 +4784,7 @@ void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_inst
__ B(codegen_->GetLabelOf(default_block));
}
} else {
- JumpTableARM64* jump_table = new (GetGraph()->GetArena()) JumpTableARM64(switch_instr);
- codegen_->AddJumpTable(jump_table);
+ JumpTableARM64* jump_table = codegen_->CreateJumpTable(switch_instr);
UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index ec46a34615..422963e7d0 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -83,7 +83,7 @@ class SlowPathCodeARM64 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64);
};
-class JumpTableARM64 : public ArenaObject<kArenaAllocSwitchTable> {
+class JumpTableARM64 : public DeletableArenaObject<kArenaAllocSwitchTable> {
public:
explicit JumpTableARM64(HPackedSwitch* switch_instr)
: switch_instr_(switch_instr), table_start_() {}
@@ -352,8 +352,9 @@ class CodeGeneratorARM64 : public CodeGenerator {
void Bind(HBasicBlock* block) OVERRIDE;
- vixl::Label* GetLabelOf(HBasicBlock* block) const {
- return CommonGetLabelOf<vixl::Label>(block_labels_, block);
+ vixl::Label* GetLabelOf(HBasicBlock* block) {
+ block = FirstNonEmptyBlock(block);
+ return &(block_labels_[block->GetBlockId()]);
}
size_t GetWordSize() const OVERRIDE {
@@ -365,7 +366,7 @@ class CodeGeneratorARM64 : public CodeGenerator {
return kArm64WordSize;
}
- uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
vixl::Label* block_entry_label = GetLabelOf(block);
DCHECK(block_entry_label->IsBound());
return block_entry_label->location();
@@ -413,11 +414,12 @@ class CodeGeneratorARM64 : public CodeGenerator {
}
void Initialize() OVERRIDE {
- block_labels_ = CommonInitializeLabels<vixl::Label>();
+ block_labels_.resize(GetGraph()->GetBlocks().size());
}
- void AddJumpTable(JumpTableARM64* jump_table) {
- jump_tables_.push_back(jump_table);
+ JumpTableARM64* CreateJumpTable(HPackedSwitch* switch_instr) {
+ jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARM64(switch_instr));
+ return jump_tables_.back().get();
}
void Finalize(CodeAllocator* allocator) OVERRIDE;
@@ -616,9 +618,10 @@ class CodeGeneratorARM64 : public CodeGenerator {
void EmitJumpTables();
// Labels for each block that will be compiled.
- vixl::Label* block_labels_; // Indexed by block id.
+ // We use a deque so that the `vixl::Label` objects do not move in memory.
+ ArenaDeque<vixl::Label> block_labels_; // Indexed by block id.
vixl::Label frame_entry_label_;
- ArenaVector<JumpTableARM64*> jump_tables_;
+ ArenaVector<std::unique_ptr<JumpTableARM64>> jump_tables_;
LocationsBuilderARM64 location_builder_;
InstructionCodeGeneratorARM64 instruction_visitor_;
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 5e6fec8cf5..435a869368 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -275,7 +275,7 @@ class CodeGeneratorMIPS : public CodeGenerator {
size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMipsDoublewordSize; }
- uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
return assembler_.GetLabelLocation(GetLabelOf(block));
}
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 4e15cdd7b5..9785a2e8a8 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -271,7 +271,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMips64DoublewordSize; }
- uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
return assembler_.GetLabelLocation(GetLabelOf(block));
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 69a625306f..1739eec4c1 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -361,7 +361,7 @@ class CodeGeneratorX86 : public CodeGenerator {
return assembler_;
}
- uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
return GetLabelOf(block)->Position();
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index d7ce7c649f..3a211c5027 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -346,7 +346,7 @@ class CodeGeneratorX86_64 : public CodeGenerator {
return &move_resolver_;
}
- uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
+ uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE {
return GetLabelOf(block)->Position();
}
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index f5e49c2235..12cb826395 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -897,12 +897,12 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc)
Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache();
bool finalizable;
- bool can_throw = NeedsAccessCheck(type_index, dex_cache, &finalizable);
+ bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable);
// Only the non-resolved entrypoint handles the finalizable class case. If we
// need access checks, then we haven't resolved the method and the class may
// again be finalizable.
- QuickEntrypointEnum entrypoint = (finalizable || can_throw)
+ QuickEntrypointEnum entrypoint = (finalizable || needs_access_check)
? kQuickAllocObject
: kQuickAllocObjectInitialized;
@@ -917,7 +917,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc)
outer_dex_file,
IsOutermostCompilingClass(type_index),
dex_pc,
- /*needs_access_check*/ can_throw,
+ needs_access_check,
compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index));
AppendInstruction(load_class);
@@ -933,7 +933,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc)
dex_pc,
type_index,
*dex_compilation_unit_->GetDexFile(),
- can_throw,
+ needs_access_check,
finalizable,
entrypoint));
return true;
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index 7a1e06b951..5a0b89c90a 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -79,8 +79,15 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info)
void LICM::Run() {
DCHECK(side_effects_.HasRun());
+
// Only used during debug.
- ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM);
+ ArenaBitVector* visited = nullptr;
+ if (kIsDebugBuild) {
+ visited = new (graph_->GetArena()) ArenaBitVector(graph_->GetArena(),
+ graph_->GetBlocks().size(),
+ false,
+ kArenaAllocLICM);
+ }
// Post order visit to visit inner loops before outer loops.
for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
@@ -109,10 +116,12 @@ void LICM::Run() {
DCHECK(inner->IsInLoop());
if (inner->GetLoopInformation() != loop_info) {
// Thanks to post order visit, inner loops were already visited.
- DCHECK(visited.IsBitSet(inner->GetBlockId()));
+ DCHECK(visited->IsBitSet(inner->GetBlockId()));
continue;
}
- visited.SetBit(inner->GetBlockId());
+ if (kIsDebugBuild) {
+ visited->SetBit(inner->GetBlockId());
+ }
if (contains_irreducible_loop) {
// We cannot licm in an irreducible loop, or in a natural loop containing an
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index d446539700..2a62643465 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -169,13 +169,11 @@ TEST_F(LICMTest, ArrayHoisting) {
BuildLoop();
// Populate the loop with instructions: set/get array with different types.
- // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
- // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, int_constant_, Primitive::kPrimByte, 0);
+ parameter_, int_constant_, Primitive::kPrimInt, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0);
+ parameter_, int_constant_, float_constant_, Primitive::kPrimFloat, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
@@ -189,13 +187,11 @@ TEST_F(LICMTest, NoArrayHoisting) {
BuildLoop();
// Populate the loop with instructions: set/get array with same types.
- // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
- // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, int_constant_, Primitive::kPrimByte, 0);
+ parameter_, int_constant_, Primitive::kPrimFloat, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, get_array, float_constant_, Primitive::kPrimByte, 0);
+ parameter_, get_array, float_constant_, Primitive::kPrimFloat, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index e1977b1798..ac7ed86d1d 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -480,7 +480,7 @@ class HeapLocationCollector : public HGraphVisitor {
// alias analysis and won't be as effective.
bool has_volatile_; // If there are volatile field accesses.
bool has_monitor_operations_; // If there are monitor operations.
- bool may_deoptimize_;
+ bool may_deoptimize_; // Only true for HDeoptimize with single-frame deoptimization.
DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector);
};
@@ -551,19 +551,20 @@ class LSEVisitor : public HGraphVisitor {
}
// At this point, stores in possibly_removed_stores_ can be safely removed.
- size = possibly_removed_stores_.size();
- for (size_t i = 0; i < size; i++) {
+ for (size_t i = 0, e = possibly_removed_stores_.size(); i < e; i++) {
HInstruction* store = possibly_removed_stores_[i];
DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet());
store->GetBlock()->RemoveInstruction(store);
}
- // TODO: remove unnecessary allocations.
- // Eliminate instructions in singleton_new_instances_ that:
- // - don't have uses,
- // - don't have finalizers,
- // - are instantiable and accessible,
- // - have no/separate clinit check.
+ // Eliminate allocations that are not used.
+ for (size_t i = 0, e = singleton_new_instances_.size(); i < e; i++) {
+ HInstruction* new_instance = singleton_new_instances_[i];
+ if (!new_instance->HasNonEnvironmentUses()) {
+ new_instance->RemoveEnvironmentUsers();
+ new_instance->GetBlock()->RemoveInstruction(new_instance);
+ }
+ }
}
private:
@@ -969,8 +970,8 @@ class LSEVisitor : public HGraphVisitor {
if (!heap_location_collector_.MayDeoptimize() &&
ref_info->IsSingletonAndNotReturned() &&
!new_instance->IsFinalizable() &&
- !new_instance->CanThrow()) {
- // TODO: add new_instance to singleton_new_instances_ and enable allocation elimination.
+ !new_instance->NeedsAccessCheck()) {
+ singleton_new_instances_.push_back(new_instance);
}
ArenaVector<HInstruction*>& heap_values =
heap_values_for_[new_instance->GetBlock()->GetBlockId()];
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index dc5a8fa9cb..1ea2247da6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1551,21 +1551,21 @@ class SideEffects : public ValueObject {
static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) {
return is_volatile
? AllWritesAndReads()
- : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset));
+ : SideEffects(TypeFlag(type, kFieldWriteOffset));
}
static SideEffects ArrayWriteOfType(Primitive::Type type) {
- return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset));
+ return SideEffects(TypeFlag(type, kArrayWriteOffset));
}
static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) {
return is_volatile
? AllWritesAndReads()
- : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset));
+ : SideEffects(TypeFlag(type, kFieldReadOffset));
}
static SideEffects ArrayReadOfType(Primitive::Type type) {
- return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset));
+ return SideEffects(TypeFlag(type, kArrayReadOffset));
}
static SideEffects CanTriggerGC() {
@@ -1692,23 +1692,6 @@ class SideEffects : public ValueObject {
static constexpr uint64_t kAllReads =
((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset;
- // Work around the fact that HIR aliases I/F and J/D.
- // TODO: remove this interceptor once HIR types are clean
- static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) {
- switch (type) {
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- return TypeFlag(Primitive::kPrimInt, offset) |
- TypeFlag(Primitive::kPrimFloat, offset);
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return TypeFlag(Primitive::kPrimLong, offset) |
- TypeFlag(Primitive::kPrimDouble, offset);
- default:
- return TypeFlag(type, offset);
- }
- }
-
// Translates type to bit flag.
static uint64_t TypeFlag(Primitive::Type type, int offset) {
CHECK_NE(type, Primitive::kPrimVoid);
@@ -3652,14 +3635,14 @@ class HNewInstance : public HExpression<2> {
uint32_t dex_pc,
uint16_t type_index,
const DexFile& dex_file,
- bool can_throw,
+ bool needs_access_check,
bool finalizable,
QuickEntrypointEnum entrypoint)
: HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
entrypoint_(entrypoint) {
- SetPackedFlag<kFlagCanThrow>(can_throw);
+ SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
SetPackedFlag<kFlagFinalizable>(finalizable);
SetRawInputAt(0, cls);
SetRawInputAt(1, current_method);
@@ -3671,10 +3654,11 @@ class HNewInstance : public HExpression<2> {
// Calls runtime so needs an environment.
bool NeedsEnvironment() const OVERRIDE { return true; }
- // It may throw when called on type that's not instantiable/accessible.
- // It can throw OOME.
- // TODO: distinguish between the two cases so we can for example allow allocation elimination.
- bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; }
+ // Can throw errors when out-of-memory or if it's not instantiable/accessible.
+ bool CanThrow() const OVERRIDE { return true; }
+
+ // Needs to call into runtime to make sure it's instantiable/accessible.
+ bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); }
@@ -3691,8 +3675,8 @@ class HNewInstance : public HExpression<2> {
DECLARE_INSTRUCTION(NewInstance);
private:
- static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits;
- static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1;
+ static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1;
static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
@@ -5137,14 +5121,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
class HArrayGet : public HExpression<2> {
public:
- HArrayGet(HInstruction* array,
- HInstruction* index,
- Primitive::Type type,
- uint32_t dex_pc,
- SideEffects additional_side_effects = SideEffects::None())
- : HExpression(type,
- SideEffects::ArrayReadOfType(type).Union(additional_side_effects),
- dex_pc) {
+ HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc)
+ : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
@@ -5193,13 +5171,8 @@ class HArraySet : public HTemplateInstruction<3> {
HInstruction* index,
HInstruction* value,
Primitive::Type expected_component_type,
- uint32_t dex_pc,
- SideEffects additional_side_effects = SideEffects::None())
- : HTemplateInstruction(
- SideEffects::ArrayWriteOfType(expected_component_type).Union(
- SideEffectsForArchRuntimeCalls(value->GetType())).Union(
- additional_side_effects),
- dex_pc) {
+ uint32_t dex_pc)
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
SetPackedField<ExpectedComponentTypeField>(expected_component_type);
SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot);
SetPackedFlag<kFlagValueCanBeNull>(true);
@@ -5207,6 +5180,8 @@ class HArraySet : public HTemplateInstruction<3> {
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
+ // Make a best guess now, may be refined during SSA building.
+ ComputeSideEffects();
}
bool NeedsEnvironment() const OVERRIDE {
@@ -5259,6 +5234,12 @@ class HArraySet : public HTemplateInstruction<3> {
return GetPackedField<ExpectedComponentTypeField>();
}
+ void ComputeSideEffects() {
+ Primitive::Type type = GetComponentType();
+ SetSideEffects(SideEffects::ArrayWriteOfType(type).Union(
+ SideEffectsForArchRuntimeCalls(type)));
+ }
+
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) {
return (value_type == Primitive::kPrimNot) ? SideEffects::CanTriggerGC() : SideEffects::None();
}
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index 9bbc354290..b01bc1ca0d 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -148,19 +148,19 @@ TEST(SideEffectsTest, VolatileDependences) {
EXPECT_FALSE(any_write.MayDependOn(volatile_read));
}
-TEST(SideEffectsTest, SameWidthTypes) {
+TEST(SideEffectsTest, SameWidthTypesNoAlias) {
// Type I/F.
- testWriteAndReadDependence(
+ testNoWriteAndReadDependence(
SideEffects::FieldWriteOfType(Primitive::kPrimInt, /* is_volatile */ false),
SideEffects::FieldReadOfType(Primitive::kPrimFloat, /* is_volatile */ false));
- testWriteAndReadDependence(
+ testNoWriteAndReadDependence(
SideEffects::ArrayWriteOfType(Primitive::kPrimInt),
SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
// Type L/D.
- testWriteAndReadDependence(
+ testNoWriteAndReadDependence(
SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false),
SideEffects::FieldReadOfType(Primitive::kPrimDouble, /* is_volatile */ false));
- testWriteAndReadDependence(
+ testNoWriteAndReadDependence(
SideEffects::ArrayWriteOfType(Primitive::kPrimLong),
SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
}
@@ -216,14 +216,32 @@ TEST(SideEffectsTest, BitStrings) {
"||||||L|",
SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str());
EXPECT_STREQ(
+ "||DFJISCBZL|DFJISCBZL||DFJISCBZL|DFJISCBZL|",
+ SideEffects::FieldWriteOfType(Primitive::kPrimNot, true).ToString().c_str());
+ EXPECT_STREQ(
"|||||Z||",
SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str());
EXPECT_STREQ(
+ "|||||C||",
+ SideEffects::ArrayWriteOfType(Primitive::kPrimChar).ToString().c_str());
+ EXPECT_STREQ(
+ "|||||S||",
+ SideEffects::ArrayWriteOfType(Primitive::kPrimShort).ToString().c_str());
+ EXPECT_STREQ(
"|||B||||",
SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str());
EXPECT_STREQ(
- "||DJ|||||", // note: DJ alias
+ "||D|||||",
SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str());
+ EXPECT_STREQ(
+ "||J|||||",
+ SideEffects::ArrayReadOfType(Primitive::kPrimLong).ToString().c_str());
+ EXPECT_STREQ(
+ "||F|||||",
+ SideEffects::ArrayReadOfType(Primitive::kPrimFloat).ToString().c_str());
+ EXPECT_STREQ(
+ "||I|||||",
+ SideEffects::ArrayReadOfType(Primitive::kPrimInt).ToString().c_str());
SideEffects s = SideEffects::None();
s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, /* is_volatile */ false));
s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false));
@@ -231,9 +249,7 @@ TEST(SideEffectsTest, BitStrings) {
s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt, /* is_volatile */ false));
s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
- EXPECT_STREQ(
- "||DFJI|FI||S|DJC|", // note: DJ/FI alias.
- s.ToString().c_str());
+ EXPECT_STREQ("||DF|I||S|JC|", s.ToString().c_str());
}
} // namespace art
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index eeadbeb0d1..2fe2f2053a 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -391,6 +391,9 @@ bool SsaBuilder::FixAmbiguousArrayOps() {
worklist.push_back(equivalent->AsPhi());
}
}
+ // Refine the side effects of this floating point aset. Note that we do this even if
+ // no replacement occurs, since the right-hand-side may have been corrected already.
+ aset->ComputeSideEffects();
} else {
// Array elements are integral and the value assigned to it initially
// was integral too. Nothing to do.
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 97f2aeeb1e..719feec468 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -969,6 +969,38 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> {
return false;
}
+ bool IsLinearOrderWellFormed(const HGraph& graph) {
+ for (HBasicBlock* header : graph.GetBlocks()) {
+ if (!header->IsLoopHeader()) {
+ continue;
+ }
+
+ HLoopInformation* loop = header->GetLoopInformation();
+ size_t num_blocks = loop->GetBlocks().NumSetBits();
+ size_t found_blocks = 0u;
+
+ for (HLinearOrderIterator it(graph); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ if (loop->Contains(*current)) {
+ found_blocks++;
+ if (found_blocks == 1u && current != header) {
+ // First block is not the header.
+ return false;
+ } else if (found_blocks == num_blocks && !loop->IsBackEdge(*current)) {
+ // Last block is not a back edge.
+ return false;
+ }
+ } else if (found_blocks != 0u && found_blocks != num_blocks) {
+ // Blocks are not adjacent.
+ return false;
+ }
+ }
+ DCHECK_EQ(found_blocks, num_blocks);
+ }
+
+ return true;
+ }
+
void AddBackEdgeUses(const HBasicBlock& block_at_use) {
DCHECK(block_at_use.IsInLoop());
// Add synthesized uses at the back edge of loops to help the register allocator.
@@ -995,12 +1027,30 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> {
if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) {
// There was a use already seen in this loop. Therefore the previous call to `AddUse`
// already inserted the backedge use. We can stop going outward.
- DCHECK(HasSynthesizeUseAt(back_edge_use_position));
+ if (kIsDebugBuild) {
+ if (!HasSynthesizeUseAt(back_edge_use_position)) {
+ // There exists a use prior to `back_edge_use_position` but there is
+ // no synthesized use at the back edge. This can happen in the presence
+ // of irreducible loops, when blocks of the loop are not adjacent in
+ // linear order, i.e. when there is an out-of-loop block between
+ // `block_at_use` and `back_edge_position` that uses this interval.
+ DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops());
+ DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph()));
+ }
+ }
break;
}
- DCHECK(last_in_new_list == nullptr
- || back_edge_use_position > last_in_new_list->GetPosition());
+ if (last_in_new_list != nullptr &&
+ back_edge_use_position <= last_in_new_list->GetPosition()) {
+ // Loops are not properly nested in the linear order, i.e. the back edge
+ // of an outer loop preceeds blocks of an inner loop. This can happen
+ // in the presence of irreducible loops.
+ DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops());
+ DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph()));
+ // We must bail out, otherwise we would generate an unsorted use list.
+ break;
+ }
UsePosition* new_use = new (allocator_) UsePosition(
/* user */ nullptr,
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index eb851f9534..eb5112b464 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -32,8 +32,8 @@ namespace arm64 {
#endif
void Arm64Assembler::FinalizeCode() {
- for (Arm64Exception* exception : exception_blocks_) {
- EmitExceptionPoll(exception);
+ for (const std::unique_ptr<Arm64Exception>& exception : exception_blocks_) {
+ EmitExceptionPoll(exception.get());
}
___ FinalizeCode();
}
@@ -611,10 +611,9 @@ void Arm64Assembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg,
void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) {
CHECK_ALIGNED(stack_adjust, kStackAlignment);
Arm64ManagedRegister scratch = m_scratch.AsArm64();
- Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust);
- exception_blocks_.push_back(current_exception);
+ exception_blocks_.emplace_back(new Arm64Exception(scratch, stack_adjust));
LoadFromOffset(scratch.AsXRegister(), TR, Thread::ExceptionOffset<8>().Int32Value());
- ___ Cbnz(reg_x(scratch.AsXRegister()), current_exception->Entry());
+ ___ Cbnz(reg_x(scratch.AsXRegister()), exception_blocks_.back()->Entry());
}
void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) {
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index 03ae996952..c4e5de7a67 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -62,7 +62,25 @@ enum StoreOperandType {
kStoreDWord
};
-class Arm64Exception;
+class Arm64Exception {
+ private:
+ Arm64Exception(Arm64ManagedRegister scratch, size_t stack_adjust)
+ : scratch_(scratch), stack_adjust_(stack_adjust) {
+ }
+
+ vixl::Label* Entry() { return &exception_entry_; }
+
+ // Register used for passing Thread::Current()->exception_ .
+ const Arm64ManagedRegister scratch_;
+
+ // Stack adjust for ExceptionPool.
+ const size_t stack_adjust_;
+
+ vixl::Label exception_entry_;
+
+ friend class Arm64Assembler;
+ DISALLOW_COPY_AND_ASSIGN(Arm64Exception);
+};
class Arm64Assembler FINAL : public Assembler {
public:
@@ -253,7 +271,7 @@ class Arm64Assembler FINAL : public Assembler {
void AddConstant(XRegister rd, XRegister rn, int32_t value, vixl::Condition cond = vixl::al);
// List of exception blocks to generate at the end of the code cache.
- ArenaVector<Arm64Exception*> exception_blocks_;
+ ArenaVector<std::unique_ptr<Arm64Exception>> exception_blocks_;
public:
// Vixl assembler.
@@ -263,26 +281,6 @@ class Arm64Assembler FINAL : public Assembler {
friend class Arm64ManagedRegister_VixlRegisters_Test;
};
-class Arm64Exception {
- private:
- Arm64Exception(Arm64ManagedRegister scratch, size_t stack_adjust)
- : scratch_(scratch), stack_adjust_(stack_adjust) {
- }
-
- vixl::Label* Entry() { return &exception_entry_; }
-
- // Register used for passing Thread::Current()->exception_ .
- const Arm64ManagedRegister scratch_;
-
- // Stack adjust for ExceptionPool.
- const size_t stack_adjust_;
-
- vixl::Label exception_entry_;
-
- friend class Arm64Assembler;
- DISALLOW_COPY_AND_ASSIGN(Arm64Exception);
-};
-
} // namespace arm64
} // namespace art
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 4ea85a2c18..f70fe04ed1 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -306,8 +306,10 @@ class DebugFrameOpCodeWriterForAssembler FINAL
// Override the last delayed PC. The new PC can be out of order.
void OverrideDelayedPC(size_t pc) {
DCHECK(delay_emitting_advance_pc_);
- DCHECK(!delayed_advance_pcs_.empty());
- delayed_advance_pcs_.back().pc = pc;
+ if (enabled_) {
+ DCHECK(!delayed_advance_pcs_.empty());
+ delayed_advance_pcs_.back().pc = pc;
+ }
}
// Return the number of delayed advance PC entries.
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 4b3c03ebb4..bd1af04608 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -694,7 +694,7 @@ extern "C" int16_t MterpAddHotnessBatch(ArtMethod* method,
return MterpSetUpHotnessCountdown(method, shadow_frame);
}
-// TUNING: Unused by arm/arm64/x86. Remove when x86_64/mips/mips64 mterps support batch updates.
+// TUNING: Unused by arm/arm64/x86/x86_64. Remove when mips/mips64 mterps support batch updates.
extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int32_t offset)
SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* method = shadow_frame->GetMethod();
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index 685b9b69d7..e46f9cde94 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -12989,7 +12989,7 @@ MterpCommonTakenBranch:
movl %eax, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- movl rINST, OUT_ARG3(%esp)
+ movl $2, OUT_ARG2(%esp)
call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
testb %al, %al
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index a1360e0934..62dce6e777 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -67,7 +67,7 @@ Mterp and x86_64 notes:
Some key interpreter variables will be assigned to registers.
nick reg purpose
- rSELF rbp pointer to ThreadSelf.
+ rPROFILE rbp countdown register for jit profiling
rPC r12 interpreted program counter, used for fetching instructions
rFP r13 interpreted frame pointer, used for accessing locals and args
rINSTw bx first 16-bit code of current instruction
@@ -120,6 +120,21 @@ unspecified registers or condition codes.
.cfi_restore \_reg
.endm
+/*
+ * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
+ * to access other shadow frame fields, we need to use a backwards offset. Define those here.
+ */
+#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
+#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
+#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
+#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
+#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
+#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
+#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
+
/* Frame size must be 16-byte aligned.
* Remember about 8 bytes for return address + 6 * 8 for spills.
*/
@@ -130,6 +145,8 @@ unspecified registers or condition codes.
#define IN_ARG2 %rdx
#define IN_ARG1 %rsi
#define IN_ARG0 %rdi
+/* Spill offsets relative to %esp */
+#define SELF_SPILL (FRAME_SIZE - 8)
/* Out Args */
#define OUT_ARG3 %rcx
#define OUT_ARG2 %rdx
@@ -144,7 +161,7 @@ unspecified registers or condition codes.
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
-#define rSELF %rbp
+#define rSELF SELF_SPILL(%rsp)
#define rPC %r12
#define rFP %r13
#define rINST %ebx
@@ -154,40 +171,11 @@ unspecified registers or condition codes.
#define rINSTbl %bl
#define rIBASE %r14
#define rREFS %r15
+#define rPROFILE %ebp
-/*
- * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
- * to access other shadow frame fields, we need to use a backwards offset. Define those here.
- */
-#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
-#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
-#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
-#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
-#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
-#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
-#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
-#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-
-#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
- * Profile branch. rINST should contain the offset. %eax is scratch.
- */
-.macro MTERP_PROFILE_BRANCH
-#ifdef MTERP_PROFILE_BRANCHES
- EXPORT_PC
- movq rSELF, OUT_ARG0
- leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl rINST, OUT_32_ARG2
- call SYMBOL(MterpProfileBranch)
- testb %al, %al
- jnz MterpOnStackReplacement
-#endif
-.endm
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -211,7 +199,8 @@ unspecified registers or condition codes.
*
*/
.macro REFRESH_IBASE
- movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE
+ movq rSELF, rIBASE
+ movq THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
.endm
/*
@@ -377,6 +366,12 @@ SYMBOL(ExecuteMterpImpl):
movq IN_ARG0, rSELF
REFRESH_IBASE
+ /* Set up for backwards branches & osr profiling */
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ call SYMBOL(MterpSetUpHotnessCountdown)
+ movswl %ax, rPROFILE
+
/* start executing the instruction at rPC */
FETCH_INST
GOTO_NEXT
@@ -579,9 +574,10 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.L_op_move_exception: /* 0x0d */
/* File: x86_64/op_move_exception.S */
/* move-exception vAA */
- movl THREAD_EXCEPTION_OFFSET(rSELF), %eax
+ movq rSELF, %rcx
+ movl THREAD_EXCEPTION_OFFSET(%rcx), %eax
SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object
- movl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movl $0, THREAD_EXCEPTION_OFFSET(%rcx)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
/* ------------------------------ */
@@ -590,9 +586,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
/* File: x86_64/op_return_void.S */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
@@ -610,9 +606,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
/* op vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_VREG %eax, rINSTq # eax <- vAA
@@ -628,9 +624,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
/* return-wide vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_WIDE_VREG %rax, rINSTq # eax <- v[AA]
@@ -649,9 +645,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
/* op vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_VREG %eax, rINSTq # eax <- vAA
@@ -854,7 +850,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq rSELF, OUT_ARG3
call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movsbl %al, %eax
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
andb $0xf, rINSTbl # rINSTbl <- A
SET_VREG %eax, rINSTq
@@ -988,7 +985,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, rINSTq # eax<- vAA (exception object)
testb %al, %al
jz common_errNullObject
- movq %rax, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ movq %rax, THREAD_EXCEPTION_OFFSET(%rcx)
jmp MterpException
/* ------------------------------ */
@@ -1003,12 +1001,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* goto +AA */
movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1022,12 +1016,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* goto/16 +AAAA */
movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1044,12 +1034,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* goto/32 +AAAAAAAA */
movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1069,13 +1055,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2
GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA
call SYMBOL(MterpDoPackedSwitch)
+ testl %eax, %eax
movslq %eax, rINSTq
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
.balign 128
@@ -1096,13 +1078,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2
GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA
call SYMBOL(MterpDoSparseSwitch)
+ testl %eax, %eax
movslq %eax, rINSTq
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ jmp MterpCommonTakenBranch
/* ------------------------------ */
@@ -1309,16 +1287,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jne 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1339,16 +1315,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
je 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1369,16 +1343,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jge 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1399,16 +1371,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jl 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1429,16 +1399,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jle 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1459,16 +1427,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $2, rINST # assume not taken
jg 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1485,16 +1451,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jne 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1511,16 +1475,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
je 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1537,16 +1499,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jge 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1563,16 +1523,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jl 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1589,16 +1547,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jle 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1615,16 +1571,14 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $2, rINST # assume branch not taken
jg 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -1767,7 +1721,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index)
EXPORT_PC
call SYMBOL(artAGetObjectFromMterp) # (array, index)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
SET_VREG_OBJECT %eax, rINSTq
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2099,7 +2054,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGet32InstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2131,7 +2087,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGet64InstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2164,7 +2121,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetObjInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 1
@@ -2197,7 +2155,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetBooleanInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2230,7 +2189,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetByteInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2263,7 +2223,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetCharInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2296,7 +2257,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL(artGetShortInstanceFromCode)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
.if 0
@@ -2489,7 +2451,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGet32StaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2519,7 +2482,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGet64StaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2550,7 +2514,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetObjStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 1
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2581,7 +2546,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetBooleanStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2612,7 +2578,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetByteStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2643,7 +2610,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetCharStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -2674,7 +2642,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL(artGetShortStaticFromCode)
- cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if 0
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -3002,9 +2971,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
.balign 128
.L_op_return_void_no_barrier: /* 0x73 */
/* File: x86_64/op_return_void_no_barrier.S */
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
@@ -5712,7 +5681,8 @@ movswl %ax, %eax
movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset
EXPORT_PC
callq SYMBOL(artIGetObjectFromMterp) # (obj, offset)
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
@@ -11849,7 +11819,7 @@ MterpSuspendFallback:
#if MTERP_LOGGING
movq rSELF, OUT_ARG0
leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2
+ movl THREAD_FLAGS_OFFSET(OUT_ARG0), OUT_32_ARG2
call SYMBOL(MterpLogSuspendFallback)
#endif
jmp MterpCommonFallback
@@ -11860,7 +11830,8 @@ MterpSuspendFallback:
* interpreter.
*/
MterpPossibleException:
- cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $0, THREAD_EXCEPTION_OFFSET(%rcx)
jz MterpFallback
/* intentional fallthrough - handle pending exception. */
@@ -11891,19 +11862,113 @@ MterpException:
/* NOTE: no fallthrough */
/*
- * Check for suspend check request. Assumes rINST already loaded, rPC advanced and
- * still needs to get the opcode and branch to it, and flags are in lr.
+ * Common handling for branches with support for Jit profiling.
+ * On entry:
+ * rINST <= signed offset
+ * rPROFILE <= signed hotness countdown (expanded to 32 bits)
+ * condition bits <= set to establish sign of offset (use "NoFlags" entry if not)
+ *
+ * We have quite a few different cases for branch profiling, OSR detection and
+ * suspend check support here.
+ *
+ * Taken backward branches:
+ * If profiling active, do hotness countdown and report if we hit zero.
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ * Is there a pending suspend request? If so, suspend.
+ *
+ * Taken forward branches and not-taken backward branches:
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ *
+ * Our most common case is expected to be a taken backward branch with active jit profiling,
+ * but no full OSR check and no pending suspend request.
+ * Next most common case is not-taken branch with no full OSR check.
+ *
+ */
+MterpCommonTakenBranch:
+ jg .L_forward_branch # don't add forward branches to hotness
+/*
+ * We need to subtract 1 from positive values and we should not see 0 here,
+ * so we may use the result of the comparison with -1.
*/
-MterpCheckSuspendAndContinue:
- REFRESH_IBASE
- testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
+#if JIT_CHECK_OSR != -1
+# error "JIT_CHECK_OSR must be -1."
+#endif
+ cmpl $JIT_CHECK_OSR, rPROFILE
+ je .L_osr_check
+ decl rPROFILE
+ je .L_add_batch # counted down to zero - report
+.L_resume_backward_branch:
+ movq rSELF, %rax
+ testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax)
+ REFRESH_IBASE
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
+ jnz .L_suspend_request_pending
+ GOTO_NEXT
+
+.L_suspend_request_pending:
EXPORT_PC
movq rSELF, OUT_ARG0
- call SYMBOL(MterpSuspendCheck)
-1:
+ call SYMBOL(MterpSuspendCheck) # (self)
+ testb %al, %al
+ jnz MterpFallback
+ REFRESH_IBASE # might have changed during suspend
+ GOTO_NEXT
+
+.L_no_count_backwards:
+ cmpl $JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ jne .L_resume_backward_branch
+.L_osr_check:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_backward_branch
+ jmp MterpOnStackReplacement
+
+.L_forward_branch:
+ cmpl $JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ je .L_check_osr_forward
+.L_resume_forward_branch:
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
GOTO_NEXT
+.L_check_osr_forward:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_forward_branch
+ jmp MterpOnStackReplacement
+
+.L_add_batch:
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movswl %ax, rPROFILE
+ jmp .L_no_count_backwards
+
+/*
+ * Entered from the conditional branch handlers when OSR check request active on
+ * not-taken path. All Dalvik not-taken conditional branch offsets are 2.
+ */
+.L_check_not_taken_osr:
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movl $2, OUT_32_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jnz MterpOnStackReplacement
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
+
/*
* On-stack replacement has happened, and now we've returned from the compiled method.
*/
@@ -11943,7 +12008,28 @@ MterpReturn:
movq %rax, (%rdx)
movl $1, %eax
MterpDone:
+/*
+ * At this point, we expect rPROFILE to be non-zero. If negative, hotness is disabled or we're
+ * checking for OSR. If greater than zero, we might have unreported hotness to register
+ * (the difference between the ending rPROFILE and the cached hotness counter). rPROFILE
+ * should only reach zero immediately after a hotness decrement, and is then reset to either
+ * a negative special state or the new non-zero countdown value.
+ */
+ testl rPROFILE, rPROFILE
+ jle MRestoreFrame # if > 0, we may have some counts to report.
+
+ movl %eax, rINST # stash return value
+ /* Report cached hotness counts */
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movl rINST, %eax # restore return value
+
/* pop up frame */
+MRestoreFrame:
addq $FRAME_SIZE, %rsp
.cfi_adjust_cfa_offset -FRAME_SIZE
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index df10ff03e1..fa03e78fe3 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -238,7 +238,7 @@ MterpCommonTakenBranch:
movl %eax, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
- movl rINST, OUT_ARG3(%esp)
+ movl $$2, OUT_ARG2(%esp)
call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
testb %al, %al
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S
index a16050b371..6601483ebe 100644
--- a/runtime/interpreter/mterp/x86_64/bincmp.S
+++ b/runtime/interpreter/mterp/x86_64/bincmp.S
@@ -11,13 +11,11 @@
andb $$0xf, %cl # rcx <- A
GET_VREG %eax, %rcx # eax <- vA
cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB)
- movl $$2, rINST # assume not taken
j${revcmp} 1f
movswq 2(rPC), rINSTq # Get signed branch offset
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rax <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $$JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S
index 69b2371dea..d992956769 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -65,6 +65,12 @@ SYMBOL(ExecuteMterpImpl):
movq IN_ARG0, rSELF
REFRESH_IBASE
+ /* Set up for backwards branches & osr profiling */
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ call SYMBOL(MterpSetUpHotnessCountdown)
+ movswl %ax, rPROFILE
+
/* start executing the instruction at rPC */
FETCH_INST
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S
index 573256b781..54d0cb1ce4 100644
--- a/runtime/interpreter/mterp/x86_64/footer.S
+++ b/runtime/interpreter/mterp/x86_64/footer.S
@@ -71,7 +71,7 @@ MterpSuspendFallback:
#if MTERP_LOGGING
movq rSELF, OUT_ARG0
leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2
+ movl THREAD_FLAGS_OFFSET(OUT_ARG0), OUT_32_ARG2
call SYMBOL(MterpLogSuspendFallback)
#endif
jmp MterpCommonFallback
@@ -82,7 +82,8 @@ MterpSuspendFallback:
* interpreter.
*/
MterpPossibleException:
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jz MterpFallback
/* intentional fallthrough - handle pending exception. */
@@ -113,19 +114,113 @@ MterpException:
/* NOTE: no fallthrough */
/*
- * Check for suspend check request. Assumes rINST already loaded, rPC advanced and
- * still needs to get the opcode and branch to it, and flags are in lr.
+ * Common handling for branches with support for Jit profiling.
+ * On entry:
+ * rINST <= signed offset
+ * rPROFILE <= signed hotness countdown (expanded to 32 bits)
+ * condition bits <= set to establish sign of offset (use "NoFlags" entry if not)
+ *
+ * We have quite a few different cases for branch profiling, OSR detection and
+ * suspend check support here.
+ *
+ * Taken backward branches:
+ * If profiling active, do hotness countdown and report if we hit zero.
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ * Is there a pending suspend request? If so, suspend.
+ *
+ * Taken forward branches and not-taken backward branches:
+ * If in osr check mode, see if our target is a compiled loop header entry and do OSR if so.
+ *
+ * Our most common case is expected to be a taken backward branch with active jit profiling,
+ * but no full OSR check and no pending suspend request.
+ * Next most common case is not-taken branch with no full OSR check.
+ *
+ */
+MterpCommonTakenBranch:
+ jg .L_forward_branch # don't add forward branches to hotness
+/*
+ * We need to subtract 1 from positive values and we should not see 0 here,
+ * so we may use the result of the comparison with -1.
*/
-MterpCheckSuspendAndContinue:
+#if JIT_CHECK_OSR != -1
+# error "JIT_CHECK_OSR must be -1."
+#endif
+ cmpl $$JIT_CHECK_OSR, rPROFILE
+ je .L_osr_check
+ decl rPROFILE
+ je .L_add_batch # counted down to zero - report
+.L_resume_backward_branch:
+ movq rSELF, %rax
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%rax)
REFRESH_IBASE
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
+ jnz .L_suspend_request_pending
+ GOTO_NEXT
+
+.L_suspend_request_pending:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ call SYMBOL(MterpSuspendCheck) # (self)
+ testb %al, %al
+ jnz MterpFallback
+ REFRESH_IBASE # might have changed during suspend
+ GOTO_NEXT
+
+.L_no_count_backwards:
+ cmpl $$JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ jne .L_resume_backward_branch
+.L_osr_check:
EXPORT_PC
movq rSELF, OUT_ARG0
- call SYMBOL(MterpSuspendCheck)
-1:
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_backward_branch
+ jmp MterpOnStackReplacement
+
+.L_forward_branch:
+ cmpl $$JIT_CHECK_OSR, rPROFILE # possible OSR re-entry?
+ je .L_check_osr_forward
+.L_resume_forward_branch:
+ leaq (rPC, rINSTq, 2), rPC
+ FETCH_INST
GOTO_NEXT
+.L_check_osr_forward:
+ EXPORT_PC
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movq rINSTq, OUT_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jz .L_resume_forward_branch
+ jmp MterpOnStackReplacement
+
+.L_add_batch:
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movswl %ax, rPROFILE
+ jmp .L_no_count_backwards
+
+/*
+ * Entered from the conditional branch handlers when OSR check request active on
+ * not-taken path. All Dalvik not-taken conditional branch offsets are 2.
+ */
+.L_check_not_taken_osr:
+ movq rSELF, OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movl $$2, OUT_32_ARG2
+ call SYMBOL(MterpMaybeDoOnStackReplacement) # (self, shadow_frame, offset)
+ testb %al, %al
+ jnz MterpOnStackReplacement
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
+
/*
* On-stack replacement has happened, and now we've returned from the compiled method.
*/
@@ -165,7 +260,28 @@ MterpReturn:
movq %rax, (%rdx)
movl $$1, %eax
MterpDone:
+/*
+ * At this point, we expect rPROFILE to be non-zero. If negative, hotness is disabled or we're
+ * checking for OSR. If greater than zero, we might have unreported hotness to register
+ * (the difference between the ending rPROFILE and the cached hotness counter). rPROFILE
+ * should only reach zero immediately after a hotness decrement, and is then reset to either
+ * a negative special state or the new non-zero countdown value.
+ */
+ testl rPROFILE, rPROFILE
+ jle MRestoreFrame # if > 0, we may have some counts to report.
+
+ movl %eax, rINST # stash return value
+ /* Report cached hotness counts */
+ movl rPROFILE, %eax
+ movq OFF_FP_METHOD(rFP), OUT_ARG0
+ leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
+ movw %ax, OFF_FP_COUNTDOWN_OFFSET(rFP)
+ movq rSELF, OUT_ARG2
+ call SYMBOL(MterpAddHotnessBatch) # (method, shadow_frame, self)
+ movl rINST, %eax # restore return value
+
/* pop up frame */
+MRestoreFrame:
addq $$FRAME_SIZE, %rsp
.cfi_adjust_cfa_offset -FRAME_SIZE
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index eb84ea1eb5..7699fc4dd7 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -60,7 +60,7 @@ Mterp and x86_64 notes:
Some key interpreter variables will be assigned to registers.
nick reg purpose
- rSELF rbp pointer to ThreadSelf.
+ rPROFILE rbp countdown register for jit profiling
rPC r12 interpreted program counter, used for fetching instructions
rFP r13 interpreted frame pointer, used for accessing locals and args
rINSTw bx first 16-bit code of current instruction
@@ -113,6 +113,21 @@ unspecified registers or condition codes.
.cfi_restore \_reg
.endm
+/*
+ * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
+ * to access other shadow frame fields, we need to use a backwards offset. Define those here.
+ */
+#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
+#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
+#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
+#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
+#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
+#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
+#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
+#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
+#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
+
/* Frame size must be 16-byte aligned.
* Remember about 8 bytes for return address + 6 * 8 for spills.
*/
@@ -123,6 +138,8 @@ unspecified registers or condition codes.
#define IN_ARG2 %rdx
#define IN_ARG1 %rsi
#define IN_ARG0 %rdi
+/* Spill offsets relative to %esp */
+#define SELF_SPILL (FRAME_SIZE - 8)
/* Out Args */
#define OUT_ARG3 %rcx
#define OUT_ARG2 %rdx
@@ -137,7 +154,7 @@ unspecified registers or condition codes.
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
-#define rSELF %rbp
+#define rSELF SELF_SPILL(%rsp)
#define rPC %r12
#define rFP %r13
#define rINST %ebx
@@ -147,40 +164,11 @@ unspecified registers or condition codes.
#define rINSTbl %bl
#define rIBASE %r14
#define rREFS %r15
+#define rPROFILE %ebp
-/*
- * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
- * to access other shadow frame fields, we need to use a backwards offset. Define those here.
- */
-#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET)
-#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET)
-#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET)
-#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET)
-#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
-#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
-#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
-#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
-
-#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
/*
- * Profile branch. rINST should contain the offset. %eax is scratch.
- */
-.macro MTERP_PROFILE_BRANCH
-#ifdef MTERP_PROFILE_BRANCHES
- EXPORT_PC
- movq rSELF, OUT_ARG0
- leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
- movl rINST, OUT_32_ARG2
- call SYMBOL(MterpProfileBranch)
- testb %al, %al
- jnz MterpOnStackReplacement
-#endif
-.endm
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -204,7 +192,8 @@ unspecified registers or condition codes.
*
*/
.macro REFRESH_IBASE
- movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE
+ movq rSELF, rIBASE
+ movq THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
.endm
/*
diff --git a/runtime/interpreter/mterp/x86_64/op_aget_object.S b/runtime/interpreter/mterp/x86_64/op_aget_object.S
index 8baedeab5e..5f77a97748 100644
--- a/runtime/interpreter/mterp/x86_64/op_aget_object.S
+++ b/runtime/interpreter/mterp/x86_64/op_aget_object.S
@@ -10,7 +10,8 @@
GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index)
EXPORT_PC
call SYMBOL(artAGetObjectFromMterp) # (array, index)
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
SET_VREG_OBJECT %eax, rINSTq
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S
index c4fc97644f..9749901f5a 100644
--- a/runtime/interpreter/mterp/x86_64/op_goto.S
+++ b/runtime/interpreter/mterp/x86_64/op_goto.S
@@ -6,9 +6,5 @@
*/
/* goto +AA */
movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S
index 8cb9a5c50f..77688e05e4 100644
--- a/runtime/interpreter/mterp/x86_64/op_goto_16.S
+++ b/runtime/interpreter/mterp/x86_64/op_goto_16.S
@@ -6,9 +6,5 @@
*/
/* goto/16 +AAAA */
movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S
index 4ecdacd3e6..29d777b5a6 100644
--- a/runtime/interpreter/mterp/x86_64/op_goto_32.S
+++ b/runtime/interpreter/mterp/x86_64/op_goto_32.S
@@ -9,9 +9,5 @@
*/
/* goto/32 +AAAAAAAA */
movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S
index a0d0fafba1..df43efe6a4 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget.S
@@ -12,7 +12,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer
movq rSELF, OUT_ARG3
call SYMBOL($helper)
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
.if $is_object
diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S
index 964d20ad74..176c9544ef 100644
--- a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S
+++ b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S
@@ -7,7 +7,8 @@
movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset
EXPORT_PC
callq SYMBOL(artIGetObjectFromMterp) # (obj, offset)
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
diff --git a/runtime/interpreter/mterp/x86_64/op_instance_of.S b/runtime/interpreter/mterp/x86_64/op_instance_of.S
index 6be37f9166..4819833658 100644
--- a/runtime/interpreter/mterp/x86_64/op_instance_of.S
+++ b/runtime/interpreter/mterp/x86_64/op_instance_of.S
@@ -14,7 +14,8 @@
movq rSELF, OUT_ARG3
call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movsbl %al, %eax
- cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpq $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
andb $$0xf, rINSTbl # rINSTbl <- A
SET_VREG %eax, rINSTq
diff --git a/runtime/interpreter/mterp/x86_64/op_move_exception.S b/runtime/interpreter/mterp/x86_64/op_move_exception.S
index d0a14fdc8d..33db878236 100644
--- a/runtime/interpreter/mterp/x86_64/op_move_exception.S
+++ b/runtime/interpreter/mterp/x86_64/op_move_exception.S
@@ -1,5 +1,6 @@
/* move-exception vAA */
- movl THREAD_EXCEPTION_OFFSET(rSELF), %eax
+ movq rSELF, %rcx
+ movl THREAD_EXCEPTION_OFFSET(%rcx), %eax
SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object
- movl $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movl $$0, THREAD_EXCEPTION_OFFSET(%rcx)
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S
index cb0acb7a72..fdf5a50f9c 100644
--- a/runtime/interpreter/mterp/x86_64/op_packed_switch.S
+++ b/runtime/interpreter/mterp/x86_64/op_packed_switch.S
@@ -13,10 +13,6 @@
leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2
GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA
call SYMBOL($func)
+ testl %eax, %eax
movslq %eax, rINSTq
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue
- GOTO_NEXT
+ jmp MterpCommonTakenBranch
diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S
index 14f4f8a446..07e0e5357c 100644
--- a/runtime/interpreter/mterp/x86_64/op_return.S
+++ b/runtime/interpreter/mterp/x86_64/op_return.S
@@ -6,9 +6,9 @@
/* op vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_VREG %eax, rINSTq # eax <- vAA
diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S
index 46a5753c87..6a12df318b 100644
--- a/runtime/interpreter/mterp/x86_64/op_return_void.S
+++ b/runtime/interpreter/mterp/x86_64/op_return_void.S
@@ -1,8 +1,8 @@
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S
index 92e3506d1d..822b2e85e6 100644
--- a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S
+++ b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S
@@ -1,6 +1,6 @@
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
xorq %rax, %rax
diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S
index f2d6e04cab..288eb96f8c 100644
--- a/runtime/interpreter/mterp/x86_64/op_return_wide.S
+++ b/runtime/interpreter/mterp/x86_64/op_return_wide.S
@@ -4,9 +4,9 @@
/* return-wide vAA */
.extern MterpThreadFenceForConstructor
call SYMBOL(MterpThreadFenceForConstructor)
- testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF)
- jz 1f
movq rSELF, OUT_ARG0
+ testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(OUT_ARG0)
+ jz 1f
call SYMBOL(MterpSuspendCheck)
1:
GET_WIDE_VREG %rax, rINSTq # eax <- v[AA]
diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S
index 38d9a5e6c8..d39e6c4396 100644
--- a/runtime/interpreter/mterp/x86_64/op_sget.S
+++ b/runtime/interpreter/mterp/x86_64/op_sget.S
@@ -11,7 +11,8 @@
movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer
movq rSELF, OUT_ARG2 # self
call SYMBOL($helper)
- cmpl $$0, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ cmpl $$0, THREAD_EXCEPTION_OFFSET(%rcx)
jnz MterpException
.if $is_object
SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value
diff --git a/runtime/interpreter/mterp/x86_64/op_throw.S b/runtime/interpreter/mterp/x86_64/op_throw.S
index 22ed990645..8095c25b08 100644
--- a/runtime/interpreter/mterp/x86_64/op_throw.S
+++ b/runtime/interpreter/mterp/x86_64/op_throw.S
@@ -6,5 +6,6 @@
GET_VREG %eax, rINSTq # eax<- vAA (exception object)
testb %al, %al
jz common_errNullObject
- movq %rax, THREAD_EXCEPTION_OFFSET(rSELF)
+ movq rSELF, %rcx
+ movq %rax, THREAD_EXCEPTION_OFFSET(%rcx)
jmp MterpException
diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S
index 0051407cad..fb8ae6af6e 100644
--- a/runtime/interpreter/mterp/x86_64/zcmp.S
+++ b/runtime/interpreter/mterp/x86_64/zcmp.S
@@ -7,13 +7,11 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0)
- movl $$2, rINST # assume branch not taken
j${revcmp} 1f
movswq 2(rPC), rINSTq # fetch signed displacement
+ testq rINSTq, rINSTq
+ jmp MterpCommonTakenBranch
1:
- MTERP_PROFILE_BRANCH
- addq rINSTq, rINSTq # rINSTq <- AA * 2
- leaq (rPC, rINSTq), rPC
- FETCH_INST
- jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
- GOTO_NEXT
+ cmpl $$JIT_CHECK_OSR, rPROFILE
+ je .L_check_not_taken_osr
+ ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 02e05c5429..fe388cd16d 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -484,6 +484,101 @@ void UnstartedRuntime::UnstartedSystemGetSecurityManager(
result->SetL(nullptr);
}
+static constexpr const char* kAndroidHardcodedSystemPropertiesFieldName = "STATIC_PROPERTIES";
+
+static void GetSystemProperty(Thread* self,
+ ShadowFrame* shadow_frame,
+ JValue* result,
+ size_t arg_offset,
+ bool is_default_version)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ StackHandleScope<4> hs(self);
+ Handle<mirror::String> h_key(
+ hs.NewHandle(reinterpret_cast<mirror::String*>(shadow_frame->GetVRegReference(arg_offset))));
+ if (h_key.Get() == nullptr) {
+ AbortTransactionOrFail(self, "getProperty key was null");
+ return;
+ }
+
+ // This is overall inefficient, but reflecting the values here is not great, either. So
+ // for simplicity, and with the assumption that the number of getProperty calls is not
+ // too great, just iterate each time.
+
+ // Get the storage class.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> h_props_class(hs.NewHandle(
+ class_linker->FindClass(self,
+ "Ljava/lang/AndroidHardcodedSystemProperties;",
+ ScopedNullHandle<mirror::ClassLoader>())));
+ if (h_props_class.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not find AndroidHardcodedSystemProperties");
+ return;
+ }
+ if (!class_linker->EnsureInitialized(self, h_props_class, true, true)) {
+ AbortTransactionOrFail(self, "Could not initialize AndroidHardcodedSystemProperties");
+ return;
+ }
+
+ // Get the storage array.
+ ArtField* static_properties =
+ h_props_class->FindDeclaredStaticField(kAndroidHardcodedSystemPropertiesFieldName,
+ "[[Ljava/lang/String;");
+ if (static_properties == nullptr) {
+ AbortTransactionOrFail(self,
+ "Could not find %s field",
+ kAndroidHardcodedSystemPropertiesFieldName);
+ return;
+ }
+ Handle<mirror::ObjectArray<mirror::ObjectArray<mirror::String>>> h_2string_array(
+ hs.NewHandle(reinterpret_cast<mirror::ObjectArray<mirror::ObjectArray<mirror::String>>*>(
+ static_properties->GetObject(h_props_class.Get()))));
+ if (h_2string_array.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Field %s is null", kAndroidHardcodedSystemPropertiesFieldName);
+ return;
+ }
+
+ // Iterate over it.
+ const int32_t prop_count = h_2string_array->GetLength();
+ // Use the third handle as mutable.
+ MutableHandle<mirror::ObjectArray<mirror::String>> h_string_array(
+ hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr));
+ for (int32_t i = 0; i < prop_count; ++i) {
+ h_string_array.Assign(h_2string_array->Get(i));
+ if (h_string_array.Get() == nullptr ||
+ h_string_array->GetLength() != 2 ||
+ h_string_array->Get(0) == nullptr) {
+ AbortTransactionOrFail(self,
+ "Unexpected content of %s",
+ kAndroidHardcodedSystemPropertiesFieldName);
+ return;
+ }
+ if (h_key->Equals(h_string_array->Get(0))) {
+ // Found a value.
+ if (h_string_array->Get(1) == nullptr && is_default_version) {
+ // Null is being delegated to the default map, and then resolved to the given default value.
+ // As there's no default map, return the given value.
+ result->SetL(shadow_frame->GetVRegReference(arg_offset + 1));
+ } else {
+ result->SetL(h_string_array->Get(1));
+ }
+ return;
+ }
+ }
+
+ // Key is not supported.
+ AbortTransactionOrFail(self, "getProperty key %s not supported", h_key->ToModifiedUtf8().c_str());
+}
+
+void UnstartedRuntime::UnstartedSystemGetProperty(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ GetSystemProperty(self, shadow_frame, result, arg_offset, false);
+}
+
+void UnstartedRuntime::UnstartedSystemGetPropertyWithDefault(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ GetSystemProperty(self, shadow_frame, result, arg_offset, true);
+}
+
void UnstartedRuntime::UnstartedThreadLocalGet(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset ATTRIBUTE_UNUSED) {
std::string caller(PrettyMethod(shadow_frame->GetLink()->GetMethod()));
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 3312701135..3b5bee829e 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -34,6 +34,8 @@
V(SystemArraycopyChar, "void java.lang.System.arraycopy(char[], int, char[], int, int)") \
V(SystemArraycopyInt, "void java.lang.System.arraycopy(int[], int, int[], int, int)") \
V(SystemGetSecurityManager, "java.lang.SecurityManager java.lang.System.getSecurityManager()") \
+ V(SystemGetProperty, "java.lang.String java.lang.System.getProperty(java.lang.String)") \
+ V(SystemGetPropertyWithDefault, "java.lang.String java.lang.System.getProperty(java.lang.String, java.lang.String)") \
V(ThreadLocalGet, "java.lang.Object java.lang.ThreadLocal.get()") \
V(MathCeil, "double java.lang.Math.ceil(double)") \
V(MathFloor, "double java.lang.Math.floor(double)") \
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 3a71bbaec1..d04d2de756 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -170,6 +170,7 @@ class ProfilingInfo {
: number_of_inline_caches_(entries.size()),
method_(method),
is_method_being_compiled_(false),
+ is_osr_method_being_compiled_(false),
current_inline_uses_(0),
saved_entry_point_(nullptr) {
memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index cdf468c809..0e7f7f3bc4 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -33,8 +33,8 @@ namespace art {
namespace mirror {
inline uint32_t String::ClassSize(size_t pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 53;
- return Class::ComputeClassSize(true, vtable_entries, 0, 2, 0, 1, 2, pointer_size);
+ uint32_t vtable_entries = Object::kVTableLength + 52;
+ return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
}
// Sets string count in the allocation code path to ensure it is guarded by a CAS.
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 78e372ad02..3f95772b4f 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -492,10 +492,21 @@ bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
const ImageInfo* image_info = GetImageInfo();
if (image_info == nullptr) {
VLOG(oat) << "No image for oat image checksum to match against.";
- return true;
- }
- if (file.GetOatHeader().GetImageFileLocationOatChecksum() != GetCombinedImageChecksum()) {
+ if (HasOriginalDexFiles()) {
+ return true;
+ }
+
+ // If there is no original dex file to fall back to, grudgingly accept
+ // the oat file. This could technically lead to crashes, but there's no
+ // way we could find a better oat file to use for this dex location,
+ // and it's better than being stuck in a boot loop with no way out.
+ // The problem will hopefully resolve itself the next time the runtime
+ // starts up.
+ LOG(WARNING) << "Dex location " << dex_location_ << " does not seem to include dex file. "
+ << "Allow oat file use. This is potentially dangerous.";
+ } else if (file.GetOatHeader().GetImageFileLocationOatChecksum()
+ != GetCombinedImageChecksum()) {
VLOG(oat) << "Oat image checksum does not match image checksum.";
return true;
}
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 94f6345bb0..3846605400 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -364,7 +364,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
// However, if the app was part of /system and preopted, there is no original dex file
// available. In that case grudgingly accept the oat file.
- if (!DexFile::MaybeDex(dex_location)) {
+ if (!oat_file_assistant.HasOriginalDexFiles()) {
accept_oat_file = true;
LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
<< "Allow oat file use. This is potentially dangerous.";
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index aff9b61f3a..1f33651243 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -116,7 +116,18 @@ JNIEXPORT jint JVM_Write(jint fd, char* buf, jint nbytes) {
/* posix lseek() */
JNIEXPORT jlong JVM_Lseek(jint fd, jlong offset, jint whence) {
- return TEMP_FAILURE_RETRY(lseek(fd, offset, whence));
+#if !defined(__APPLE__)
+ // NOTE: Using TEMP_FAILURE_RETRY here is busted for LP32 on glibc - the return
+ // value will be coerced into an int32_t.
+ //
+ // lseek64 isn't specified to return EINTR so it shouldn't be necessary
+ // anyway.
+ return lseek64(fd, offset, whence);
+#else
+ // NOTE: This code is compiled for Mac OS but isn't ever run on that
+ // platform.
+ return lseek(fd, offset, whence);
+#endif
}
/*
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index d5319fdb0c..647578e58a 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4577,8 +4577,18 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id
// Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
// of C1. For resolution to occur the declared class of the field must be compatible with
// obj_type, we've discovered this wasn't so, so report the field didn't exist.
- Fail(VERIFY_ERROR_NO_FIELD) << "cannot access instance field " << PrettyField(field)
- << " from object of type " << obj_type;
+ VerifyError type;
+ bool is_aot = Runtime::Current()->IsAotCompiler();
+ if (is_aot && (field_klass.IsUnresolvedTypes() || obj_type.IsUnresolvedTypes())) {
+ // Compiler & unresolved types involved, retry at runtime.
+ type = VerifyError::VERIFY_ERROR_NO_CLASS;
+ } else {
+ // Classes known (resolved; and thus assignability check is precise), or we are at runtime
+ // and still missing classes. This is a hard failure.
+ type = VerifyError::VERIFY_ERROR_BAD_CLASS_HARD;
+ }
+ Fail(type) << "cannot access instance field " << PrettyField(field)
+ << " from object of type " << obj_type;
return nullptr;
} else {
return field;
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
index f41ff2a94a..50bbe5178e 100644
--- a/test/098-ddmc/src/Main.java
+++ b/test/098-ddmc/src/Main.java
@@ -44,7 +44,12 @@ public class Main {
System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value.
for (int i = 0; i < overflowAllocations; i++) {
- new Object();
+ new Object() {
+ // Add a finalizer so that the allocation won't be eliminated.
+ public void finalize() {
+ System.out.print("");
+ }
+ };
}
Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index e4988c9b5f..d878e69aed 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -32,8 +32,8 @@ z (class java.lang.Character)
62 (class java.lang.Long)
14 (class java.lang.Short)
[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
-[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, private static int java.lang.String.HASHING_SEED, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[int java.lang.String.hash32(), native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.getHashingSeed(), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int)]
+[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
+[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/147-stripped-dex-fallback/expected.txt b/test/147-stripped-dex-fallback/expected.txt
new file mode 100644
index 0000000000..af5626b4a1
--- /dev/null
+++ b/test/147-stripped-dex-fallback/expected.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/147-stripped-dex-fallback/info.txt b/test/147-stripped-dex-fallback/info.txt
new file mode 100644
index 0000000000..72a2ca8d4c
--- /dev/null
+++ b/test/147-stripped-dex-fallback/info.txt
@@ -0,0 +1,2 @@
+Verify that we fallback to running out of dex code in the oat file if there is
+no image and the original dex code has been stripped.
diff --git a/test/147-stripped-dex-fallback/run b/test/147-stripped-dex-fallback/run
new file mode 100755
index 0000000000..e594010b9e
--- /dev/null
+++ b/test/147-stripped-dex-fallback/run
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ensure flags includes prebuild.
+flags="$@"
+if [[ "${flags}" == *--no-prebuild* ]] ; then
+ echo "Test 147-stripped-dex-fallback is not intended to run in no-prebuild mode."
+ exit 1
+fi
+
+${RUN} ${flags} --strip-dex --no-dex2oat
diff --git a/test/147-stripped-dex-fallback/src/Main.java b/test/147-stripped-dex-fallback/src/Main.java
new file mode 100644
index 0000000000..1ef6289559
--- /dev/null
+++ b/test/147-stripped-dex-fallback/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/test/525-checker-arrays-and-fields/info.txt b/test/525-checker-arrays-and-fields/info.txt
deleted file mode 100644
index 3e16abf204..0000000000
--- a/test/525-checker-arrays-and-fields/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test on (in)variant static and instance field and array references in loops.
diff --git a/test/525-checker-arrays-fields1/expected.txt b/test/525-checker-arrays-fields1/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/525-checker-arrays-fields1/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/525-checker-arrays-fields1/info.txt b/test/525-checker-arrays-fields1/info.txt
new file mode 100644
index 0000000000..7d0a088934
--- /dev/null
+++ b/test/525-checker-arrays-fields1/info.txt
@@ -0,0 +1 @@
+Test on (in)variant static field and array references in loops.
diff --git a/test/525-checker-arrays-fields1/src/Main.java b/test/525-checker-arrays-fields1/src/Main.java
new file mode 100644
index 0000000000..ba0476af5f
--- /dev/null
+++ b/test/525-checker-arrays-fields1/src/Main.java
@@ -0,0 +1,711 @@
+/*
+ * 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.
+ */
+
+//
+// Test on (in)variant static field and array references in loops.
+//
+public class Main {
+
+ private static Object anObject = new Object();
+ private static Object anotherObject = new Object();
+
+ //
+ // Static fields.
+ //
+
+ private static boolean sZ;
+ private static byte sB;
+ private static char sC;
+ private static short sS;
+ private static int sI;
+ private static long sJ;
+ private static float sF;
+ private static double sD;
+ private static Object sL;
+
+ //
+ // Static arrays.
+ //
+
+ private static boolean[] sArrZ;
+ private static byte[] sArrB;
+ private static char[] sArrC;
+ private static short[] sArrS;
+ private static int[] sArrI;
+ private static long[] sArrJ;
+ private static float[] sArrF;
+ private static double[] sArrD;
+ private static Object[] sArrL;
+
+ //
+ // Loops on static arrays with invariant static field references.
+ // The checker is used to ensure hoisting occurred.
+ //
+
+ /// CHECK-START: void Main.InvLoopZ() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopZ() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopZ() {
+ for (int i = 0; i < sArrZ.length; i++) {
+ sArrZ[i] = sZ;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopB() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopB() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopB() {
+ for (int i = 0; i < sArrB.length; i++) {
+ sArrB[i] = sB;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopC() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopC() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopC() {
+ for (int i = 0; i < sArrC.length; i++) {
+ sArrC[i] = sC;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopS() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopS() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopS() {
+ for (int i = 0; i < sArrS.length; i++) {
+ sArrS[i] = sS;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopI() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopI() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopI() {
+ for (int i = 0; i < sArrI.length; i++) {
+ sArrI[i] = sI;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopJ() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopJ() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopJ() {
+ for (int i = 0; i < sArrJ.length; i++) {
+ sArrJ[i] = sJ;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopF() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopF() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopF() {
+ for (int i = 0; i < sArrF.length; i++) {
+ sArrF[i] = sF;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopD() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopD() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopD() {
+ for (int i = 0; i < sArrD.length; i++) {
+ sArrD[i] = sD;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopL() licm (before)
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopL() licm (after)
+ /// CHECK-DAG: StaticFieldGet loop:none
+ /// CHECK-DAG: StaticFieldGet loop:none
+
+ private static void InvLoopL() {
+ for (int i = 0; i < sArrL.length; i++) {
+ sArrL[i] = sL;
+ }
+ }
+
+ //
+ // Loops on static arrays with variant static field references.
+ // Incorrect hoisting is detected by incorrect outcome.
+ //
+
+ private static void VarLoopZ() {
+ for (int i = 0; i < sArrZ.length; i++) {
+ sArrZ[i] = sZ;
+ if (i == 10)
+ sZ = !sZ;
+ }
+ }
+
+ private static void VarLoopB() {
+ for (int i = 0; i < sArrB.length; i++) {
+ sArrB[i] = sB;
+ if (i == 10)
+ sB++;
+ }
+ }
+
+ private static void VarLoopC() {
+ for (int i = 0; i < sArrC.length; i++) {
+ sArrC[i] = sC;
+ if (i == 10)
+ sC++;
+ }
+ }
+
+ private static void VarLoopS() {
+ for (int i = 0; i < sArrS.length; i++) {
+ sArrS[i] = sS;
+ if (i == 10)
+ sS++;
+ }
+ }
+
+ private static void VarLoopI() {
+ for (int i = 0; i < sArrI.length; i++) {
+ sArrI[i] = sI;
+ if (i == 10)
+ sI++;
+ }
+ }
+
+ private static void VarLoopJ() {
+ for (int i = 0; i < sArrJ.length; i++) {
+ sArrJ[i] = sJ;
+ if (i == 10)
+ sJ++;
+ }
+ }
+
+ private static void VarLoopF() {
+ for (int i = 0; i < sArrF.length; i++) {
+ sArrF[i] = sF;
+ if (i == 10)
+ sF++;
+ }
+ }
+
+ private static void VarLoopD() {
+ for (int i = 0; i < sArrD.length; i++) {
+ sArrD[i] = sD;
+ if (i == 10)
+ sD++;
+ }
+ }
+
+ private static void VarLoopL() {
+ for (int i = 0; i < sArrL.length; i++) {
+ sArrL[i] = sL;
+ if (i == 10)
+ sL = anotherObject;
+ }
+ }
+
+ //
+ // Loops on static arrays with a cross-over reference.
+ // Incorrect hoisting is detected by incorrect outcome.
+ // In addition, the checker is used to detect no hoisting.
+ //
+
+ /// CHECK-START: void Main.CrossOverLoopZ() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopZ() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopZ() {
+ sArrZ[20] = false;
+ for (int i = 0; i < sArrZ.length; i++) {
+ sArrZ[i] = !sArrZ[20];
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopB() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopB() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopB() {
+ sArrB[20] = 11;
+ for (int i = 0; i < sArrB.length; i++) {
+ sArrB[i] = (byte)(sArrB[20] + 2);
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopC() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopC() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopC() {
+ sArrC[20] = 11;
+ for (int i = 0; i < sArrC.length; i++) {
+ sArrC[i] = (char)(sArrC[20] + 2);
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopS() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopS() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopS() {
+ sArrS[20] = 11;
+ for (int i = 0; i < sArrS.length; i++) {
+ sArrS[i] = (short)(sArrS[20] + 2);
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopI() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopI() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopI() {
+ sArrI[20] = 11;
+ for (int i = 0; i < sArrI.length; i++) {
+ sArrI[i] = sArrI[20] + 2;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopJ() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopJ() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopJ() {
+ sArrJ[20] = 11;
+ for (int i = 0; i < sArrJ.length; i++) {
+ sArrJ[i] = sArrJ[20] + 2;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopF() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopF() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopF() {
+ sArrF[20] = 11;
+ for (int i = 0; i < sArrF.length; i++) {
+ sArrF[i] = sArrF[20] + 2;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopD() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopD() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopD() {
+ sArrD[20] = 11;
+ for (int i = 0; i < sArrD.length; i++) {
+ sArrD[i] = sArrD[20] + 2;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoopL() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.CrossOverLoopL() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void CrossOverLoopL() {
+ sArrL[20] = anotherObject;
+ for (int i = 0; i < sArrL.length; i++) {
+ sArrL[i] = (sArrL[20] == anObject) ? anotherObject : anObject;
+ }
+ }
+
+ //
+ // False cross-over loops on static arrays with data types (I/F and J/D) that used
+ // to be aliased in an older version of the compiler. This alias has been removed,
+ // however, which enables hoisting the invariant array reference.
+ //
+
+ /// CHECK-START: void Main.FalseCrossOverLoop1() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop1() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void FalseCrossOverLoop1() {
+ sArrF[20] = -1;
+ for (int i = 0; i < sArrI.length; i++) {
+ sArrI[i] = (int) sArrF[20] - 2;
+ }
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop2() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop2() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void FalseCrossOverLoop2() {
+ sArrI[20] = -2;
+ for (int i = 0; i < sArrF.length; i++) {
+ sArrF[i] = sArrI[20] - 2;
+ }
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop3() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop3() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void FalseCrossOverLoop3() {
+ sArrD[20] = -3;
+ for (int i = 0; i < sArrJ.length; i++) {
+ sArrJ[i] = (long) sArrD[20] - 2;
+ }
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop4() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop4() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private static void FalseCrossOverLoop4() {
+ sArrJ[20] = -4;
+ for (int i = 0; i < sArrD.length; i++) {
+ sArrD[i] = sArrJ[20] - 2;
+ }
+ }
+
+ //
+ // Main driver and testers.
+ //
+
+ public static void main(String[] args) {
+ DoStaticTests();
+ System.out.println("passed");
+ }
+
+ private static void DoStaticTests() {
+ // Type Z.
+ sZ = true;
+ sArrZ = new boolean[100];
+ InvLoopZ();
+ for (int i = 0; i < sArrZ.length; i++) {
+ expectEquals(true, sArrZ[i]);
+ }
+ VarLoopZ();
+ for (int i = 0; i < sArrZ.length; i++) {
+ expectEquals(i <= 10, sArrZ[i]);
+ }
+ CrossOverLoopZ();
+ for (int i = 0; i < sArrZ.length; i++) {
+ expectEquals(i <= 20, sArrZ[i]);
+ }
+ // Type B.
+ sB = 1;
+ sArrB = new byte[100];
+ InvLoopB();
+ for (int i = 0; i < sArrB.length; i++) {
+ expectEquals(1, sArrB[i]);
+ }
+ VarLoopB();
+ for (int i = 0; i < sArrB.length; i++) {
+ expectEquals(i <= 10 ? 1 : 2, sArrB[i]);
+ }
+ CrossOverLoopB();
+ for (int i = 0; i < sArrB.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrB[i]);
+ }
+ // Type C.
+ sC = 2;
+ sArrC = new char[100];
+ InvLoopC();
+ for (int i = 0; i < sArrC.length; i++) {
+ expectEquals(2, sArrC[i]);
+ }
+ VarLoopC();
+ for (int i = 0; i < sArrC.length; i++) {
+ expectEquals(i <= 10 ? 2 : 3, sArrC[i]);
+ }
+ CrossOverLoopC();
+ for (int i = 0; i < sArrC.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrC[i]);
+ }
+ // Type S.
+ sS = 3;
+ sArrS = new short[100];
+ InvLoopS();
+ for (int i = 0; i < sArrS.length; i++) {
+ expectEquals(3, sArrS[i]);
+ }
+ VarLoopS();
+ for (int i = 0; i < sArrS.length; i++) {
+ expectEquals(i <= 10 ? 3 : 4, sArrS[i]);
+ }
+ CrossOverLoopS();
+ for (int i = 0; i < sArrS.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrS[i]);
+ }
+ // Type I.
+ sI = 4;
+ sArrI = new int[100];
+ InvLoopI();
+ for (int i = 0; i < sArrI.length; i++) {
+ expectEquals(4, sArrI[i]);
+ }
+ VarLoopI();
+ for (int i = 0; i < sArrI.length; i++) {
+ expectEquals(i <= 10 ? 4 : 5, sArrI[i]);
+ }
+ CrossOverLoopI();
+ for (int i = 0; i < sArrI.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrI[i]);
+ }
+ // Type J.
+ sJ = 5;
+ sArrJ = new long[100];
+ InvLoopJ();
+ for (int i = 0; i < sArrJ.length; i++) {
+ expectEquals(5, sArrJ[i]);
+ }
+ VarLoopJ();
+ for (int i = 0; i < sArrJ.length; i++) {
+ expectEquals(i <= 10 ? 5 : 6, sArrJ[i]);
+ }
+ CrossOverLoopJ();
+ for (int i = 0; i < sArrJ.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrJ[i]);
+ }
+ // Type F.
+ sF = 6.0f;
+ sArrF = new float[100];
+ InvLoopF();
+ for (int i = 0; i < sArrF.length; i++) {
+ expectEquals(6, sArrF[i]);
+ }
+ VarLoopF();
+ for (int i = 0; i < sArrF.length; i++) {
+ expectEquals(i <= 10 ? 6 : 7, sArrF[i]);
+ }
+ CrossOverLoopF();
+ for (int i = 0; i < sArrF.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrF[i]);
+ }
+ // Type D.
+ sD = 7.0;
+ sArrD = new double[100];
+ InvLoopD();
+ for (int i = 0; i < sArrD.length; i++) {
+ expectEquals(7.0, sArrD[i]);
+ }
+ VarLoopD();
+ for (int i = 0; i < sArrD.length; i++) {
+ expectEquals(i <= 10 ? 7 : 8, sArrD[i]);
+ }
+ CrossOverLoopD();
+ for (int i = 0; i < sArrD.length; i++) {
+ expectEquals(i <= 20 ? 13 : 15, sArrD[i]);
+ }
+ // Type L.
+ sL = anObject;
+ sArrL = new Object[100];
+ InvLoopL();
+ for (int i = 0; i < sArrL.length; i++) {
+ expectEquals(anObject, sArrL[i]);
+ }
+ VarLoopL();
+ for (int i = 0; i < sArrL.length; i++) {
+ expectEquals(i <= 10 ? anObject : anotherObject, sArrL[i]);
+ }
+ CrossOverLoopL();
+ for (int i = 0; i < sArrL.length; i++) {
+ expectEquals(i <= 20 ? anObject : anotherObject, sArrL[i]);
+ }
+ // False cross-over.
+ FalseCrossOverLoop1();
+ for (int i = 0; i < sArrI.length; i++) {
+ expectEquals(-3, sArrI[i]);
+ }
+ FalseCrossOverLoop2();
+ for (int i = 0; i < sArrF.length; i++) {
+ expectEquals(-4, sArrF[i]);
+ }
+ FalseCrossOverLoop3();
+ for (int i = 0; i < sArrJ.length; i++) {
+ expectEquals(-5, sArrJ[i]);
+ }
+ FalseCrossOverLoop4();
+ for (int i = 0; i < sArrD.length; i++) {
+ expectEquals(-6, sArrD[i]);
+ }
+ }
+
+ private static void expectEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(byte expected, byte result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(char expected, char result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(short expected, short result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(double expected, double result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(Object expected, Object result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/525-checker-arrays-fields2/expected.txt b/test/525-checker-arrays-fields2/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/525-checker-arrays-fields2/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/525-checker-arrays-fields2/info.txt b/test/525-checker-arrays-fields2/info.txt
new file mode 100644
index 0000000000..3464e540a6
--- /dev/null
+++ b/test/525-checker-arrays-fields2/info.txt
@@ -0,0 +1 @@
+Test on (in)variant instance field and array references in loops.
diff --git a/test/525-checker-arrays-and-fields/src/Main.java b/test/525-checker-arrays-fields2/src/Main.java
index a635a5157f..2aa40fcdc1 100644
--- a/test/525-checker-arrays-and-fields/src/Main.java
+++ b/test/525-checker-arrays-fields2/src/Main.java
@@ -15,7 +15,7 @@
*/
//
-// Test on (in)variant static and instance field and array references in loops.
+// Test on (in)variant instance field and array references in loops.
//
public class Main {
@@ -23,34 +23,6 @@ public class Main {
private static Object anotherObject = new Object();
//
- // Static fields.
- //
-
- private static boolean sZ;
- private static byte sB;
- private static char sC;
- private static short sS;
- private static int sI;
- private static long sJ;
- private static float sF;
- private static double sD;
- private static Object sL;
-
- //
- // Static arrays.
- //
-
- private static boolean[] sArrZ;
- private static byte[] sArrB;
- private static char[] sArrC;
- private static short[] sArrS;
- private static int[] sArrI;
- private static long[] sArrJ;
- private static float[] sArrF;
- private static double[] sArrD;
- private static Object[] sArrL;
-
- //
// Instance fields.
//
@@ -79,346 +51,6 @@ public class Main {
private Object[] mArrL;
//
- // Loops on static arrays with invariant static field references.
- // The checker is used to ensure hoisting occurred.
- //
-
- /// CHECK-START: void Main.SInvLoopZ() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopZ() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopZ() {
- for (int i = 0; i < sArrZ.length; i++) {
- sArrZ[i] = sZ;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopB() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopB() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopB() {
- for (int i = 0; i < sArrB.length; i++) {
- sArrB[i] = sB;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopC() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopC() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopC() {
- for (int i = 0; i < sArrC.length; i++) {
- sArrC[i] = sC;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopS() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopS() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopS() {
- for (int i = 0; i < sArrS.length; i++) {
- sArrS[i] = sS;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopI() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopI() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopI() {
- for (int i = 0; i < sArrI.length; i++) {
- sArrI[i] = sI;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopJ() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopJ() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopJ() {
- for (int i = 0; i < sArrJ.length; i++) {
- sArrJ[i] = sJ;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopF() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopF() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopF() {
- for (int i = 0; i < sArrF.length; i++) {
- sArrF[i] = sF;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopD() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopD() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopD() {
- for (int i = 0; i < sArrD.length; i++) {
- sArrD[i] = sD;
- }
- }
-
- /// CHECK-START: void Main.SInvLoopL() licm (before)
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
- /// CHECK-DAG: StaticFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SInvLoopL() licm (after)
- /// CHECK-DAG: StaticFieldGet loop:none
- /// CHECK-DAG: StaticFieldGet loop:none
-
- private static void SInvLoopL() {
- for (int i = 0; i < sArrL.length; i++) {
- sArrL[i] = sL;
- }
- }
-
- //
- // Loops on static arrays with variant static field references.
- // Incorrect hoisting is detected by incorrect outcome.
- //
-
- private static void SVarLoopZ() {
- for (int i = 0; i < sArrZ.length; i++) {
- sArrZ[i] = sZ;
- if (i == 10)
- sZ = !sZ;
- }
- }
-
- private static void SVarLoopB() {
- for (int i = 0; i < sArrB.length; i++) {
- sArrB[i] = sB;
- if (i == 10)
- sB++;
- }
- }
-
- private static void SVarLoopC() {
- for (int i = 0; i < sArrC.length; i++) {
- sArrC[i] = sC;
- if (i == 10)
- sC++;
- }
- }
-
- private static void SVarLoopS() {
- for (int i = 0; i < sArrS.length; i++) {
- sArrS[i] = sS;
- if (i == 10)
- sS++;
- }
- }
-
- private static void SVarLoopI() {
- for (int i = 0; i < sArrI.length; i++) {
- sArrI[i] = sI;
- if (i == 10)
- sI++;
- }
- }
-
- private static void SVarLoopJ() {
- for (int i = 0; i < sArrJ.length; i++) {
- sArrJ[i] = sJ;
- if (i == 10)
- sJ++;
- }
- }
-
- private static void SVarLoopF() {
- for (int i = 0; i < sArrF.length; i++) {
- sArrF[i] = sF;
- if (i == 10)
- sF++;
- }
- }
-
- private static void SVarLoopD() {
- for (int i = 0; i < sArrD.length; i++) {
- sArrD[i] = sD;
- if (i == 10)
- sD++;
- }
- }
-
- private static void SVarLoopL() {
- for (int i = 0; i < sArrL.length; i++) {
- sArrL[i] = sL;
- if (i == 10)
- sL = anotherObject;
- }
- }
-
- //
- // Loops on static arrays with a cross-over reference.
- // Incorrect hoisting is detected by incorrect outcome.
- // In addition, the checker is used to detect no hoisting.
- //
-
- /// CHECK-START: void Main.SCrossOverLoopZ() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopZ() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopZ() {
- for (int i = 0; i < sArrZ.length; i++) {
- sArrZ[i] = !sArrZ[20];
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopB() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopB() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopB() {
- for (int i = 0; i < sArrB.length; i++) {
- sArrB[i] = (byte)(sArrB[20] + 2);
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopC() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopC() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopC() {
- for (int i = 0; i < sArrC.length; i++) {
- sArrC[i] = (char)(sArrC[20] + 2);
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopS() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopS() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopS() {
- for (int i = 0; i < sArrS.length; i++) {
- sArrS[i] = (short)(sArrS[20] + 2);
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopI() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopI() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopI() {
- for (int i = 0; i < sArrI.length; i++) {
- sArrI[i] = sArrI[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopJ() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopJ() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopJ() {
- for (int i = 0; i < sArrJ.length; i++) {
- sArrJ[i] = sArrJ[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopF() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopF() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopF() {
- for (int i = 0; i < sArrF.length; i++) {
- sArrF[i] = sArrF[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopD() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopD() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopD() {
- for (int i = 0; i < sArrD.length; i++) {
- sArrD[i] = sArrD[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.SCrossOverLoopL() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.SCrossOverLoopL() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void SCrossOverLoopL() {
- for (int i = 0; i < sArrL.length; i++) {
- sArrL[i] = (sArrL[20] == anObject) ? anotherObject : anObject;
- }
- }
-
- //
// Loops on instance arrays with invariant instance field references.
// The checker is used to ensure hoisting occurred.
//
@@ -633,278 +265,241 @@ public class Main {
//
/// CHECK-START: void Main.CrossOverLoopZ() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopZ() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopZ() {
+ mArrZ[20] = false;
for (int i = 0; i < mArrZ.length; i++) {
mArrZ[i] = !mArrZ[20];
}
}
/// CHECK-START: void Main.CrossOverLoopB() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopB() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopB() {
+ mArrB[20] = 111;
for (int i = 0; i < mArrB.length; i++) {
mArrB[i] = (byte)(mArrB[20] + 2);
}
}
/// CHECK-START: void Main.CrossOverLoopC() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopC() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopC() {
+ mArrC[20] = 111;
for (int i = 0; i < mArrC.length; i++) {
mArrC[i] = (char)(mArrC[20] + 2);
}
}
/// CHECK-START: void Main.CrossOverLoopS() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopS() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopS() {
+ mArrS[20] = 111;
for (int i = 0; i < mArrS.length; i++) {
mArrS[i] = (short)(mArrS[20] + 2);
}
}
/// CHECK-START: void Main.CrossOverLoopI() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopI() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopI() {
+ mArrI[20] = 111;
for (int i = 0; i < mArrI.length; i++) {
mArrI[i] = mArrI[20] + 2;
}
}
/// CHECK-START: void Main.CrossOverLoopJ() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopJ() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopJ() {
+ mArrJ[20] = 111;
for (int i = 0; i < mArrJ.length; i++) {
mArrJ[i] = mArrJ[20] + 2;
}
}
/// CHECK-START: void Main.CrossOverLoopF() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopF() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopF() {
+ mArrF[20] = 111;
for (int i = 0; i < mArrF.length; i++) {
mArrF[i] = mArrF[20] + 2;
}
}
/// CHECK-START: void Main.CrossOverLoopD() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopD() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopD() {
+ mArrD[20] = 111;
for (int i = 0; i < mArrD.length; i++) {
mArrD[i] = mArrD[20] + 2;
}
}
/// CHECK-START: void Main.CrossOverLoopL() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
/// CHECK-START: void Main.CrossOverLoopL() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
/// CHECK-DAG: ArrayGet loop:{{B\d+}}
/// CHECK-DAG: ArraySet loop:{{B\d+}}
private void CrossOverLoopL() {
+ mArrL[20] = anotherObject;
for (int i = 0; i < mArrL.length; i++) {
mArrL[i] = (mArrL[20] == anObject) ? anotherObject : anObject;
}
}
//
- // Driver and testers.
+ // False cross-over loops on instance arrays with data types (I/F and J/D) that used
+ // to be aliased in an older version of the compiler. This alias has been removed,
+ // however, which enables hoisting the invariant array reference.
//
- public static void main(String[] args) {
- DoStaticTests();
- new Main().DoInstanceTests();
- }
+ /// CHECK-START: void Main.FalseCrossOverLoop1() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
- private static void DoStaticTests() {
- // Type Z.
- sZ = true;
- sArrZ = new boolean[100];
- SInvLoopZ();
- for (int i = 0; i < sArrZ.length; i++) {
- expectEquals(true, sArrZ[i]);
- }
- SVarLoopZ();
- for (int i = 0; i < sArrZ.length; i++) {
- expectEquals(i <= 10, sArrZ[i]);
- }
- SCrossOverLoopZ();
- for (int i = 0; i < sArrZ.length; i++) {
- expectEquals(i <= 20, sArrZ[i]);
- }
- // Type B.
- sB = 1;
- sArrB = new byte[100];
- SInvLoopB();
- for (int i = 0; i < sArrB.length; i++) {
- expectEquals(1, sArrB[i]);
- }
- SVarLoopB();
- for (int i = 0; i < sArrB.length; i++) {
- expectEquals(i <= 10 ? 1 : 2, sArrB[i]);
- }
- SCrossOverLoopB();
- for (int i = 0; i < sArrB.length; i++) {
- expectEquals(i <= 20 ? 4 : 6, sArrB[i]);
- }
- // Type C.
- sC = 2;
- sArrC = new char[100];
- SInvLoopC();
- for (int i = 0; i < sArrC.length; i++) {
- expectEquals(2, sArrC[i]);
- }
- SVarLoopC();
- for (int i = 0; i < sArrC.length; i++) {
- expectEquals(i <= 10 ? 2 : 3, sArrC[i]);
- }
- SCrossOverLoopC();
- for (int i = 0; i < sArrC.length; i++) {
- expectEquals(i <= 20 ? 5 : 7, sArrC[i]);
- }
- // Type S.
- sS = 3;
- sArrS = new short[100];
- SInvLoopS();
- for (int i = 0; i < sArrS.length; i++) {
- expectEquals(3, sArrS[i]);
- }
- SVarLoopS();
- for (int i = 0; i < sArrS.length; i++) {
- expectEquals(i <= 10 ? 3 : 4, sArrS[i]);
- }
- SCrossOverLoopS();
- for (int i = 0; i < sArrS.length; i++) {
- expectEquals(i <= 20 ? 6 : 8, sArrS[i]);
- }
- // Type I.
- sI = 4;
- sArrI = new int[100];
- SInvLoopI();
- for (int i = 0; i < sArrI.length; i++) {
- expectEquals(4, sArrI[i]);
- }
- SVarLoopI();
- for (int i = 0; i < sArrI.length; i++) {
- expectEquals(i <= 10 ? 4 : 5, sArrI[i]);
- }
- SCrossOverLoopI();
- for (int i = 0; i < sArrI.length; i++) {
- expectEquals(i <= 20 ? 7 : 9, sArrI[i]);
- }
- // Type J.
- sJ = 5;
- sArrJ = new long[100];
- SInvLoopJ();
- for (int i = 0; i < sArrJ.length; i++) {
- expectEquals(5, sArrJ[i]);
- }
- SVarLoopJ();
- for (int i = 0; i < sArrJ.length; i++) {
- expectEquals(i <= 10 ? 5 : 6, sArrJ[i]);
- }
- SCrossOverLoopJ();
- for (int i = 0; i < sArrJ.length; i++) {
- expectEquals(i <= 20 ? 8 : 10, sArrJ[i]);
- }
- // Type F.
- sF = 6.0f;
- sArrF = new float[100];
- SInvLoopF();
- for (int i = 0; i < sArrF.length; i++) {
- expectEquals(6, sArrF[i]);
- }
- SVarLoopF();
- for (int i = 0; i < sArrF.length; i++) {
- expectEquals(i <= 10 ? 6 : 7, sArrF[i]);
- }
- SCrossOverLoopF();
- for (int i = 0; i < sArrF.length; i++) {
- expectEquals(i <= 20 ? 9 : 11, sArrF[i]);
- }
- // Type D.
- sD = 7.0;
- sArrD = new double[100];
- SInvLoopD();
- for (int i = 0; i < sArrD.length; i++) {
- expectEquals(7.0, sArrD[i]);
- }
- SVarLoopD();
- for (int i = 0; i < sArrD.length; i++) {
- expectEquals(i <= 10 ? 7 : 8, sArrD[i]);
- }
- SCrossOverLoopD();
- for (int i = 0; i < sArrD.length; i++) {
- expectEquals(i <= 20 ? 10 : 12, sArrD[i]);
+ /// CHECK-START: void Main.FalseCrossOverLoop1() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private void FalseCrossOverLoop1() {
+ mArrF[20] = -1;
+ for (int i = 0; i < mArrI.length; i++) {
+ mArrI[i] = (int) mArrF[20] - 2;
}
- // Type L.
- sL = anObject;
- sArrL = new Object[100];
- SInvLoopL();
- for (int i = 0; i < sArrL.length; i++) {
- expectEquals(anObject, sArrL[i]);
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop2() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop2() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private void FalseCrossOverLoop2() {
+ mArrI[20] = -2;
+ for (int i = 0; i < mArrF.length; i++) {
+ mArrF[i] = mArrI[20] - 2;
}
- SVarLoopL();
- for (int i = 0; i < sArrL.length; i++) {
- expectEquals(i <= 10 ? anObject : anotherObject, sArrL[i]);
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop3() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop3() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private void FalseCrossOverLoop3() {
+ mArrD[20] = -3;
+ for (int i = 0; i < mArrJ.length; i++) {
+ mArrJ[i] = (long) mArrD[20] - 2;
}
- SCrossOverLoopL();
- for (int i = 0; i < sArrL.length; i++) {
- expectEquals(i <= 20 ? anObject : anotherObject, sArrL[i]);
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop4() licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.FalseCrossOverLoop4() licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+
+ private void FalseCrossOverLoop4() {
+ mArrJ[20] = -4;
+ for (int i = 0; i < mArrD.length; i++) {
+ mArrD[i] = mArrJ[20] - 2;
}
}
+ //
+ // Main driver and testers.
+ //
+
+ public static void main(String[] args) {
+ new Main().DoInstanceTests();
+ System.out.println("passed");
+ }
+
private void DoInstanceTests() {
// Type Z.
mZ = true;
@@ -934,7 +529,7 @@ public class Main {
}
CrossOverLoopB();
for (int i = 0; i < mArrB.length; i++) {
- expectEquals(i <= 20 ? 4 : 6, mArrB[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrB[i]);
}
// Type C.
mC = 2;
@@ -949,7 +544,7 @@ public class Main {
}
CrossOverLoopC();
for (int i = 0; i < mArrC.length; i++) {
- expectEquals(i <= 20 ? 5 : 7, mArrC[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrC[i]);
}
// Type S.
mS = 3;
@@ -964,7 +559,7 @@ public class Main {
}
CrossOverLoopS();
for (int i = 0; i < mArrS.length; i++) {
- expectEquals(i <= 20 ? 6 : 8, mArrS[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrS[i]);
}
// Type I.
mI = 4;
@@ -979,7 +574,7 @@ public class Main {
}
CrossOverLoopI();
for (int i = 0; i < mArrI.length; i++) {
- expectEquals(i <= 20 ? 7 : 9, mArrI[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrI[i]);
}
// Type J.
mJ = 5;
@@ -994,7 +589,7 @@ public class Main {
}
CrossOverLoopJ();
for (int i = 0; i < mArrJ.length; i++) {
- expectEquals(i <= 20 ? 8 : 10, mArrJ[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrJ[i]);
}
// Type F.
mF = 6.0f;
@@ -1009,7 +604,7 @@ public class Main {
}
CrossOverLoopF();
for (int i = 0; i < mArrF.length; i++) {
- expectEquals(i <= 20 ? 9 : 11, mArrF[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrF[i]);
}
// Type D.
mD = 7.0;
@@ -1024,7 +619,7 @@ public class Main {
}
CrossOverLoopD();
for (int i = 0; i < mArrD.length; i++) {
- expectEquals(i <= 20 ? 10 : 12, mArrD[i]);
+ expectEquals(i <= 20 ? 113 : 115, mArrD[i]);
}
// Type L.
mL = anObject;
@@ -1041,6 +636,23 @@ public class Main {
for (int i = 0; i < mArrL.length; i++) {
expectEquals(i <= 20 ? anObject : anotherObject, mArrL[i]);
}
+ // False cross-over.
+ FalseCrossOverLoop1();
+ for (int i = 0; i < mArrI.length; i++) {
+ expectEquals(-3, mArrI[i]);
+ }
+ FalseCrossOverLoop2();
+ for (int i = 0; i < mArrF.length; i++) {
+ expectEquals(-4, mArrF[i]);
+ }
+ FalseCrossOverLoop3();
+ for (int i = 0; i < mArrJ.length; i++) {
+ expectEquals(-5, mArrJ[i]);
+ }
+ FalseCrossOverLoop4();
+ for (int i = 0; i < mArrD.length; i++) {
+ expectEquals(-6, mArrD[i]);
+ }
}
private static void expectEquals(boolean expected, boolean result) {
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 4d6ea06fe0..89875d7fef 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -70,6 +70,10 @@ class Finalizable {
}
}
+interface Filter {
+ public boolean isValid(int i);
+}
+
public class Main {
/// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (before)
@@ -78,7 +82,7 @@ public class Main {
/// CHECK: InstanceFieldGet
/// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
@@ -124,7 +128,6 @@ public class Main {
}
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (before)
- /// CHECK: NewInstance
/// CHECK: StaticFieldGet
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
@@ -137,7 +140,6 @@ public class Main {
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (after)
- /// CHECK: NewInstance
/// CHECK: StaticFieldGet
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
@@ -149,9 +151,6 @@ public class Main {
// A new allocation (even non-singleton) shouldn't alias with pre-existing values.
static int test3(TestClass obj) {
- // Do an allocation here to avoid the HLoadClass and HClinitCheck
- // at the second allocation.
- new TestClass();
TestClass obj1 = TestClass.sTestClassObj;
TestClass obj2 = new TestClass(); // Cannot alias with obj or obj1 which pre-exist.
obj.next = obj2; // Make obj2 a non-singleton.
@@ -256,7 +255,7 @@ public class Main {
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test8() load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK: InvokeVirtual
/// CHECK-NOT: NullCheck
@@ -414,7 +413,7 @@ public class Main {
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test16() load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
@@ -431,7 +430,7 @@ public class Main {
/// CHECK-START: int Main.test17() load_store_elimination (after)
/// CHECK: <<Const0:i\d+>> IntConstant 0
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
/// CHECK: Return [<<Const0>>]
@@ -527,12 +526,12 @@ public class Main {
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test22() load_store_elimination (after)
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
- /// CHECK: NewInstance
+ /// CHECK-NOT: NewInstance
/// CHECK-NOT: InstanceFieldSet
/// CHECK-NOT: InstanceFieldGet
/// CHECK-NOT: InstanceFieldGet
@@ -673,7 +672,7 @@ public class Main {
/// CHECK: Select
// Test that HSelect creates alias.
- public static int $noinline$testHSelect(boolean b) {
+ static int $noinline$testHSelect(boolean b) {
if (sFlag) {
throw new Error();
}
@@ -686,19 +685,51 @@ public class Main {
return obj2.i;
}
- public static void assertIntEquals(int result, int expected) {
+ static int sumWithFilter(int[] array, Filter f) {
+ int sum = 0;
+ for (int i = 0; i < array.length; i++) {
+ if (f.isValid(array[i])) {
+ sum += array[i];
+ }
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (before)
+ /// CHECK: NewInstance
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.sumWithinRange(int[], int, int) load_store_elimination (after)
+ /// CHECK-NOT: NewInstance
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldGet
+
+ // A lambda-style allocation can be eliminated after inlining.
+ static int sumWithinRange(int[] array, final int low, final int high) {
+ Filter filter = new Filter() {
+ public boolean isValid(int i) {
+ return (i >= low) && (i <= high);
+ }
+ };
+ return sumWithFilter(array, filter);
+ }
+
+ static void assertIntEquals(int result, int expected) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
}
}
- public static void assertFloatEquals(float result, float expected) {
+ static void assertFloatEquals(float result, float expected) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
}
}
- public static void assertDoubleEquals(double result, double expected) {
+ static void assertDoubleEquals(double result, double expected) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
}
@@ -746,6 +777,8 @@ public class Main {
assertFloatEquals(test24(), 8.0f);
testFinalizableByForcingGc();
assertIntEquals($noinline$testHSelect(true), 0xdead);
+ int[] array = {2, 5, 9, -1, -3, 10, 8, 4};
+ assertIntEquals(sumWithinRange(array, 1, 5), 11);
}
static boolean sFlag;
diff --git a/test/594-checker-array-alias/expected.txt b/test/594-checker-array-alias/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/594-checker-array-alias/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/594-checker-array-alias/info.txt b/test/594-checker-array-alias/info.txt
new file mode 100644
index 0000000000..57c6de541f
--- /dev/null
+++ b/test/594-checker-array-alias/info.txt
@@ -0,0 +1 @@
+Tests on array parameters with and without alias.
diff --git a/test/594-checker-array-alias/src/Main.java b/test/594-checker-array-alias/src/Main.java
new file mode 100644
index 0000000000..5ece2e295e
--- /dev/null
+++ b/test/594-checker-array-alias/src/Main.java
@@ -0,0 +1,255 @@
+/*
+ * 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.util.Arrays;
+
+//
+// Test on array parameters with or without potential aliasing.
+//
+public class Main {
+
+ //
+ // Cross-over on parameters with potential aliasing on parameters.
+ // The arrays a and b may point to the same memory, which (without
+ // further runtime tests) prevents hoisting the seemingly invariant
+ // array reference.
+ //
+
+ /// CHECK-START: void Main.CrossOverLoop1(int[], int[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.CrossOverLoop1(int[], int[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void CrossOverLoop1(int a[], int b[]) {
+ b[20] = 99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = b[20] - 7;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoop2(float[], float[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.CrossOverLoop2(float[], float[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void CrossOverLoop2(float a[], float b[]) {
+ b[20] = 99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = b[20] - 7;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoop3(long[], long[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.CrossOverLoop3(long[], long[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void CrossOverLoop3(long a[], long b[]) {
+ b[20] = 99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = b[20] - 7;
+ }
+ }
+
+ /// CHECK-START: void Main.CrossOverLoop4(double[], double[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.CrossOverLoop4(double[], double[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void CrossOverLoop4(double a[], double b[]) {
+ b[20] = 99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = b[20] - 7;
+ }
+ }
+
+ //
+ // False cross-over on parameters. Parameters have same width (which used to
+ // cause a false type aliasing in an older version of the compiler), but since
+ // the types are different cannot be aliased. Thus, the invariant array
+ // reference can be hoisted.
+ //
+
+ /// CHECK-START: void Main.FalseCrossOverLoop1(int[], float[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.FalseCrossOverLoop1(int[], float[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void FalseCrossOverLoop1(int a[], float b[]) {
+ b[20] = -99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = (int) b[20] - 7;
+ }
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop2(float[], int[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.FalseCrossOverLoop2(float[], int[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void FalseCrossOverLoop2(float a[], int b[]) {
+ b[20] = -99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = b[20] - 7;
+ }
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop3(long[], double[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.FalseCrossOverLoop3(long[], double[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void FalseCrossOverLoop3(long a[], double b[]) {
+ b[20] = -99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = (long) b[20] - 7;
+ }
+ }
+
+ /// CHECK-START: void Main.FalseCrossOverLoop4(double[], long[]) licm (before)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.FalseCrossOverLoop4(double[], long[]) licm (after)
+ /// CHECK-DAG: ArraySet loop:none
+ /// CHECK-DAG: ArrayGet loop:none
+ /// CHECK-DAG: ArraySet loop:{{B\d+}}
+ private static void FalseCrossOverLoop4(double a[], long b[]) {
+ b[20] = -99;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = b[20] - 7;
+ }
+ }
+
+ //
+ // Main driver and testers.
+ //
+
+ public static void main(String[] args) {
+ int[] aI = new int[100];
+ float[] aF = new float[100];
+ long[] aJ = new long[100];
+ double[] aD = new double[100];
+
+ // Type I.
+ CrossOverLoop1(aI, aI);
+ for (int i = 0; i < aI.length; i++) {
+ expectEquals(i <= 20 ? 92 : 85, aI[i]);
+ }
+ // Type F.
+ CrossOverLoop2(aF, aF);
+ for (int i = 0; i < aF.length; i++) {
+ expectEquals(i <= 20 ? 92 : 85, aF[i]);
+ }
+ // Type J.
+ CrossOverLoop3(aJ, aJ);
+ for (int i = 0; i < aJ.length; i++) {
+ expectEquals(i <= 20 ? 92 : 85, aJ[i]);
+ }
+ // Type D.
+ CrossOverLoop4(aD, aD);
+ for (int i = 0; i < aD.length; i++) {
+ expectEquals(i <= 20 ? 92 : 85, aD[i]);
+ }
+
+ // Type I vs F.
+ FalseCrossOverLoop1(aI, aF);
+ for (int i = 0; i < aI.length; i++) {
+ expectEquals(-106, aI[i]);
+ }
+ // Type F vs I.
+ FalseCrossOverLoop2(aF, aI);
+ for (int i = 0; i < aF.length; i++) {
+ expectEquals(-106, aF[i]);
+ }
+ // Type J vs D.
+ FalseCrossOverLoop3(aJ, aD);
+ for (int i = 0; i < aJ.length; i++) {
+ expectEquals(-106, aJ[i]);
+ }
+ // Type D vs J.
+ FalseCrossOverLoop4(aD, aJ);
+ for (int i = 0; i < aD.length; i++) {
+ expectEquals(-106, aD[i]);
+ }
+
+ // Real-world example where incorrect type assignment could introduce a bug.
+ // The library sorting algorithm is heavy on array reads and writes, and
+ // assigning the wrong J/D type to one of these would introduce errors.
+ for (int i = 0; i < aD.length; i++) {
+ aD[i] = aD.length - i - 1;
+ }
+ Arrays.sort(aD);
+ for (int i = 0; i < aD.length; i++) {
+ expectEquals((double) i, aD[i]);
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(double expected, double result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/525-checker-arrays-and-fields/expected.txt b/test/594-checker-irreducible-linorder/expected.txt
index e69de29bb2..e69de29bb2 100644
--- a/test/525-checker-arrays-and-fields/expected.txt
+++ b/test/594-checker-irreducible-linorder/expected.txt
diff --git a/test/594-checker-irreducible-linorder/info.txt b/test/594-checker-irreducible-linorder/info.txt
new file mode 100644
index 0000000000..a1783f8ec1
--- /dev/null
+++ b/test/594-checker-irreducible-linorder/info.txt
@@ -0,0 +1,2 @@
+Regression test for a failing DCHECK in SSA liveness analysis in the presence
+of irreducible loops.
diff --git a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000000..8e01084841
--- /dev/null
+++ b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
@@ -0,0 +1,64 @@
+# Copyright (C) 2016 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 LIrreducibleLoop;
+.super Ljava/lang/Object;
+
+# Test case where liveness analysis produces linear order where loop blocks are
+# not adjacent.
+
+## CHECK-START: int IrreducibleLoop.liveness(boolean, boolean, boolean, int) builder (after)
+## CHECK-DAG: Add loop:none
+## CHECK-DAG: Mul loop:<<Loop:B\d+>>
+## CHECK-DAG: Not loop:<<Loop>>
+
+## CHECK-START: int IrreducibleLoop.liveness(boolean, boolean, boolean, int) liveness (after)
+## CHECK-DAG: Add liveness:<<LPreEntry:\d+>>
+## CHECK-DAG: Mul liveness:<<LHeader:\d+>>
+## CHECK-DAG: Not liveness:<<LBackEdge:\d+>>
+## CHECK-EVAL: (<<LHeader>> < <<LPreEntry>>) and (<<LPreEntry>> < <<LBackEdge>>)
+
+.method public static liveness(ZZZI)I
+ .registers 10
+ const/16 v0, 42
+
+ if-eqz p0, :header
+
+ :pre_entry
+ add-int/2addr p3, p3
+ invoke-static {v0}, Ljava/lang/System;->exit(I)V
+ goto :body1
+
+ :header
+ mul-int/2addr p3, p3
+ if-eqz p1, :body2
+
+ :body1
+ goto :body_merge
+
+ :body2
+ invoke-static {v0}, Ljava/lang/System;->exit(I)V
+ goto :body_merge
+
+ :body_merge
+ if-eqz p2, :exit
+
+ :back_edge
+ not-int p3, p3
+ goto :header
+
+ :exit
+ return p3
+
+.end method
diff --git a/test/594-checker-irreducible-linorder/src/Main.java b/test/594-checker-irreducible-linorder/src/Main.java
new file mode 100644
index 0000000000..38b2ab4384
--- /dev/null
+++ b/test/594-checker-irreducible-linorder/src/Main.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) {
+ // Nothing to run. This regression test merely makes sure the smali test
+ // case successfully compiles.
+ }
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index c2a9a31aeb..11150c296d 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -66,4 +66,5 @@ b/27799205 (3)
b/27799205 (4)
b/27799205 (5)
b/27799205 (6)
+b/28187158
Done!
diff --git a/test/800-smali/smali/b_28187158.smali b/test/800-smali/smali/b_28187158.smali
new file mode 100644
index 0000000000..14d5cec7ee
--- /dev/null
+++ b/test/800-smali/smali/b_28187158.smali
@@ -0,0 +1,12 @@
+.class public LB28187158;
+
+# Regression test for iget with wrong classes.
+
+.super Ljava/lang/Object;
+
+.method public static run(Ljava/lang/Integer;)V
+ .registers 2
+ iget v0, p0, Ljava/lang/System;->in:Ljava/io/InputStream;
+ return-void
+.end method
+
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 2001cb4abb..c883b7f0f5 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -174,6 +174,8 @@ public class Main {
testCases.add(new TestCase("b/27799205 (5)", "B27799205Helper", "run5", null,
new VerifyError(), null));
testCases.add(new TestCase("b/27799205 (6)", "B27799205Helper", "run6", null, null, null));
+ testCases.add(new TestCase("b/28187158", "B28187158", "run", new Object[] { null} ,
+ new VerifyError(), null));
}
public void runTests() {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 1edc5993eb..f3cda479db 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -235,8 +235,11 @@ ifdef dist_goal
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES))
endif
+# 147-stripped-dex-fallback isn't supported on device because --strip-dex
+# requires the zip command.
# 569-checker-pattern-replacement tests behaviour present only on host.
TEST_ART_BROKEN_TARGET_TESTS := \
+ 147-stripped-dex-fallback \
569-checker-pattern-replacement
ifneq (,$(filter target,$(TARGET_TYPES)))
@@ -287,6 +290,7 @@ TEST_ART_BROKEN_PREBUILD_RUN_TESTS :=
# 529 and 555: b/27784033
TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
117-nopatchoat \
+ 147-stripped-dex-fallback \
554-jit-profile-file \
529-checker-unresolved \
555-checker-regression-x86const
@@ -375,6 +379,7 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREB
# All these tests check that we have sane behavior if we don't have a patchoat or dex2oat.
# Therefore we shouldn't run them in situations where we actually don't have these since they
# explicitly test for them. These all also assume we have an image.
+# 147-stripped-dex-fallback is disabled because it requires --prebuild.
# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
116-nodex2oat \
@@ -383,6 +388,7 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \
119-noimage-patchoat \
137-cfi \
138-duplicate-classes-check2 \
+ 147-stripped-dex-fallback \
554-jit-profile-file
# This test fails without an image.
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 28a99de099..d61fc8f8fc 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -37,6 +37,7 @@ PATCHOAT=""
PREBUILD="y"
QUIET="n"
RELOCATE="y"
+STRIP_DEX="n"
SECONDARY_DEX=""
TIME_OUT="gdb" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
# Value in seconds
@@ -118,6 +119,9 @@ while true; do
elif [ "x$1" = "x--prebuild" ]; then
PREBUILD="y"
shift
+ elif [ "x$1" = "x--strip-dex" ]; then
+ STRIP_DEX="y"
+ shift
elif [ "x$1" = "x--host" ]; then
HOST="y"
ANDROID_ROOT="$ANDROID_HOST_OUT"
@@ -380,6 +384,7 @@ fi
dex2oat_cmdline="true"
mkdir_cmdline="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
+strip_cmdline="true"
# Pick a base that will force the app image to get relocated.
app_image="--base=0x4000 --app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art"
@@ -409,6 +414,10 @@ if [ "$PREBUILD" = "y" ]; then
fi
fi
+if [ "$STRIP_DEX" = "y" ]; then
+ strip_cmdline="zip --quiet --delete $DEX_LOCATION/$TEST_NAME.jar classes.dex"
+fi
+
DALVIKVM_ISA_FEATURES_ARGS=""
if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
@@ -478,6 +487,7 @@ if [ "$HOST" = "n" ]; then
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
export PATH=$ANDROID_ROOT/bin:$PATH && \
$dex2oat_cmdline && \
+ $strip_cmdline && \
$dalvikvm_cmdline"
cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
@@ -548,13 +558,7 @@ else
fi
if [ "$DEV_MODE" = "y" ]; then
- if [ "$PREBUILD" = "y" ]; then
- echo "$mkdir_cmdline && $dex2oat_cmdline && $cmdline"
- elif [ "$RELOCATE" = "y" ]; then
- echo "$mkdir_cmdline && $cmdline"
- else
- echo $cmdline
- fi
+ echo "$mkdir_cmdline && $dex2oat_cmdline && $strip_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
@@ -562,6 +566,7 @@ else
rm -rf ${DEX_LOCATION}/dalvik-cache/
$mkdir_cmdline || exit 1
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+ $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
# For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use
# the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
diff --git a/test/run-test b/test/run-test
index 013fc63e83..fc57d0914f 100755
--- a/test/run-test
+++ b/test/run-test
@@ -190,6 +190,9 @@ while true; do
run_args="${run_args} --prebuild"
prebuild_mode="yes"
shift;
+ elif [ "x$1" = "x--strip-dex" ]; then
+ run_args="${run_args} --strip-dex"
+ shift;
elif [ "x$1" = "x--debuggable" ]; then
run_args="${run_args} -Xcompiler-option --debuggable"
debuggable="yes"
@@ -449,7 +452,7 @@ if [ "$runtime" = "dalvik" ]; then
if [ "$target_mode" = "no" ]; then
framework="${ANDROID_PRODUCT_OUT}/system/framework"
bpath="${framework}/core-libart.jar:${framework}/core-oj.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
- run_args="${run_args} --boot -Xbootclasspath:${bpath}"
+ run_args="${run_args} --boot --runtime-option -Xbootclasspath:${bpath}"
else
true # defaults to using target BOOTCLASSPATH
fi
@@ -571,6 +574,7 @@ if [ "$usage" = "yes" ]; then
echo " --prebuild Run dex2oat on the files before starting test. (default)"
echo " --no-prebuild Do not run dex2oat on the files before starting"
echo " the test."
+ echo " --strip-dex Strip the dex files before starting test."
echo " --relocate Force the use of relocating in the test, making"
echo " the image and oat files be relocated to a random"
echo " address before running. (default)"