text: // Copyright 2014 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/code-generator.h"
  #include "src/compiler/backend/instruction.h"
  #include "src/compiler/common-operator.h"
  #include "src/compiler/graph.h"
  #include "src/compiler/linkage.h"
  #include "src/compiler/machine-operator.h"
  #include "src/compiler/node.h"
  #include "src/compiler/operator.h"
  #include "src/compiler/schedule.h"
  #include "src/compiler/scheduler.h"
  #include "src/objects/objects-inl.h"
  #include "test/cctest/cctest.h"
  
  namespace v8 {
  namespace internal {
  namespace compiler {
  
  using TestInstr = v8::internal::compiler::Instruction;
  using TestInstrSeq = v8::internal::compiler::InstructionSequence;
  
  // A testing helper for the register code abstraction.
  class InstructionTester : public HandleAndZoneScope {
   public:  // We're all friends here.
    InstructionTester()
        : HandleAndZoneScope(kCompressGraphZone),
          graph(zone()),
          schedule(zone()),
          common(zone()),
          machine(zone()),
          code(nullptr) {}
  
    Graph graph;
    Schedule schedule;
    CommonOperatorBuilder common;
    MachineOperatorBuilder machine;
    TestInstrSeq* code;
  
    Zone* zone() { return main_zone(); }
  
    void allocCode() {
      if (schedule.rpo_order()->size() == 0) {
        // Compute the RPO order.
        Scheduler::ComputeSpecialRPO(main_zone(), &schedule);
        CHECK_NE(0u, schedule.rpo_order()->size());
      }
      InstructionBlocks* instruction_blocks =
          TestInstrSeq::InstructionBlocksFor(main_zone(), &schedule);
      code = main_zone()->New<TestInstrSeq>(main_isolate(), main_zone(),
                                            instruction_blocks);
    }
  
    Node* Int32Constant(int32_t val) {
      Node* node = graph.NewNode(common.Int32Constant(val));
      schedule.AddNode(schedule.start(), node);
      return node;
    }
  
    Node* Float64Constant(double val) {
      Node* node = graph.NewNode(common.Float64Constant(val));
      schedule.AddNode(schedule.start(), node);
      return node;
    }
  
    Node* Parameter(int32_t which) {
      Node* node = graph.NewNode(common.Parameter(which));
      schedule.AddNode(schedule.start(), node);
      return node;
    }
  
    Node* NewNode(BasicBlock* block) {
      Node* node = graph.NewNode(common.Int32Constant(111));
      schedule.AddNode(block, node);
      return node;
    }
  
    int NewInstr() {
      InstructionCode opcode = static_cast<InstructionCode>(110);
      TestInstr* instr = TestInstr::New(zone(), opcode);
      return code->AddInstruction(instr);
    }
  
    int NewNop() {
      TestInstr* instr = TestInstr::New(zone(), kArchNop);
      return code->AddInstruction(instr);
    }
  
    UnallocatedOperand Unallocated(int vreg) {
      return UnallocatedOperand(UnallocatedOperand::REGISTER_OR_SLOT, vreg);
    }
  
    RpoNumber RpoFor(BasicBlock* block) {
      return RpoNumber::FromInt(block->rpo_number());
    }
  
    InstructionBlock* BlockAt(BasicBlock* block) {
      return code->InstructionBlockAt(RpoFor(block));
    }
    BasicBlock* GetBasicBlock(int instruction_index) {
      const InstructionBlock* block =
          code->GetInstructionBlock(instruction_index);
      return schedule.rpo_order()->at(block->rpo_number().ToSize());
    }
    int first_instruction_index(BasicBlock* block) {
      return BlockAt(block)->first_instruction_index();
    }
    int last_instruction_index(BasicBlock* block) {
      return BlockAt(block)->last_instruction_index();
    }
  };
  
  
  TEST(InstructionBasic) {
    InstructionTester R;
  
    for (int i = 0; i < 10; i++) {
      R.Int32Constant(i);  // Add some nodes to the graph.
    }
  
    BasicBlock* last = R.schedule.start();
    for (int i = 0; i < 5; i++) {
      BasicBlock* block = R.schedule.NewBasicBlock();
      R.schedule.AddGoto(last, block);
      last = block;
    }
  
    R.allocCode();
  
    BasicBlockVector* blocks = R.schedule.rpo_order();
    CHECK_EQ(static_cast<int>(blocks->size()), R.code->InstructionBlockCount());
  
    for (auto block : *blocks) {
      CHECK_EQ(block->rpo_number(), R.BlockAt(block)->rpo_number().ToInt());
      CHECK(!block->loop_end());
    }
  }
  
  
  TEST(InstructionGetBasicBlock) {
    InstructionTester R;
  
    BasicBlock* b0 = R.schedule.start();
    BasicBlock* b1 = R.schedule.NewBasicBlock();
    BasicBlock* b2 = R.schedule.NewBasicBlock();
    BasicBlock* b3 = R.schedule.end();
  
    R.schedule.AddGoto(b0, b1);
    R.schedule.AddGoto(b1, b2);
    R.schedule.AddGoto(b2, b3);
  
    R.allocCode();
  
    R.code->StartBlock(R.RpoFor(b0));
    int i0 = R.NewInstr();
    int i1 = R.NewInstr();
    R.code->EndBlock(R.RpoFor(b0));
    R.code->StartBlock(R.RpoFor(b1));
    int i2 = R.NewInstr();
    int i3 = R.NewInstr();
    int i4 = R.NewInstr();
    int i5 = R.NewInstr();
    R.code->EndBlock(R.RpoFor(b1));
    R.code->StartBlock(R.RpoFor(b2));
    int i6 = R.NewInstr();
    int i7 = R.NewInstr();
    int i8 = R.NewInstr();
    R.code->EndBlock(R.RpoFor(b2));
    R.code->StartBlock(R.RpoFor(b3));
    R.NewNop();
    R.code->EndBlock(R.RpoFor(b3));
  
    CHECK_EQ(b0, R.GetBasicBlock(i0));
    CHECK_EQ(b0, R.GetBasicBlock(i1));
  
    CHECK_EQ(b1, R.GetBasicBlock(i2));
    CHECK_EQ(b1, R.GetBasicBlock(i3));
    CHECK_EQ(b1, R.GetBasicBlock(i4));
    CHECK_EQ(b1, R.GetBasicBlock(i5));
  
    CHECK_EQ(b2, R.GetBasicBlock(i6));
    CHECK_EQ(b2, R.GetBasicBlock(i7));
    CHECK_EQ(b2, R.GetBasicBlock(i8));
  
    CHECK_EQ(b0, R.GetBasicBlock(R.first_instruction_index(b0)));
    CHECK_EQ(b0, R.GetBasicBlock(R.last_instruction_index(b0)));
  
    CHECK_EQ(b1, R.GetBasicBlock(R.first_instruction_index(b1)));
    CHECK_EQ(b1, R.GetBasicBlock(R.last_instruction_index(b1)));
  
    CHECK_EQ(b2, R.GetBasicBlock(R.first_instruction_index(b2)));
    CHECK_EQ(b2, R.GetBasicBlock(R.last_instruction_index(b2)));
  
    CHECK_EQ(b3, R.GetBasicBlock(R.first_instruction_index(b3)));
    CHECK_EQ(b3, R.GetBasicBlock(R.last_instruction_index(b3)));
  }
  
  
  TEST(InstructionIsGapAt) {
    InstructionTester R;
  
    BasicBlock* b0 = R.schedule.start();
    R.schedule.AddReturn(b0, R.Int32Constant(1));
  
    R.allocCode();
    TestInstr* i0 = TestInstr::New(R.zone(), 100);
    TestInstr* g = TestInstr::New(R.zone(), 103);
    R.code->StartBlock(R.RpoFor(b0));
    R.code->AddInstruction(i0);
    R.code->AddInstruction(g);
    R.code->EndBlock(R.RpoFor(b0));
  
    CHECK_EQ(2, R.code->instructions().size());
  }
  
  
  TEST(InstructionIsGapAt2) {
    InstructionTester R;
  
    BasicBlock* b0 = R.schedule.start();
    BasicBlock* b1 = R.schedule.end();
    R.schedule.AddGoto(b0, b1);
    R.schedule.AddReturn(b1, R.Int32Constant(1));
  
    R.allocCode();
    TestInstr* i0 = TestInstr::New(R.zone(), 100);
    TestInstr* g = TestInstr::New(R.zone(), 103);
    R.code->StartBlock(R.RpoFor(b0));
    R.code->AddInstruction(i0);
    R.code->AddInstruction(g);
    R.code->EndBlock(R.RpoFor(b0));
  
    TestInstr* i1 = TestInstr::New(R.zone(), 102);
    TestInstr* g1 = TestInstr::New(R.zone(), 104);
    R.code->StartBlock(R.RpoFor(b1));
    R.code->AddInstruction(i1);
    R.code->AddInstruction(g1);
    R.code->EndBlock(R.RpoFor(b1));
  
    CHECK_EQ(4, R.code->instructions().size());
  }
  
  
  TEST(InstructionAddGapMove) {
    InstructionTester R;
  
    BasicBlock* b0 = R.schedule.start();
    R.schedule.AddReturn(b0, R.Int32Constant(1));
  
    R.allocCode();
    TestInstr* i0 = TestInstr::New(R.zone(), 100);
    TestInstr* g = TestInstr::New(R.zone(), 103);
    R.code->StartBlock(R.RpoFor(b0));
    R.code->AddInstruction(i0);
    R.code->AddInstruction(g);
    R.code->EndBlock(R.RpoFor(b0));
  
    CHECK_EQ(2, R.code->instructions().size());
  
    int index = 0;
    for (auto instr : R.code->instructions()) {
      UnallocatedOperand op1 = R.Unallocated(index++);
      UnallocatedOperand op2 = R.Unallocated(index++);
      instr->GetOrCreateParallelMove(TestInstr::START, R.zone())
          ->AddMove(op1, op2);
      ParallelMove* move = instr->GetParallelMove(TestInstr::START);
      CHECK(move);
      CHECK_EQ(1u, move->size());
      MoveOperands* cur = move->at(0);
      CHECK(op1.Equals(cur->source()));
      CHECK(op2.Equals(cur->destination()));
    }
  }
  
  
  TEST(InstructionOperands) {
    v8::internal::AccountingAllocator allocator;
    Zone zone(&allocator, ZONE_NAME);
  
    {
      TestInstr* i = TestInstr::New(&zone, 101);
      CHECK_EQ(0, static_cast<int>(i->OutputCount()));
      CHECK_EQ(0, static_cast<int>(i->InputCount()));
      CHECK_EQ(0, static_cast<int>(i->TempCount()));
    }
  
    int vreg = 15;
    InstructionOperand outputs[] = {
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)};
  
    InstructionOperand inputs[] = {
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)};
  
    InstructionOperand temps[] = {
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg),
        UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg)};
  
    for (size_t i = 0; i < arraysize(outputs); i++) {
      for (size_t j = 0; j < arraysize(inputs); j++) {
        for (size_t k = 0; k < arraysize(temps); k++) {
          TestInstr* m =
              TestInstr::New(&zone, 101, i, outputs, j, inputs, k, temps);
          CHECK(i == m->OutputCount());
          CHECK(j == m->InputCount());
          CHECK(k == m->TempCount());
  
          for (size_t z = 0; z < i; z++) {
            CHECK(outputs[z].Equals(*m->OutputAt(z)));
          }
  
          for (size_t z = 0; z < j; z++) {
            CHECK(inputs[z].Equals(*m->InputAt(z)));
          }
  
          for (size_t z = 0; z < k; z++) {
            CHECK(temps[z].Equals(*m->TempAt(z)));
          }
        }
      }
    }
  }
  
  }  // namespace compiler
  }  // namespace internal
  }  // namespace v8
  