1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_UNITTESTS_COMPILER_INSTRUCTION_SEQUENCE_UNITTEST_H_
6 #define V8_UNITTESTS_COMPILER_INSTRUCTION_SEQUENCE_UNITTEST_H_
7 
8 #include "src/compiler/instruction.h"
9 #include "test/unittests/test-utils.h"
10 #include "testing/gmock/include/gmock/gmock.h"
11 
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15 
16 class InstructionSequenceTest : public TestWithIsolateAndZone {
17  public:
18   static const int kDefaultNRegs = 8;
19   static const int kNoValue = kMinInt;
20 
21   typedef RpoNumber Rpo;
22 
23   struct VReg {
VRegVReg24     VReg() : value_(kNoValue) {}
VRegVReg25     VReg(PhiInstruction* phi) : value_(phi->virtual_register()) {}  // NOLINT
VRegVReg26     explicit VReg(int value) : value_(value) {}
27     int value_;
28   };
29 
30   typedef std::pair<VReg, VReg> VRegPair;
31 
32   enum TestOperandType {
33     kInvalid,
34     kSameAsFirst,
35     kRegister,
36     kFixedRegister,
37     kSlot,
38     kFixedSlot,
39     kExplicit,
40     kImmediate,
41     kNone,
42     kConstant,
43     kUnique,
44     kUniqueRegister
45   };
46 
47   struct TestOperand {
TestOperandTestOperand48     TestOperand() : type_(kInvalid), vreg_(), value_(kNoValue) {}
TestOperandTestOperand49     TestOperand(TestOperandType type, int imm)
50         : type_(type), vreg_(), value_(imm) {}
51     TestOperand(TestOperandType type, VReg vreg, int value = kNoValue)
type_TestOperand52         : type_(type), vreg_(vreg), value_(value) {}
53 
54     TestOperandType type_;
55     VReg vreg_;
56     int value_;
57   };
58 
Same()59   static TestOperand Same() { return TestOperand(kSameAsFirst, VReg()); }
60 
ExplicitReg(int index)61   static TestOperand ExplicitReg(int index) {
62     TestOperandType type = kExplicit;
63     return TestOperand(type, VReg(), index);
64   }
65 
66   static TestOperand Reg(VReg vreg, int index = kNoValue) {
67     TestOperandType type = kRegister;
68     if (index != kNoValue) type = kFixedRegister;
69     return TestOperand(type, vreg, index);
70   }
71 
72   static TestOperand Reg(int index = kNoValue) { return Reg(VReg(), index); }
73 
74   static TestOperand Slot(VReg vreg, int index = kNoValue) {
75     TestOperandType type = kSlot;
76     if (index != kNoValue) type = kFixedSlot;
77     return TestOperand(type, vreg, index);
78   }
79 
80   static TestOperand Slot(int index = kNoValue) { return Slot(VReg(), index); }
81 
Const(int index)82   static TestOperand Const(int index) {
83     CHECK_NE(kNoValue, index);
84     return TestOperand(kConstant, VReg(), index);
85   }
86 
Use(VReg vreg)87   static TestOperand Use(VReg vreg) { return TestOperand(kNone, vreg); }
88 
Use()89   static TestOperand Use() { return Use(VReg()); }
90 
Unique(VReg vreg)91   static TestOperand Unique(VReg vreg) { return TestOperand(kUnique, vreg); }
92 
UniqueReg(VReg vreg)93   static TestOperand UniqueReg(VReg vreg) {
94     return TestOperand(kUniqueRegister, vreg);
95   }
96 
97   enum BlockCompletionType { kBlockEnd, kFallThrough, kBranch, kJump };
98 
99   struct BlockCompletion {
100     BlockCompletionType type_;
101     TestOperand op_;
102     int offset_0_;
103     int offset_1_;
104   };
105 
FallThrough()106   static BlockCompletion FallThrough() {
107     BlockCompletion completion = {kFallThrough, TestOperand(), 1, kNoValue};
108     return completion;
109   }
110 
Jump(int offset)111   static BlockCompletion Jump(int offset) {
112     BlockCompletion completion = {kJump, TestOperand(), offset, kNoValue};
113     return completion;
114   }
115 
Branch(TestOperand op,int left_offset,int right_offset)116   static BlockCompletion Branch(TestOperand op, int left_offset,
117                                 int right_offset) {
118     BlockCompletion completion = {kBranch, op, left_offset, right_offset};
119     return completion;
120   }
121 
Last()122   static BlockCompletion Last() {
123     BlockCompletion completion = {kBlockEnd, TestOperand(), kNoValue, kNoValue};
124     return completion;
125   }
126 
127   InstructionSequenceTest();
128 
129   void SetNumRegs(int num_general_registers, int num_double_registers);
130   RegisterConfiguration* config();
131   InstructionSequence* sequence();
132 
133   void StartLoop(int loop_blocks);
134   void EndLoop();
135   void StartBlock(bool deferred = false);
136   Instruction* EndBlock(BlockCompletion completion = FallThrough());
137 
138   TestOperand Imm(int32_t imm = 0);
139   VReg Define(TestOperand output_op);
140   VReg Parameter(TestOperand output_op = Reg()) { return Define(output_op); }
141 
142   Instruction* Return(TestOperand input_op_0);
Return(VReg vreg)143   Instruction* Return(VReg vreg) { return Return(Reg(vreg, 0)); }
144 
145   PhiInstruction* Phi(VReg incoming_vreg_0 = VReg(),
146                       VReg incoming_vreg_1 = VReg(),
147                       VReg incoming_vreg_2 = VReg(),
148                       VReg incoming_vreg_3 = VReg());
149   PhiInstruction* Phi(VReg incoming_vreg_0, size_t input_count);
150   void SetInput(PhiInstruction* phi, size_t input, VReg vreg);
151 
152   VReg DefineConstant(int32_t imm = 0);
153   Instruction* EmitNop();
154   Instruction* EmitI(size_t input_size, TestOperand* inputs);
155   Instruction* EmitI(TestOperand input_op_0 = TestOperand(),
156                      TestOperand input_op_1 = TestOperand(),
157                      TestOperand input_op_2 = TestOperand(),
158                      TestOperand input_op_3 = TestOperand());
159   VReg EmitOI(TestOperand output_op, size_t input_size, TestOperand* inputs);
160   VReg EmitOI(TestOperand output_op, TestOperand input_op_0 = TestOperand(),
161               TestOperand input_op_1 = TestOperand(),
162               TestOperand input_op_2 = TestOperand(),
163               TestOperand input_op_3 = TestOperand());
164   VRegPair EmitOOI(TestOperand output_op_0, TestOperand output_op_1,
165                    size_t input_size, TestOperand* inputs);
166   VRegPair EmitOOI(TestOperand output_op_0, TestOperand output_op_1,
167                    TestOperand input_op_0 = TestOperand(),
168                    TestOperand input_op_1 = TestOperand(),
169                    TestOperand input_op_2 = TestOperand(),
170                    TestOperand input_op_3 = TestOperand());
171   VReg EmitCall(TestOperand output_op, size_t input_size, TestOperand* inputs);
172   VReg EmitCall(TestOperand output_op, TestOperand input_op_0 = TestOperand(),
173                 TestOperand input_op_1 = TestOperand(),
174                 TestOperand input_op_2 = TestOperand(),
175                 TestOperand input_op_3 = TestOperand());
176 
current_block()177   InstructionBlock* current_block() const { return current_block_; }
num_general_registers()178   int num_general_registers() const { return num_general_registers_; }
num_double_registers()179   int num_double_registers() const { return num_double_registers_; }
180 
181   // Called after all instructions have been inserted.
182   void WireBlocks();
183 
184  private:
NewReg()185   VReg NewReg() { return VReg(sequence()->NextVirtualRegister()); }
186 
Invalid()187   static TestOperand Invalid() { return TestOperand(kInvalid, VReg()); }
188 
189   Instruction* EmitBranch(TestOperand input_op);
190   Instruction* EmitFallThrough();
191   Instruction* EmitJump();
192   Instruction* NewInstruction(InstructionCode code, size_t outputs_size,
193                               InstructionOperand* outputs,
194                               size_t inputs_size = 0,
195                               InstructionOperand* inputs = nullptr,
196                               size_t temps_size = 0,
197                               InstructionOperand* temps = nullptr);
198   InstructionOperand Unallocated(TestOperand op,
199                                  UnallocatedOperand::ExtendedPolicy policy);
200   InstructionOperand Unallocated(TestOperand op,
201                                  UnallocatedOperand::ExtendedPolicy policy,
202                                  UnallocatedOperand::Lifetime lifetime);
203   InstructionOperand Unallocated(TestOperand op,
204                                  UnallocatedOperand::ExtendedPolicy policy,
205                                  int index);
206   InstructionOperand Unallocated(TestOperand op,
207                                  UnallocatedOperand::BasicPolicy policy,
208                                  int index);
209   InstructionOperand* ConvertInputs(size_t input_size, TestOperand* inputs);
210   InstructionOperand ConvertInputOp(TestOperand op);
211   InstructionOperand ConvertOutputOp(VReg vreg, TestOperand op);
212   InstructionBlock* NewBlock(bool deferred = false);
213   void WireBlock(size_t block_offset, int jump_offset);
214 
215   Instruction* Emit(InstructionCode code, size_t outputs_size = 0,
216                     InstructionOperand* outputs = nullptr,
217                     size_t inputs_size = 0,
218                     InstructionOperand* inputs = nullptr, size_t temps_size = 0,
219                     InstructionOperand* temps = nullptr, bool is_call = false);
220 
221   Instruction* AddInstruction(Instruction* instruction);
222 
223   struct LoopData {
224     Rpo loop_header_;
225     int expected_blocks_;
226   };
227 
228   typedef std::vector<LoopData> LoopBlocks;
229   typedef std::map<int, const Instruction*> Instructions;
230   typedef std::vector<BlockCompletion> Completions;
231 
232   base::SmartPointer<RegisterConfiguration> config_;
233   InstructionSequence* sequence_;
234   int num_general_registers_;
235   int num_double_registers_;
236 
237   // Block building state.
238   InstructionBlocks instruction_blocks_;
239   Instructions instructions_;
240   Completions completions_;
241   LoopBlocks loop_blocks_;
242   InstructionBlock* current_block_;
243   bool block_returns_;
244 };
245 
246 }  // namespace compiler
247 }  // namespace internal
248 }  // namespace v8
249 
250 #endif  // V8_UNITTESTS_COMPILER_INSTRUCTION_SEQUENCE_UNITTEST_H_
251