diff options
Diffstat (limited to 'compiler/optimizing')
-rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.cc | 320 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.h | 35 | ||||
-rw-r--r-- | compiler/optimizing/licm.cc | 20 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 31 |
4 files changed, 372 insertions, 34 deletions
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 1c1cc95ab1..f7957d402f 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -65,6 +65,7 @@ static bool ExpectedPairLayout(Location location) { static constexpr int kCurrentMethodStackOffset = 0; static constexpr size_t kArmInstrMaxSizeInBytes = 4u; +static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; #ifdef __ #error "ARM Codegen VIXL macro-assembler macro already defined." @@ -657,6 +658,7 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, compiler_options, stats), block_labels_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), location_builder_(graph, this), instruction_visitor_(graph, this), move_resolver_(graph->GetArena(), this), @@ -675,9 +677,44 @@ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d15); } +void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) { + uint32_t num_entries = switch_instr_->GetNumEntries(); + DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold); + + // We are about to use the assembler to place literals directly. Make sure we have enough + // underlying code buffer and we have generated the jump table with right size. + codegen->GetVIXLAssembler()->GetBuffer().Align(); + AssemblerAccurateScope aas(codegen->GetVIXLAssembler(), + num_entries * sizeof(int32_t), + CodeBufferCheckScope::kMaximumSize); + // TODO(VIXL): Check that using lower case bind is fine here. + codegen->GetVIXLAssembler()->bind(&table_start_); + const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); + for (uint32_t i = 0; i < num_entries; i++) { + vixl32::Label* target_label = codegen->GetLabelOf(successors[i]); + DCHECK(target_label->IsBound()); + int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation(); + // When doing BX to address we need to have lower bit set to 1 in T32. + if (codegen->GetVIXLAssembler()->IsUsingT32()) { + jump_offset++; + } + DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min()); + DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max()); + vixl32::Literal<int32_t> literal(jump_offset); + codegen->GetVIXLAssembler()->place(&literal); + } +} + +void CodeGeneratorARMVIXL::EmitJumpTables() { + for (auto&& jump_table : jump_tables_) { + jump_table->EmitTable(this); + } +} + #define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) { + EmitJumpTables(); GetAssembler()->FinalizeCode(); CodeGenerator::Finalize(allocator); } @@ -1253,6 +1290,14 @@ void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) { __ Bind(&false_target); } +void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) { + new (GetGraph()->GetArena()) LocationSummary(info); +} + +void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. +} + void CodeGeneratorARMVIXL::GenerateNop() { __ Nop(); } @@ -2495,7 +2540,12 @@ void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) { locations->SetInAt(1, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } else { - TODO_VIXL32(FATAL); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the former. + locations->SetOut(LocationFrom(r0)); } break; } @@ -2532,7 +2582,13 @@ void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) { } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1)); } else { - TODO_VIXL32(FATAL); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs))); + DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs))); + DCHECK(r0.Is(OutputRegister(div))); + + codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc()); + CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); } break; } @@ -2561,6 +2617,140 @@ void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) { } } +void LocationsBuilderARMVIXL::VisitRem(HRem* rem) { + Primitive::Type type = rem->GetResultType(); + + // Most remainders are implemented in the runtime. + LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly; + if (rem->GetResultType() == Primitive::kPrimInt && rem->InputAt(1)->IsConstant()) { + // sdiv will be replaced by other instruction sequence. + call_kind = LocationSummary::kNoCall; + } else if ((rem->GetResultType() == Primitive::kPrimInt) + && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Have hardware divide instruction for int, do it with three instructions. + call_kind = LocationSummary::kNoCall; + } + + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); + + switch (type) { + case Primitive::kPrimInt: { + if (rem->InputAt(1)->IsConstant()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant())); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue(); + if (value == 1 || value == 0 || value == -1) { + // No temp register required. + } else { + locations->AddTemp(Location::RequiresRegister()); + if (!IsPowerOfTwo(AbsOrMin(value))) { + locations->AddTemp(Location::RequiresRegister()); + } + } + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + } else { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the latter. + locations->SetOut(LocationFrom(r1)); + } + break; + } + case Primitive::kPrimLong: { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom( + calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); + locations->SetInAt(1, LocationFrom( + calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); + // The runtime helper puts the output in R2,R3. + locations->SetOut(LocationFrom(r2, r3)); + break; + } + case Primitive::kPrimFloat: { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1))); + locations->SetOut(LocationFrom(s0)); + break; + } + + case Primitive::kPrimDouble: { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom( + calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1))); + locations->SetInAt(1, LocationFrom( + calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3))); + locations->SetOut(LocationFrom(s0, s1)); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + +void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) { + LocationSummary* locations = rem->GetLocations(); + Location second = locations->InAt(1); + + Primitive::Type type = rem->GetResultType(); + switch (type) { + case Primitive::kPrimInt: { + vixl32::Register reg1 = InputRegisterAt(rem, 0); + vixl32::Register out_reg = OutputRegister(rem); + if (second.IsConstant()) { + GenerateDivRemConstantIntegral(rem); + } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + vixl32::Register reg2 = RegisterFrom(second); + vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); + + // temp = reg1 / reg2 (integer division) + // dest = reg1 - temp * reg2 + __ Sdiv(temp, reg1, reg2); + __ Mls(out_reg, temp, reg2, reg1); + } else { + InvokeRuntimeCallingConventionARMVIXL calling_convention; + DCHECK(reg1.Is(calling_convention.GetRegisterAt(0))); + DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1))); + DCHECK(out_reg.Is(r1)); + + codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>(); + } + break; + } + + case Primitive::kPrimLong: { + codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>(); + break; + } + + case Primitive::kPrimFloat: { + codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickFmodf, float, float, float>(); + break; + } + + case Primitive::kPrimDouble: { + codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc()); + CheckEntrypointTypes<kQuickFmod, double, double, double>(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + + void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) { // TODO(VIXL): https://android-review.googlesource.com/#/c/275337/ LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() @@ -5257,6 +5447,24 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { __ Bind(type_check_slow_path->GetExitLabel()); } +void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); +} + +void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) { + codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject, + instruction, + instruction->GetDexPc()); + if (instruction->IsEnter()) { + CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>(); + } else { + CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>(); + } +} + void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction, AND); } @@ -5659,6 +5867,103 @@ void CodeGeneratorARMVIXL::GenerateVirtualCall(HInvokeVirtual* invoke, Location __ Blx(lr); } +void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { + // Nothing to do, this should be removed during prepare for register allocator. + LOG(FATAL) << "Unreachable"; +} + +void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { + // Nothing to do, this should be removed during prepare for register allocator. + LOG(FATAL) << "Unreachable"; +} + +// Simple implementation of packed switch - generate cascaded compare/jumps. +void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold && + codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) { + locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base. + if (switch_instr->GetStartValue() != 0) { + locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias. + } + } +} + +// TODO(VIXL): Investigate and reach the parity with old arm codegen. +void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) { + int32_t lower_bound = switch_instr->GetStartValue(); + uint32_t num_entries = switch_instr->GetNumEntries(); + LocationSummary* locations = switch_instr->GetLocations(); + vixl32::Register value_reg = InputRegisterAt(switch_instr, 0); + HBasicBlock* default_block = switch_instr->GetDefaultBlock(); + + if (num_entries <= kPackedSwitchCompareJumpThreshold || + !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) { + // Create a series of compare/jumps. + UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + vixl32::Register temp_reg = temps.Acquire(); + // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store + // the immediate, because IP is used as the destination register. For the other + // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant, + // and they can be encoded in the instruction without making use of IP register. + __ Adds(temp_reg, value_reg, -lower_bound); + + const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors(); + // Jump to successors[0] if value == lower_bound. + __ B(eq, codegen_->GetLabelOf(successors[0])); + int32_t last_index = 0; + for (; num_entries - last_index > 2; last_index += 2) { + __ Adds(temp_reg, temp_reg, -2); + // Jump to successors[last_index + 1] if value < case_value[last_index + 2]. + __ B(lo, codegen_->GetLabelOf(successors[last_index + 1])); + // Jump to successors[last_index + 2] if value == case_value[last_index + 2]. + __ B(eq, codegen_->GetLabelOf(successors[last_index + 2])); + } + if (num_entries - last_index == 2) { + // The last missing case_value. + __ Cmp(temp_reg, 1); + __ B(eq, codegen_->GetLabelOf(successors[last_index + 1])); + } + + // And the default for any other value. + if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) { + __ B(codegen_->GetLabelOf(default_block)); + } + } else { + // Create a table lookup. + vixl32::Register table_base = RegisterFrom(locations->GetTemp(0)); + + JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr); + + // Remove the bias. + vixl32::Register key_reg; + if (lower_bound != 0) { + key_reg = RegisterFrom(locations->GetTemp(1)); + __ Sub(key_reg, value_reg, lower_bound); + } else { + key_reg = value_reg; + } + + // Check whether the value is in the table, jump to default block if not. + __ Cmp(key_reg, num_entries - 1); + __ B(hi, codegen_->GetLabelOf(default_block)); + + UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler()); + vixl32::Register jump_offset = temps.Acquire(); + + // Load jump offset from the table. + __ Adr(table_base, jump_table->GetTableStartLabel()); + __ Ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2)); + + // Jump to target block by branching to table_base(pc related) + offset. + vixl32::Register target_address = table_base; + __ Add(target_address, table_base, jump_offset); + __ Bx(target_address); + } +} + // Copy the result of a call into the given target. void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type type) { if (!trg.IsValid()) { @@ -5687,6 +5992,17 @@ void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, Primitive::Type } } +void LocationsBuilderARMVIXL::VisitClassTableGet( + HClassTableGet* instruction ATTRIBUTE_UNUSED) { + TODO_VIXL32(FATAL); +} + +void InstructionCodeGeneratorARMVIXL::VisitClassTableGet( + HClassTableGet* instruction ATTRIBUTE_UNUSED) { + TODO_VIXL32(FATAL); +} + + #undef __ #undef QUICK_ENTRY_POINT #undef TODO_VIXL32 diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 2ccc30ff5f..ccd866c367 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -114,7 +114,9 @@ class LoadClassSlowPathARMVIXL; M(BelowOrEqual) \ M(BooleanNot) \ M(BoundsCheck) \ + M(BoundType) \ M(CheckCast) \ + M(ClassTableGet) \ M(ClearException) \ M(ClinitCheck) \ M(Compare) \ @@ -145,7 +147,9 @@ class LoadClassSlowPathARMVIXL; M(LoadString) \ M(LongConstant) \ M(MemoryBarrier) \ + M(MonitorOperation) \ M(Mul) \ + M(NativeDebugInfo) \ M(Neg) \ M(NewArray) \ M(NewInstance) \ @@ -154,9 +158,11 @@ class LoadClassSlowPathARMVIXL; M(NullCheck) \ M(NullConstant) \ M(Or) \ + M(PackedSwitch) \ M(ParallelMove) \ M(ParameterValue) \ M(Phi) \ + M(Rem) \ M(Return) \ M(ReturnVoid) \ M(Ror) \ @@ -181,17 +187,27 @@ class LoadClassSlowPathARMVIXL; #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ M(ArmDexCacheArraysBase) \ M(BitwiseNegatedRight) \ - M(BoundType) \ - M(ClassTableGet) \ M(IntermediateAddress) \ - M(MonitorOperation) \ M(MultiplyAccumulate) \ - M(NativeDebugInfo) \ - M(PackedSwitch) \ - M(Rem) \ class CodeGeneratorARMVIXL; +class JumpTableARMVIXL : public DeletableArenaObject<kArenaAllocSwitchTable> { + public: + explicit JumpTableARMVIXL(HPackedSwitch* switch_instr) + : switch_instr_(switch_instr), table_start_() {} + + vixl::aarch32::Label* GetTableStartLabel() { return &table_start_; } + + void EmitTable(CodeGeneratorARMVIXL* codegen); + + private: + HPackedSwitch* const switch_instr_; + vixl::aarch32::Label table_start_; + + DISALLOW_COPY_AND_ASSIGN(JumpTableARMVIXL); +}; + class InvokeRuntimeCallingConventionARMVIXL : public CallingConvention<vixl::aarch32::Register, vixl::aarch32::SRegister> { public: @@ -488,10 +504,16 @@ class CodeGeneratorARMVIXL : public CodeGenerator { return block_entry_label->GetLocation(); } + JumpTableARMVIXL* CreateJumpTable(HPackedSwitch* switch_instr) { + jump_tables_.emplace_back(new (GetGraph()->GetArena()) JumpTableARMVIXL(switch_instr)); + return jump_tables_.back().get(); + } + HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } + void EmitJumpTables(); void GenerateMemoryBarrier(MemBarrierKind kind); void Finalize(CodeAllocator* allocator) OVERRIDE; void SetupBlockedRegisters() const OVERRIDE; @@ -673,6 +695,7 @@ class CodeGeneratorARMVIXL : public CodeGenerator { ArenaDeque<vixl::aarch32::Label> block_labels_; // Indexed by block id. vixl::aarch32::Label frame_entry_label_; + ArenaVector<std::unique_ptr<JumpTableARMVIXL>> jump_tables_; LocationsBuilderARMVIXL location_builder_; InstructionCodeGeneratorARMVIXL instruction_visitor_; ParallelMoveResolverARMVIXL move_resolver_; diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index eb2d18dd88..f0086fb202 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -120,17 +120,17 @@ void LICM::Run() { } DCHECK(!loop_info->IsIrreducible()); - // We can move an instruction that can throw only if it is the first - // throwing instruction in the loop. Note that the first potentially - // throwing instruction encountered that is not hoisted stops this - // optimization. Non-throwing instruction can still be hoisted. - bool found_first_non_hoisted_throwing_instruction_in_loop = !inner->IsLoopHeader(); + // We can move an instruction that can throw only as long as it is the first visible + // instruction (throw or write) in the loop. Note that the first potentially visible + // instruction that is not hoisted stops this optimization. Non-throwing instructions, + // on the other hand, can still be hoisted. + bool found_first_non_hoisted_visible_instruction_in_loop = !inner->IsLoopHeader(); for (HInstructionIterator inst_it(inner->GetInstructions()); !inst_it.Done(); inst_it.Advance()) { HInstruction* instruction = inst_it.Current(); if (instruction->CanBeMoved() - && (!instruction->CanThrow() || !found_first_non_hoisted_throwing_instruction_in_loop) + && (!instruction->CanThrow() || !found_first_non_hoisted_visible_instruction_in_loop) && !instruction->GetSideEffects().MayDependOn(loop_effects) && InputsAreDefinedBeforeLoop(instruction)) { // We need to update the environment if the instruction has a loop header @@ -142,10 +142,10 @@ void LICM::Run() { } instruction->MoveBefore(pre_header->GetLastInstruction()); MaybeRecordStat(MethodCompilationStat::kLoopInvariantMoved); - } else if (instruction->CanThrow()) { - // If `instruction` can throw, we cannot move further instructions - // that can throw as well. - found_first_non_hoisted_throwing_instruction_in_loop = true; + } else if (instruction->CanThrow() || instruction->DoesAnyWrite()) { + // If `instruction` can do something visible (throw or write), + // we cannot move further instructions that can throw. + found_first_non_hoisted_visible_instruction_in_loop = true; } } } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 6f84cdcc4f..7a930cce71 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -167,24 +167,13 @@ class PassObserver : public ValueObject { LOG(INFO) << "TIMINGS " << GetMethodName(); LOG(INFO) << Dumpable<TimingLogger>(timing_logger_); } - if (visualizer_enabled_) { - MutexLock mu(Thread::Current(), visualizer_dump_mutex_); - *visualizer_output_ << visualizer_oss_.str(); - // The destructor of `visualizer_output_` is normally - // responsible for flushing (and closing) the stream, but it - // won't be invoked during fast exits in non-debug mode -- see - // art::Dex2Oat::~Dex2Oat, which explicitly abandons some - // objects (such as the compiler driver) in non-debug mode, to - // avoid the cost of destructing them. Therefore we explicitly - // flush the stream here to prevent truncated CFG visualizer - // files. - visualizer_output_->flush(); - } + DCHECK(visualizer_oss_.str().empty()); } - void DumpDisassembly() const { + void DumpDisassembly() REQUIRES(!visualizer_dump_mutex_) { if (visualizer_enabled_) { visualizer_.DumpGraphWithDisassembly(); + FlushVisualizer(); } } @@ -199,24 +188,34 @@ class PassObserver : public ValueObject { } private: - void StartPass(const char* pass_name) { + void StartPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) { VLOG(compiler) << "Starting pass: " << pass_name; // Dump graph first, then start timer. if (visualizer_enabled_) { visualizer_.DumpGraph(pass_name, /* is_after_pass */ false, graph_in_bad_state_); + FlushVisualizer(); } if (timing_logger_enabled_) { timing_logger_.StartTiming(pass_name); } } - void EndPass(const char* pass_name) { + void FlushVisualizer() REQUIRES(!visualizer_dump_mutex_) { + MutexLock mu(Thread::Current(), visualizer_dump_mutex_); + *visualizer_output_ << visualizer_oss_.str(); + visualizer_output_->flush(); + visualizer_oss_.str(""); + visualizer_oss_.clear(); + } + + void EndPass(const char* pass_name) REQUIRES(!visualizer_dump_mutex_) { // Pause timer first, then dump graph. if (timing_logger_enabled_) { timing_logger_.EndTiming(); } if (visualizer_enabled_) { visualizer_.DumpGraph(pass_name, /* is_after_pass */ true, graph_in_bad_state_); + FlushVisualizer(); } // Validate the HGraph if running in debug mode. |