test-instruction.cc 9.51 KB
Newer Older
1 2 3 4 5 6 7 8
// 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/code-generator.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/instruction.h"
9
#include "src/compiler/linkage.h"
10 11 12 13 14
#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"
15
#include "src/objects-inl.h"
16
#include "test/cctest/cctest.h"
17

18 19 20
namespace v8 {
namespace internal {
namespace compiler {
21 22 23 24 25 26 27

typedef v8::internal::compiler::Instruction TestInstr;
typedef v8::internal::compiler::InstructionSequence TestInstrSeq;

// A testing helper for the register code abstraction.
class InstructionTester : public HandleAndZoneScope {
 public:  // We're all friends here.
28
  InstructionTester()
29
      : graph(zone()),
30 31
        schedule(zone()),
        common(zone()),
32
        machine(zone()),
33
        code(nullptr) {}
34 35 36 37 38 39 40 41 42 43 44 45

  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.
46
      Scheduler::ComputeSpecialRPO(main_zone(), &schedule);
47
      CHECK_NE(0u, schedule.rpo_order()->size());
48
    }
49
    InstructionBlocks* instruction_blocks =
50
        TestInstrSeq::InstructionBlocksFor(main_zone(), &schedule);
51 52
    code = new (main_zone())
        TestInstrSeq(main_isolate(), main_zone(), instruction_blocks);
53 54 55 56
  }

  Node* Int32Constant(int32_t val) {
    Node* node = graph.NewNode(common.Int32Constant(val));
57
    schedule.AddNode(schedule.start(), node);
58 59 60 61 62
    return node;
  }

  Node* Float64Constant(double val) {
    Node* node = graph.NewNode(common.Float64Constant(val));
63
    schedule.AddNode(schedule.start(), node);
64 65 66 67 68
    return node;
  }

  Node* Parameter(int32_t which) {
    Node* node = graph.NewNode(common.Parameter(which));
69
    schedule.AddNode(schedule.start(), node);
70 71 72 73 74 75 76 77 78
    return node;
  }

  Node* NewNode(BasicBlock* block) {
    Node* node = graph.NewNode(common.Int32Constant(111));
    schedule.AddNode(block, node);
    return node;
  }

79
  int NewInstr() {
80 81
    InstructionCode opcode = static_cast<InstructionCode>(110);
    TestInstr* instr = TestInstr::New(zone(), opcode);
82
    return code->AddInstruction(instr);
83 84
  }

85 86
  UnallocatedOperand Unallocated(int vreg) {
    return UnallocatedOperand(UnallocatedOperand::ANY, vreg);
87
  }
88

89 90 91 92
  RpoNumber RpoFor(BasicBlock* block) {
    return RpoNumber::FromInt(block->rpo_number());
  }

93
  InstructionBlock* BlockAt(BasicBlock* block) {
94
    return code->InstructionBlockAt(RpoFor(block));
95 96 97 98 99 100 101 102 103 104 105 106
  }
  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();
  }
107 108 109 110 111 112 113 114 115 116
};


TEST(InstructionBasic) {
  InstructionTester R;

  for (int i = 0; i < 10; i++) {
    R.Int32Constant(i);  // Add some nodes to the graph.
  }

117
  BasicBlock* last = R.schedule.start();
118 119 120 121 122 123 124 125 126
  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();
127
  CHECK_EQ(static_cast<int>(blocks->size()), R.code->InstructionBlockCount());
128

129
  for (auto block : *blocks) {
130
    CHECK_EQ(block->rpo_number(), R.BlockAt(block)->rpo_number().ToInt());
131
    CHECK(!block->loop_end());
132 133 134 135 136 137 138
  }
}


TEST(InstructionGetBasicBlock) {
  InstructionTester R;

139
  BasicBlock* b0 = R.schedule.start();
140 141
  BasicBlock* b1 = R.schedule.NewBasicBlock();
  BasicBlock* b2 = R.schedule.NewBasicBlock();
142
  BasicBlock* b3 = R.schedule.end();
143 144 145 146 147 148 149

  R.schedule.AddGoto(b0, b1);
  R.schedule.AddGoto(b1, b2);
  R.schedule.AddGoto(b2, b3);

  R.allocCode();

150
  R.code->StartBlock(R.RpoFor(b0));
151 152
  int i0 = R.NewInstr();
  int i1 = R.NewInstr();
153 154
  R.code->EndBlock(R.RpoFor(b0));
  R.code->StartBlock(R.RpoFor(b1));
155 156 157 158
  int i2 = R.NewInstr();
  int i3 = R.NewInstr();
  int i4 = R.NewInstr();
  int i5 = R.NewInstr();
159 160
  R.code->EndBlock(R.RpoFor(b1));
  R.code->StartBlock(R.RpoFor(b2));
161 162 163
  int i6 = R.NewInstr();
  int i7 = R.NewInstr();
  int i8 = R.NewInstr();
164 165 166
  R.code->EndBlock(R.RpoFor(b2));
  R.code->StartBlock(R.RpoFor(b3));
  R.code->EndBlock(R.RpoFor(b3));
167

168 169
  CHECK_EQ(b0, R.GetBasicBlock(i0));
  CHECK_EQ(b0, R.GetBasicBlock(i1));
170

171 172 173 174
  CHECK_EQ(b1, R.GetBasicBlock(i2));
  CHECK_EQ(b1, R.GetBasicBlock(i3));
  CHECK_EQ(b1, R.GetBasicBlock(i4));
  CHECK_EQ(b1, R.GetBasicBlock(i5));
175

176 177 178
  CHECK_EQ(b2, R.GetBasicBlock(i6));
  CHECK_EQ(b2, R.GetBasicBlock(i7));
  CHECK_EQ(b2, R.GetBasicBlock(i8));
179

180 181
  CHECK_EQ(b0, R.GetBasicBlock(R.first_instruction_index(b0)));
  CHECK_EQ(b0, R.GetBasicBlock(R.last_instruction_index(b0)));
182

183 184
  CHECK_EQ(b1, R.GetBasicBlock(R.first_instruction_index(b1)));
  CHECK_EQ(b1, R.GetBasicBlock(R.last_instruction_index(b1)));
185

186 187
  CHECK_EQ(b2, R.GetBasicBlock(R.first_instruction_index(b2)));
  CHECK_EQ(b2, R.GetBasicBlock(R.last_instruction_index(b2)));
188

189 190
  CHECK_EQ(b3, R.GetBasicBlock(R.first_instruction_index(b3)));
  CHECK_EQ(b3, R.GetBasicBlock(R.last_instruction_index(b3)));
191 192 193 194 195 196
}


