diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/compiled_method.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/builder.cc | 24 | ||||
-rw-r--r-- | compiler/optimizing/builder.h | 3 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 38 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 35 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 35 | ||||
-rw-r--r-- | compiler/optimizing/codegen_test.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/live_ranges_test.cc | 3 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 20 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_unit_test.h | 13 | ||||
-rw-r--r-- | compiler/optimizing/pretty_printer_test.cc | 47 | ||||
-rw-r--r-- | compiler/optimizing/register_allocator.cc | 3 | ||||
-rw-r--r-- | compiler/optimizing/register_allocator.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/register_allocator_test.cc | 55 | ||||
-rw-r--r-- | compiler/optimizing/ssa_test.cc | 3 | ||||
-rw-r--r-- | compiler/optimizing/suspend_check_test.cc | 95 |
16 files changed, 358 insertions, 24 deletions
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 3e34144836..cc46b92dc5 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -154,7 +154,7 @@ class SrcMap FINAL : public std::vector<SrcMapElem> { // get rid of the highest values size_t i = size() - 1; for (; i > 0 ; i--) { - if ((*this)[i].from_ >= highest_pc) { + if ((*this)[i].from_ < highest_pc) { break; } } diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index ecd6802ca4..a03588f4fd 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -138,13 +138,15 @@ static bool CanHandleCodeItem(const DexFile::CodeItem& code_item) { template<typename T> void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_offset) { + int32_t target_offset = instruction.GetTargetOffset(); + PotentiallyAddSuspendCheck(target_offset, dex_offset); HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); T* comparison = new (arena_) T(first, second); current_block_->AddInstruction(comparison); HInstruction* ifinst = new (arena_) HIf(comparison); current_block_->AddInstruction(ifinst); - HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset()); + HBasicBlock* target = FindBlockStartingAt(dex_offset + target_offset); DCHECK(target != nullptr); current_block_->AddSuccessor(target); target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); @@ -155,12 +157,14 @@ void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_offset) template<typename T> void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_offset) { + int32_t target_offset = instruction.GetTargetOffset(); + PotentiallyAddSuspendCheck(target_offset, dex_offset); HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); T* comparison = new (arena_) T(value, GetIntConstant(0)); current_block_->AddInstruction(comparison); HInstruction* ifinst = new (arena_) HIf(comparison); current_block_->AddInstruction(ifinst); - HBasicBlock* target = FindBlockStartingAt(dex_offset + instruction.GetTargetOffset()); + HBasicBlock* target = FindBlockStartingAt(dex_offset + target_offset); DCHECK(target != nullptr); current_block_->AddSuccessor(target); target = FindBlockStartingAt(dex_offset + instruction.SizeInCodeUnits()); @@ -209,6 +213,8 @@ HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { // Add the exit block at the end to give it the highest id. graph_->AddBlock(exit_block_); exit_block_->AddInstruction(new (arena_) HExit()); + // Add the suspend check to the entry block. + entry_block_->AddInstruction(new (arena_) HSuspendCheck(0)); entry_block_->AddInstruction(new (arena_) HGoto()); return graph_; } @@ -462,7 +468,15 @@ void HGraphBuilder::BuildArrayAccess(const Instruction& instruction, } } -bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset) { +void HGraphBuilder::PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset) { + if (target_offset <= 0) { + // Unconditionnally add a suspend check to backward branches. We can remove + // them after we recognize loops in the graph. + current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_offset)); + } +} + +bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_offset) { if (current_block_ == nullptr) { return true; // Dead code } @@ -580,7 +594,9 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, int32_ case Instruction::GOTO: case Instruction::GOTO_16: case Instruction::GOTO_32: { - HBasicBlock* target = FindBlockStartingAt(instruction.GetTargetOffset() + dex_offset); + int32_t offset = instruction.GetTargetOffset(); + PotentiallyAddSuspendCheck(offset, dex_offset); + HBasicBlock* target = FindBlockStartingAt(offset + dex_offset); DCHECK(target != nullptr); current_block_->AddInstruction(new (arena_) HGoto()); current_block_->AddSuccessor(target); diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 170c42761a..e143786be7 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -54,7 +54,7 @@ class HGraphBuilder : public ValueObject { // Analyzes the dex instruction and adds HInstruction to the graph // to execute that instruction. Returns whether the instruction can // be handled. - bool AnalyzeDexInstruction(const Instruction& instruction, int32_t dex_offset); + bool AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_offset); // Finds all instructions that start a new block, and populates branch_targets_ with // the newly created blocks. @@ -70,6 +70,7 @@ class HGraphBuilder : public ValueObject { HLocal* GetLocalAt(int register_index) const; void UpdateLocal(int register_index, HInstruction* instruction) const; HInstruction* LoadLocal(int register_index, Primitive::Type type) const; + void PotentiallyAddSuspendCheck(int32_t target_offset, uint32_t dex_offset); // Temporarily returns whether the compiler supports the parameters // of the method. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index e72e39ba71..99030922a7 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -90,6 +90,29 @@ class StackOverflowCheckSlowPathARM : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM); }; +class SuspendCheckSlowPathARM : public SlowPathCode { + public: + explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction) + : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value(); + __ ldr(LR, Address(TR, offset)); + __ blx(LR); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + __ b(GetReturnLabel()); + } + + Label* GetReturnLabel() { return &return_label_; } + + private: + HSuspendCheck* const instruction_; + Label return_label_; + + DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM); +}; + class BoundsCheckSlowPathARM : public SlowPathCode { public: explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction, @@ -1494,6 +1517,21 @@ void InstructionCodeGeneratorARM::VisitParallelMove(HParallelMove* instruction) codegen_->GetMoveResolver()->EmitNativeCode(instruction); } +void LocationsBuilderARM::VisitSuspendCheck(HSuspendCheck* instruction) { + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); +} + +void InstructionCodeGeneratorARM::VisitSuspendCheck(HSuspendCheck* instruction) { + SuspendCheckSlowPathARM* slow_path = + new (GetGraph()->GetArena()) SuspendCheckSlowPathARM(instruction); + codegen_->AddSlowPath(slow_path); + + __ AddConstant(R4, R4, -1); + __ cmp(R4, ShifterOperand(0)); + __ b(slow_path->GetEntryLabel(), LE); + __ Bind(slow_path->GetReturnLabel()); +} + ArmAssembler* ParallelMoveResolverARM::GetAssembler() const { return codegen_->GetAssembler(); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 6602d3fb45..3dd9b37158 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -114,6 +114,27 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); }; +class SuspendCheckSlowPathX86 : public SlowPathCode { + public: + explicit SuspendCheckSlowPathX86(HSuspendCheck* instruction) + : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pTestSuspend))); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + __ jmp(GetReturnLabel()); + } + + Label* GetReturnLabel() { return &return_label_; } + + private: + HSuspendCheck* const instruction_; + Label return_label_; + + DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86); +}; + #undef __ #define __ reinterpret_cast<X86Assembler*>(GetAssembler())-> @@ -1483,6 +1504,20 @@ void InstructionCodeGeneratorX86::VisitParallelMove(HParallelMove* instruction) codegen_->GetMoveResolver()->EmitNativeCode(instruction); } +void LocationsBuilderX86::VisitSuspendCheck(HSuspendCheck* instruction) { + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); +} + +void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction) { + SuspendCheckSlowPathX86* slow_path = + new (GetGraph()->GetArena()) SuspendCheckSlowPathX86(instruction); + codegen_->AddSlowPath(slow_path); + __ fs()->cmpl(Address::Absolute( + Thread::ThreadFlagsOffset<kX86WordSize>().Int32Value()), Immediate(0)); + __ j(kNotEqual, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetReturnLabel()); +} + X86Assembler* ParallelMoveResolverX86::GetAssembler() const { return codegen_->GetAssembler(); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index b2d81e35dd..2f352e0838 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -95,6 +95,27 @@ class StackOverflowCheckSlowPathX86_64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64); }; +class SuspendCheckSlowPathX86_64 : public SlowPathCode { + public: + explicit SuspendCheckSlowPathX86_64(HSuspendCheck* instruction) + : instruction_(instruction) {} + + virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pTestSuspend), true)); + codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + __ jmp(GetReturnLabel()); + } + + Label* GetReturnLabel() { return &return_label_; } + + private: + HSuspendCheck* const instruction_; + Label return_label_; + + DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86_64); +}; + class BoundsCheckSlowPathX86_64 : public SlowPathCode { public: explicit BoundsCheckSlowPathX86_64(HBoundsCheck* instruction, @@ -1329,6 +1350,20 @@ void InstructionCodeGeneratorX86_64::VisitParallelMove(HParallelMove* instructio codegen_->GetMoveResolver()->EmitNativeCode(instruction); } +void LocationsBuilderX86_64::VisitSuspendCheck(HSuspendCheck* instruction) { + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); +} + +void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instruction) { + SuspendCheckSlowPathX86_64* slow_path = + new (GetGraph()->GetArena()) SuspendCheckSlowPathX86_64(instruction); + codegen_->AddSlowPath(slow_path); + __ gs()->cmpl(Address::Absolute( + Thread::ThreadFlagsOffset<kX86_64WordSize>().Int32Value(), true), Immediate(0)); + __ j(kNotEqual, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetReturnLabel()); +} + X86_64Assembler* ParallelMoveResolverX86_64::GetAssembler() const { return codegen_->GetAssembler(); } diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index b9712e148c..7161eed9f9 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -72,6 +72,8 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe HGraphBuilder builder(&arena); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); + // Remove suspend checks, they cannot be executed in this context. + RemoveSuspendChecks(graph); ASSERT_NE(graph, nullptr); InternalCodeAllocator allocator; diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 21e634de04..a81a30e457 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -32,6 +32,9 @@ static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) { HGraphBuilder builder(allocator); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); + // Suspend checks implementation may change in the future, and this test relies + // on how instructions are ordered. + RemoveSuspendChecks(graph); graph->BuildDominatorTree(); graph->TransformToSSA(); graph->FindNaturalLoops(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9018fee0a5..ed6dd939de 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -443,6 +443,7 @@ class HBasicBlock : public ArenaObject { M(BoundsCheck) \ M(NullCheck) \ M(Temporary) \ + M(SuspendCheck) \ #define FOR_EACH_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION(M) \ @@ -1593,6 +1594,25 @@ class HTemporary : public HTemplateInstruction<0> { DISALLOW_COPY_AND_ASSIGN(HTemporary); }; +class HSuspendCheck : public HTemplateInstruction<0> { + public: + explicit HSuspendCheck(uint32_t dex_pc) + : HTemplateInstruction(SideEffects::ChangesSomething()), dex_pc_(dex_pc) {} + + virtual bool NeedsEnvironment() const { + return true; + } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(SuspendCheck); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HSuspendCheck); +}; + class MoveOperands : public ArenaObject { public: MoveOperands(Location source, Location destination) diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 36a6a21d01..c409529727 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -48,6 +48,19 @@ LiveInterval* BuildInterval(const size_t ranges[][2], return interval; } +void RemoveSuspendChecks(HGraph* graph) { + for (size_t i = 0, e = graph->GetBlocks().Size(); i < e; ++i) { + for (HInstructionIterator it(graph->GetBlocks().Get(i)->GetInstructions()); + !it.Done(); + it.Advance()) { + HInstruction* current = it.Current(); + if (current->IsSuspendCheck()) { + current->GetBlock()->RemoveInstruction(current); + } + } + } +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 7e604e99b4..da6b294d71 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -45,7 +45,8 @@ TEST(PrettyPrinterTest, ReturnVoid) { const char* expected = "BasicBlock 0, succ: 1\n" - " 2: Goto 1\n" + " 2: SuspendCheck\n" + " 3: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" " 0: ReturnVoid\n" "BasicBlock 2, pred: 1\n" @@ -57,7 +58,8 @@ TEST(PrettyPrinterTest, ReturnVoid) { TEST(PrettyPrinterTest, CFG1) { const char* expected = "BasicBlock 0, succ: 1\n" - " 3: Goto 1\n" + " 3: SuspendCheck\n" + " 4: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" " 0: Goto 2\n" "BasicBlock 2, pred: 1, succ: 3\n" @@ -76,7 +78,8 @@ TEST(PrettyPrinterTest, CFG1) { TEST(PrettyPrinterTest, CFG2) { const char* expected = "BasicBlock 0, succ: 1\n" - " 4: Goto 1\n" + " 4: SuspendCheck\n" + " 5: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" " 0: Goto 2\n" "BasicBlock 2, pred: 1, succ: 3\n" @@ -97,15 +100,17 @@ TEST(PrettyPrinterTest, CFG2) { TEST(PrettyPrinterTest, CFG3) { const char* expected = "BasicBlock 0, succ: 1\n" - " 4: Goto 1\n" + " 5: SuspendCheck\n" + " 6: Goto 1\n" "BasicBlock 1, pred: 0, succ: 3\n" " 0: Goto 3\n" "BasicBlock 2, pred: 3, succ: 4\n" " 1: ReturnVoid\n" "BasicBlock 3, pred: 1, succ: 2\n" - " 2: Goto 2\n" + " 2: SuspendCheck\n" + " 3: Goto 2\n" "BasicBlock 4, pred: 2\n" - " 3: Exit\n"; + " 4: Exit\n"; const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x200, @@ -132,11 +137,13 @@ TEST(PrettyPrinterTest, CFG3) { TEST(PrettyPrinterTest, CFG4) { const char* expected = "BasicBlock 0, succ: 1\n" - " 2: Goto 1\n" + " 3: SuspendCheck\n" + " 4: Goto 1\n" "BasicBlock 1, pred: 0, 1, succ: 1\n" - " 0: Goto 1\n" + " 0: SuspendCheck\n" + " 1: Goto 1\n" "BasicBlock 2\n" - " 1: Exit\n"; + " 2: Exit\n"; const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, @@ -153,13 +160,15 @@ TEST(PrettyPrinterTest, CFG4) { TEST(PrettyPrinterTest, CFG5) { const char* expected = "BasicBlock 0, succ: 1\n" - " 3: Goto 1\n" + " 4: SuspendCheck\n" + " 5: Goto 1\n" "BasicBlock 1, pred: 0, 2, succ: 3\n" " 0: ReturnVoid\n" "BasicBlock 2, succ: 1\n" - " 1: Goto 1\n" + " 1: SuspendCheck\n" + " 2: Goto 1\n" "BasicBlock 3, pred: 1\n" - " 2: Exit\n"; + " 3: Exit\n"; const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID, @@ -174,7 +183,8 @@ TEST(PrettyPrinterTest, CFG6) { "BasicBlock 0, succ: 1\n" " 0: Local [4, 3, 2]\n" " 1: IntConstant [2]\n" - " 10: Goto 1\n" + " 10: SuspendCheck\n" + " 11: Goto 1\n" "BasicBlock 1, pred: 0, succ: 3, 2\n" " 2: StoreLocal(0, 1)\n" " 3: LoadLocal(0) [5]\n" @@ -202,7 +212,8 @@ TEST(PrettyPrinterTest, CFG7) { "BasicBlock 0, succ: 1\n" " 0: Local [4, 3, 2]\n" " 1: IntConstant [2]\n" - " 10: Goto 1\n" + " 11: SuspendCheck\n" + " 12: Goto 1\n" "BasicBlock 1, pred: 0, succ: 3, 2\n" " 2: StoreLocal(0, 1)\n" " 3: LoadLocal(0) [5]\n" @@ -212,9 +223,10 @@ TEST(PrettyPrinterTest, CFG7) { "BasicBlock 2, pred: 1, 3, succ: 3\n" " 7: Goto 3\n" "BasicBlock 3, pred: 1, 2, succ: 2\n" - " 8: Goto 2\n" + " 8: SuspendCheck\n" + " 9: Goto 2\n" "BasicBlock 4\n" - " 9: Exit\n"; + " 10: Exit\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -230,7 +242,8 @@ TEST(PrettyPrinterTest, IntConstant) { "BasicBlock 0, succ: 1\n" " 0: Local [2]\n" " 1: IntConstant [2]\n" - " 5: Goto 1\n" + " 5: SuspendCheck\n" + " 6: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" " 2: StoreLocal(0, 1)\n" " 3: ReturnVoid\n" diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index b451ef4c4b..786261121b 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -440,7 +440,8 @@ bool RegisterAllocator::TryAllocateFreeReg(LiveInterval* current) { DCHECK(inactive->HasRegister()); size_t next_intersection = inactive->FirstIntersectionWith(current); if (next_intersection != kNoLifetime) { - free_until[inactive->GetRegister()] = next_intersection; + free_until[inactive->GetRegister()] = + std::min(free_until[inactive->GetRegister()], next_intersection); } } diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index f737491026..7d397e3649 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -21,6 +21,8 @@ #include "primitive.h" #include "utils/growable_array.h" +#include "gtest/gtest.h" + namespace art { class CodeGenerator; @@ -177,6 +179,8 @@ class RegisterAllocator { // Slots reserved for out arguments. size_t reserved_out_slots_; + FRIEND_TEST(RegisterAllocatorTest, FreeUntil); + DISALLOW_COPY_AND_ASSIGN(RegisterAllocator); }; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index dcae46b4bd..3e3b6b12a2 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -392,4 +392,59 @@ TEST(RegisterAllocatorTest, DeadPhi) { ASSERT_TRUE(register_allocator.Validate(false)); } +/** + * Test that the TryAllocateFreeReg method works in the presence of inactive intervals + * that share the same register. It should split the interval it is currently + * allocating for at the minimum lifetime position between the two inactive intervals. + */ +TEST(RegisterAllocatorTest, FreeUntil) { + const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::RETURN); + + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraph* graph = BuildSSAGraph(data, &allocator); + SsaDeadPhiElimination(graph).Run(); + x86::CodeGeneratorX86 codegen(graph); + SsaLivenessAnalysis liveness(*graph, &codegen); + liveness.Analyze(); + RegisterAllocator register_allocator(&allocator, &codegen, liveness); + + // Add an artifical range to cover the temps that will be put in the unhandled list. + LiveInterval* unhandled = graph->GetEntryBlock()->GetFirstInstruction()->GetLiveInterval(); + unhandled->AddLoopRange(0, 60); + + // Add three temps holding the same register, and starting at different positions. + // Put the one that should be picked in the middle of the inactive list to ensure + // we do not depend on an order. + LiveInterval* interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt); + interval->SetRegister(0); + interval->AddRange(40, 50); + register_allocator.inactive_.Add(interval); + + interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt); + interval->SetRegister(0); + interval->AddRange(20, 30); + register_allocator.inactive_.Add(interval); + + interval = LiveInterval::MakeTempInterval(&allocator, nullptr, Primitive::kPrimInt); + interval->SetRegister(0); + interval->AddRange(60, 70); + register_allocator.inactive_.Add(interval); + + register_allocator.number_of_registers_ = 1; + register_allocator.registers_array_ = allocator.AllocArray<size_t>(1); + register_allocator.processing_core_registers_ = true; + register_allocator.unhandled_ = ®ister_allocator.unhandled_core_intervals_; + + register_allocator.TryAllocateFreeReg(unhandled); + + // Check that we have split the interval. + ASSERT_EQ(1u, register_allocator.unhandled_->Size()); + // Check that we know need to find a new register where the next interval + // that uses the register starts. + ASSERT_EQ(20u, register_allocator.unhandled_->Get(0)->GetStart()); +} + } // namespace art diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index 088a5c4240..99fd9ebacb 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -83,6 +83,9 @@ static void TestCode(const uint16_t* data, const char* expected) { HGraph* graph = builder.BuildGraph(*item); ASSERT_NE(graph, nullptr); + // Suspend checks implementation may change in the future, and this test relies + // on how instructions are ordered. + RemoveSuspendChecks(graph); graph->BuildDominatorTree(); graph->TransformToSSA(); ReNumberInstructions(graph); diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc new file mode 100644 index 0000000000..2e48ee8e7e --- /dev/null +++ b/compiler/optimizing/suspend_check_test.cc @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "builder.h" +#include "dex_instruction.h" +#include "nodes.h" +#include "optimizing_unit_test.h" + +#include "gtest/gtest.h" + +namespace art { + +/** + * Check that the HGraphBuilder adds suspend checks to backward branches. + */ + +static void TestCode(const uint16_t* data) { + ArenaPool pool; + ArenaAllocator allocator(&pool); + HGraphBuilder builder(&allocator); + const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); + HGraph* graph = builder.BuildGraph(*item); + ASSERT_NE(graph, nullptr); + + HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors().Get(0); + HInstruction* first_instruction = first_block->GetFirstInstruction(); + // Account for some tests having a store local as first instruction. + ASSERT_TRUE(first_instruction->IsSuspendCheck() + || first_instruction->GetNext()->IsSuspendCheck()); +} + +TEST(CodegenTest, CFG1) { + const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + Instruction::NOP, + Instruction::GOTO | 0xFF00); + + TestCode(data); +} + +TEST(CodegenTest, CFG2) { + const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + Instruction::GOTO_32, 0, 0); + + TestCode(data); +} + +TEST(CodegenTest, CFG3) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQ, 0xFFFF, + Instruction::RETURN_VOID); + + TestCode(data); +} + +TEST(CodegenTest, CFG4) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_NE, 0xFFFF, + Instruction::RETURN_VOID); + + TestCode(data); +} + +TEST(CodegenTest, CFG5) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_EQZ, 0xFFFF, + Instruction::RETURN_VOID); + + TestCode(data); +} + +TEST(CodegenTest, CFG6) { + const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + Instruction::CONST_4 | 0 | 0, + Instruction::IF_NEZ, 0xFFFF, + Instruction::RETURN_VOID); + + TestCode(data); +} +} // namespace art |