Merge "Add weight to compiled/interpreter transitions."
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index a771cc1..e7fa4e4 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -187,7 +187,8 @@
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 87832a2..d69c410 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -67,7 +67,7 @@
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 @@
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 @@
// 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 @@
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 144d58d..0020f7b 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -339,7 +339,7 @@
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 efe4c06..e8e6b68 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -899,7 +899,7 @@
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 @@
#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 @@
__ 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 ec46a34..422963e 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -83,7 +83,7 @@
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 @@
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 @@
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 @@
}
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 @@
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 5e6fec8..435a869 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -275,7 +275,7 @@
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 4e15cdd..9785a2e 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -271,7 +271,7 @@
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 69a6253..1739eec 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -361,7 +361,7 @@
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 d7ce7c6..3a211c5 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -346,7 +346,7 @@
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 f5e49c2..12cb826 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -897,12 +897,12 @@
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 @@
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 @@
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 7a1e06b..5a0b89c 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -79,8 +79,15 @@
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 @@
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 d446539..2a62643 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -169,13 +169,11 @@
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 @@
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 e1977b1..ac7ed86 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -480,7 +480,7 @@
// 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 @@
}
// 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 @@
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 dc5a8fa..1ea2247 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1551,21 +1551,21 @@
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 @@
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 @@
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 @@
// 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 @@
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 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 @@
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 @@
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 @@
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 9bbc354..b01bc1c 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -148,19 +148,19 @@
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 @@
"||||||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 @@
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 eeadbeb..2fe2f20 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -391,6 +391,9 @@
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 97f2aee..719feec 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -969,6 +969,38 @@
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 @@
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 eb851f9..eb5112b 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -32,8 +32,8 @@
#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::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 03ae996..c4e5de7 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -62,7 +62,25 @@
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 @@
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 @@
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 4ea85a2..f70fe04 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -306,8 +306,10 @@
// 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 4b3c03e..bd1af04 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -694,7 +694,7 @@
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 685b9b6..e46f9cd 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -12989,7 +12989,7 @@
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 a1360e0..62dce6e 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -67,7 +67,7 @@
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 @@
.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 @@
#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 @@
/* 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 @@
#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 @@
*
*/
.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 @@
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 @@
.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 @@
/* 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 @@
/* 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 @@
/* 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 @@
/* 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 @@
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 @@
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 @@
*/
/* 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 @@
*/
/* 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 @@
*/
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*/
/* 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 @@
*/
/* 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 @@
*/
/* 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 @@
*/
/* 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 @@
*/
/* 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 @@
*/
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
#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 @@
* 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 @@
/* 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.
+ *
*/
-MterpCheckSuspendAndContinue:
+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.
+ */
+#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)
-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 @@
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 df10ff0..fa03e78 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -238,7 +238,7 @@
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 a16050b..6601483 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 69b2371..d992956 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -65,6 +65,12 @@
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 573256b..54d0cb1 100644
--- a/runtime/interpreter/mterp/x86_64/footer.S
+++ b/runtime/interpreter/mterp/x86_64/footer.S
@@ -71,7 +71,7 @@
#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 @@
* 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 @@
/* 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.
+ *
*/
-MterpCheckSuspendAndContinue:
+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.
+ */
+#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)
-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.
*/
@@ -165,7 +260,28 @@
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 eb84ea1..7699fc4 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -60,7 +60,7 @@
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 @@
.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 @@
#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 @@
/* 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 @@
#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 @@
*
*/
.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 8baedea..5f77a97 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 c4fc976..9749901 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 8cb9a5c..77688e0 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 4ecdacd..29d777b 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 a0d0faf..df43efe 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 964d20a..176c954 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 6be37f9..4819833 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 d0a14fd..33db878 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 cb0acb7..fdf5a50 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 14f4f8a..07e0e53 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 46a5753..6a12df3 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 92e3506..822b2e8 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 f2d6e04..288eb96 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 38d9a5e..d39e6c4 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 22ed990..8095c25 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 0051407..fb8ae6a 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 02e05c5..fe388cd 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -484,6 +484,101 @@
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 3312701..3b5bee8 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 3a71bba..d04d2de 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -170,6 +170,7 @@
: 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 cdf468c..0e7f7f3 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -33,8 +33,8 @@
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 78e372a..3f95772 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -492,10 +492,21 @@
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 94f6345..3846605 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -364,7 +364,7 @@
// 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 aff9b61..1f33651 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -116,7 +116,18 @@
/* 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 d5319fd..647578e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4577,8 +4577,18 @@
// 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 f41ff2a..50bbe51 100644
--- a/test/098-ddmc/src/Main.java
+++ b/test/098-ddmc/src/Main.java
@@ -44,7 +44,12 @@
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 e4988c9..d878e69 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -32,8 +32,8 @@
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 0000000..af5626b
--- /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 0000000..72a2ca8
--- /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 0000000..e594010
--- /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 0000000..1ef6289
--- /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 3e16abf..0000000
--- 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-and-fields/src/Main.java b/test/525-checker-arrays-and-fields/src/Main.java
deleted file mode 100644
index a635a51..0000000
--- a/test/525-checker-arrays-and-fields/src/Main.java
+++ /dev/null
@@ -1,1099 +0,0 @@
-/*
- * 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 and instance 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;
-
- //
- // Instance fields.
- //
-
- private boolean mZ;
- private byte mB;
- private char mC;
- private short mS;
- private int mI;
- private long mJ;
- private float mF;
- private double mD;
- private Object mL;
-
- //
- // Instance arrays.
- //
-
- private boolean[] mArrZ;
- private byte[] mArrB;
- private char[] mArrC;
- private short[] mArrS;
- private int[] mArrI;
- private long[] mArrJ;
- private float[] mArrF;
- private double[] mArrD;
- 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.
- //
-
- /// CHECK-START: void Main.InvLoopZ() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopZ() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopZ() {
- for (int i = 0; i < mArrZ.length; i++) {
- mArrZ[i] = mZ;
- }
- }
-
- /// CHECK-START: void Main.InvLoopB() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopB() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopB() {
- for (int i = 0; i < mArrB.length; i++) {
- mArrB[i] = mB;
- }
- }
-
- /// CHECK-START: void Main.InvLoopC() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopC() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopC() {
- for (int i = 0; i < mArrC.length; i++) {
- mArrC[i] = mC;
- }
- }
-
- /// CHECK-START: void Main.InvLoopS() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopS() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopS() {
- for (int i = 0; i < mArrS.length; i++) {
- mArrS[i] = mS;
- }
- }
-
- /// CHECK-START: void Main.InvLoopI() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopI() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopI() {
- for (int i = 0; i < mArrI.length; i++) {
- mArrI[i] = mI;
- }
- }
-
- /// CHECK-START: void Main.InvLoopJ() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopJ() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopJ() {
- for (int i = 0; i < mArrJ.length; i++) {
- mArrJ[i] = mJ;
- }
- }
-
- /// CHECK-START: void Main.InvLoopF() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopF() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopF() {
- for (int i = 0; i < mArrF.length; i++) {
- mArrF[i] = mF;
- }
- }
-
- /// CHECK-START: void Main.InvLoopD() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopD() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopD() {
- for (int i = 0; i < mArrD.length; i++) {
- mArrD[i] = mD;
- }
- }
-
- /// CHECK-START: void Main.InvLoopL() licm (before)
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
- /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
-
- /// CHECK-START: void Main.InvLoopL() licm (after)
- /// CHECK-DAG: InstanceFieldGet loop:none
- /// CHECK-DAG: InstanceFieldGet loop:none
-
- private void InvLoopL() {
- for (int i = 0; i < mArrL.length; i++) {
- mArrL[i] = mL;
- }
- }
-
- //
- // Loops on instance arrays with variant instance field references.
- // Incorrect hoisting is detected by incorrect outcome.
- //
-
- private void VarLoopZ() {
- for (int i = 0; i < mArrZ.length; i++) {
- mArrZ[i] = mZ;
- if (i == 10)
- mZ = !mZ;
- }
- }
-
- private void VarLoopB() {
- for (int i = 0; i < mArrB.length; i++) {
- mArrB[i] = mB;
- if (i == 10)
- mB++;
- }
- }
-
- private void VarLoopC() {
- for (int i = 0; i < mArrC.length; i++) {
- mArrC[i] = mC;
- if (i == 10)
- mC++;
- }
- }
-
- private void VarLoopS() {
- for (int i = 0; i < mArrS.length; i++) {
- mArrS[i] = mS;
- if (i == 10)
- mS++;
- }
- }
-
- private void VarLoopI() {
- for (int i = 0; i < mArrI.length; i++) {
- mArrI[i] = mI;
- if (i == 10)
- mI++;
- }
- }
-
- private void VarLoopJ() {
- for (int i = 0; i < mArrJ.length; i++) {
- mArrJ[i] = mJ;
- if (i == 10)
- mJ++;
- }
- }
-
- private void VarLoopF() {
- for (int i = 0; i < mArrF.length; i++) {
- mArrF[i] = mF;
- if (i == 10)
- mF++;
- }
- }
-
- private void VarLoopD() {
- for (int i = 0; i < mArrD.length; i++) {
- mArrD[i] = mD;
- if (i == 10)
- mD++;
- }
- }
-
- private void VarLoopL() {
- for (int i = 0; i < mArrL.length; i++) {
- mArrL[i] = mL;
- if (i == 10)
- mL = anotherObject;
- }
- }
-
- //
- // Loops on instance 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: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopZ() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopZ() {
- for (int i = 0; i < mArrZ.length; i++) {
- mArrZ[i] = !mArrZ[20];
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopB() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopB() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopB() {
- for (int i = 0; i < mArrB.length; i++) {
- mArrB[i] = (byte)(mArrB[20] + 2);
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopC() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopC() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopC() {
- for (int i = 0; i < mArrC.length; i++) {
- mArrC[i] = (char)(mArrC[20] + 2);
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopS() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopS() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopS() {
- for (int i = 0; i < mArrS.length; i++) {
- mArrS[i] = (short)(mArrS[20] + 2);
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopI() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopI() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopI() {
- for (int i = 0; i < mArrI.length; i++) {
- mArrI[i] = mArrI[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopJ() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopJ() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopJ() {
- for (int i = 0; i < mArrJ.length; i++) {
- mArrJ[i] = mArrJ[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopF() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopF() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopF() {
- for (int i = 0; i < mArrF.length; i++) {
- mArrF[i] = mArrF[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopD() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopD() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopD() {
- for (int i = 0; i < mArrD.length; i++) {
- mArrD[i] = mArrD[20] + 2;
- }
- }
-
- /// CHECK-START: void Main.CrossOverLoopL() licm (before)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.CrossOverLoopL() licm (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private void CrossOverLoopL() {
- for (int i = 0; i < mArrL.length; i++) {
- mArrL[i] = (mArrL[20] == anObject) ? anotherObject : anObject;
- }
- }
-
- //
- // Driver and testers.
- //
-
- public static void main(String[] args) {
- DoStaticTests();
- new Main().DoInstanceTests();
- }
-
- 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]);
- }
- // Type L.
- sL = anObject;
- sArrL = new Object[100];
- SInvLoopL();
- for (int i = 0; i < sArrL.length; i++) {
- expectEquals(anObject, sArrL[i]);
- }
- SVarLoopL();
- for (int i = 0; i < sArrL.length; i++) {
- expectEquals(i <= 10 ? anObject : anotherObject, sArrL[i]);
- }
- SCrossOverLoopL();
- for (int i = 0; i < sArrL.length; i++) {
- expectEquals(i <= 20 ? anObject : anotherObject, sArrL[i]);
- }
- }
-
- private void DoInstanceTests() {
- // Type Z.
- mZ = true;
- mArrZ = new boolean[100];
- InvLoopZ();
- for (int i = 0; i < mArrZ.length; i++) {
- expectEquals(true, mArrZ[i]);
- }
- VarLoopZ();
- for (int i = 0; i < mArrZ.length; i++) {
- expectEquals(i <= 10, mArrZ[i]);
- }
- CrossOverLoopZ();
- for (int i = 0; i < mArrZ.length; i++) {
- expectEquals(i <= 20, mArrZ[i]);
- }
- // Type B.
- mB = 1;
- mArrB = new byte[100];
- InvLoopB();
- for (int i = 0; i < mArrB.length; i++) {
- expectEquals(1, mArrB[i]);
- }
- VarLoopB();
- for (int i = 0; i < mArrB.length; i++) {
- expectEquals(i <= 10 ? 1 : 2, mArrB[i]);
- }
- CrossOverLoopB();
- for (int i = 0; i < mArrB.length; i++) {
- expectEquals(i <= 20 ? 4 : 6, mArrB[i]);
- }
- // Type C.
- mC = 2;
- mArrC = new char[100];
- InvLoopC();
- for (int i = 0; i < mArrC.length; i++) {
- expectEquals(2, mArrC[i]);
- }
- VarLoopC();
- for (int i = 0; i < mArrC.length; i++) {
- expectEquals(i <= 10 ? 2 : 3, mArrC[i]);
- }
- CrossOverLoopC();
- for (int i = 0; i < mArrC.length; i++) {
- expectEquals(i <= 20 ? 5 : 7, mArrC[i]);
- }
- // Type S.
- mS = 3;
- mArrS = new short[100];
- InvLoopS();
- for (int i = 0; i < mArrS.length; i++) {
- expectEquals(3, mArrS[i]);
- }
- VarLoopS();
- for (int i = 0; i < mArrS.length; i++) {
- expectEquals(i <= 10 ? 3 : 4, mArrS[i]);
- }
- CrossOverLoopS();
- for (int i = 0; i < mArrS.length; i++) {
- expectEquals(i <= 20 ? 6 : 8, mArrS[i]);
- }
- // Type I.
- mI = 4;
- mArrI = new int[100];
- InvLoopI();
- for (int i = 0; i < mArrI.length; i++) {
- expectEquals(4, mArrI[i]);
- }
- VarLoopI();
- for (int i = 0; i < mArrI.length; i++) {
- expectEquals(i <= 10 ? 4 : 5, mArrI[i]);
- }
- CrossOverLoopI();
- for (int i = 0; i < mArrI.length; i++) {
- expectEquals(i <= 20 ? 7 : 9, mArrI[i]);
- }
- // Type J.
- mJ = 5;
- mArrJ = new long[100];
- InvLoopJ();
- for (int i = 0; i < mArrJ.length; i++) {
- expectEquals(5, mArrJ[i]);
- }
- VarLoopJ();
- for (int i = 0; i < mArrJ.length; i++) {
- expectEquals(i <= 10 ? 5 : 6, mArrJ[i]);
- }
- CrossOverLoopJ();
- for (int i = 0; i < mArrJ.length; i++) {
- expectEquals(i <= 20 ? 8 : 10, mArrJ[i]);
- }
- // Type F.
- mF = 6.0f;
- mArrF = new float[100];
- InvLoopF();
- for (int i = 0; i < mArrF.length; i++) {
- expectEquals(6, mArrF[i]);
- }
- VarLoopF();
- for (int i = 0; i < mArrF.length; i++) {
- expectEquals(i <= 10 ? 6 : 7, mArrF[i]);
- }
- CrossOverLoopF();
- for (int i = 0; i < mArrF.length; i++) {
- expectEquals(i <= 20 ? 9 : 11, mArrF[i]);
- }
- // Type D.
- mD = 7.0;
- mArrD = new double[100];
- InvLoopD();
- for (int i = 0; i < mArrD.length; i++) {
- expectEquals(7.0, mArrD[i]);
- }
- VarLoopD();
- for (int i = 0; i < mArrD.length; i++) {
- expectEquals(i <= 10 ? 7 : 8, mArrD[i]);
- }
- CrossOverLoopD();
- for (int i = 0; i < mArrD.length; i++) {
- expectEquals(i <= 20 ? 10 : 12, mArrD[i]);
- }
- // Type L.
- mL = anObject;
- mArrL = new Object[100];
- InvLoopL();
- for (int i = 0; i < mArrL.length; i++) {
- expectEquals(anObject, mArrL[i]);
- }
- VarLoopL();
- for (int i = 0; i < mArrL.length; i++) {
- expectEquals(i <= 10 ? anObject : anotherObject, mArrL[i]);
- }
- CrossOverLoopL();
- for (int i = 0; i < mArrL.length; i++) {
- expectEquals(i <= 20 ? anObject : anotherObject, mArrL[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-fields1/expected.txt b/test/525-checker-arrays-fields1/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /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 0000000..7d0a088
--- /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 0000000..ba0476a
--- /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 0000000..b0aad4d
--- /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 0000000..3464e54
--- /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-fields2/src/Main.java b/test/525-checker-arrays-fields2/src/Main.java
new file mode 100644
index 0000000..2aa40fc
--- /dev/null
+++ b/test/525-checker-arrays-fields2/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 instance field and array references in loops.
+//
+public class Main {
+
+ private static Object anObject = new Object();
+ private static Object anotherObject = new Object();
+
+ //
+ // Instance fields.
+ //
+
+ private boolean mZ;
+ private byte mB;
+ private char mC;
+ private short mS;
+ private int mI;
+ private long mJ;
+ private float mF;
+ private double mD;
+ private Object mL;
+
+ //
+ // Instance arrays.
+ //
+
+ private boolean[] mArrZ;
+ private byte[] mArrB;
+ private char[] mArrC;
+ private short[] mArrS;
+ private int[] mArrI;
+ private long[] mArrJ;
+ private float[] mArrF;
+ private double[] mArrD;
+ private Object[] mArrL;
+
+ //
+ // Loops on instance arrays with invariant instance field references.
+ // The checker is used to ensure hoisting occurred.
+ //
+
+ /// CHECK-START: void Main.InvLoopZ() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopZ() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopZ() {
+ for (int i = 0; i < mArrZ.length; i++) {
+ mArrZ[i] = mZ;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopB() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopB() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopB() {
+ for (int i = 0; i < mArrB.length; i++) {
+ mArrB[i] = mB;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopC() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopC() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopC() {
+ for (int i = 0; i < mArrC.length; i++) {
+ mArrC[i] = mC;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopS() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopS() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopS() {
+ for (int i = 0; i < mArrS.length; i++) {
+ mArrS[i] = mS;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopI() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopI() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopI() {
+ for (int i = 0; i < mArrI.length; i++) {
+ mArrI[i] = mI;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopJ() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopJ() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopJ() {
+ for (int i = 0; i < mArrJ.length; i++) {
+ mArrJ[i] = mJ;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopF() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopF() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopF() {
+ for (int i = 0; i < mArrF.length; i++) {
+ mArrF[i] = mF;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopD() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopD() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopD() {
+ for (int i = 0; i < mArrD.length; i++) {
+ mArrD[i] = mD;
+ }
+ }
+
+ /// CHECK-START: void Main.InvLoopL() licm (before)
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+ /// CHECK-DAG: InstanceFieldGet loop:{{B\d+}}
+
+ /// CHECK-START: void Main.InvLoopL() licm (after)
+ /// CHECK-DAG: InstanceFieldGet loop:none
+ /// CHECK-DAG: InstanceFieldGet loop:none
+
+ private void InvLoopL() {
+ for (int i = 0; i < mArrL.length; i++) {
+ mArrL[i] = mL;
+ }
+ }
+
+ //
+ // Loops on instance arrays with variant instance field references.
+ // Incorrect hoisting is detected by incorrect outcome.
+ //
+
+ private void VarLoopZ() {
+ for (int i = 0; i < mArrZ.length; i++) {
+ mArrZ[i] = mZ;
+ if (i == 10)
+ mZ = !mZ;
+ }
+ }
+
+ private void VarLoopB() {
+ for (int i = 0; i < mArrB.length; i++) {
+ mArrB[i] = mB;
+ if (i == 10)
+ mB++;
+ }
+ }
+
+ private void VarLoopC() {
+ for (int i = 0; i < mArrC.length; i++) {
+ mArrC[i] = mC;
+ if (i == 10)
+ mC++;
+ }
+ }
+
+ private void VarLoopS() {
+ for (int i = 0; i < mArrS.length; i++) {
+ mArrS[i] = mS;
+ if (i == 10)
+ mS++;
+ }
+ }
+
+ private void VarLoopI() {
+ for (int i = 0; i < mArrI.length; i++) {
+ mArrI[i] = mI;
+ if (i == 10)
+ mI++;
+ }
+ }
+
+ private void VarLoopJ() {
+ for (int i = 0; i < mArrJ.length; i++) {
+ mArrJ[i] = mJ;
+ if (i == 10)
+ mJ++;
+ }
+ }
+
+ private void VarLoopF() {
+ for (int i = 0; i < mArrF.length; i++) {
+ mArrF[i] = mF;
+ if (i == 10)
+ mF++;
+ }
+ }
+
+ private void VarLoopD() {
+ for (int i = 0; i < mArrD.length; i++) {
+ mArrD[i] = mD;
+ if (i == 10)
+ mD++;
+ }
+ }
+
+ private void VarLoopL() {
+ for (int i = 0; i < mArrL.length; i++) {
+ mArrL[i] = mL;
+ if (i == 10)
+ mL = anotherObject;
+ }
+ }
+
+ //
+ // Loops on instance 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 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;
+ }
+ }
+
+ //
+ // 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.
+ //
+
+ /// 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 void FalseCrossOverLoop1() {
+ mArrF[20] = -1;
+ for (int i = 0; i < mArrI.length; i++) {
+ mArrI[i] = (int) mArrF[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 void FalseCrossOverLoop2() {
+ mArrI[20] = -2;
+ for (int i = 0; i < mArrF.length; i++) {
+ mArrF[i] = mArrI[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 void FalseCrossOverLoop3() {
+ mArrD[20] = -3;
+ for (int i = 0; i < mArrJ.length; i++) {
+ mArrJ[i] = (long) mArrD[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 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;
+ mArrZ = new boolean[100];
+ InvLoopZ();
+ for (int i = 0; i < mArrZ.length; i++) {
+ expectEquals(true, mArrZ[i]);
+ }
+ VarLoopZ();
+ for (int i = 0; i < mArrZ.length; i++) {
+ expectEquals(i <= 10, mArrZ[i]);
+ }
+ CrossOverLoopZ();
+ for (int i = 0; i < mArrZ.length; i++) {
+ expectEquals(i <= 20, mArrZ[i]);
+ }
+ // Type B.
+ mB = 1;
+ mArrB = new byte[100];
+ InvLoopB();
+ for (int i = 0; i < mArrB.length; i++) {
+ expectEquals(1, mArrB[i]);
+ }
+ VarLoopB();
+ for (int i = 0; i < mArrB.length; i++) {
+ expectEquals(i <= 10 ? 1 : 2, mArrB[i]);
+ }
+ CrossOverLoopB();
+ for (int i = 0; i < mArrB.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrB[i]);
+ }
+ // Type C.
+ mC = 2;
+ mArrC = new char[100];
+ InvLoopC();
+ for (int i = 0; i < mArrC.length; i++) {
+ expectEquals(2, mArrC[i]);
+ }
+ VarLoopC();
+ for (int i = 0; i < mArrC.length; i++) {
+ expectEquals(i <= 10 ? 2 : 3, mArrC[i]);
+ }
+ CrossOverLoopC();
+ for (int i = 0; i < mArrC.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrC[i]);
+ }
+ // Type S.
+ mS = 3;
+ mArrS = new short[100];
+ InvLoopS();
+ for (int i = 0; i < mArrS.length; i++) {
+ expectEquals(3, mArrS[i]);
+ }
+ VarLoopS();
+ for (int i = 0; i < mArrS.length; i++) {
+ expectEquals(i <= 10 ? 3 : 4, mArrS[i]);
+ }
+ CrossOverLoopS();
+ for (int i = 0; i < mArrS.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrS[i]);
+ }
+ // Type I.
+ mI = 4;
+ mArrI = new int[100];
+ InvLoopI();
+ for (int i = 0; i < mArrI.length; i++) {
+ expectEquals(4, mArrI[i]);
+ }
+ VarLoopI();
+ for (int i = 0; i < mArrI.length; i++) {
+ expectEquals(i <= 10 ? 4 : 5, mArrI[i]);
+ }
+ CrossOverLoopI();
+ for (int i = 0; i < mArrI.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrI[i]);
+ }
+ // Type J.
+ mJ = 5;
+ mArrJ = new long[100];
+ InvLoopJ();
+ for (int i = 0; i < mArrJ.length; i++) {
+ expectEquals(5, mArrJ[i]);
+ }
+ VarLoopJ();
+ for (int i = 0; i < mArrJ.length; i++) {
+ expectEquals(i <= 10 ? 5 : 6, mArrJ[i]);
+ }
+ CrossOverLoopJ();
+ for (int i = 0; i < mArrJ.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrJ[i]);
+ }
+ // Type F.
+ mF = 6.0f;
+ mArrF = new float[100];
+ InvLoopF();
+ for (int i = 0; i < mArrF.length; i++) {
+ expectEquals(6, mArrF[i]);
+ }
+ VarLoopF();
+ for (int i = 0; i < mArrF.length; i++) {
+ expectEquals(i <= 10 ? 6 : 7, mArrF[i]);
+ }
+ CrossOverLoopF();
+ for (int i = 0; i < mArrF.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrF[i]);
+ }
+ // Type D.
+ mD = 7.0;
+ mArrD = new double[100];
+ InvLoopD();
+ for (int i = 0; i < mArrD.length; i++) {
+ expectEquals(7.0, mArrD[i]);
+ }
+ VarLoopD();
+ for (int i = 0; i < mArrD.length; i++) {
+ expectEquals(i <= 10 ? 7 : 8, mArrD[i]);
+ }
+ CrossOverLoopD();
+ for (int i = 0; i < mArrD.length; i++) {
+ expectEquals(i <= 20 ? 113 : 115, mArrD[i]);
+ }
+ // Type L.
+ mL = anObject;
+ mArrL = new Object[100];
+ InvLoopL();
+ for (int i = 0; i < mArrL.length; i++) {
+ expectEquals(anObject, mArrL[i]);
+ }
+ VarLoopL();
+ for (int i = 0; i < mArrL.length; i++) {
+ expectEquals(i <= 10 ? anObject : anotherObject, mArrL[i]);
+ }
+ CrossOverLoopL();
+ 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) {
+ 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/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 4d6ea06..89875d7 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -70,6 +70,10 @@
}
}
+interface Filter {
+ public boolean isValid(int i);
+}
+
public class Main {
/// CHECK-START: double Main.calcCircleArea(double) load_store_elimination (before)
@@ -78,7 +82,7 @@
/// 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 @@
}
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (before)
- /// CHECK: NewInstance
/// CHECK: StaticFieldGet
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
@@ -137,7 +140,6 @@
/// CHECK: InstanceFieldGet
/// CHECK-START: int Main.test3(TestClass) load_store_elimination (after)
- /// CHECK: NewInstance
/// CHECK: StaticFieldGet
/// CHECK: NewInstance
/// CHECK: InstanceFieldSet
@@ -149,9 +151,6 @@
// 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 @@
/// 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 @@
/// 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 @@
/// 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 @@
/// 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 @@
/// 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 @@
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 @@
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 0000000..b0aad4d
--- /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 0000000..57c6de5
--- /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 0000000..5ece2e2
--- /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
similarity index 100%
rename from test/525-checker-arrays-and-fields/expected.txt
rename to 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 0000000..a1783f8
--- /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 0000000..8e01084
--- /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 0000000..38b2ab4
--- /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 c2a9a31..11150c2 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -66,4 +66,5 @@
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 0000000..14d5cec
--- /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 2001cb4..c883b7f 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -174,6 +174,8 @@
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 1edc599..f3cda47 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -235,8 +235,11 @@
$(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 @@
# 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 @@
# 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 @@
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 28a99de..d61fc8f 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -37,6 +37,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 013fc63..fc57d09 100755
--- a/test/run-test
+++ b/test/run-test
@@ -190,6 +190,9 @@
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 [ "$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 @@
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)"