TEST(InstructionIsGapAt) {
  InstructionTester R;

197
  BasicBlock* b0 = R.schedule.start();
198 199 200 201
  R.schedule.AddReturn(b0, R.Int32Constant(1));

  R.allocCode();
  TestInstr* i0 = TestInstr::New(R.zone(), 100);
202
  TestInstr* g = TestInstr::New(R.zone(), 103);
203
  R.code->StartBlock(R.RpoFor(b0));
204 205
  R.code->AddInstruction(i0);
  R.code->AddInstruction(g);
206
  R.code->EndBlock(R.RpoFor(b0));
207

208
  CHECK_EQ(2, R.code->instructions().size());
209 210 211 212 213 214
}


TEST(InstructionIsGapAt2) {
  InstructionTester R;

215 216
  BasicBlock* b0 = R.schedule.start();
  BasicBlock* b1 = R.schedule.end();
217 218 219 220 221
  R.schedule.AddGoto(b0, b1);
  R.schedule.AddReturn(b1, R.Int32Constant(1));

  R.allocCode();
  TestInstr* i0 = TestInstr::New(R.zone(), 100);
222
  TestInstr* g = TestInstr::New(R.zone(), 103);
223
  R.code->StartBlock(R.RpoFor(b0));
224 225
  R.code->AddInstruction(i0);
  R.code->AddInstruction(g);
226
  R.code->EndBlock(R.RpoFor(b0));
227 228

  TestInstr* i1 = TestInstr::New(R.zone(), 102);
229
  TestInstr* g1 = TestInstr::New(R.zone(), 104);
230
  R.code->StartBlock(R.RpoFor(b1));
231 232
  R.code->AddInstruction(i1);
  R.code->AddInstruction(g1);
233
  R.code->EndBlock(R.RpoFor(b1));
234

235
  CHECK_EQ(4, R.code->instructions().size());
236 237 238 239 240 241
}


TEST(InstructionAddGapMove) {
  InstructionTester R;

242
  BasicBlock* b0 = R.schedule.start();
243 244 245 246
  R.schedule.AddReturn(b0, R.Int32Constant(1));

  R.allocCode();
  TestInstr* i0 = TestInstr::New(R.zone(), 100);
247
  TestInstr* g = TestInstr::New(R.zone(), 103);
248
  R.code->StartBlock(R.RpoFor(b0));
249 250
  R.code->AddInstruction(i0);
  R.code->AddInstruction(g);
251
  R.code->EndBlock(R.RpoFor(b0));
252

253
  CHECK_EQ(2, R.code->instructions().size());
254

255 256
  int index = 0;
  for (auto instr : R.code->instructions()) {
257 258
    UnallocatedOperand op1 = R.Unallocated(index++);
    UnallocatedOperand op2 = R.Unallocated(index++);
259
    instr->GetOrCreateParallelMove(TestInstr::START, R.zone())
260
        ->AddMove(op1, op2);
261
    ParallelMove* move = instr->GetParallelMove(TestInstr::START);
262
    CHECK(move);
263 264
    CHECK_EQ(1u, move->size());
    MoveOperands* cur = move->at(0);
265 266
    CHECK(op1.Equals(cur->source()));
    CHECK(op2.Equals(cur->destination()));
267 268 269 270 271
  }
}


TEST(InstructionOperands) {
272
  v8::internal::AccountingAllocator allocator;
273
  Zone zone(&allocator, ZONE_NAME);
274 275 276

  {
    TestInstr* i = TestInstr::New(&zone, 101);
277 278 279
    CHECK_EQ(0, static_cast<int>(i->OutputCount()));
    CHECK_EQ(0, static_cast<int>(i->InputCount()));
    CHECK_EQ(0, static_cast<int>(i->TempCount()));
280 281
  }

282
  int vreg = 15;
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
  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)};
300

301 302 303
  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++) {
304 305 306 307 308 309 310
        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++) {
311
          CHECK(outputs[z].Equals(*m->OutputAt(z)));
312 313 314
        }

        for (size_t z = 0; z < j; z++) {
315
          CHECK(inputs[z].Equals(*m->InputAt(z)));
316 317 318
        }

        for (size_t z = 0; z < k; z++) {
319
          CHECK(temps[z].Equals(*m->TempAt(z)));
320 321 322 323 324
        }
      }
    }
  }
}
325 326 327 328

}  // namespace compiler
}  // namespace internal
}  // namespace v8