// 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/codegen.h" #include "src/compilation-info.h" #include "src/compiler/code-generator.h" #include "src/compiler/instruction.h" #include "src/compiler/linkage.h" #include "src/isolate.h" #include "test/cctest/cctest.h" namespace v8 { namespace internal { namespace compiler { class CodeGeneratorTester : public InitializedHandleScope { public: CodeGeneratorTester() : zone_(main_isolate()->allocator(), ZONE_NAME), info_(ArrayVector("test"), main_isolate(), &zone_, Code::ComputeFlags(Code::STUB)), descriptor_(Linkage::GetJSCallDescriptor(&zone_, false, 0, CallDescriptor::kNoFlags)), linkage_(descriptor_), blocks_(&zone_), sequence_(main_isolate(), &zone_, &blocks_), frame_(descriptor_->CalculateFixedFrameSize()), generator_(&zone_, &frame_, &linkage_, &sequence_, &info_, base::Optional(), kNoSourcePosition) { info_.set_prologue_offset(generator_.tasm()->pc_offset()); } enum PushTypeFlag { kRegisterPush = CodeGenerator::kRegisterPush, kStackSlotPush = CodeGenerator::kStackSlotPush, kScalarPush = CodeGenerator::kScalarPush }; void CheckAssembleTailCallGaps(Instruction* instr, int first_unused_stack_slot, CodeGeneratorTester::PushTypeFlag push_type) { generator_.AssembleTailCallBeforeGap(instr, first_unused_stack_slot); #if defined(V8_TARGET_ARCH_ARM) // Only folding register pushes is supported on ARM. bool supported = ((push_type & CodeGenerator::kRegisterPush) == push_type); #elif defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_IA32) || \ defined(V8_TARGET_ARCH_X87) || defined(V8_TARGET_ARCH_S390X) || \ defined(V8_TARGET_ARCH_PPC64) bool supported = ((push_type & CodeGenerator::kScalarPush) == push_type); #else bool supported = false; #endif if (supported) { // Architectures supporting folding adjacent pushes should now have // resolved all moves. for (const auto& move : *instr->parallel_moves()[Instruction::FIRST_GAP_POSITION]) { CHECK(move->IsEliminated()); } } generator_.AssembleGaps(instr); generator_.AssembleTailCallAfterGap(instr, first_unused_stack_slot); } Handle Finalize() { generator_.FinishCode(); generator_.safepoints()->Emit(generator_.tasm(), frame_.GetTotalFrameSlotCount()); return generator_.FinalizeCode(); } void Disassemble() { HandleScope scope(main_isolate()); Handle code = Finalize(); if (FLAG_print_code) { code->Print(); } } Zone* zone() { return &zone_; } private: Zone zone_; CompilationInfo info_; CallDescriptor* descriptor_; Linkage linkage_; ZoneVector blocks_; InstructionSequence sequence_; Frame frame_; CodeGenerator generator_; }; TEST(AssembleTailCallGap) { const RegisterConfiguration* conf = RegisterConfiguration::Turbofan(); // This test assumes at least 4 registers are allocatable. CHECK(conf->num_allocatable_general_registers() >= 4); auto r0 = AllocatedOperand(LocationOperand::REGISTER, MachineRepresentation::kTagged, conf->GetAllocatableGeneralCode(0)); auto r1 = AllocatedOperand(LocationOperand::REGISTER, MachineRepresentation::kTagged, conf->GetAllocatableGeneralCode(1)); auto r2 = AllocatedOperand(LocationOperand::REGISTER, MachineRepresentation::kTagged, conf->GetAllocatableGeneralCode(2)); auto r3 = AllocatedOperand(LocationOperand::REGISTER, MachineRepresentation::kTagged, conf->GetAllocatableGeneralCode(3)); auto slot_minus_4 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, -4); auto slot_minus_3 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, -3); auto slot_minus_2 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, -2); auto slot_minus_1 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, -1); // Avoid slot 0 for architectures which use it store the return address. int first_slot = V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK ? 1 : 0; auto slot_0 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, first_slot); auto slot_1 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, first_slot + 1); auto slot_2 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, first_slot + 2); auto slot_3 = AllocatedOperand(LocationOperand::STACK_SLOT, MachineRepresentation::kTagged, first_slot + 3); // These tests all generate series of moves that the code generator should // detect as adjacent pushes. Depending on the architecture, we make sure // these moves get eliminated. // Also, disassembling with `--print-code` is useful when debugging. { // Generate a series of register pushes only. CodeGeneratorTester c; Instruction* instr = Instruction::New(c.zone(), kArchNop); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(r3, slot_0); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(r2, slot_1); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(r1, slot_2); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(r0, slot_3); c.CheckAssembleTailCallGaps(instr, first_slot + 4, CodeGeneratorTester::kRegisterPush); c.Disassemble(); } { // Generate a series of stack pushes only. CodeGeneratorTester c; Instruction* instr = Instruction::New(c.zone(), kArchNop); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(slot_minus_4, slot_0); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(slot_minus_3, slot_1); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(slot_minus_2, slot_2); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(slot_minus_1, slot_3); c.CheckAssembleTailCallGaps(instr, first_slot + 4, CodeGeneratorTester::kStackSlotPush); c.Disassemble(); } { // Generate a mix of stack and register pushes. CodeGeneratorTester c; Instruction* instr = Instruction::New(c.zone(), kArchNop); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(slot_minus_2, slot_0); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(r1, slot_1); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(slot_minus_1, slot_2); instr->GetOrCreateParallelMove(Instruction::FIRST_GAP_POSITION, c.zone()) ->AddMove(r0, slot_3); c.CheckAssembleTailCallGaps(instr, first_slot + 4, CodeGeneratorTester::kScalarPush); c.Disassemble(); } } } // namespace compiler } // namespace internal } // namespace v8