2014-07-30 13:54:45 +00:00
|
|
|
// 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/v8.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
|
|
|
|
#include "src/compiler/code-generator.h"
|
|
|
|
#include "src/compiler/common-operator.h"
|
|
|
|
#include "src/compiler/graph.h"
|
|
|
|
#include "src/compiler/instruction.h"
|
2014-09-12 11:59:26 +00:00
|
|
|
#include "src/compiler/linkage.h"
|
2014-07-30 13:54:45 +00:00
|
|
|
#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"
|
|
|
|
#include "src/lithium.h"
|
|
|
|
|
|
|
|
using namespace v8::internal;
|
|
|
|
using namespace v8::internal::compiler;
|
|
|
|
|
|
|
|
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.
|
2014-08-12 13:33:35 +00:00
|
|
|
InstructionTester()
|
2014-07-30 13:54:45 +00:00
|
|
|
: isolate(main_isolate()),
|
|
|
|
graph(zone()),
|
|
|
|
schedule(zone()),
|
2015-02-12 12:57:07 +00:00
|
|
|
fake_stub(main_isolate()),
|
|
|
|
info(&fake_stub, main_isolate()),
|
2014-07-30 13:54:45 +00:00
|
|
|
common(zone()),
|
2014-11-10 14:28:09 +00:00
|
|
|
machine(zone()),
|
2014-07-30 13:54:45 +00:00
|
|
|
code(NULL) {}
|
|
|
|
|
|
|
|
Isolate* isolate;
|
|
|
|
Graph graph;
|
|
|
|
Schedule schedule;
|
2015-02-12 12:57:07 +00:00
|
|
|
FakeStubForTesting fake_stub;
|
2014-07-30 13:54:45 +00:00
|
|
|
CompilationInfoWithZone info;
|
|
|
|
CommonOperatorBuilder common;
|
|
|
|
MachineOperatorBuilder machine;
|
|
|
|
TestInstrSeq* code;
|
|
|
|
|
|
|
|
Zone* zone() { return main_zone(); }
|
|
|
|
|
|
|
|
void allocCode() {
|
|
|
|
if (schedule.rpo_order()->size() == 0) {
|
|
|
|
// Compute the RPO order.
|
2014-11-11 15:29:12 +00:00
|
|
|
Scheduler::ComputeSpecialRPO(main_zone(), &schedule);
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(schedule.rpo_order()->size() > 0);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
2014-11-03 13:26:46 +00:00
|
|
|
InstructionBlocks* instruction_blocks =
|
2014-11-03 13:41:56 +00:00
|
|
|
TestInstrSeq::InstructionBlocksFor(main_zone(), &schedule);
|
2015-01-23 15:19:34 +00:00
|
|
|
code = new (main_zone())
|
|
|
|
TestInstrSeq(main_isolate(), main_zone(), instruction_blocks);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Node* Int32Constant(int32_t val) {
|
|
|
|
Node* node = graph.NewNode(common.Int32Constant(val));
|
2014-08-18 13:28:10 +00:00
|
|
|
schedule.AddNode(schedule.start(), node);
|
2014-07-30 13:54:45 +00:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* Float64Constant(double val) {
|
|
|
|
Node* node = graph.NewNode(common.Float64Constant(val));
|
2014-08-18 13:28:10 +00:00
|
|
|
schedule.AddNode(schedule.start(), node);
|
2014-07-30 13:54:45 +00:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* Parameter(int32_t which) {
|
|
|
|
Node* node = graph.NewNode(common.Parameter(which));
|
2014-08-18 13:28:10 +00:00
|
|
|
schedule.AddNode(schedule.start(), node);
|
2014-07-30 13:54:45 +00:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* NewNode(BasicBlock* block) {
|
|
|
|
Node* node = graph.NewNode(common.Int32Constant(111));
|
|
|
|
schedule.AddNode(block, node);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2014-10-14 08:51:22 +00:00
|
|
|
int NewInstr() {
|
2014-07-30 13:54:45 +00:00
|
|
|
InstructionCode opcode = static_cast<InstructionCode>(110);
|
|
|
|
TestInstr* instr = TestInstr::New(zone(), opcode);
|
2014-10-14 08:51:22 +00:00
|
|
|
return code->AddInstruction(instr);
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UnallocatedOperand* NewUnallocated(int vreg) {
|
2015-02-09 14:20:06 +00:00
|
|
|
return UnallocatedOperand(UnallocatedOperand::ANY, vreg).Copy(zone());
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
2014-10-20 10:19:15 +00:00
|
|
|
|
|
|
|
InstructionBlock* BlockAt(BasicBlock* block) {
|
|
|
|
return code->InstructionBlockAt(block->GetRpoNumber());
|
|
|
|
}
|
|
|
|
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();
|
|
|
|
}
|
2014-07-30 13:54:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TEST(InstructionBasic) {
|
|
|
|
InstructionTester R;
|
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
R.Int32Constant(i); // Add some nodes to the graph.
|
|
|
|
}
|
|
|
|
|
2014-08-18 13:28:10 +00:00
|
|
|
BasicBlock* last = R.schedule.start();
|
2014-07-30 13:54:45 +00:00
|
|
|
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();
|
2014-10-21 06:59:50 +00:00
|
|
|
CHECK_EQ(static_cast<int>(blocks->size()), R.code->InstructionBlockCount());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2015-01-22 13:01:02 +00:00
|
|
|
for (auto block : *blocks) {
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(block->rpo_number(), R.BlockAt(block)->rpo_number().ToInt());
|
|
|
|
CHECK_EQ(block->id().ToInt(), R.BlockAt(block)->id().ToInt());
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(!block->loop_end());
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(InstructionGetBasicBlock) {
|
|
|
|
InstructionTester R;
|
|
|
|
|
2014-08-18 13:28:10 +00:00
|
|
|
BasicBlock* b0 = R.schedule.start();
|
2014-07-30 13:54:45 +00:00
|
|
|
BasicBlock* b1 = R.schedule.NewBasicBlock();
|
|
|
|
BasicBlock* b2 = R.schedule.NewBasicBlock();
|
2014-08-18 13:28:10 +00:00
|
|
|
BasicBlock* b3 = R.schedule.end();
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
R.schedule.AddGoto(b0, b1);
|
|
|
|
R.schedule.AddGoto(b1, b2);
|
|
|
|
R.schedule.AddGoto(b2, b3);
|
|
|
|
|
|
|
|
R.allocCode();
|
|
|
|
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->StartBlock(b0->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
int i0 = R.NewInstr();
|
|
|
|
int i1 = R.NewInstr();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b0->GetRpoNumber());
|
|
|
|
R.code->StartBlock(b1->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
int i2 = R.NewInstr();
|
|
|
|
int i3 = R.NewInstr();
|
|
|
|
int i4 = R.NewInstr();
|
|
|
|
int i5 = R.NewInstr();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b1->GetRpoNumber());
|
|
|
|
R.code->StartBlock(b2->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
int i6 = R.NewInstr();
|
|
|
|
int i7 = R.NewInstr();
|
|
|
|
int i8 = R.NewInstr();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b2->GetRpoNumber());
|
|
|
|
R.code->StartBlock(b3->GetRpoNumber());
|
|
|
|
R.code->EndBlock(b3->GetRpoNumber());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b0, R.GetBasicBlock(i0));
|
|
|
|
CHECK_EQ(b0, R.GetBasicBlock(i1));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b1, R.GetBasicBlock(i2));
|
|
|
|
CHECK_EQ(b1, R.GetBasicBlock(i3));
|
|
|
|
CHECK_EQ(b1, R.GetBasicBlock(i4));
|
|
|
|
CHECK_EQ(b1, R.GetBasicBlock(i5));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b2, R.GetBasicBlock(i6));
|
|
|
|
CHECK_EQ(b2, R.GetBasicBlock(i7));
|
|
|
|
CHECK_EQ(b2, R.GetBasicBlock(i8));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b0, R.GetBasicBlock(R.first_instruction_index(b0)));
|
|
|
|
CHECK_EQ(b0, R.GetBasicBlock(R.last_instruction_index(b0)));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b1, R.GetBasicBlock(R.first_instruction_index(b1)));
|
|
|
|
CHECK_EQ(b1, R.GetBasicBlock(R.last_instruction_index(b1)));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b2, R.GetBasicBlock(R.first_instruction_index(b2)));
|
|
|
|
CHECK_EQ(b2, R.GetBasicBlock(R.last_instruction_index(b2)));
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-10-20 10:19:15 +00:00
|
|
|
CHECK_EQ(b3, R.GetBasicBlock(R.first_instruction_index(b3)));
|
|
|
|
CHECK_EQ(b3, R.GetBasicBlock(R.last_instruction_index(b3)));
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(InstructionIsGapAt) {
|
|
|
|
InstructionTester R;
|
|
|
|
|
2014-08-18 13:28:10 +00:00
|
|
|
BasicBlock* b0 = R.schedule.start();
|
2014-07-30 13:54:45 +00:00
|
|
|
R.schedule.AddReturn(b0, R.Int32Constant(1));
|
|
|
|
|
|
|
|
R.allocCode();
|
|
|
|
TestInstr* i0 = TestInstr::New(R.zone(), 100);
|
2014-12-18 14:42:06 +00:00
|
|
|
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->StartBlock(b0->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
R.code->AddInstruction(i0);
|
|
|
|
R.code->AddInstruction(g);
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b0->GetRpoNumber());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2015-01-15 09:05:52 +00:00
|
|
|
CHECK(R.code->instructions().size() == 4);
|
|
|
|
for (size_t i = 0; i < R.code->instructions().size(); ++i) {
|
|
|
|
CHECK_EQ(i % 2 == 0, R.code->instructions()[i]->IsGapMoves());
|
|
|
|
}
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(InstructionIsGapAt2) {
|
|
|
|
InstructionTester R;
|
|
|
|
|
2014-08-18 13:28:10 +00:00
|
|
|
BasicBlock* b0 = R.schedule.start();
|
|
|
|
BasicBlock* b1 = R.schedule.end();
|
2014-07-30 13:54:45 +00:00
|
|
|
R.schedule.AddGoto(b0, b1);
|
|
|
|
R.schedule.AddReturn(b1, R.Int32Constant(1));
|
|
|
|
|
|
|
|
R.allocCode();
|
|
|
|
TestInstr* i0 = TestInstr::New(R.zone(), 100);
|
2014-12-18 14:42:06 +00:00
|
|
|
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->StartBlock(b0->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
R.code->AddInstruction(i0);
|
|
|
|
R.code->AddInstruction(g);
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b0->GetRpoNumber());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
TestInstr* i1 = TestInstr::New(R.zone(), 102);
|
2014-12-18 14:42:06 +00:00
|
|
|
TestInstr* g1 = TestInstr::New(R.zone(), 104)->MarkAsControl();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->StartBlock(b1->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
R.code->AddInstruction(i1);
|
|
|
|
R.code->AddInstruction(g1);
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b1->GetRpoNumber());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2015-01-15 09:05:52 +00:00
|
|
|
CHECK(R.code->instructions().size() == 8);
|
|
|
|
for (size_t i = 0; i < R.code->instructions().size(); ++i) {
|
|
|
|
CHECK_EQ(i % 2 == 0, R.code->instructions()[i]->IsGapMoves());
|
|
|
|
}
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(InstructionAddGapMove) {
|
|
|
|
InstructionTester R;
|
|
|
|
|
2014-08-18 13:28:10 +00:00
|
|
|
BasicBlock* b0 = R.schedule.start();
|
2014-07-30 13:54:45 +00:00
|
|
|
R.schedule.AddReturn(b0, R.Int32Constant(1));
|
|
|
|
|
|
|
|
R.allocCode();
|
|
|
|
TestInstr* i0 = TestInstr::New(R.zone(), 100);
|
2014-12-18 14:42:06 +00:00
|
|
|
TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->StartBlock(b0->GetRpoNumber());
|
2014-10-14 08:51:22 +00:00
|
|
|
R.code->AddInstruction(i0);
|
|
|
|
R.code->AddInstruction(g);
|
2014-11-06 08:28:15 +00:00
|
|
|
R.code->EndBlock(b0->GetRpoNumber());
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2015-01-15 09:05:52 +00:00
|
|
|
CHECK(R.code->instructions().size() == 4);
|
|
|
|
for (size_t i = 0; i < R.code->instructions().size(); ++i) {
|
|
|
|
CHECK_EQ(i % 2 == 0, R.code->instructions()[i]->IsGapMoves());
|
|
|
|
}
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2015-01-15 09:05:52 +00:00
|
|
|
int indexes[] = {0, 2, -1};
|
2014-07-30 13:54:45 +00:00
|
|
|
for (int i = 0; indexes[i] >= 0; i++) {
|
|
|
|
int index = indexes[i];
|
|
|
|
|
|
|
|
UnallocatedOperand* op1 = R.NewUnallocated(index + 6);
|
|
|
|
UnallocatedOperand* op2 = R.NewUnallocated(index + 12);
|
|
|
|
|
|
|
|
R.code->AddGapMove(index, op1, op2);
|
|
|
|
GapInstruction* gap = R.code->GapAt(index);
|
|
|
|
ParallelMove* move = gap->GetParallelMove(GapInstruction::START);
|
2015-01-30 09:29:25 +00:00
|
|
|
CHECK(move);
|
2014-07-30 13:54:45 +00:00
|
|
|
const ZoneList<MoveOperands>* move_operands = move->move_operands();
|
|
|
|
CHECK_EQ(1, move_operands->length());
|
|
|
|
MoveOperands* cur = &move_operands->at(0);
|
|
|
|
CHECK_EQ(op1, cur->source());
|
|
|
|
CHECK_EQ(op2, cur->destination());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TEST(InstructionOperands) {
|
2015-01-23 15:19:34 +00:00
|
|
|
Zone zone;
|
2014-07-30 13:54:45 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
TestInstr* i = TestInstr::New(&zone, 101);
|
2014-07-30 16:21:36 +00:00
|
|
|
CHECK_EQ(0, static_cast<int>(i->OutputCount()));
|
|
|
|
CHECK_EQ(0, static_cast<int>(i->InputCount()));
|
|
|
|
CHECK_EQ(0, static_cast<int>(i->TempCount()));
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 11:38:29 +00:00
|
|
|
int vreg = 15;
|
2015-02-04 12:38:47 +00:00
|
|
|
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)};
|
2014-07-30 13:54:45 +00:00
|
|
|
|
2014-08-26 09:19:24 +00:00
|
|
|
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++) {
|
2014-07-30 13:54:45 +00:00
|
|
|
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++) {
|
2015-02-04 12:38:47 +00:00
|
|
|
CHECK(outputs[z].Equals(m->OutputAt(z)));
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t z = 0; z < j; z++) {
|
2015-02-04 12:38:47 +00:00
|
|
|
CHECK(inputs[z].Equals(m->InputAt(z)));
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t z = 0; z < k; z++) {
|
2015-02-04 12:38:47 +00:00
|
|
|
CHECK(temps[z].Equals(m->TempAt(z)));
|
2014-07-30 13:54:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|