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.
  
  #ifndef V8_UNITTESTS_COMPILER_INSTRUCTION_SELECTOR_UNITTEST_H_
  #define V8_UNITTESTS_COMPILER_INSTRUCTION_SELECTOR_UNITTEST_H_
  
  #include <deque>
  #include <set>
  
  #include "src/base/utils/random-number-generator.h"
  #include "src/compiler/instruction-selector.h"
  #include "src/compiler/raw-machine-assembler.h"
  #include "src/macro-assembler.h"
  #include "test/unittests/test-utils.h"
  
  namespace v8 {
  namespace internal {
  namespace compiler {
  
  class InstructionSelectorTest : public TestWithContext,
                                  public TestWithIsolateAndZone {
   public:
    InstructionSelectorTest();
    ~InstructionSelectorTest() override;
  
    base::RandomNumberGenerator* rng() { return &rng_; }
  
    class Stream;
  
    enum StreamBuilderMode {
      kAllInstructions,
      kTargetInstructions,
      kAllExceptNopInstructions
    };
  
    class StreamBuilder final : public RawMachineAssembler {
     public:
      StreamBuilder(InstructionSelectorTest* test, MachineType return_type)
          : RawMachineAssembler(test->isolate(),
                                new (test->zone()) Graph(test->zone()),
                                MakeCallDescriptor(test->zone(), return_type),
                                MachineType::PointerRepresentation(),
                                MachineOperatorBuilder::kAllOptionalOps),
            test_(test) {}
      StreamBuilder(InstructionSelectorTest* test, MachineType return_type,
                    MachineType parameter0_type)
          : RawMachineAssembler(
                test->isolate(), new (test->zone()) Graph(test->zone()),
                MakeCallDescriptor(test->zone(), return_type, parameter0_type),
                MachineType::PointerRepresentation(),
                MachineOperatorBuilder::kAllOptionalOps,
                InstructionSelector::AlignmentRequirements()),
            test_(test) {}
      StreamBuilder(InstructionSelectorTest* test, MachineType return_type,
                    MachineType parameter0_type, MachineType parameter1_type)
          : RawMachineAssembler(
                test->isolate(), new (test->zone()) Graph(test->zone()),
                MakeCallDescriptor(test->zone(), return_type, parameter0_type,
                                   parameter1_type),
                MachineType::PointerRepresentation(),
                MachineOperatorBuilder::kAllOptionalOps),
            test_(test) {}
      StreamBuilder(InstructionSelectorTest* test, MachineType return_type,
                    MachineType parameter0_type, MachineType parameter1_type,
                    MachineType parameter2_type)
          : RawMachineAssembler(
                test->isolate(), new (test->zone()) Graph(test->zone()),
                MakeCallDescriptor(test->zone(), return_type, parameter0_type,
                                   parameter1_type, parameter2_type),
                MachineType::PointerRepresentation(),
                MachineOperatorBuilder::kAllOptionalOps),
            test_(test) {}
  
      Stream Build(CpuFeature feature) {
        return Build(InstructionSelector::Features(feature));
      }
      Stream Build(CpuFeature feature1, CpuFeature feature2) {
        return Build(InstructionSelector::Features(feature1, feature2));
      }
      Stream Build(StreamBuilderMode mode = kTargetInstructions) {
        return Build(InstructionSelector::Features(), mode);
      }
      Stream Build(InstructionSelector::Features features,
                   StreamBuilderMode mode = kTargetInstructions,
                   InstructionSelector::SourcePositionMode source_position_mode =
                       InstructionSelector::kAllSourcePositions);
  
      const FrameStateFunctionInfo* GetFrameStateFunctionInfo(int parameter_count,
                                                              int local_count);
  
     private:
      CallDescriptor* MakeCallDescriptor(Zone* zone, MachineType return_type) {
        MachineSignature::Builder builder(zone, 1, 0);
        builder.AddReturn(return_type);
        return MakeSimpleCallDescriptor(zone, builder.Build());
      }
  
      CallDescriptor* MakeCallDescriptor(Zone* zone, MachineType return_type,
                                         MachineType parameter0_type) {
        MachineSignature::Builder builder(zone, 1, 1);
        builder.AddReturn(return_type);
        builder.AddParam(parameter0_type);
        return MakeSimpleCallDescriptor(zone, builder.Build());
      }
  
      CallDescriptor* MakeCallDescriptor(Zone* zone, MachineType return_type,
                                         MachineType parameter0_type,
                                         MachineType parameter1_type) {
        MachineSignature::Builder builder(zone, 1, 2);
        builder.AddReturn(return_type);
        builder.AddParam(parameter0_type);
        builder.AddParam(parameter1_type);
        return MakeSimpleCallDescriptor(zone, builder.Build());
      }
  
      CallDescriptor* MakeCallDescriptor(Zone* zone, MachineType return_type,
                                         MachineType parameter0_type,
                                         MachineType parameter1_type,
                                         MachineType parameter2_type) {
        MachineSignature::Builder builder(zone, 1, 3);
        builder.AddReturn(return_type);
        builder.AddParam(parameter0_type);
        builder.AddParam(parameter1_type);
        builder.AddParam(parameter2_type);
        return MakeSimpleCallDescriptor(zone, builder.Build());
      }
  
     private:
      InstructionSelectorTest* test_;
  
      // Create a simple call descriptor for testing.
      CallDescriptor* MakeSimpleCallDescriptor(Zone* zone,
                                               MachineSignature* msig) {
        LocationSignature::Builder locations(zone, msig->return_count(),
                                             msig->parameter_count());
  
        // Add return location(s).
        const int return_count = static_cast<int>(msig->return_count());
        for (int i = 0; i < return_count; i++) {
          locations.AddReturn(
              LinkageLocation::ForCallerFrameSlot(-1 - i, msig->GetReturn(i)));
        }
  
        // Just put all parameters on the stack.
        const int parameter_count = static_cast<int>(msig->parameter_count());
        for (int i = 0; i < parameter_count; i++) {
          locations.AddParam(
              LinkageLocation::ForCallerFrameSlot(-1 - i, msig->GetParam(i)));
        }
  
        const RegList kCalleeSaveRegisters = 0;
        const RegList kCalleeSaveFPRegisters = 0;
  
        MachineType target_type = MachineType::Pointer();
        LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
        return new (zone) CallDescriptor(  // --
            CallDescriptor::kCallAddress,  // kind
            target_type,                   // target MachineType
            target_loc,                    // target location
            locations.Build(),             // location_sig
            0,                             // stack_parameter_count
            Operator::kNoProperties,       // properties
            kCalleeSaveRegisters,          // callee-saved registers
            kCalleeSaveFPRegisters,        // callee-saved fp regs
            CallDescriptor::kNoFlags,      // flags
            "iselect-test-call");
      }
    };
  
    class Stream final {
     public:
      size_t size() const { return instructions_.size(); }
      const Instruction* operator[](size_t index) const {
        EXPECT_LT(index, size());
        return instructions_[index];
      }
  
      bool IsDouble(const InstructionOperand* operand) const {
        return IsDouble(ToVreg(operand));
      }
  
      bool IsDouble(const Node* node) const { return IsDouble(ToVreg(node)); }
  
      bool IsInteger(const InstructionOperand* operand) const {
        return IsInteger(ToVreg(operand));
      }
  
      bool IsInteger(const Node* node) const { return IsInteger(ToVreg(node)); }
  
      bool IsReference(const InstructionOperand* operand) const {
        return IsReference(ToVreg(operand));
      }
  
      bool IsReference(const Node* node) const {
        return IsReference(ToVreg(node));
      }
  
      float ToFloat32(const InstructionOperand* operand) const {
        return ToConstant(operand).ToFloat32();
      }
  
      double ToFloat64(const InstructionOperand* operand) const {
        return ToConstant(operand).ToFloat64();
      }
  
      int32_t ToInt32(const InstructionOperand* operand) const {
        return ToConstant(operand).ToInt32();
      }
  
      int64_t ToInt64(const InstructionOperand* operand) const {
        return ToConstant(operand).ToInt64();
      }
  
      Handle<HeapObject> ToHeapObject(const InstructionOperand* operand) const {
        return ToConstant(operand).ToHeapObject();
      }
  
      int ToVreg(const InstructionOperand* operand) const {
        if (operand->IsConstant()) {
          return ConstantOperand::cast(operand)->virtual_register();
        }
        EXPECT_EQ(InstructionOperand::UNALLOCATED, operand->kind());
        return UnallocatedOperand::cast(operand)->virtual_register();
      }
  
      int ToVreg(const Node* node) const;
  
      bool IsFixed(const InstructionOperand* operand, Register reg) const;
      bool IsSameAsFirst(const InstructionOperand* operand) const;
      bool IsUsedAtStart(const InstructionOperand* operand) const;
  
      FrameStateDescriptor* GetFrameStateDescriptor(int deoptimization_id) {
        EXPECT_LT(deoptimization_id, GetFrameStateDescriptorCount());
        return deoptimization_entries_[deoptimization_id];
      }
  
      int GetFrameStateDescriptorCount() {
        return static_cast<int>(deoptimization_entries_.size());
      }
  
     private:
      bool IsDouble(int virtual_register) const {
        return doubles_.find(virtual_register) != doubles_.end();
      }
  
      bool IsInteger(int virtual_register) const {
        return !IsDouble(virtual_register) && !IsReference(virtual_register);
      }
  
      bool IsReference(int virtual_register) const {
        return references_.find(virtual_register) != references_.end();
      }
  
      Constant ToConstant(const InstructionOperand* operand) const {
        ConstantMap::const_iterator i;
        if (operand->IsConstant()) {
          i = constants_.find(ConstantOperand::cast(operand)->virtual_register());
          EXPECT_EQ(ConstantOperand::cast(operand)->virtual_register(), i->first);
          EXPECT_FALSE(constants_.end() == i);
        } else {
          EXPECT_EQ(InstructionOperand::IMMEDIATE, operand->kind());
          auto imm = ImmediateOperand::cast(operand);
          if (imm->type() == ImmediateOperand::INLINE) {
            return Constant(imm->inline_value());
          }
          i = immediates_.find(imm->indexed_value());
          EXPECT_EQ(imm->indexed_value(), i->first);
          EXPECT_FALSE(immediates_.end() == i);
        }
        return i->second;
      }
  
      friend class StreamBuilder;
  
      typedef std::map<int, Constant> ConstantMap;
      typedef std::map<NodeId, int> VirtualRegisters;
  
      ConstantMap constants_;
      ConstantMap immediates_;
      std::deque<Instruction*> instructions_;
      std::set<int> doubles_;
      std::set<int> references_;
      VirtualRegisters virtual_registers_;
      std::deque<FrameStateDescriptor*> deoptimization_entries_;
    };
  
    base::RandomNumberGenerator rng_;
  };
  
  
  template <typename T>
  class InstructionSelectorTestWithParam
      : public InstructionSelectorTest,
        public ::testing::WithParamInterface<T> {};
  
  }  // namespace compiler
  }  // namespace internal
  }  // namespace v8
  
  #endif  // V8_UNITTESTS_COMPILER_INSTRUCTION_SELECTOR_UNITTEST_H_
  