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/v8.h"
6#include "test/cctest/cctest.h"
7
8#include "src/compiler/code-generator.h"
9#include "src/compiler/common-operator.h"
10#include "src/compiler/graph.h"
11#include "src/compiler/instruction.h"
12#include "src/compiler/linkage.h"
13#include "src/compiler/machine-operator.h"
14#include "src/compiler/node.h"
15#include "src/compiler/operator.h"
16#include "src/compiler/schedule.h"
17#include "src/compiler/scheduler.h"
18#include "src/lithium.h"
19
20using namespace v8::internal;
21using namespace v8::internal::compiler;
22
23typedef v8::internal::compiler::Instruction TestInstr;
24typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
25
26// A testing helper for the register code abstraction.
27class InstructionTester : public HandleAndZoneScope {
28 public:  // We're all friends here.
29  InstructionTester()
30      : isolate(main_isolate()),
31        graph(zone()),
32        schedule(zone()),
33        info(static_cast<HydrogenCodeStub*>(NULL), main_isolate()),
34        linkage(&info),
35        common(zone()),
36        code(NULL) {}
37
38  ~InstructionTester() { delete code; }
39
40  Isolate* isolate;
41  Graph graph;
42  Schedule schedule;
43  CompilationInfoWithZone info;
44  Linkage linkage;
45  CommonOperatorBuilder common;
46  MachineOperatorBuilder machine;
47  TestInstrSeq* code;
48
49  Zone* zone() { return main_zone(); }
50
51  void allocCode() {
52    if (schedule.rpo_order()->size() == 0) {
53      // Compute the RPO order.
54      Scheduler::ComputeSpecialRPO(&schedule);
55      DCHECK(schedule.rpo_order()->size() > 0);
56    }
57    code = new TestInstrSeq(&linkage, &graph, &schedule);
58  }
59
60  Node* Int32Constant(int32_t val) {
61    Node* node = graph.NewNode(common.Int32Constant(val));
62    schedule.AddNode(schedule.start(), node);
63    return node;
64  }
65
66  Node* Float64Constant(double val) {
67    Node* node = graph.NewNode(common.Float64Constant(val));
68    schedule.AddNode(schedule.start(), node);
69    return node;
70  }
71
72  Node* Parameter(int32_t which) {
73    Node* node = graph.NewNode(common.Parameter(which));
74    schedule.AddNode(schedule.start(), node);
75    return node;
76  }
77
78  Node* NewNode(BasicBlock* block) {
79    Node* node = graph.NewNode(common.Int32Constant(111));
80    schedule.AddNode(block, node);
81    return node;
82  }
83
84  int NewInstr(BasicBlock* block) {
85    InstructionCode opcode = static_cast<InstructionCode>(110);
86    TestInstr* instr = TestInstr::New(zone(), opcode);
87    return code->AddInstruction(instr, block);
88  }
89
90  UnallocatedOperand* NewUnallocated(int vreg) {
91    UnallocatedOperand* unallocated =
92        new (zone()) UnallocatedOperand(UnallocatedOperand::ANY);
93    unallocated->set_virtual_register(vreg);
94    return unallocated;
95  }
96};
97
98
99TEST(InstructionBasic) {
100  InstructionTester R;
101
102  for (int i = 0; i < 10; i++) {
103    R.Int32Constant(i);  // Add some nodes to the graph.
104  }
105
106  BasicBlock* last = R.schedule.start();
107  for (int i = 0; i < 5; i++) {
108    BasicBlock* block = R.schedule.NewBasicBlock();
109    R.schedule.AddGoto(last, block);
110    last = block;
111  }
112
113  R.allocCode();
114
115  CHECK_EQ(R.graph.NodeCount(), R.code->ValueCount());
116
117  BasicBlockVector* blocks = R.schedule.rpo_order();
118  CHECK_EQ(static_cast<int>(blocks->size()), R.code->BasicBlockCount());
119
120  int index = 0;
121  for (BasicBlockVectorIter i = blocks->begin(); i != blocks->end();
122       i++, index++) {
123    BasicBlock* block = *i;
124    CHECK_EQ(block, R.code->BlockAt(index));
125    CHECK_EQ(-1, R.code->GetLoopEnd(block));
126  }
127}
128
129
130TEST(InstructionGetBasicBlock) {
131  InstructionTester R;
132
133  BasicBlock* b0 = R.schedule.start();
134  BasicBlock* b1 = R.schedule.NewBasicBlock();
135  BasicBlock* b2 = R.schedule.NewBasicBlock();
136  BasicBlock* b3 = R.schedule.end();
137
138  R.schedule.AddGoto(b0, b1);
139  R.schedule.AddGoto(b1, b2);
140  R.schedule.AddGoto(b2, b3);
141
142  R.allocCode();
143
144  R.code->StartBlock(b0);
145  int i0 = R.NewInstr(b0);
146  int i1 = R.NewInstr(b0);
147  R.code->EndBlock(b0);
148  R.code->StartBlock(b1);
149  int i2 = R.NewInstr(b1);
150  int i3 = R.NewInstr(b1);
151  int i4 = R.NewInstr(b1);
152  int i5 = R.NewInstr(b1);
153  R.code->EndBlock(b1);
154  R.code->StartBlock(b2);
155  int i6 = R.NewInstr(b2);
156  int i7 = R.NewInstr(b2);
157  int i8 = R.NewInstr(b2);
158  R.code->EndBlock(b2);
159  R.code->StartBlock(b3);
160  R.code->EndBlock(b3);
161
162  CHECK_EQ(b0, R.code->GetBasicBlock(i0));
163  CHECK_EQ(b0, R.code->GetBasicBlock(i1));
164
165  CHECK_EQ(b1, R.code->GetBasicBlock(i2));
166  CHECK_EQ(b1, R.code->GetBasicBlock(i3));
167  CHECK_EQ(b1, R.code->GetBasicBlock(i4));
168  CHECK_EQ(b1, R.code->GetBasicBlock(i5));
169
170  CHECK_EQ(b2, R.code->GetBasicBlock(i6));
171  CHECK_EQ(b2, R.code->GetBasicBlock(i7));
172  CHECK_EQ(b2, R.code->GetBasicBlock(i8));
173
174  CHECK_EQ(b0, R.code->GetBasicBlock(b0->first_instruction_index()));
175  CHECK_EQ(b0, R.code->GetBasicBlock(b0->last_instruction_index()));
176
177  CHECK_EQ(b1, R.code->GetBasicBlock(b1->first_instruction_index()));
178  CHECK_EQ(b1, R.code->GetBasicBlock(b1->last_instruction_index()));
179
180  CHECK_EQ(b2, R.code->GetBasicBlock(b2->first_instruction_index()));
181  CHECK_EQ(b2, R.code->GetBasicBlock(b2->last_instruction_index()));
182
183  CHECK_EQ(b3, R.code->GetBasicBlock(b3->first_instruction_index()));
184  CHECK_EQ(b3, R.code->GetBasicBlock(b3->last_instruction_index()));
185}
186
187
188TEST(InstructionIsGapAt) {
189  InstructionTester R;
190
191  BasicBlock* b0 = R.schedule.start();
192  R.schedule.AddReturn(b0, R.Int32Constant(1));
193
194  R.allocCode();
195  TestInstr* i0 = TestInstr::New(R.zone(), 100);
196  TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
197  R.code->StartBlock(b0);
198  R.code->AddInstruction(i0, b0);
199  R.code->AddInstruction(g, b0);
200  R.code->EndBlock(b0);
201
202  CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
203
204  CHECK_EQ(true, R.code->IsGapAt(0));   // Label
205  CHECK_EQ(true, R.code->IsGapAt(1));   // Gap
206  CHECK_EQ(false, R.code->IsGapAt(2));  // i0
207  CHECK_EQ(true, R.code->IsGapAt(3));   // Gap
208  CHECK_EQ(true, R.code->IsGapAt(4));   // Gap
209  CHECK_EQ(false, R.code->IsGapAt(5));  // g
210}
211
212
213TEST(InstructionIsGapAt2) {
214  InstructionTester R;
215
216  BasicBlock* b0 = R.schedule.start();
217  BasicBlock* b1 = R.schedule.end();
218  R.schedule.AddGoto(b0, b1);
219  R.schedule.AddReturn(b1, R.Int32Constant(1));
220
221  R.allocCode();
222  TestInstr* i0 = TestInstr::New(R.zone(), 100);
223  TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
224  R.code->StartBlock(b0);
225  R.code->AddInstruction(i0, b0);
226  R.code->AddInstruction(g, b0);
227  R.code->EndBlock(b0);
228
229  TestInstr* i1 = TestInstr::New(R.zone(), 102);
230  TestInstr* g1 = TestInstr::New(R.zone(), 104)->MarkAsControl();
231  R.code->StartBlock(b1);
232  R.code->AddInstruction(i1, b1);
233  R.code->AddInstruction(g1, b1);
234  R.code->EndBlock(b1);
235
236  CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
237
238  CHECK_EQ(true, R.code->IsGapAt(0));   // Label
239  CHECK_EQ(true, R.code->IsGapAt(1));   // Gap
240  CHECK_EQ(false, R.code->IsGapAt(2));  // i0
241  CHECK_EQ(true, R.code->IsGapAt(3));   // Gap
242  CHECK_EQ(true, R.code->IsGapAt(4));   // Gap
243  CHECK_EQ(false, R.code->IsGapAt(5));  // g
244
245  CHECK_EQ(true, R.code->InstructionAt(6)->IsBlockStart());
246
247  CHECK_EQ(true, R.code->IsGapAt(6));    // Label
248  CHECK_EQ(true, R.code->IsGapAt(7));    // Gap
249  CHECK_EQ(false, R.code->IsGapAt(8));   // i1
250  CHECK_EQ(true, R.code->IsGapAt(9));    // Gap
251  CHECK_EQ(true, R.code->IsGapAt(10));   // Gap
252  CHECK_EQ(false, R.code->IsGapAt(11));  // g1
253}
254
255
256TEST(InstructionAddGapMove) {
257  InstructionTester R;
258
259  BasicBlock* b0 = R.schedule.start();
260  R.schedule.AddReturn(b0, R.Int32Constant(1));
261
262  R.allocCode();
263  TestInstr* i0 = TestInstr::New(R.zone(), 100);
264  TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
265  R.code->StartBlock(b0);
266  R.code->AddInstruction(i0, b0);
267  R.code->AddInstruction(g, b0);
268  R.code->EndBlock(b0);
269
270  CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
271
272  CHECK_EQ(true, R.code->IsGapAt(0));   // Label
273  CHECK_EQ(true, R.code->IsGapAt(1));   // Gap
274  CHECK_EQ(false, R.code->IsGapAt(2));  // i0
275  CHECK_EQ(true, R.code->IsGapAt(3));   // Gap
276  CHECK_EQ(true, R.code->IsGapAt(4));   // Gap
277  CHECK_EQ(false, R.code->IsGapAt(5));  // g
278
279  int indexes[] = {0, 1, 3, 4, -1};
280  for (int i = 0; indexes[i] >= 0; i++) {
281    int index = indexes[i];
282
283    UnallocatedOperand* op1 = R.NewUnallocated(index + 6);
284    UnallocatedOperand* op2 = R.NewUnallocated(index + 12);
285
286    R.code->AddGapMove(index, op1, op2);
287    GapInstruction* gap = R.code->GapAt(index);
288    ParallelMove* move = gap->GetParallelMove(GapInstruction::START);
289    CHECK_NE(NULL, move);
290    const ZoneList<MoveOperands>* move_operands = move->move_operands();
291    CHECK_EQ(1, move_operands->length());
292    MoveOperands* cur = &move_operands->at(0);
293    CHECK_EQ(op1, cur->source());
294    CHECK_EQ(op2, cur->destination());
295  }
296}
297
298
299TEST(InstructionOperands) {
300  Zone zone(CcTest::InitIsolateOnce());
301
302  {
303    TestInstr* i = TestInstr::New(&zone, 101);
304    CHECK_EQ(0, static_cast<int>(i->OutputCount()));
305    CHECK_EQ(0, static_cast<int>(i->InputCount()));
306    CHECK_EQ(0, static_cast<int>(i->TempCount()));
307  }
308
309  InstructionOperand* outputs[] = {
310      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
311      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
312      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
313      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER)};
314
315  InstructionOperand* inputs[] = {
316      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
317      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
318      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
319      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER)};
320
321  InstructionOperand* temps[] = {
322      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
323      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
324      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
325      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER)};
326
327  for (size_t i = 0; i < arraysize(outputs); i++) {
328    for (size_t j = 0; j < arraysize(inputs); j++) {
329      for (size_t k = 0; k < arraysize(temps); k++) {
330        TestInstr* m =
331            TestInstr::New(&zone, 101, i, outputs, j, inputs, k, temps);
332        CHECK(i == m->OutputCount());
333        CHECK(j == m->InputCount());
334        CHECK(k == m->TempCount());
335
336        for (size_t z = 0; z < i; z++) {
337          CHECK_EQ(outputs[z], m->OutputAt(z));
338        }
339
340        for (size_t z = 0; z < j; z++) {
341          CHECK_EQ(inputs[z], m->InputAt(z));
342        }
343
344        for (size_t z = 0; z < k; z++) {
345          CHECK_EQ(temps[z], m->TempAt(z));
346        }
347      }
348    }
349  }
350}
351