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 #include "src/base/utils/random-number-generator.h"
6 #include "src/compiler/pipeline.h"
7 #include "test/unittests/compiler/instruction-sequence-unittest.h"
8 #include "test/unittests/test-utils.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 
11 namespace v8 {
12 namespace internal {
13 namespace compiler {
14 
15 static const char*
16     general_register_names_[RegisterConfiguration::kMaxGeneralRegisters];
17 static const char*
18     double_register_names_[RegisterConfiguration::kMaxDoubleRegisters];
19 static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters +
20                                   RegisterConfiguration::kMaxDoubleRegisters)];
21 
22 
23 namespace {
24 static int allocatable_codes[InstructionSequenceTest::kDefaultNRegs] = {
25     0, 1, 2, 3, 4, 5, 6, 7};
26 static int allocatable_double_codes[InstructionSequenceTest::kDefaultNRegs] = {
27     0, 1, 2, 3, 4, 5, 6, 7};
28 }
29 
30 
InitializeRegisterNames()31 static void InitializeRegisterNames() {
32   char* loc = register_names_;
33   for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) {
34     general_register_names_[i] = loc;
35     loc += base::OS::SNPrintF(loc, 100, "gp_%d", i);
36     *loc++ = 0;
37   }
38   for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) {
39     double_register_names_[i] = loc;
40     loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1;
41     *loc++ = 0;
42   }
43 }
44 
45 
InstructionSequenceTest()46 InstructionSequenceTest::InstructionSequenceTest()
47     : sequence_(nullptr),
48       num_general_registers_(kDefaultNRegs),
49       num_double_registers_(kDefaultNRegs),
50       instruction_blocks_(zone()),
51       current_block_(nullptr),
52       block_returns_(false) {
53   InitializeRegisterNames();
54 }
55 
56 
SetNumRegs(int num_general_registers,int num_double_registers)57 void InstructionSequenceTest::SetNumRegs(int num_general_registers,
58                                          int num_double_registers) {
59   CHECK(config_.is_empty());
60   CHECK(instructions_.empty());
61   CHECK(instruction_blocks_.empty());
62   num_general_registers_ = num_general_registers;
63   num_double_registers_ = num_double_registers;
64 }
65 
66 
config()67 RegisterConfiguration* InstructionSequenceTest::config() {
68   if (config_.is_empty()) {
69     config_.Reset(new RegisterConfiguration(
70         num_general_registers_, num_double_registers_, num_general_registers_,
71         num_double_registers_, num_double_registers_, allocatable_codes,
72         allocatable_double_codes, general_register_names_,
73         double_register_names_));
74   }
75   return config_.get();
76 }
77 
78 
sequence()79 InstructionSequence* InstructionSequenceTest::sequence() {
80   if (sequence_ == nullptr) {
81     sequence_ = new (zone())
82         InstructionSequence(isolate(), zone(), &instruction_blocks_);
83   }
84   return sequence_;
85 }
86 
87 
StartLoop(int loop_blocks)88 void InstructionSequenceTest::StartLoop(int loop_blocks) {
89   CHECK(current_block_ == nullptr);
90   if (!loop_blocks_.empty()) {
91     CHECK(!loop_blocks_.back().loop_header_.IsValid());
92   }
93   LoopData loop_data = {Rpo::Invalid(), loop_blocks};
94   loop_blocks_.push_back(loop_data);
95 }
96 
97 
EndLoop()98 void InstructionSequenceTest::EndLoop() {
99   CHECK(current_block_ == nullptr);
100   CHECK(!loop_blocks_.empty());
101   CHECK_EQ(0, loop_blocks_.back().expected_blocks_);
102   loop_blocks_.pop_back();
103 }
104 
105 
StartBlock(bool deferred)106 void InstructionSequenceTest::StartBlock(bool deferred) {
107   block_returns_ = false;
108   NewBlock(deferred);
109 }
110 
111 
EndBlock(BlockCompletion completion)112 Instruction* InstructionSequenceTest::EndBlock(BlockCompletion completion) {
113   Instruction* result = nullptr;
114   if (block_returns_) {
115     CHECK(completion.type_ == kBlockEnd || completion.type_ == kFallThrough);
116     completion.type_ = kBlockEnd;
117   }
118   switch (completion.type_) {
119     case kBlockEnd:
120       break;
121     case kFallThrough:
122       result = EmitJump();
123       break;
124     case kJump:
125       CHECK(!block_returns_);
126       result = EmitJump();
127       break;
128     case kBranch:
129       CHECK(!block_returns_);
130       result = EmitBranch(completion.op_);
131       break;
132   }
133   completions_.push_back(completion);
134   CHECK(current_block_ != nullptr);
135   sequence()->EndBlock(current_block_->rpo_number());
136   current_block_ = nullptr;
137   return result;
138 }
139 
140 
Imm(int32_t imm)141 InstructionSequenceTest::TestOperand InstructionSequenceTest::Imm(int32_t imm) {
142   return TestOperand(kImmediate, imm);
143 }
144 
145 
Define(TestOperand output_op)146 InstructionSequenceTest::VReg InstructionSequenceTest::Define(
147     TestOperand output_op) {
148   VReg vreg = NewReg();
149   InstructionOperand outputs[1]{ConvertOutputOp(vreg, output_op)};
150   Emit(kArchNop, 1, outputs);
151   return vreg;
152 }
153 
154 
Return(TestOperand input_op_0)155 Instruction* InstructionSequenceTest::Return(TestOperand input_op_0) {
156   block_returns_ = true;
157   InstructionOperand inputs[1]{ConvertInputOp(input_op_0)};
158   return Emit(kArchRet, 0, nullptr, 1, inputs);
159 }
160 
161 
Phi(VReg incoming_vreg_0,VReg incoming_vreg_1,VReg incoming_vreg_2,VReg incoming_vreg_3)162 PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
163                                              VReg incoming_vreg_1,
164                                              VReg incoming_vreg_2,
165                                              VReg incoming_vreg_3) {
166   VReg inputs[] = {incoming_vreg_0, incoming_vreg_1, incoming_vreg_2,
167                    incoming_vreg_3};
168   size_t input_count = 0;
169   for (; input_count < arraysize(inputs); ++input_count) {
170     if (inputs[input_count].value_ == kNoValue) break;
171   }
172   CHECK(input_count > 0);
173   auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, input_count);
174   for (size_t i = 0; i < input_count; ++i) {
175     SetInput(phi, i, inputs[i]);
176   }
177   current_block_->AddPhi(phi);
178   return phi;
179 }
180 
181 
Phi(VReg incoming_vreg_0,size_t input_count)182 PhiInstruction* InstructionSequenceTest::Phi(VReg incoming_vreg_0,
183                                              size_t input_count) {
184   auto phi = new (zone()) PhiInstruction(zone(), NewReg().value_, input_count);
185   SetInput(phi, 0, incoming_vreg_0);
186   current_block_->AddPhi(phi);
187   return phi;
188 }
189 
190 
SetInput(PhiInstruction * phi,size_t input,VReg vreg)191 void InstructionSequenceTest::SetInput(PhiInstruction* phi, size_t input,
192                                        VReg vreg) {
193   CHECK(vreg.value_ != kNoValue);
194   phi->SetInput(input, vreg.value_);
195 }
196 
197 
DefineConstant(int32_t imm)198 InstructionSequenceTest::VReg InstructionSequenceTest::DefineConstant(
199     int32_t imm) {
200   VReg vreg = NewReg();
201   sequence()->AddConstant(vreg.value_, Constant(imm));
202   InstructionOperand outputs[1]{ConstantOperand(vreg.value_)};
203   Emit(kArchNop, 1, outputs);
204   return vreg;
205 }
206 
207 
EmitNop()208 Instruction* InstructionSequenceTest::EmitNop() { return Emit(kArchNop); }
209 
210 
CountInputs(size_t size,InstructionSequenceTest::TestOperand * inputs)211 static size_t CountInputs(size_t size,
212                           InstructionSequenceTest::TestOperand* inputs) {
213   size_t i = 0;
214   for (; i < size; ++i) {
215     if (inputs[i].type_ == InstructionSequenceTest::kInvalid) break;
216   }
217   return i;
218 }
219 
220 
EmitI(size_t input_size,TestOperand * inputs)221 Instruction* InstructionSequenceTest::EmitI(size_t input_size,
222                                             TestOperand* inputs) {
223   InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
224   return Emit(kArchNop, 0, nullptr, input_size, mapped_inputs);
225 }
226 
227 
EmitI(TestOperand input_op_0,TestOperand input_op_1,TestOperand input_op_2,TestOperand input_op_3)228 Instruction* InstructionSequenceTest::EmitI(TestOperand input_op_0,
229                                             TestOperand input_op_1,
230                                             TestOperand input_op_2,
231                                             TestOperand input_op_3) {
232   TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
233   return EmitI(CountInputs(arraysize(inputs), inputs), inputs);
234 }
235 
236 
EmitOI(TestOperand output_op,size_t input_size,TestOperand * inputs)237 InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
238     TestOperand output_op, size_t input_size, TestOperand* inputs) {
239   VReg output_vreg = NewReg();
240   InstructionOperand outputs[1]{ConvertOutputOp(output_vreg, output_op)};
241   InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
242   Emit(kArchNop, 1, outputs, input_size, mapped_inputs);
243   return output_vreg;
244 }
245 
246 
EmitOI(TestOperand output_op,TestOperand input_op_0,TestOperand input_op_1,TestOperand input_op_2,TestOperand input_op_3)247 InstructionSequenceTest::VReg InstructionSequenceTest::EmitOI(
248     TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
249     TestOperand input_op_2, TestOperand input_op_3) {
250   TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
251   return EmitOI(output_op, CountInputs(arraysize(inputs), inputs), inputs);
252 }
253 
254 
EmitOOI(TestOperand output_op_0,TestOperand output_op_1,size_t input_size,TestOperand * inputs)255 InstructionSequenceTest::VRegPair InstructionSequenceTest::EmitOOI(
256     TestOperand output_op_0, TestOperand output_op_1, size_t input_size,
257     TestOperand* inputs) {
258   VRegPair output_vregs = std::make_pair(NewReg(), NewReg());
259   InstructionOperand outputs[2]{
260       ConvertOutputOp(output_vregs.first, output_op_0),
261       ConvertOutputOp(output_vregs.second, output_op_1)};
262   InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
263   Emit(kArchNop, 2, outputs, input_size, mapped_inputs);
264   return output_vregs;
265 }
266 
267 
EmitOOI(TestOperand output_op_0,TestOperand output_op_1,TestOperand input_op_0,TestOperand input_op_1,TestOperand input_op_2,TestOperand input_op_3)268 InstructionSequenceTest::VRegPair InstructionSequenceTest::EmitOOI(
269     TestOperand output_op_0, TestOperand output_op_1, TestOperand input_op_0,
270     TestOperand input_op_1, TestOperand input_op_2, TestOperand input_op_3) {
271   TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
272   return EmitOOI(output_op_0, output_op_1,
273                  CountInputs(arraysize(inputs), inputs), inputs);
274 }
275 
276 
EmitCall(TestOperand output_op,size_t input_size,TestOperand * inputs)277 InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
278     TestOperand output_op, size_t input_size, TestOperand* inputs) {
279   VReg output_vreg = NewReg();
280   InstructionOperand outputs[1]{ConvertOutputOp(output_vreg, output_op)};
281   CHECK(UnallocatedOperand::cast(outputs[0]).HasFixedPolicy());
282   InstructionOperand* mapped_inputs = ConvertInputs(input_size, inputs);
283   Emit(kArchCallCodeObject, 1, outputs, input_size, mapped_inputs, 0, nullptr,
284        true);
285   return output_vreg;
286 }
287 
288 
EmitCall(TestOperand output_op,TestOperand input_op_0,TestOperand input_op_1,TestOperand input_op_2,TestOperand input_op_3)289 InstructionSequenceTest::VReg InstructionSequenceTest::EmitCall(
290     TestOperand output_op, TestOperand input_op_0, TestOperand input_op_1,
291     TestOperand input_op_2, TestOperand input_op_3) {
292   TestOperand inputs[] = {input_op_0, input_op_1, input_op_2, input_op_3};
293   return EmitCall(output_op, CountInputs(arraysize(inputs), inputs), inputs);
294 }
295 
296 
EmitBranch(TestOperand input_op)297 Instruction* InstructionSequenceTest::EmitBranch(TestOperand input_op) {
298   InstructionOperand inputs[4]{ConvertInputOp(input_op), ConvertInputOp(Imm()),
299                                ConvertInputOp(Imm()), ConvertInputOp(Imm())};
300   InstructionCode opcode = kArchJmp | FlagsModeField::encode(kFlags_branch) |
301                            FlagsConditionField::encode(kEqual);
302   auto instruction = NewInstruction(opcode, 0, nullptr, 4, inputs);
303   return AddInstruction(instruction);
304 }
305 
306 
EmitFallThrough()307 Instruction* InstructionSequenceTest::EmitFallThrough() {
308   auto instruction = NewInstruction(kArchNop, 0, nullptr);
309   return AddInstruction(instruction);
310 }
311 
312 
EmitJump()313 Instruction* InstructionSequenceTest::EmitJump() {
314   InstructionOperand inputs[1]{ConvertInputOp(Imm())};
315   auto instruction = NewInstruction(kArchJmp, 0, nullptr, 1, inputs);
316   return AddInstruction(instruction);
317 }
318 
319 
NewInstruction(InstructionCode code,size_t outputs_size,InstructionOperand * outputs,size_t inputs_size,InstructionOperand * inputs,size_t temps_size,InstructionOperand * temps)320 Instruction* InstructionSequenceTest::NewInstruction(
321     InstructionCode code, size_t outputs_size, InstructionOperand* outputs,
322     size_t inputs_size, InstructionOperand* inputs, size_t temps_size,
323     InstructionOperand* temps) {
324   CHECK(current_block_);
325   return Instruction::New(zone(), code, outputs_size, outputs, inputs_size,
326                           inputs, temps_size, temps);
327 }
328 
329 
Unallocated(TestOperand op,UnallocatedOperand::ExtendedPolicy policy)330 InstructionOperand InstructionSequenceTest::Unallocated(
331     TestOperand op, UnallocatedOperand::ExtendedPolicy policy) {
332   return UnallocatedOperand(policy, op.vreg_.value_);
333 }
334 
335 
Unallocated(TestOperand op,UnallocatedOperand::ExtendedPolicy policy,UnallocatedOperand::Lifetime lifetime)336 InstructionOperand InstructionSequenceTest::Unallocated(
337     TestOperand op, UnallocatedOperand::ExtendedPolicy policy,
338     UnallocatedOperand::Lifetime lifetime) {
339   return UnallocatedOperand(policy, lifetime, op.vreg_.value_);
340 }
341 
342 
Unallocated(TestOperand op,UnallocatedOperand::ExtendedPolicy policy,int index)343 InstructionOperand InstructionSequenceTest::Unallocated(
344     TestOperand op, UnallocatedOperand::ExtendedPolicy policy, int index) {
345   return UnallocatedOperand(policy, index, op.vreg_.value_);
346 }
347 
348 
Unallocated(TestOperand op,UnallocatedOperand::BasicPolicy policy,int index)349 InstructionOperand InstructionSequenceTest::Unallocated(
350     TestOperand op, UnallocatedOperand::BasicPolicy policy, int index) {
351   return UnallocatedOperand(policy, index, op.vreg_.value_);
352 }
353 
354 
ConvertInputs(size_t input_size,TestOperand * inputs)355 InstructionOperand* InstructionSequenceTest::ConvertInputs(
356     size_t input_size, TestOperand* inputs) {
357   InstructionOperand* mapped_inputs =
358       zone()->NewArray<InstructionOperand>(static_cast<int>(input_size));
359   for (size_t i = 0; i < input_size; ++i) {
360     mapped_inputs[i] = ConvertInputOp(inputs[i]);
361   }
362   return mapped_inputs;
363 }
364 
365 
ConvertInputOp(TestOperand op)366 InstructionOperand InstructionSequenceTest::ConvertInputOp(TestOperand op) {
367   if (op.type_ == kImmediate) {
368     CHECK_EQ(op.vreg_.value_, kNoValue);
369     return ImmediateOperand(ImmediateOperand::INLINE, op.value_);
370   }
371   CHECK_NE(op.vreg_.value_, kNoValue);
372   switch (op.type_) {
373     case kNone:
374       return Unallocated(op, UnallocatedOperand::NONE,
375                          UnallocatedOperand::USED_AT_START);
376     case kUnique:
377       return Unallocated(op, UnallocatedOperand::NONE);
378     case kUniqueRegister:
379       return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
380     case kRegister:
381       return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER,
382                          UnallocatedOperand::USED_AT_START);
383     case kSlot:
384       return Unallocated(op, UnallocatedOperand::MUST_HAVE_SLOT,
385                          UnallocatedOperand::USED_AT_START);
386     case kFixedRegister:
387       CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
388       return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
389     case kFixedSlot:
390       return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
391     default:
392       break;
393   }
394   CHECK(false);
395   return InstructionOperand();
396 }
397 
398 
ConvertOutputOp(VReg vreg,TestOperand op)399 InstructionOperand InstructionSequenceTest::ConvertOutputOp(VReg vreg,
400                                                             TestOperand op) {
401   CHECK_EQ(op.vreg_.value_, kNoValue);
402   op.vreg_ = vreg;
403   switch (op.type_) {
404     case kSameAsFirst:
405       return Unallocated(op, UnallocatedOperand::SAME_AS_FIRST_INPUT);
406     case kRegister:
407       return Unallocated(op, UnallocatedOperand::MUST_HAVE_REGISTER);
408     case kFixedSlot:
409       return Unallocated(op, UnallocatedOperand::FIXED_SLOT, op.value_);
410     case kFixedRegister:
411       CHECK(0 <= op.value_ && op.value_ < num_general_registers_);
412       return Unallocated(op, UnallocatedOperand::FIXED_REGISTER, op.value_);
413     default:
414       break;
415   }
416   CHECK(false);
417   return InstructionOperand();
418 }
419 
420 
NewBlock(bool deferred)421 InstructionBlock* InstructionSequenceTest::NewBlock(bool deferred) {
422   CHECK(current_block_ == nullptr);
423   Rpo rpo = Rpo::FromInt(static_cast<int>(instruction_blocks_.size()));
424   Rpo loop_header = Rpo::Invalid();
425   Rpo loop_end = Rpo::Invalid();
426   if (!loop_blocks_.empty()) {
427     auto& loop_data = loop_blocks_.back();
428     // This is a loop header.
429     if (!loop_data.loop_header_.IsValid()) {
430       loop_end = Rpo::FromInt(rpo.ToInt() + loop_data.expected_blocks_);
431       loop_data.expected_blocks_--;
432       loop_data.loop_header_ = rpo;
433     } else {
434       // This is a loop body.
435       CHECK_NE(0, loop_data.expected_blocks_);
436       // TODO(dcarney): handle nested loops.
437       loop_data.expected_blocks_--;
438       loop_header = loop_data.loop_header_;
439     }
440   }
441   // Construct instruction block.
442   auto instruction_block = new (zone())
443       InstructionBlock(zone(), rpo, loop_header, loop_end, deferred, false);
444   instruction_blocks_.push_back(instruction_block);
445   current_block_ = instruction_block;
446   sequence()->StartBlock(rpo);
447   return instruction_block;
448 }
449 
450 
WireBlocks()451 void InstructionSequenceTest::WireBlocks() {
452   CHECK(!current_block());
453   CHECK(instruction_blocks_.size() == completions_.size());
454   CHECK(loop_blocks_.empty());
455   // Wire in end block to look like a scheduler produced cfg.
456   auto end_block = NewBlock();
457   current_block_ = nullptr;
458   sequence()->EndBlock(end_block->rpo_number());
459   size_t offset = 0;
460   for (const auto& completion : completions_) {
461     switch (completion.type_) {
462       case kBlockEnd: {
463         auto block = instruction_blocks_[offset];
464         block->successors().push_back(end_block->rpo_number());
465         end_block->predecessors().push_back(block->rpo_number());
466         break;
467       }
468       case kFallThrough:  // Fallthrough.
469       case kJump:
470         WireBlock(offset, completion.offset_0_);
471         break;
472       case kBranch:
473         WireBlock(offset, completion.offset_0_);
474         WireBlock(offset, completion.offset_1_);
475         break;
476     }
477     ++offset;
478   }
479 }
480 
481 
WireBlock(size_t block_offset,int jump_offset)482 void InstructionSequenceTest::WireBlock(size_t block_offset, int jump_offset) {
483   size_t target_block_offset = block_offset + static_cast<size_t>(jump_offset);
484   CHECK(block_offset < instruction_blocks_.size());
485   CHECK(target_block_offset < instruction_blocks_.size());
486   auto block = instruction_blocks_[block_offset];
487   auto target = instruction_blocks_[target_block_offset];
488   block->successors().push_back(target->rpo_number());
489   target->predecessors().push_back(block->rpo_number());
490 }
491 
492 
Emit(InstructionCode code,size_t outputs_size,InstructionOperand * outputs,size_t inputs_size,InstructionOperand * inputs,size_t temps_size,InstructionOperand * temps,bool is_call)493 Instruction* InstructionSequenceTest::Emit(
494     InstructionCode code, size_t outputs_size, InstructionOperand* outputs,
495     size_t inputs_size, InstructionOperand* inputs, size_t temps_size,
496     InstructionOperand* temps, bool is_call) {
497   auto instruction = NewInstruction(code, outputs_size, outputs, inputs_size,
498                                     inputs, temps_size, temps);
499   if (is_call) instruction->MarkAsCall();
500   return AddInstruction(instruction);
501 }
502 
503 
AddInstruction(Instruction * instruction)504 Instruction* InstructionSequenceTest::AddInstruction(Instruction* instruction) {
505   sequence()->AddInstruction(instruction);
506   return instruction;
507 }
508 
509 }  // namespace compiler
510 }  // namespace internal
511 }  // namespace v8
512