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