diff options
| author | 2015-08-20 17:47:48 +0100 | |
|---|---|---|
| committer | 2015-09-14 20:42:58 +0100 | |
| commit | b022fa1300e6d78639b3b910af0cf85c43df44bb (patch) | |
| tree | 780c7d6bdee784c2f8248979de348491cfb63b34 /compiler | |
| parent | e481c006e8b055a31d9c7cff27f4145e57e3c113 (diff) | |
ART: Register allocation and runtime support for try/catch
This patch completes a series of CLs that add support for try/catch
in the Optimizing compiler. With it, Optimizing can compile all
methods containing try/catch, provided they don't contain catch loops.
Future work will focus on improving performance of the generated code.
SsaLivenessAnalysis was updated to propagate liveness information of
instructions live at catch blocks, and to keep location information on
instructions which may be caught by catch phis.
RegisterAllocator was extended to spill values used after catch, and
to allocate spill slots for catch phis. Catch phis generated for the
same vreg share a spill slot as the raw value must be the same.
Location builders and slow paths were updated to reflect the fact that
throwing an exception may not lead to escaping the method.
Instruction code generators are forbidden from using of implicit null
checks in try blocks as live registers need to be saved before handing
over to the runtime.
CodeGenerator emits a stack map for each catch block, storing locations
of catch phis. CodeInfo and StackMapStream recognize this new type of
stack map and store them separate from other stack maps to avoid dex_pc
conflicts.
After having found the target catch block to deliver an exception to,
QuickExceptionHandler looks up the dex register maps at the throwing
instruction and the catch block and copies the values over to their
respective locations.
The runtime-support approach was selected because it allows for the
best performance in the normal control-flow path, since no propagation
of catch phi values is necessary until the exception is thrown. In
addition, it also greatly simplifies the register allocation phase.
ConstantHoisting was removed from LICMTest because it instantiated
(now abstract) HConstant and was bogus anyway (constants are always in
the entry block).
Change-Id: Ie31038ad8e3ee0c13a5bbbbaf5f0b3e532310e4e
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/optimizing/builder.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 92 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.h | 11 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 32 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 32 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 32 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 36 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 36 | ||||
| -rw-r--r-- | compiler/optimizing/graph_checker.cc | 79 | ||||
| -rw-r--r-- | compiler/optimizing/licm_test.cc | 21 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.cc | 10 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 37 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 64 | ||||
| -rw-r--r-- | compiler/optimizing/register_allocator.cc | 150 | ||||
| -rw-r--r-- | compiler/optimizing/register_allocator.h | 18 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_liveness_analysis.cc | 27 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_liveness_analysis.h | 3 | ||||
| -rw-r--r-- | compiler/optimizing/stack_map_stream.cc | 2 |
18 files changed, 500 insertions, 184 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 9895953e1f..0a3f083e10 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -478,6 +478,8 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { graph_->SetEntryBlock(entry_block_); graph_->SetExitBlock(exit_block_); + graph_->SetHasTryCatch(code_item.tries_size_ != 0); + InitializeLocals(code_item.registers_size_); graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 0bb90b27e6..3bbff6ae17 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -248,6 +248,12 @@ void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) GenerateSlowPaths(); + // Emit catch stack maps at the end of the stack map stream as expected by the + // runtime exception handler. + if (!is_baseline && graph_->HasTryCatch()) { + RecordCatchBlockInfo(); + } + // Finalize instructions in assember; Finalize(allocator); } @@ -805,6 +811,73 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, stack_map_stream_.EndStackMapEntry(); } +void CodeGenerator::RecordCatchBlockInfo() { + ArenaAllocator* arena = graph_->GetArena(); + + for (size_t i = 0, e = block_order_->Size(); i < e; ++i) { + HBasicBlock* block = block_order_->Get(i); + if (!block->IsCatchBlock()) { + continue; + } + + uint32_t dex_pc = block->GetDexPc(); + uint32_t num_vregs = graph_->GetNumberOfVRegs(); + uint32_t inlining_depth = 0; // Inlining of catch blocks is not supported at the moment. + uint32_t native_pc = GetAddressOf(block); + uint32_t register_mask = 0; // Not used. + + // The stack mask is not used, so we leave it empty. + ArenaBitVector* stack_mask = new (arena) ArenaBitVector(arena, 0, /* expandable */ true); + + stack_map_stream_.BeginStackMapEntry(dex_pc, + native_pc, + register_mask, + stack_mask, + num_vregs, + inlining_depth); + + HInstruction* current_phi = block->GetFirstPhi(); + for (size_t vreg = 0; vreg < num_vregs; ++vreg) { + while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) { + HInstruction* next_phi = current_phi->GetNext(); + DCHECK(next_phi == nullptr || + current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber()) + << "Phis need to be sorted by vreg number to keep this a linear-time loop."; + current_phi = next_phi; + } + + if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) { + stack_map_stream_.AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0); + } else { + Location location = current_phi->GetLiveInterval()->ToLocation(); + switch (location.GetKind()) { + case Location::kStackSlot: { + stack_map_stream_.AddDexRegisterEntry( + DexRegisterLocation::Kind::kInStack, location.GetStackIndex()); + break; + } + case Location::kDoubleStackSlot: { + stack_map_stream_.AddDexRegisterEntry( + DexRegisterLocation::Kind::kInStack, location.GetStackIndex()); + stack_map_stream_.AddDexRegisterEntry( + DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize)); + ++vreg; + DCHECK_LT(vreg, num_vregs); + break; + } + default: { + // All catch phis must be allocated to a stack slot. + LOG(FATAL) << "Unexpected kind " << location.GetKind(); + UNREACHABLE(); + } + } + } + } + + stack_map_stream_.EndStackMapEntry(); + } +} + void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slow_path) { if (environment == nullptr) return; @@ -975,6 +1048,13 @@ void CodeGenerator::EmitEnvironment(HEnvironment* environment, SlowPathCode* slo } } +bool CodeGenerator::IsImplicitNullCheckAllowed(HNullCheck* null_check) const { + return compiler_options_.GetImplicitNullChecks() && + // Null checks which might throw into a catch block need to save live + // registers and therefore cannot be done implicitly. + !null_check->CanThrowIntoCatchBlock(); +} + bool CodeGenerator::CanMoveNullCheckToUser(HNullCheck* null_check) { HInstruction* first_next_not_move = null_check->GetNextDisregardingMoves(); @@ -990,10 +1070,6 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { return; } - if (!compiler_options_.GetImplicitNullChecks()) { - return; - } - if (!instr->CanDoImplicitNullCheckOn(instr->InputAt(0))) { return; } @@ -1005,9 +1081,11 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { // and needs to record the pc. if (first_prev_not_move != nullptr && first_prev_not_move->IsNullCheck()) { HNullCheck* null_check = first_prev_not_move->AsNullCheck(); - // TODO: The parallel moves modify the environment. Their changes need to be reverted - // otherwise the stack maps at the throw point will not be correct. - RecordPcInfo(null_check, null_check->GetDexPc()); + if (IsImplicitNullCheckAllowed(null_check)) { + // TODO: The parallel moves modify the environment. Their changes need to be + // reverted otherwise the stack maps at the throw point will not be correct. + RecordPcInfo(null_check, null_check->GetDexPc()); + } } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index b3c4d727e0..a93d07ad50 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -237,6 +237,17 @@ class CodeGenerator { bool CanMoveNullCheckToUser(HNullCheck* null_check); void MaybeRecordImplicitNullCheck(HInstruction* instruction); + // Records a stack map which the runtime might use to set catch phi values + // during exception delivery. + // TODO: Replace with a catch-entering instruction that records the environment. + void RecordCatchBlockInfo(); + + // Returns true if implicit null checks are allowed in the compiler options + // and if the null check is not inside a try block. We currently cannot do + // implicit null checks in that case because we need the NullCheckSlowPath to + // save live registers, which may be needed by the runtime to set catch phis. + bool IsImplicitNullCheckAllowed(HNullCheck* null_check) const; + void AddSlowPath(SlowPathCode* slow_path) { slow_paths_.Add(slow_path); } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index a4c58b095a..b3e38f0946 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -66,6 +66,10 @@ class NullCheckSlowPathARM : public SlowPathCodeARM { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); } @@ -86,6 +90,10 @@ class DivZeroCheckSlowPathARM : public SlowPathCodeARM { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); } @@ -150,6 +158,10 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { LocationSummary* locations = instruction_->GetLocations(); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -2741,8 +2753,10 @@ void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { } void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -3495,8 +3509,10 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi } void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -3524,7 +3540,7 @@ void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruct } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); @@ -3863,8 +3879,10 @@ void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { } void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); if (instruction->HasUses()) { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6b1457bc31..5094f6761a 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -198,6 +198,10 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -226,6 +230,10 @@ class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickThrowDivZero, void, void>(); @@ -338,6 +346,10 @@ class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickThrowNullPointer, void, void>(); @@ -1580,8 +1592,10 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { } void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction)); if (instruction->HasUses()) { @@ -1977,8 +1991,10 @@ void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) { } void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2875,8 +2891,10 @@ void InstructionCodeGeneratorARM64::VisitBooleanNot(HBooleanNot* instruction) { } void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2905,7 +2923,7 @@ void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instru } void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 10942ef3a5..8d60026b41 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -118,6 +118,10 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -151,6 +155,10 @@ class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), @@ -269,6 +277,10 @@ class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), @@ -1566,8 +1578,10 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { } void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); if (instruction->HasUses()) { @@ -1862,8 +1876,10 @@ void InstructionCodeGeneratorMIPS64::VisitDiv(HDiv* instruction) { } void LocationsBuilderMIPS64::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2824,8 +2840,10 @@ void InstructionCodeGeneratorMIPS64::VisitBooleanNot(HBooleanNot* instruction) { } void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -2852,7 +2870,7 @@ void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instr } void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a5ad226e0b..dc5c86efc6 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -56,6 +56,10 @@ class NullCheckSlowPathX86 : public SlowPathCodeX86 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), @@ -78,6 +82,10 @@ class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), @@ -125,6 +133,10 @@ class BoundsCheckSlowPathX86 : public SlowPathCodeX86 { __ Bind(GetEntryLabel()); // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } InvokeRuntimeCallingConvention calling_convention; x86_codegen->EmitParallelMoves( locations->InAt(0), @@ -3039,8 +3051,10 @@ void InstructionCodeGeneratorX86::VisitRem(HRem* rem) { } void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); switch (instruction->GetType()) { case Primitive::kPrimByte: case Primitive::kPrimChar: @@ -3984,9 +3998,11 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instr } void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks() + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + Location loc = codegen_->IsImplicitNullCheckAllowed(instruction) ? Location::RequiresRegister() : Location::Any(); locations->SetInAt(0, loc); @@ -4019,7 +4035,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct __ cmpl(Address(ESP, obj.GetStackIndex()), Immediate(0)); } else { DCHECK(obj.IsConstant()) << obj; - DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); + DCHECK(obj.GetConstant()->IsNullConstant()); __ jmp(slow_path->GetEntryLabel()); return; } @@ -4027,7 +4043,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct } void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); @@ -4432,8 +4448,10 @@ void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { } void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (instruction->HasUses()) { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 0f3eb74c64..0cf1089cf8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -57,6 +57,10 @@ class NullCheckSlowPathX86_64 : public SlowPathCodeX86_64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc(), @@ -79,6 +83,10 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } x64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc(), @@ -177,6 +185,10 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 { LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + if (instruction_->CanThrowIntoCatchBlock()) { + // Live registers will be restored in the catch block if caught. + SaveLiveRegisters(codegen, instruction_->GetLocations()); + } // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. InvokeRuntimeCallingConvention calling_convention; @@ -3194,8 +3206,10 @@ void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) { } void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::Any()); if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); @@ -3748,9 +3762,11 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru } void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - Location loc = codegen_->GetCompilerOptions().GetImplicitNullChecks() + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + Location loc = codegen_->IsImplicitNullCheckAllowed(instruction) ? Location::RequiresRegister() : Location::Any(); locations->SetInAt(0, loc); @@ -3783,7 +3799,7 @@ void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instr __ cmpl(Address(CpuRegister(RSP), obj.GetStackIndex()), Immediate(0)); } else { DCHECK(obj.IsConstant()) << obj; - DCHECK_EQ(obj.GetConstant()->AsIntConstant()->GetValue(), 0); + DCHECK(obj.GetConstant()->IsNullConstant()); __ jmp(slow_path->GetEntryLabel()); return; } @@ -3791,7 +3807,7 @@ void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instr } void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->GetCompilerOptions().GetImplicitNullChecks()) { + if (codegen_->IsImplicitNullCheckAllowed(instruction)) { GenerateImplicitNullCheck(instruction); } else { GenerateExplicitNullCheck(instruction); @@ -4175,8 +4191,10 @@ void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) } void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = - new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (instruction->HasUses()) { diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 3e358358ae..074ed71025 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -382,17 +382,6 @@ void SSAChecker::VisitBasicBlock(HBasicBlock* block) { } } - // Check Phi uniqueness (no two Phis with the same type refer to the same register). - for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { - HPhi* phi = it.Current()->AsPhi(); - if (phi->GetNextEquivalentPhiWithSameType() != nullptr) { - std::stringstream type_str; - type_str << phi->GetType(); - AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s", - phi->GetId(), phi->GetRegNumber(), type_str.str().c_str())); - } - } - // Ensure try membership information is consistent. if (block->IsCatchBlock()) { if (block->IsTryBlock()) { @@ -577,6 +566,35 @@ static Primitive::Type PrimitiveKind(Primitive::Type type) { } } +static bool IsSameSizeConstant(HInstruction* insn1, HInstruction* insn2) { + return insn1->IsConstant() + && insn2->IsConstant() + && Primitive::Is64BitType(insn1->GetType()) == Primitive::Is64BitType(insn2->GetType()); +} + +static bool IsConstantEquivalent(HInstruction* insn1, HInstruction* insn2, BitVector* visited) { + if (insn1->IsPhi() && + insn1->AsPhi()->IsVRegEquivalentOf(insn2) && + insn1->InputCount() == insn2->InputCount()) { + // Testing only one of the two inputs for recursion is sufficient. + if (visited->IsBitSet(insn1->GetId())) { + return true; + } + visited->SetBit(insn1->GetId()); + + for (size_t i = 0, e = insn1->InputCount(); i < e; ++i) { + if (!IsConstantEquivalent(insn1->InputAt(i), insn2->InputAt(i), visited)) { + return false; + } + } + return true; + } else if (IsSameSizeConstant(insn1, insn2)) { + return insn1->AsConstant()->GetValueAsUint64() == insn2->AsConstant()->GetValueAsUint64(); + } else { + return false; + } +} + void SSAChecker::VisitPhi(HPhi* phi) { VisitInstruction(phi); @@ -636,6 +654,45 @@ void SSAChecker::VisitPhi(HPhi* phi) { } } } + + // Ensure that catch phis are sorted by their vreg number, as required by + // the register allocator and code generator. This does not apply to normal + // phis which can be constructed artifically. + if (phi->IsCatchPhi()) { + HInstruction* next_phi = phi->GetNext(); + if (next_phi != nullptr && phi->GetRegNumber() > next_phi->AsPhi()->GetRegNumber()) { + AddError(StringPrintf("Catch phis %d and %d in block %d are not sorted by their " + "vreg numbers.", + phi->GetId(), + next_phi->GetId(), + phi->GetBlock()->GetBlockId())); + } + } + + // Test phi equivalents. There should not be two of the same type and they + // should only be created for constants which were untyped in DEX. + for (HInstructionIterator phi_it(phi->GetBlock()->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + HPhi* other_phi = phi_it.Current()->AsPhi(); + if (phi != other_phi && phi->GetRegNumber() == other_phi->GetRegNumber()) { + if (phi->GetType() == other_phi->GetType()) { + std::stringstream type_str; + type_str << phi->GetType(); + AddError(StringPrintf("Equivalent phi (%d) found for VReg %d with type: %s.", + phi->GetId(), + phi->GetRegNumber(), + type_str.str().c_str())); + } else { + ArenaBitVector visited(GetGraph()->GetArena(), 0, /* expandable */ true); + if (!IsConstantEquivalent(phi, other_phi, &visited)) { + AddError(StringPrintf("Two phis (%d and %d) found for VReg %d but they " + "are not equivalents of constants.", + phi->GetId(), + other_phi->GetId(), + phi->GetRegNumber())); + } + } + } + } } void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) { diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index bc4a663b86..ec4a9ec916 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -63,8 +63,8 @@ class LICMTest : public testing::Test { // Provide boiler-plate instructions. parameter_ = new (&allocator_) HParameterValue(0, Primitive::kPrimNot); entry_->AddInstruction(parameter_); - constant_ = new (&allocator_) HConstant(Primitive::kPrimInt); - loop_preheader_->AddInstruction(constant_); + constant_ = graph_->GetIntConstant(42); + loop_preheader_->AddInstruction(new (&allocator_) HGoto()); loop_header_->AddInstruction(new (&allocator_) HIf(parameter_)); loop_body_->AddInstruction(new (&allocator_) HGoto()); exit_->AddInstruction(new (&allocator_) HExit()); @@ -99,23 +99,6 @@ class LICMTest : public testing::Test { // The actual LICM tests. // -TEST_F(LICMTest, ConstantHoisting) { - BuildLoop(); - - // Populate the loop with instructions: set array to constant. - HInstruction* constant = new (&allocator_) HConstant(Primitive::kPrimDouble); - loop_body_->InsertInstructionBefore(constant, loop_body_->GetLastInstruction()); - HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, constant_, constant, Primitive::kPrimDouble, 0); - loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); - - EXPECT_EQ(constant->GetBlock(), loop_body_); - EXPECT_EQ(set_array->GetBlock(), loop_body_); - PerformLICM(); - EXPECT_EQ(constant->GetBlock(), loop_preheader_); - EXPECT_EQ(set_array->GetBlock(), loop_body_); -} - TEST_F(LICMTest, FieldHoisting) { BuildLoop(); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index b3cf0b3745..46ef04a868 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -345,16 +345,6 @@ void HGraph::ComputeTryBlockInformation() { } } -bool HGraph::HasTryCatch() const { - for (size_t i = 0, e = blocks_.Size(); i < e; ++i) { - HBasicBlock* block = blocks_.Get(i); - if (block != nullptr && (block->IsTryBlock() || block->IsCatchBlock())) { - return true; - } - } - return false; -} - void HGraph::SimplifyCFG() { // Simplify the CFG for future analysis, and code generation: // (1): Split critical edges. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 5ec3f22e81..d52a4f7575 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -157,6 +157,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { number_of_in_vregs_(0), temporaries_vreg_slots_(0), has_bounds_checks_(false), + has_try_catch_(false), debuggable_(debuggable), current_instruction_id_(start_instruction_id), dex_file_(dex_file), @@ -282,7 +283,6 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { } uint16_t GetNumberOfVRegs() const { - DCHECK(!in_ssa_form_); return number_of_vregs_; } @@ -360,8 +360,8 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { return instruction_set_; } - // TODO: Remove once the full compilation pipeline is enabled for try/catch. - bool HasTryCatch() const; + bool HasTryCatch() const { return has_try_catch_; } + void SetHasTryCatch(bool value) { has_try_catch_ = value; } private: void VisitBlockForDominatorTree(HBasicBlock* block, @@ -433,6 +433,10 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { // Has bounds checks. We can totally skip BCE if it's false. bool has_bounds_checks_; + // Flag whether there are any try/catch blocks in the graph. We will skip + // try/catch-related passes if false. + bool has_try_catch_; + // Indicates whether the graph should be compiled in a way that // ensures full debuggability. If false, we can apply more // aggressive optimizations that may limit the level of debugging. @@ -2187,6 +2191,8 @@ class HConstant : public HExpression<0> { virtual bool IsZero() const { return false; } virtual bool IsOne() const { return false; } + virtual uint64_t GetValueAsUint64() const = 0; + DECLARE_INSTRUCTION(Constant); private: @@ -2199,6 +2205,8 @@ class HNullConstant : public HConstant { return true; } + uint64_t GetValueAsUint64() const OVERRIDE { return 0; } + size_t ComputeHashCode() const OVERRIDE { return 0; } DECLARE_INSTRUCTION(NullConstant); @@ -2216,6 +2224,8 @@ class HIntConstant : public HConstant { public: int32_t GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { return static_cast<uint64_t>(value_); } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsIntConstant()); return other->AsIntConstant()->value_ == value_; @@ -2247,6 +2257,8 @@ class HLongConstant : public HConstant { public: int64_t GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { return value_; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsLongConstant()); return other->AsLongConstant()->value_ == value_; @@ -2866,10 +2878,13 @@ class HFloatConstant : public HConstant { public: float GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { + return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_)); + } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsFloatConstant()); - return bit_cast<uint32_t, float>(other->AsFloatConstant()->value_) == - bit_cast<uint32_t, float>(value_); + return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64(); } size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } @@ -2907,10 +2922,11 @@ class HDoubleConstant : public HConstant { public: double GetValue() const { return value_; } + uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { DCHECK(other->IsDoubleConstant()); - return bit_cast<uint64_t, double>(other->AsDoubleConstant()->value_) == - bit_cast<uint64_t, double>(value_); + return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64(); } size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); } @@ -4003,6 +4019,13 @@ class HPhi : public HInstruction { bool IsDead() const { return !is_live_; } bool IsLive() const { return is_live_; } + bool IsVRegEquivalentOf(HInstruction* other) const { + return other != nullptr + && other->IsPhi() + && other->AsPhi()->GetBlock() == GetBlock() + && other->AsPhi()->GetRegNumber() == GetRegNumber(); + } + // Returns the next equivalent phi (starting from the current one) or null if there is none. // An equivalent phi is a phi having the same dex register and type. // It assumes that phis with the same dex register are adjacent. diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f549ba8391..0099b21343 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -485,34 +485,43 @@ static void RunOptimizations(HGraph* graph, RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer); + // TODO: Update passes incompatible with try/catch so we have the same + // pipeline for all methods. if (graph->HasTryCatch()) { - // TODO: Update the optimizations below to work correctly under try/catch - // semantics. The optimizations above suffice for running codegen - // in the meanwhile. - return; + HOptimization* optimizations2[] = { + side_effects, + gvn, + dce2, + // The codegen has a few assumptions that only the instruction simplifier + // can satisfy. For example, the code generator does not expect to see a + // HTypeConversion from a type to the same type. + simplify4, + }; + + RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); + } else { + MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles); + + HOptimization* optimizations2[] = { + // BooleanSimplifier depends on the InstructionSimplifier removing + // redundant suspend checks to recognize empty blocks. + boolean_simplify, + fold2, // TODO: if we don't inline we can also skip fold2. + side_effects, + gvn, + licm, + bce, + simplify3, + dce2, + // The codegen has a few assumptions that only the instruction simplifier + // can satisfy. For example, the code generator does not expect to see a + // HTypeConversion from a type to the same type. + simplify4, + }; + + RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); } - MaybeRunInliner(graph, driver, stats, dex_compilation_unit, pass_observer, handles); - - HOptimization* optimizations2[] = { - // BooleanSimplifier depends on the InstructionSimplifier removing redundant - // suspend checks to recognize empty blocks. - boolean_simplify, - fold2, // TODO: if we don't inline we can also skip fold2. - side_effects, - gvn, - licm, - bce, - simplify3, - dce2, - // The codegen has a few assumptions that only the instruction simplifier can - // satisfy. For example, the code generator does not expect to see a - // HTypeConversion from a type to the same type. - simplify4, - }; - - RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); - RunArchOptimizations(driver->GetInstructionSet(), graph, stats, pass_observer); } @@ -566,11 +575,6 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, RunOptimizations(graph, compiler_driver, compilation_stats_.get(), dex_compilation_unit, pass_observer, &handles); - if (graph->HasTryCatch()) { - soa.Self()->TransitionFromSuspendedToRunnable(); - return nullptr; - } - AllocateRegisters(graph, codegen, pass_observer); ArenaAllocator* arena = graph->GetArena(); diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 6f1f6af62d..a4f1f458fd 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -56,6 +56,7 @@ RegisterAllocator::RegisterAllocator(ArenaAllocator* allocator, long_spill_slots_(allocator, kDefaultNumberOfSpillSlots), float_spill_slots_(allocator, kDefaultNumberOfSpillSlots), double_spill_slots_(allocator, kDefaultNumberOfSpillSlots), + catch_phi_spill_slots_(0), safepoints_(allocator, 0), processing_core_registers_(false), number_of_registers_(-1), @@ -124,9 +125,7 @@ void RegisterAllocator::AllocateRegisters() { } } -void RegisterAllocator::BlockRegister(Location location, - size_t start, - size_t end) { +void RegisterAllocator::BlockRegister(Location location, size_t start, size_t end) { int reg = location.reg(); DCHECK(location.IsRegister() || location.IsFpuRegister()); LiveInterval* interval = location.IsRegister() @@ -147,6 +146,19 @@ void RegisterAllocator::BlockRegister(Location location, interval->AddRange(start, end); } +void RegisterAllocator::BlockRegisters(size_t start, size_t end, bool caller_save_only) { + for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) { + if (!caller_save_only || !codegen_->IsCoreCalleeSaveRegister(i)) { + BlockRegister(Location::RegisterLocation(i), start, end); + } + } + for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) { + if (!caller_save_only || !codegen_->IsFloatingPointCalleeSaveRegister(i)) { + BlockRegister(Location::FpuRegisterLocation(i), start, end); + } + } +} + void RegisterAllocator::AllocateRegistersInternal() { // Iterate post-order, to ensure the list is sorted, and the last added interval // is the one with the lowest start position. @@ -159,6 +171,13 @@ void RegisterAllocator::AllocateRegistersInternal() { for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { ProcessInstruction(inst_it.Current()); } + + if (block->IsCatchBlock()) { + // By blocking all registers at the top of each catch block, we force + // intervals used after catch to spill. + size_t position = block->GetLifetimeStart(); + BlockRegisters(position, position + 1); + } } number_of_registers_ = codegen_->GetNumberOfCoreRegisters(); @@ -275,21 +294,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { } if (locations->WillCall()) { - // Block all registers. - for (size_t i = 0; i < codegen_->GetNumberOfCoreRegisters(); ++i) { - if (!codegen_->IsCoreCalleeSaveRegister(i)) { - BlockRegister(Location::RegisterLocation(i), - position, - position + 1); - } - } - for (size_t i = 0; i < codegen_->GetNumberOfFloatingPointRegisters(); ++i) { - if (!codegen_->IsFloatingPointCalleeSaveRegister(i)) { - BlockRegister(Location::FpuRegisterLocation(i), - position, - position + 1); - } - } + BlockRegisters(position, position + 1, /* caller_save_only */ true); } for (size_t i = 0; i < instruction->InputCount(); ++i) { @@ -378,6 +383,10 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { DCHECK(output.IsUnallocated() || output.IsConstant()); } + if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) { + AllocateSpillSlotForCatchPhi(instruction->AsPhi()); + } + // If needed, add interval to the list of unhandled intervals. if (current->HasSpillSlot() || instruction->IsConstant()) { // Split just before first register use. @@ -1282,6 +1291,8 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { } HInstruction* defined_by = parent->GetDefinedBy(); + DCHECK(!defined_by->IsPhi() || !defined_by->AsPhi()->IsCatchPhi()); + if (defined_by->IsParameterValue()) { // Parameters have their own stack slot. parent->SetSpillSlot(codegen_->GetStackSlotOfParameter(defined_by->AsParameterValue())); @@ -1298,12 +1309,6 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { return; } - LiveInterval* last_sibling = interval; - while (last_sibling->GetNextSibling() != nullptr) { - last_sibling = last_sibling->GetNextSibling(); - } - size_t end = last_sibling->GetEnd(); - GrowableArray<size_t>* spill_slots = nullptr; switch (interval->GetType()) { case Primitive::kPrimDouble: @@ -1336,6 +1341,7 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) { } } + size_t end = interval->GetLastSibling()->GetEnd(); if (parent->NeedsTwoSpillSlots()) { if (slot == spill_slots->Size()) { // We need a new spill slot. @@ -1371,6 +1377,28 @@ static bool IsValidDestination(Location destination) { || destination.IsDoubleStackSlot(); } +void RegisterAllocator::AllocateSpillSlotForCatchPhi(HPhi* phi) { + LiveInterval* interval = phi->GetLiveInterval(); + + HInstruction* previous_phi = phi->GetPrevious(); + DCHECK(previous_phi == nullptr || + previous_phi->AsPhi()->GetRegNumber() <= phi->GetRegNumber()) + << "Phis expected to be sorted by vreg number, so that equivalent phis are adjacent."; + + if (phi->IsVRegEquivalentOf(previous_phi)) { + // This is an equivalent of the previous phi. We need to assign the same + // catch phi slot. + DCHECK(previous_phi->GetLiveInterval()->HasSpillSlot()); + interval->SetSpillSlot(previous_phi->GetLiveInterval()->GetSpillSlot()); + } else { + // Allocate a new spill slot for this catch phi. + // TODO: Reuse spill slots when intervals of phis from different catch + // blocks do not overlap. + interval->SetSpillSlot(catch_phi_spill_slots_); + catch_phi_spill_slots_ += interval->NeedsTwoSpillSlots() ? 2 : 1; + } +} + void RegisterAllocator::AddMove(HParallelMove* move, Location source, Location destination, @@ -1497,7 +1525,7 @@ void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block, DCHECK(IsValidDestination(destination)) << destination; if (source.Equals(destination)) return; - DCHECK_EQ(block->GetSuccessors().size(), 1u); + DCHECK_EQ(block->NumberOfNormalSuccessors(), 1u); HInstruction* last = block->GetLastInstruction(); // We insert moves at exit for phi predecessors and connecting blocks. // A block ending with an if cannot branch to a block with phis because @@ -1724,7 +1752,7 @@ void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, // If `from` has only one successor, we can put the moves at the exit of it. Otherwise // we need to put the moves at the entry of `to`. - if (from->GetSuccessors().size() == 1) { + if (from->NumberOfNormalSuccessors() == 1) { InsertParallelMoveAtExitOf(from, interval->GetParent()->GetDefinedBy(), source->ToLocation(), @@ -1768,17 +1796,25 @@ void RegisterAllocator::Resolve() { } else if (instruction->IsCurrentMethod()) { // The current method is always at offset 0. DCHECK(!current->HasSpillSlot() || (current->GetSpillSlot() == 0)); + } else if (instruction->IsPhi() && instruction->AsPhi()->IsCatchPhi()) { + DCHECK(current->HasSpillSlot()); + size_t slot = current->GetSpillSlot() + + GetNumberOfSpillSlots() + + reserved_out_slots_ + - catch_phi_spill_slots_; + current->SetSpillSlot(slot * kVRegSize); } else if (current->HasSpillSlot()) { // Adjust the stack slot, now that we know the number of them for each type. // The way this implementation lays out the stack is the following: - // [parameter slots ] - // [double spill slots ] - // [long spill slots ] - // [float spill slots ] - // [int/ref values ] - // [maximum out values ] (number of arguments for calls) - // [art method ]. - uint32_t slot = current->GetSpillSlot(); + // [parameter slots ] + // [catch phi spill slots ] + // [double spill slots ] + // [long spill slots ] + // [float spill slots ] + // [int/ref values ] + // [maximum out values ] (number of arguments for calls) + // [art method ]. + size_t slot = current->GetSpillSlot(); switch (current->GetType()) { case Primitive::kPrimDouble: slot += long_spill_slots_.Size(); @@ -1828,12 +1864,22 @@ void RegisterAllocator::Resolve() { // Resolve non-linear control flow across branches. Order does not matter. for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - BitVector* live = liveness_.GetLiveInSet(*block); - for (uint32_t idx : live->Indexes()) { - HInstruction* current = liveness_.GetInstructionFromSsaIndex(idx); - LiveInterval* interval = current->GetLiveInterval(); - for (HBasicBlock* predecessor : block->GetPredecessors()) { - ConnectSplitSiblings(interval, predecessor, block); + if (block->IsCatchBlock()) { + // Instructions live at the top of catch blocks were forced to spill. + if (kIsDebugBuild) { + BitVector* live = liveness_.GetLiveInSet(*block); + for (uint32_t idx : live->Indexes()) { + LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval(); + DCHECK(!interval->GetSiblingAt(block->GetLifetimeStart())->HasRegister()); + } + } + } else { + BitVector* live = liveness_.GetLiveInSet(*block); + for (uint32_t idx : live->Indexes()) { + LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval(); + for (HBasicBlock* predecessor : block->GetPredecessors()) { + ConnectSplitSiblings(interval, predecessor, block); + } } } } @@ -1841,16 +1887,20 @@ void RegisterAllocator::Resolve() { // Resolve phi inputs. Order does not matter. for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) { HBasicBlock* current = it.Current(); - for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) { - HInstruction* phi = inst_it.Current(); - for (size_t i = 0, e = current->GetPredecessors().size(); i < e; ++i) { - HBasicBlock* predecessor = current->GetPredecessor(i); - DCHECK_EQ(predecessor->GetSuccessors().size(), 1u); - HInstruction* input = phi->InputAt(i); - Location source = input->GetLiveInterval()->GetLocationAt( - predecessor->GetLifetimeEnd() - 1); - Location destination = phi->GetLiveInterval()->ToLocation(); - InsertParallelMoveAtExitOf(predecessor, phi, source, destination); + if (current->IsCatchBlock()) { + // Catch phi values are set at runtime by the exception delivery mechanism. + } else { + for (HInstructionIterator inst_it(current->GetPhis()); !inst_it.Done(); inst_it.Advance()) { + HInstruction* phi = inst_it.Current(); + for (size_t i = 0, e = current->GetPredecessors().size(); i < e; ++i) { + HBasicBlock* predecessor = current->GetPredecessor(i); + DCHECK_EQ(predecessor->NumberOfNormalSuccessors(), 1u); + HInstruction* input = phi->InputAt(i); + Location source = input->GetLiveInterval()->GetLocationAt( + predecessor->GetLifetimeEnd() - 1); + Location destination = phi->GetLiveInterval()->ToLocation(); + InsertParallelMoveAtExitOf(predecessor, phi, source, destination); + } } } } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index c29fe75921..e0304643e6 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -29,6 +29,7 @@ class HBasicBlock; class HGraph; class HInstruction; class HParallelMove; +class HPhi; class LiveInterval; class Location; class SsaLivenessAnalysis; @@ -72,7 +73,8 @@ class RegisterAllocator { return int_spill_slots_.Size() + long_spill_slots_.Size() + float_spill_slots_.Size() - + double_spill_slots_.Size(); + + double_spill_slots_.Size() + + catch_phi_spill_slots_; } static constexpr const char* kRegisterAllocatorPassName = "register"; @@ -99,10 +101,17 @@ class RegisterAllocator { // Update the interval for the register in `location` to cover [start, end). void BlockRegister(Location location, size_t start, size_t end); + void BlockRegisters(size_t start, size_t end, bool caller_save_only = false); - // Allocate a spill slot for the given interval. + // Allocate a spill slot for the given interval. Should be called in linear + // order of interval starting positions. void AllocateSpillSlotFor(LiveInterval* interval); + // Allocate a spill slot for the given catch phi. Will allocate the same slot + // for phis which share the same vreg. Must be called in reverse linear order + // of lifetime positions and ascending vreg numbers for correctness. + void AllocateSpillSlotForCatchPhi(HPhi* phi); + // Connect adjacent siblings within blocks. void ConnectSiblings(LiveInterval* interval); @@ -202,6 +211,11 @@ class RegisterAllocator { GrowableArray<size_t> float_spill_slots_; GrowableArray<size_t> double_spill_slots_; + // Spill slots allocated to catch phis. This category is special-cased because + // (1) slots are allocated prior to linear scan and in reverse linear order, + // (2) equivalent phis need to share slots despite having different types. + size_t catch_phi_spill_slots_; + // Instructions that need a safepoint. GrowableArray<HInstruction*> safepoints_; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 0c1831b45f..63635f3127 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -186,14 +186,25 @@ void SsaLivenessAnalysis::ComputeLiveRanges() { // as live_in. for (HBasicBlock* successor : block->GetSuccessors()) { live_in->Union(GetLiveInSet(*successor)); - size_t phi_input_index = successor->GetPredecessorIndexOf(block); - for (HInstructionIterator inst_it(successor->GetPhis()); !inst_it.Done(); inst_it.Advance()) { - HInstruction* phi = inst_it.Current(); - HInstruction* input = phi->InputAt(phi_input_index); - input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block); - // A phi input whose last user is the phi dies at the end of the predecessor block, - // and not at the phi's lifetime position. - live_in->SetBit(input->GetSsaIndex()); + if (successor->IsCatchBlock()) { + // Inputs of catch phis will be kept alive through their environment + // uses, allowing the runtime to copy their values to the corresponding + // catch phi spill slots when an exception is thrown. + // The only instructions which may not be recorded in the environments + // are constants created by the SSA builder as typed equivalents of + // untyped constants from the bytecode, or phis with only such constants + // as inputs (verified by SSAChecker). Their raw binary value must + // therefore be the same and we only need to keep alive one. + } else { + size_t phi_input_index = successor->GetPredecessorIndexOf(block); + for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + HInstruction* phi = phi_it.Current(); + HInstruction* input = phi->InputAt(phi_input_index); + input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block); + // A phi input whose last user is the phi dies at the end of the predecessor block, + // and not at the phi's lifetime position. + live_in->SetBit(input->GetSsaIndex()); + } } } diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index a7044de850..ef396cbdba 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -1209,6 +1209,9 @@ class SsaLivenessAnalysis : public ValueObject { // A value that's not live in compiled code may still be needed in interpreter, // due to code motion, etc. if (env_holder->IsDeoptimize()) return true; + // A value live at a throwing instruction in a try block may be copied by + // the exception handler to its location at the top of the catch block. + if (env_holder->CanThrowIntoCatchBlock()) return true; if (instruction->GetBlock()->GetGraph()->IsDebuggable()) return true; return instruction->GetType() == Primitive::kPrimNot; } diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 1f1530fa1e..1f0bac59e0 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -286,7 +286,7 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetDexRegisterMapOffset( stack_map_encoding_, code_info.GetStackMapAt(entry.same_dex_register_map_as_, stack_map_encoding_) - .GetDexRegisterMapOffset(stack_map_encoding_)); + .GetDexRegisterMapOffset(stack_map_encoding_)); } else { // New dex registers maps should be added to the stack map. MemoryRegion register_region = dex_register_locations_region.Subregion( |