// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/backend/instruction-scheduler.h" #include "src/compiler/backend/instruction-selector-impl.h" #include "src/compiler/backend/instruction.h" #include "test/cctest/cctest.h" namespace v8 { namespace internal { namespace compiler { // Create InstructionBlocks with a single block. InstructionBlocks* CreateSingleBlock(Zone* zone) { InstructionBlock* block = zone->New( zone, RpoNumber::FromInt(0), RpoNumber::Invalid(), RpoNumber::Invalid(), RpoNumber::Invalid(), false, false); InstructionBlocks* blocks = zone->NewArray(1); new (blocks) InstructionBlocks(1, block, zone); return blocks; } // Wrapper around the InstructionScheduler. class InstructionSchedulerTester { public: InstructionSchedulerTester() : scope_(kCompressGraphZone), blocks_(CreateSingleBlock(scope_.main_zone())), sequence_(scope_.main_isolate(), scope_.main_zone(), blocks_), scheduler_(scope_.main_zone(), &sequence_) {} void StartBlock() { scheduler_.StartBlock(RpoNumber::FromInt(0)); } void EndBlock() { scheduler_.EndBlock(RpoNumber::FromInt(0)); } void AddInstruction(Instruction* instr) { scheduler_.AddInstruction(instr); } void AddTerminator(Instruction* instr) { scheduler_.AddTerminator(instr); } void CheckHasSideEffect(Instruction* instr) { CHECK(scheduler_.HasSideEffect(instr)); } void CheckIsDeopt(Instruction* instr) { CHECK(instr->IsDeoptimizeCall()); } void CheckInSuccessors(Instruction* instr, Instruction* successor) { InstructionScheduler::ScheduleGraphNode* node = GetNode(instr); InstructionScheduler::ScheduleGraphNode* succ_node = GetNode(successor); ZoneDeque& successors = node->successors(); CHECK_NE(std::find(successors.begin(), successors.end(), succ_node), successors.end()); } Zone* zone() { return scope_.main_zone(); } private: InstructionScheduler::ScheduleGraphNode* GetNode(Instruction* instr) { for (auto node : scheduler_.graph_) { if (node->instruction() == instr) return node; } return nullptr; } HandleAndZoneScope scope_; InstructionBlocks* blocks_; InstructionSequence sequence_; InstructionScheduler scheduler_; }; TEST(DeoptInMiddleOfBasicBlock) { InstructionSchedulerTester tester; Zone* zone = tester.zone(); tester.StartBlock(); InstructionCode jmp_opcode = kArchJmp; // Dummy node for FlagsContinuation::ForDeoptimize (which won't accept // nullptr). Node* node = Node::New(zone, 0, nullptr, 0, nullptr, false); FeedbackSource feedback; FlagsContinuation cont = FlagsContinuation::ForDeoptimize( kEqual, DeoptimizeKind::kEager, DeoptimizeReason::kUnknown, feedback, node); jmp_opcode = cont.Encode(jmp_opcode); Instruction* jmp_inst = Instruction::New(zone, jmp_opcode); tester.CheckIsDeopt(jmp_inst); tester.AddInstruction(jmp_inst); Instruction* side_effect_inst = Instruction::New(zone, kArchPrepareTailCall); tester.CheckHasSideEffect(side_effect_inst); tester.AddInstruction(side_effect_inst); Instruction* other_jmp_inst = Instruction::New(zone, jmp_opcode); tester.CheckIsDeopt(other_jmp_inst); tester.AddInstruction(other_jmp_inst); Instruction* ret_inst = Instruction::New(zone, kArchRet); tester.AddTerminator(ret_inst); // Check that an instruction with a side effect is a successor of the deopt. tester.CheckInSuccessors(jmp_inst, side_effect_inst); // Check that the second deopt is a successor of the first deopt. tester.CheckInSuccessors(jmp_inst, other_jmp_inst); // Check that the second deopt is a successor of the side-effect instruction. tester.CheckInSuccessors(side_effect_inst, other_jmp_inst); // Check that the block terminator is a successor of all other instructions. tester.CheckInSuccessors(jmp_inst, ret_inst); tester.CheckInSuccessors(side_effect_inst, ret_inst); tester.CheckInSuccessors(other_jmp_inst, ret_inst); // Schedule block. tester.EndBlock(); } } // namespace compiler } // namespace internal } // namespace v8