diff options
Diffstat (limited to 'compiler/optimizing')
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 3 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.h | 9 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 7 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.h | 21 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86_64.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_builder.cc | 8 | ||||
| -rw-r--r-- | compiler/optimizing/licm.cc | 15 | ||||
| -rw-r--r-- | compiler/optimizing/licm_test.cc | 12 | ||||
| -rw-r--r-- | compiler/optimizing/load_store_elimination.cc | 23 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 69 | ||||
| -rw-r--r-- | compiler/optimizing/side_effects_test.cc | 34 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_builder.cc | 3 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_liveness_analysis.h | 56 |
17 files changed, 165 insertions, 105 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index a771cc1567..e7fa4e472b 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -187,7 +187,8 @@ class DisassemblyScope { void CodeGenerator::GenerateSlowPaths() { size_t code_start = 0; - for (SlowPathCode* slow_path : slow_paths_) { + for (const std::unique_ptr<SlowPathCode>& slow_path_unique_ptr : slow_paths_) { + SlowPathCode* slow_path = slow_path_unique_ptr.get(); current_slow_path_ = slow_path; if (disasm_info_ != nullptr) { code_start = GetAssembler()->CodeSize(); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 87832a2d9f..d69c41055b 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -67,7 +67,7 @@ class CodeAllocator { DISALLOW_COPY_AND_ASSIGN(CodeAllocator); }; -class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> { +class SlowPathCode : public DeletableArenaObject<kArenaAllocSlowPaths> { public: explicit SlowPathCode(HInstruction* instruction) : instruction_(instruction) { for (size_t i = 0; i < kMaximumNumberOfExpectedRegisters; ++i) { @@ -205,7 +205,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { virtual const Assembler& GetAssembler() const = 0; virtual size_t GetWordSize() const = 0; virtual size_t GetFloatingPointSpillSlotSize() const = 0; - virtual uintptr_t GetAddressOf(HBasicBlock* block) const = 0; + virtual uintptr_t GetAddressOf(HBasicBlock* block) = 0; void InitializeCodeGeneration(size_t number_of_spill_slots, size_t maximum_number_of_live_core_registers, size_t maximum_number_of_live_fpu_registers, @@ -298,8 +298,9 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // save live registers, which may be needed by the runtime to set catch phis. bool IsImplicitNullCheckAllowed(HNullCheck* null_check) const; + // TODO: Avoid creating the `std::unique_ptr` here. void AddSlowPath(SlowPathCode* slow_path) { - slow_paths_.push_back(slow_path); + slow_paths_.push_back(std::unique_ptr<SlowPathCode>(slow_path)); } void BuildStackMaps(MemoryRegion region, const DexFile::CodeItem& code_item); @@ -617,7 +618,7 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { HGraph* const graph_; const CompilerOptions& compiler_options_; - ArenaVector<SlowPathCode*> slow_paths_; + ArenaVector<std::unique_ptr<SlowPathCode>> slow_paths_; // The current slow-path that we're generating code for. SlowPathCode* current_slow_path_; diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 144d58d85a..0020f7b4f4 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -339,7 +339,7 @@ class CodeGeneratorARM : public CodeGenerator { return assembler_; } - uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE { return GetLabelOf(block)->Position(); } diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index efe4c06d3f..e8e6b68975 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -899,7 +899,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, callee_saved_fp_registers.list(), compiler_options, stats), - block_labels_(nullptr), + block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), location_builder_(graph, this), instruction_visitor_(graph, this), @@ -928,7 +928,7 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, #define __ GetVIXLAssembler()-> void CodeGeneratorARM64::EmitJumpTables() { - for (auto jump_table : jump_tables_) { + for (auto&& jump_table : jump_tables_) { jump_table->EmitTable(this); } } @@ -4784,8 +4784,7 @@ void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_inst __ B(codegen_->GetLabelOf(default_block)); } } else { - JumpTableARM64* jump_table = new (GetGraph()->GetArena()) JumpTableARM64(switch_instr); - codegen_->AddJumpTable(jump_table); + JumpTableARM64* jump_table = codegen_->CreateJumpTable(switch_instr); UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index ec46a34615..422963e7d0 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -83,7 +83,7 @@ class SlowPathCodeARM64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64); }; -class JumpTableARM64 : public ArenaObject<kArenaAllocSwitchTable> { +class JumpTableARM64 : public DeletableArenaObject<kArenaAllocSwitchTable> { public: explicit JumpTableARM64(HPackedSwitch* switch_instr) : switch_instr_(switch_instr), table_start_() {} @@ -352,8 +352,9 @@ class CodeGeneratorARM64 : public CodeGenerator { void Bind(HBasicBlock* block) OVERRIDE; - vixl::Label* GetLabelOf(HBasicBlock* block) const { - return CommonGetLabelOf<vixl::Label>(block_labels_, block); + vixl::Label* GetLabelOf(HBasicBlock* block) { + block = FirstNonEmptyBlock(block); + return &(block_labels_[block->GetBlockId()]); } size_t GetWordSize() const OVERRIDE { @@ -365,7 +366,7 @@ class CodeGeneratorARM64 : public CodeGenerator { return kArm64WordSize; } - uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE { vixl::Label* block_entry_label = GetLabelOf(block); DCHECK(block_entry_label->IsBound()); return block_entry_label->location(); @@ -413,11 +414,12 @@ class CodeGeneratorARM64 : public CodeGenerator { } void Initialize() OVERRIDE { - block_labels_ = CommonInitializeLabels<vixl::Label>(); + block_labels_.resize(GetGraph()->GetBlocks().size()); } - void AddJumpTable(JumpTableARM64* jump_table) { - jump_tables_.push_back(jump_table); + JumpTableARM64* CreateJumpTable(HPackedSwitch* switch_instr) { + jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARM64(switch_instr)); + return jump_tables_.back().get(); } void Finalize(CodeAllocator* allocator) OVERRIDE; @@ -616,9 +618,10 @@ class CodeGeneratorARM64 : public CodeGenerator { void EmitJumpTables(); // Labels for each block that will be compiled. - vixl::Label* block_labels_; // Indexed by block id. + // We use a deque so that the `vixl::Label` objects do not move in memory. + ArenaDeque<vixl::Label> block_labels_; // Indexed by block id. vixl::Label frame_entry_label_; - ArenaVector<JumpTableARM64*> jump_tables_; + ArenaVector<std::unique_ptr<JumpTableARM64>> jump_tables_; LocationsBuilderARM64 location_builder_; InstructionCodeGeneratorARM64 instruction_visitor_; diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 5e6fec8cf5..435a869368 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -275,7 +275,7 @@ class CodeGeneratorMIPS : public CodeGenerator { size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMipsDoublewordSize; } - uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE { return assembler_.GetLabelLocation(GetLabelOf(block)); } diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 4e15cdd7b5..9785a2e8a8 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -271,7 +271,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMips64DoublewordSize; } - uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE { return assembler_.GetLabelLocation(GetLabelOf(block)); } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 69a625306f..1739eec4c1 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -361,7 +361,7 @@ class CodeGeneratorX86 : public CodeGenerator { return assembler_; } - uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE { return GetLabelOf(block)->Position(); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index d7ce7c649f..3a211c5027 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -346,7 +346,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { return &move_resolver_; } - uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + uintptr_t GetAddressOf(HBasicBlock* block) OVERRIDE { return GetLabelOf(block)->Position(); } diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index f5e49c2235..12cb826395 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -897,12 +897,12 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); bool finalizable; - bool can_throw = NeedsAccessCheck(type_index, dex_cache, &finalizable); + bool needs_access_check = NeedsAccessCheck(type_index, dex_cache, &finalizable); // Only the non-resolved entrypoint handles the finalizable class case. If we // need access checks, then we haven't resolved the method and the class may // again be finalizable. - QuickEntrypointEnum entrypoint = (finalizable || can_throw) + QuickEntrypointEnum entrypoint = (finalizable || needs_access_check) ? kQuickAllocObject : kQuickAllocObjectInitialized; @@ -917,7 +917,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) outer_dex_file, IsOutermostCompilingClass(type_index), dex_pc, - /*needs_access_check*/ can_throw, + needs_access_check, compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index)); AppendInstruction(load_class); @@ -933,7 +933,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) dex_pc, type_index, *dex_compilation_unit_->GetDexFile(), - can_throw, + needs_access_check, finalizable, entrypoint)); return true; diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7a1e06b951..5a0b89c90a 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -79,8 +79,15 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) void LICM::Run() { DCHECK(side_effects_.HasRun()); + // Only used during debug. - ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM); + ArenaBitVector* visited = nullptr; + if (kIsDebugBuild) { + visited = new (graph_->GetArena()) ArenaBitVector(graph_->GetArena(), + graph_->GetBlocks().size(), + false, + kArenaAllocLICM); + } // Post order visit to visit inner loops before outer loops. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { @@ -109,10 +116,12 @@ void LICM::Run() { DCHECK(inner->IsInLoop()); if (inner->GetLoopInformation() != loop_info) { // Thanks to post order visit, inner loops were already visited. - DCHECK(visited.IsBitSet(inner->GetBlockId())); + DCHECK(visited->IsBitSet(inner->GetBlockId())); continue; } - visited.SetBit(inner->GetBlockId()); + if (kIsDebugBuild) { + visited->SetBit(inner->GetBlockId()); + } if (contains_irreducible_loop) { // We cannot licm in an irreducible loop, or in a natural loop containing an diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index d446539700..2a62643465 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -169,13 +169,11 @@ TEST_F(LICMTest, ArrayHoisting) { BuildLoop(); // Populate the loop with instructions: set/get array with different types. - // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to - // avoid SsaBuilder's typing of ambiguous array operations from reference type info. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, int_constant_, Primitive::kPrimByte, 0); + parameter_, int_constant_, Primitive::kPrimInt, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0); + parameter_, int_constant_, float_constant_, Primitive::kPrimFloat, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); @@ -189,13 +187,11 @@ TEST_F(LICMTest, NoArrayHoisting) { BuildLoop(); // Populate the loop with instructions: set/get array with same types. - // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to - // avoid SsaBuilder's typing of ambiguous array operations from reference type info. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, int_constant_, Primitive::kPrimByte, 0); + parameter_, int_constant_, Primitive::kPrimFloat, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, get_array, float_constant_, Primitive::kPrimByte, 0); + parameter_, get_array, float_constant_, Primitive::kPrimFloat, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index e1977b1798..ac7ed86d1d 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -480,7 +480,7 @@ class HeapLocationCollector : public HGraphVisitor { // alias analysis and won't be as effective. bool has_volatile_; // If there are volatile field accesses. bool has_monitor_operations_; // If there are monitor operations. - bool may_deoptimize_; + bool may_deoptimize_; // Only true for HDeoptimize with single-frame deoptimization. DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector); }; @@ -551,19 +551,20 @@ class LSEVisitor : public HGraphVisitor { } // At this point, stores in possibly_removed_stores_ can be safely removed. - size = possibly_removed_stores_.size(); - for (size_t i = 0; i < size; i++) { + for (size_t i = 0, e = possibly_removed_stores_.size(); i < e; i++) { HInstruction* store = possibly_removed_stores_[i]; DCHECK(store->IsInstanceFieldSet() || store->IsStaticFieldSet() || store->IsArraySet()); store->GetBlock()->RemoveInstruction(store); } - // TODO: remove unnecessary allocations. - // Eliminate instructions in singleton_new_instances_ that: - // - don't have uses, - // - don't have finalizers, - // - are instantiable and accessible, - // - have no/separate clinit check. + // Eliminate allocations that are not used. + for (size_t i = 0, e = singleton_new_instances_.size(); i < e; i++) { + HInstruction* new_instance = singleton_new_instances_[i]; + if (!new_instance->HasNonEnvironmentUses()) { + new_instance->RemoveEnvironmentUsers(); + new_instance->GetBlock()->RemoveInstruction(new_instance); + } + } } private: @@ -969,8 +970,8 @@ class LSEVisitor : public HGraphVisitor { if (!heap_location_collector_.MayDeoptimize() && ref_info->IsSingletonAndNotReturned() && !new_instance->IsFinalizable() && - !new_instance->CanThrow()) { - // TODO: add new_instance to singleton_new_instances_ and enable allocation elimination. + !new_instance->NeedsAccessCheck()) { + singleton_new_instances_.push_back(new_instance); } ArenaVector<HInstruction*>& heap_values = heap_values_for_[new_instance->GetBlock()->GetBlockId()]; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index dc5a8fa9cb..1ea2247da6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1551,21 +1551,21 @@ class SideEffects : public ValueObject { static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) { return is_volatile ? AllWritesAndReads() - : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset)); + : SideEffects(TypeFlag(type, kFieldWriteOffset)); } static SideEffects ArrayWriteOfType(Primitive::Type type) { - return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset)); + return SideEffects(TypeFlag(type, kArrayWriteOffset)); } static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) { return is_volatile ? AllWritesAndReads() - : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset)); + : SideEffects(TypeFlag(type, kFieldReadOffset)); } static SideEffects ArrayReadOfType(Primitive::Type type) { - return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset)); + return SideEffects(TypeFlag(type, kArrayReadOffset)); } static SideEffects CanTriggerGC() { @@ -1692,23 +1692,6 @@ class SideEffects : public ValueObject { static constexpr uint64_t kAllReads = ((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset; - // Work around the fact that HIR aliases I/F and J/D. - // TODO: remove this interceptor once HIR types are clean - static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) { - switch (type) { - case Primitive::kPrimInt: - case Primitive::kPrimFloat: - return TypeFlag(Primitive::kPrimInt, offset) | - TypeFlag(Primitive::kPrimFloat, offset); - case Primitive::kPrimLong: - case Primitive::kPrimDouble: - return TypeFlag(Primitive::kPrimLong, offset) | - TypeFlag(Primitive::kPrimDouble, offset); - default: - return TypeFlag(type, offset); - } - } - // Translates type to bit flag. static uint64_t TypeFlag(Primitive::Type type, int offset) { CHECK_NE(type, Primitive::kPrimVoid); @@ -3652,14 +3635,14 @@ class HNewInstance : public HExpression<2> { uint32_t dex_pc, uint16_t type_index, const DexFile& dex_file, - bool can_throw, + bool needs_access_check, bool finalizable, QuickEntrypointEnum entrypoint) : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), entrypoint_(entrypoint) { - SetPackedFlag<kFlagCanThrow>(can_throw); + SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagFinalizable>(finalizable); SetRawInputAt(0, cls); SetRawInputAt(1, current_method); @@ -3671,10 +3654,11 @@ class HNewInstance : public HExpression<2> { // Calls runtime so needs an environment. bool NeedsEnvironment() const OVERRIDE { return true; } - // It may throw when called on type that's not instantiable/accessible. - // It can throw OOME. - // TODO: distinguish between the two cases so we can for example allow allocation elimination. - bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; } + // Can throw errors when out-of-memory or if it's not instantiable/accessible. + bool CanThrow() const OVERRIDE { return true; } + + // Needs to call into runtime to make sure it's instantiable/accessible. + bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); } @@ -3691,8 +3675,8 @@ class HNewInstance : public HExpression<2> { DECLARE_INSTRUCTION(NewInstance); private: - static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits; - static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1; + static constexpr size_t kFlagNeedsAccessCheck = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kFlagNeedsAccessCheck + 1; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -5137,14 +5121,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { class HArrayGet : public HExpression<2> { public: - HArrayGet(HInstruction* array, - HInstruction* index, - Primitive::Type type, - uint32_t dex_pc, - SideEffects additional_side_effects = SideEffects::None()) - : HExpression(type, - SideEffects::ArrayReadOfType(type).Union(additional_side_effects), - dex_pc) { + HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc) + : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) { SetRawInputAt(0, array); SetRawInputAt(1, index); } @@ -5193,13 +5171,8 @@ class HArraySet : public HTemplateInstruction<3> { HInstruction* index, HInstruction* value, Primitive::Type expected_component_type, - uint32_t dex_pc, - SideEffects additional_side_effects = SideEffects::None()) - : HTemplateInstruction( - SideEffects::ArrayWriteOfType(expected_component_type).Union( - SideEffectsForArchRuntimeCalls(value->GetType())).Union( - additional_side_effects), - dex_pc) { + uint32_t dex_pc) + : HTemplateInstruction(SideEffects::None(), dex_pc) { SetPackedField<ExpectedComponentTypeField>(expected_component_type); SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot); SetPackedFlag<kFlagValueCanBeNull>(true); @@ -5207,6 +5180,8 @@ class HArraySet : public HTemplateInstruction<3> { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); + // Make a best guess now, may be refined during SSA building. + ComputeSideEffects(); } bool NeedsEnvironment() const OVERRIDE { @@ -5259,6 +5234,12 @@ class HArraySet : public HTemplateInstruction<3> { return GetPackedField<ExpectedComponentTypeField>(); } + void ComputeSideEffects() { + Primitive::Type type = GetComponentType(); + SetSideEffects(SideEffects::ArrayWriteOfType(type).Union( + SideEffectsForArchRuntimeCalls(type))); + } + static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { return (value_type == Primitive::kPrimNot) ? SideEffects::CanTriggerGC() : SideEffects::None(); } diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc index 9bbc354290..b01bc1ca0d 100644 --- a/compiler/optimizing/side_effects_test.cc +++ b/compiler/optimizing/side_effects_test.cc @@ -148,19 +148,19 @@ TEST(SideEffectsTest, VolatileDependences) { EXPECT_FALSE(any_write.MayDependOn(volatile_read)); } -TEST(SideEffectsTest, SameWidthTypes) { +TEST(SideEffectsTest, SameWidthTypesNoAlias) { // Type I/F. - testWriteAndReadDependence( + testNoWriteAndReadDependence( SideEffects::FieldWriteOfType(Primitive::kPrimInt, /* is_volatile */ false), SideEffects::FieldReadOfType(Primitive::kPrimFloat, /* is_volatile */ false)); - testWriteAndReadDependence( + testNoWriteAndReadDependence( SideEffects::ArrayWriteOfType(Primitive::kPrimInt), SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); // Type L/D. - testWriteAndReadDependence( + testNoWriteAndReadDependence( SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false), SideEffects::FieldReadOfType(Primitive::kPrimDouble, /* is_volatile */ false)); - testWriteAndReadDependence( + testNoWriteAndReadDependence( SideEffects::ArrayWriteOfType(Primitive::kPrimLong), SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); } @@ -216,14 +216,32 @@ TEST(SideEffectsTest, BitStrings) { "||||||L|", SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str()); EXPECT_STREQ( + "||DFJISCBZL|DFJISCBZL||DFJISCBZL|DFJISCBZL|", + SideEffects::FieldWriteOfType(Primitive::kPrimNot, true).ToString().c_str()); + EXPECT_STREQ( "|||||Z||", SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str()); EXPECT_STREQ( + "|||||C||", + SideEffects::ArrayWriteOfType(Primitive::kPrimChar).ToString().c_str()); + EXPECT_STREQ( + "|||||S||", + SideEffects::ArrayWriteOfType(Primitive::kPrimShort).ToString().c_str()); + EXPECT_STREQ( "|||B||||", SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str()); EXPECT_STREQ( - "||DJ|||||", // note: DJ alias + "||D|||||", SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str()); + EXPECT_STREQ( + "||J|||||", + SideEffects::ArrayReadOfType(Primitive::kPrimLong).ToString().c_str()); + EXPECT_STREQ( + "||F|||||", + SideEffects::ArrayReadOfType(Primitive::kPrimFloat).ToString().c_str()); + EXPECT_STREQ( + "||I|||||", + SideEffects::ArrayReadOfType(Primitive::kPrimInt).ToString().c_str()); SideEffects s = SideEffects::None(); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, /* is_volatile */ false)); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false)); @@ -231,9 +249,7 @@ TEST(SideEffectsTest, BitStrings) { s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt, /* is_volatile */ false)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); - EXPECT_STREQ( - "||DFJI|FI||S|DJC|", // note: DJ/FI alias. - s.ToString().c_str()); + EXPECT_STREQ("||DF|I||S|JC|", s.ToString().c_str()); } } // namespace art diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index eeadbeb0d1..2fe2f2053a 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -391,6 +391,9 @@ bool SsaBuilder::FixAmbiguousArrayOps() { worklist.push_back(equivalent->AsPhi()); } } + // Refine the side effects of this floating point aset. Note that we do this even if + // no replacement occurs, since the right-hand-side may have been corrected already. + aset->ComputeSideEffects(); } else { // Array elements are integral and the value assigned to it initially // was integral too. Nothing to do. diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 97f2aeeb1e..719feec468 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -969,6 +969,38 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { return false; } + bool IsLinearOrderWellFormed(const HGraph& graph) { + for (HBasicBlock* header : graph.GetBlocks()) { + if (!header->IsLoopHeader()) { + continue; + } + + HLoopInformation* loop = header->GetLoopInformation(); + size_t num_blocks = loop->GetBlocks().NumSetBits(); + size_t found_blocks = 0u; + + for (HLinearOrderIterator it(graph); !it.Done(); it.Advance()) { + HBasicBlock* current = it.Current(); + if (loop->Contains(*current)) { + found_blocks++; + if (found_blocks == 1u && current != header) { + // First block is not the header. + return false; + } else if (found_blocks == num_blocks && !loop->IsBackEdge(*current)) { + // Last block is not a back edge. + return false; + } + } else if (found_blocks != 0u && found_blocks != num_blocks) { + // Blocks are not adjacent. + return false; + } + } + DCHECK_EQ(found_blocks, num_blocks); + } + + return true; + } + void AddBackEdgeUses(const HBasicBlock& block_at_use) { DCHECK(block_at_use.IsInLoop()); // Add synthesized uses at the back edge of loops to help the register allocator. @@ -995,12 +1027,30 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) { // There was a use already seen in this loop. Therefore the previous call to `AddUse` // already inserted the backedge use. We can stop going outward. - DCHECK(HasSynthesizeUseAt(back_edge_use_position)); + if (kIsDebugBuild) { + if (!HasSynthesizeUseAt(back_edge_use_position)) { + // There exists a use prior to `back_edge_use_position` but there is + // no synthesized use at the back edge. This can happen in the presence + // of irreducible loops, when blocks of the loop are not adjacent in + // linear order, i.e. when there is an out-of-loop block between + // `block_at_use` and `back_edge_position` that uses this interval. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + } + } break; } - DCHECK(last_in_new_list == nullptr - || back_edge_use_position > last_in_new_list->GetPosition()); + if (last_in_new_list != nullptr && + back_edge_use_position <= last_in_new_list->GetPosition()) { + // Loops are not properly nested in the linear order, i.e. the back edge + // of an outer loop preceeds blocks of an inner loop. This can happen + // in the presence of irreducible loops. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + // We must bail out, otherwise we would generate an unsorted use list. + break; + } UsePosition* new_use = new (allocator_) UsePosition( /* user */ nullptr, |