Line data Source code
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/assembler-inl.h"
6 : #include "src/compiler/pipeline.h"
7 : #include "test/unittests/compiler/backend/instruction-sequence-unittest.h"
8 :
9 : namespace v8 {
10 : namespace internal {
11 : namespace compiler {
12 :
13 : namespace {
14 :
15 : // We can't just use the size of the moves collection, because of
16 : // redundant moves which need to be discounted.
17 1 : int GetMoveCount(const ParallelMove& moves) {
18 : int move_count = 0;
19 2 : for (auto move : moves) {
20 0 : if (move->IsEliminated() || move->IsRedundant()) continue;
21 0 : ++move_count;
22 : }
23 1 : return move_count;
24 : }
25 :
26 13 : bool AreOperandsOfSameType(
27 : const AllocatedOperand& op,
28 : const InstructionSequenceTest::TestOperand& test_op) {
29 : bool test_op_is_reg =
30 : (test_op.type_ ==
31 13 : InstructionSequenceTest::TestOperandType::kFixedRegister ||
32 13 : test_op.type_ == InstructionSequenceTest::TestOperandType::kRegister);
33 :
34 18 : return (op.IsRegister() && test_op_is_reg) ||
35 5 : (op.IsStackSlot() && !test_op_is_reg);
36 : }
37 :
38 13 : bool AllocatedOperandMatches(
39 : const AllocatedOperand& op,
40 13 : const InstructionSequenceTest::TestOperand& test_op) {
41 26 : return AreOperandsOfSameType(op, test_op) &&
42 26 : ((op.IsRegister() ? op.GetRegister().code() : op.index()) ==
43 5 : test_op.value_ ||
44 13 : test_op.value_ == InstructionSequenceTest::kNoValue);
45 : }
46 :
47 2 : int GetParallelMoveCount(int instr_index, Instruction::GapPosition gap_pos,
48 : const InstructionSequence* sequence) {
49 : const ParallelMove* moves =
50 : sequence->InstructionAt(instr_index)->GetParallelMove(gap_pos);
51 2 : if (moves == nullptr) return 0;
52 1 : return GetMoveCount(*moves);
53 : }
54 :
55 6 : bool IsParallelMovePresent(int instr_index, Instruction::GapPosition gap_pos,
56 : const InstructionSequence* sequence,
57 : const InstructionSequenceTest::TestOperand& src,
58 : const InstructionSequenceTest::TestOperand& dest) {
59 : const ParallelMove* moves =
60 6 : sequence->InstructionAt(instr_index)->GetParallelMove(gap_pos);
61 6 : EXPECT_NE(nullptr, moves);
62 :
63 : bool found_match = false;
64 16 : for (auto move : *moves) {
65 9 : if (move->IsEliminated() || move->IsRedundant()) continue;
66 21 : if (AllocatedOperandMatches(AllocatedOperand::cast(move->source()), src) &&
67 : AllocatedOperandMatches(AllocatedOperand::cast(move->destination()),
68 13 : dest)) {
69 : found_match = true;
70 : break;
71 : }
72 : }
73 6 : return found_match;
74 : }
75 :
76 : } // namespace
77 :
78 84 : class RegisterAllocatorTest : public InstructionSequenceTest {
79 : public:
80 42 : void Allocate() {
81 42 : WireBlocks();
82 42 : Pipeline::AllocateRegistersForTesting(config(), sequence(), true);
83 42 : }
84 : };
85 :
86 15189 : TEST_F(RegisterAllocatorTest, CanAllocateThreeRegisters) {
87 : // return p0 + p1;
88 1 : StartBlock();
89 : auto a_reg = Parameter();
90 : auto b_reg = Parameter();
91 1 : auto c_reg = EmitOI(Reg(1), Reg(a_reg, 1), Reg(b_reg, 0));
92 1 : Return(c_reg);
93 1 : EndBlock(Last());
94 :
95 1 : Allocate();
96 1 : }
97 :
98 15189 : TEST_F(RegisterAllocatorTest, CanAllocateFPRegisters) {
99 1 : StartBlock();
100 : TestOperand inputs[] = {
101 2 : Reg(FPParameter(kFloat64)), Reg(FPParameter(kFloat64)),
102 2 : Reg(FPParameter(kFloat32)), Reg(FPParameter(kFloat32)),
103 2 : Reg(FPParameter(kSimd128)), Reg(FPParameter(kSimd128))};
104 1 : VReg out1 = EmitOI(FPReg(1, kFloat64), arraysize(inputs), inputs);
105 1 : Return(out1);
106 1 : EndBlock(Last());
107 :
108 1 : Allocate();
109 1 : }
110 :
111 15189 : TEST_F(RegisterAllocatorTest, SimpleLoop) {
112 : // i = K;
113 : // while(true) { i++ }
114 1 : StartBlock();
115 1 : auto i_reg = DefineConstant();
116 1 : EndBlock();
117 :
118 : {
119 1 : StartLoop(1);
120 :
121 1 : StartBlock();
122 1 : auto phi = Phi(i_reg, 2);
123 2 : auto ipp = EmitOI(Same(), Reg(phi), Use(DefineConstant()));
124 1 : SetInput(phi, 1, ipp);
125 1 : EndBlock(Jump(0));
126 :
127 1 : EndLoop();
128 : }
129 :
130 1 : Allocate();
131 1 : }
132 :
133 15189 : TEST_F(RegisterAllocatorTest, SimpleBranch) {
134 : // return i ? K1 : K2
135 1 : StartBlock();
136 1 : auto i = DefineConstant();
137 1 : EndBlock(Branch(Reg(i), 1, 2));
138 :
139 1 : StartBlock();
140 1 : Return(DefineConstant());
141 1 : EndBlock(Last());
142 :
143 1 : StartBlock();
144 1 : Return(DefineConstant());
145 1 : EndBlock(Last());
146 :
147 1 : Allocate();
148 1 : }
149 :
150 15189 : TEST_F(RegisterAllocatorTest, SimpleDiamond) {
151 : // return p0 ? p0 : p0
152 1 : StartBlock();
153 : auto param = Parameter();
154 1 : EndBlock(Branch(Reg(param), 1, 2));
155 :
156 1 : StartBlock();
157 1 : EndBlock(Jump(2));
158 :
159 1 : StartBlock();
160 1 : EndBlock(Jump(1));
161 :
162 1 : StartBlock();
163 1 : Return(param);
164 1 : EndBlock();
165 :
166 1 : Allocate();
167 1 : }
168 :
169 15189 : TEST_F(RegisterAllocatorTest, SimpleDiamondPhi) {
170 : // return i ? K1 : K2
171 1 : StartBlock();
172 2 : EndBlock(Branch(Reg(DefineConstant()), 1, 2));
173 :
174 1 : StartBlock();
175 1 : auto t_val = DefineConstant();
176 1 : EndBlock(Jump(2));
177 :
178 1 : StartBlock();
179 1 : auto f_val = DefineConstant();
180 1 : EndBlock(Jump(1));
181 :
182 1 : StartBlock();
183 2 : Return(Reg(Phi(t_val, f_val)));
184 1 : EndBlock();
185 :
186 1 : Allocate();
187 1 : }
188 :
189 15189 : TEST_F(RegisterAllocatorTest, DiamondManyPhis) {
190 : constexpr int kPhis = Register::kNumRegisters * 2;
191 :
192 1 : StartBlock();
193 2 : EndBlock(Branch(Reg(DefineConstant()), 1, 2));
194 :
195 1 : StartBlock();
196 33 : VReg t_vals[kPhis];
197 32 : for (int i = 0; i < kPhis; ++i) {
198 32 : t_vals[i] = DefineConstant();
199 : }
200 1 : EndBlock(Jump(2));
201 :
202 1 : StartBlock();
203 33 : VReg f_vals[kPhis];
204 32 : for (int i = 0; i < kPhis; ++i) {
205 32 : f_vals[i] = DefineConstant();
206 : }
207 1 : EndBlock(Jump(1));
208 :
209 1 : StartBlock();
210 33 : TestOperand merged[kPhis];
211 32 : for (int i = 0; i < kPhis; ++i) {
212 96 : merged[i] = Use(Phi(t_vals[i], f_vals[i]));
213 : }
214 1 : Return(EmitCall(Slot(-1), kPhis, merged));
215 1 : EndBlock();
216 :
217 1 : Allocate();
218 1 : }
219 :
220 15189 : TEST_F(RegisterAllocatorTest, DoubleDiamondManyRedundantPhis) {
221 : constexpr int kPhis = Register::kNumRegisters * 2;
222 :
223 : // First diamond.
224 1 : StartBlock();
225 33 : VReg vals[kPhis];
226 32 : for (int i = 0; i < kPhis; ++i) {
227 64 : vals[i] = Parameter(Slot(-1 - i));
228 : }
229 2 : EndBlock(Branch(Reg(DefineConstant()), 1, 2));
230 :
231 1 : StartBlock();
232 1 : EndBlock(Jump(2));
233 :
234 1 : StartBlock();
235 1 : EndBlock(Jump(1));
236 :
237 : // Second diamond.
238 1 : StartBlock();
239 2 : EndBlock(Branch(Reg(DefineConstant()), 1, 2));
240 :
241 1 : StartBlock();
242 1 : EndBlock(Jump(2));
243 :
244 1 : StartBlock();
245 1 : EndBlock(Jump(1));
246 :
247 1 : StartBlock();
248 33 : TestOperand merged[kPhis];
249 32 : for (int i = 0; i < kPhis; ++i) {
250 96 : merged[i] = Use(Phi(vals[i], vals[i]));
251 : }
252 1 : Return(EmitCall(Reg(0), kPhis, merged));
253 1 : EndBlock();
254 :
255 1 : Allocate();
256 1 : }
257 :
258 15189 : TEST_F(RegisterAllocatorTest, RegressionPhisNeedTooManyRegisters) {
259 : const size_t kNumRegs = 3;
260 : const size_t kParams = kNumRegs + 1;
261 : // Override number of registers.
262 1 : SetNumRegs(kNumRegs, kNumRegs);
263 :
264 1 : StartBlock();
265 5 : auto constant = DefineConstant();
266 5 : VReg parameters[kParams];
267 4 : for (size_t i = 0; i < arraysize(parameters); ++i) {
268 4 : parameters[i] = DefineConstant();
269 : }
270 1 : EndBlock();
271 :
272 : PhiInstruction* phis[kParams];
273 : {
274 1 : StartLoop(2);
275 :
276 : // Loop header.
277 1 : StartBlock();
278 :
279 5 : for (size_t i = 0; i < arraysize(parameters); ++i) {
280 4 : phis[i] = Phi(parameters[i], 2);
281 : }
282 :
283 : // Perform some computations.
284 : // something like phi[i] += const
285 4 : for (size_t i = 0; i < arraysize(parameters); ++i) {
286 8 : auto result = EmitOI(Same(), Reg(phis[i]), Use(constant));
287 4 : SetInput(phis[i], 1, result);
288 : }
289 :
290 2 : EndBlock(Branch(Reg(DefineConstant()), 1, 2));
291 :
292 : // Jump back to loop header.
293 1 : StartBlock();
294 1 : EndBlock(Jump(-1));
295 :
296 1 : EndLoop();
297 : }
298 :
299 1 : StartBlock();
300 1 : Return(DefineConstant());
301 1 : EndBlock();
302 :
303 1 : Allocate();
304 1 : }
305 :
306 15189 : TEST_F(RegisterAllocatorTest, SpillPhi) {
307 1 : StartBlock();
308 2 : EndBlock(Branch(Imm(), 1, 2));
309 :
310 1 : StartBlock();
311 1 : auto left = Define(Reg(0));
312 1 : EndBlock(Jump(2));
313 :
314 1 : StartBlock();
315 1 : auto right = Define(Reg(0));
316 1 : EndBlock();
317 :
318 1 : StartBlock();
319 1 : auto phi = Phi(left, right);
320 1 : EmitCall(Slot(-1));
321 1 : Return(Reg(phi));
322 1 : EndBlock();
323 :
324 1 : Allocate();
325 1 : }
326 :
327 15189 : TEST_F(RegisterAllocatorTest, MoveLotsOfConstants) {
328 1 : StartBlock();
329 17 : VReg constants[Register::kNumRegisters];
330 16 : for (size_t i = 0; i < arraysize(constants); ++i) {
331 16 : constants[i] = DefineConstant();
332 : }
333 32 : TestOperand call_ops[Register::kNumRegisters * 2];
334 16 : for (int i = 0; i < Register::kNumRegisters; ++i) {
335 32 : call_ops[i] = Reg(constants[i], i);
336 : }
337 16 : for (int i = 0; i < Register::kNumRegisters; ++i) {
338 32 : call_ops[i + Register::kNumRegisters] = Slot(constants[i], i);
339 : }
340 1 : EmitCall(Slot(-1), arraysize(call_ops), call_ops);
341 1 : EndBlock(Last());
342 :
343 1 : Allocate();
344 1 : }
345 :
346 15189 : TEST_F(RegisterAllocatorTest, SplitBeforeInstruction) {
347 : const int kNumRegs = 6;
348 1 : SetNumRegs(kNumRegs, kNumRegs);
349 :
350 1 : StartBlock();
351 :
352 : // Stack parameters/spilled values.
353 2 : auto p_0 = Define(Slot(-1));
354 2 : auto p_1 = Define(Slot(-2));
355 :
356 : // Fill registers.
357 7 : VReg values[kNumRegs];
358 6 : for (size_t i = 0; i < arraysize(values); ++i) {
359 12 : values[i] = Define(Reg(static_cast<int>(i)));
360 : }
361 :
362 : // values[0] will be split in the second half of this instruction.
363 : // Models Intel mod instructions.
364 1 : EmitOI(Reg(0), Reg(p_0, 1), UniqueReg(p_1));
365 2 : EmitI(Reg(values[0], 0));
366 1 : EndBlock(Last());
367 :
368 1 : Allocate();
369 1 : }
370 :
371 15189 : TEST_F(RegisterAllocatorTest, SplitBeforeInstruction2) {
372 : const int kNumRegs = 6;
373 1 : SetNumRegs(kNumRegs, kNumRegs);
374 :
375 1 : StartBlock();
376 :
377 : // Stack parameters/spilled values.
378 2 : auto p_0 = Define(Slot(-1));
379 2 : auto p_1 = Define(Slot(-2));
380 :
381 : // Fill registers.
382 7 : VReg values[kNumRegs];
383 6 : for (size_t i = 0; i < arraysize(values); ++i) {
384 12 : values[i] = Define(Reg(static_cast<int>(i)));
385 : }
386 :
387 : // values[0] and [1] will be split in the second half of this instruction.
388 1 : EmitOOI(Reg(0), Reg(1), Reg(p_0, 0), Reg(p_1, 1));
389 3 : EmitI(Reg(values[0]), Reg(values[1]));
390 1 : EndBlock(Last());
391 :
392 1 : Allocate();
393 1 : }
394 :
395 15189 : TEST_F(RegisterAllocatorTest, NestedDiamondPhiMerge) {
396 : // Outer diamond.
397 1 : StartBlock();
398 2 : EndBlock(Branch(Imm(), 1, 5));
399 :
400 : // Diamond 1
401 1 : StartBlock();
402 2 : EndBlock(Branch(Imm(), 1, 2));
403 :
404 1 : StartBlock();
405 1 : auto ll = Define(Reg());
406 1 : EndBlock(Jump(2));
407 :
408 1 : StartBlock();
409 1 : auto lr = Define(Reg());
410 1 : EndBlock();
411 :
412 1 : StartBlock();
413 1 : auto l_phi = Phi(ll, lr);
414 1 : EndBlock(Jump(5));
415 :
416 : // Diamond 2
417 1 : StartBlock();
418 2 : EndBlock(Branch(Imm(), 1, 2));
419 :
420 1 : StartBlock();
421 1 : auto rl = Define(Reg());
422 1 : EndBlock(Jump(2));
423 :
424 1 : StartBlock();
425 1 : auto rr = Define(Reg());
426 1 : EndBlock();
427 :
428 1 : StartBlock();
429 1 : auto r_phi = Phi(rl, rr);
430 1 : EndBlock();
431 :
432 : // Outer diamond merge.
433 1 : StartBlock();
434 1 : auto phi = Phi(l_phi, r_phi);
435 1 : Return(Reg(phi));
436 1 : EndBlock();
437 :
438 1 : Allocate();
439 1 : }
440 :
441 15189 : TEST_F(RegisterAllocatorTest, NestedDiamondPhiMergeDifferent) {
442 : // Outer diamond.
443 1 : StartBlock();
444 2 : EndBlock(Branch(Imm(), 1, 5));
445 :
446 : // Diamond 1
447 1 : StartBlock();
448 2 : EndBlock(Branch(Imm(), 1, 2));
449 :
450 1 : StartBlock();
451 1 : auto ll = Define(Reg(0));
452 1 : EndBlock(Jump(2));
453 :
454 1 : StartBlock();
455 1 : auto lr = Define(Reg(1));
456 1 : EndBlock();
457 :
458 1 : StartBlock();
459 1 : auto l_phi = Phi(ll, lr);
460 1 : EndBlock(Jump(5));
461 :
462 : // Diamond 2
463 1 : StartBlock();
464 2 : EndBlock(Branch(Imm(), 1, 2));
465 :
466 1 : StartBlock();
467 1 : auto rl = Define(Reg(2));
468 1 : EndBlock(Jump(2));
469 :
470 1 : StartBlock();
471 1 : auto rr = Define(Reg(3));
472 1 : EndBlock();
473 :
474 1 : StartBlock();
475 1 : auto r_phi = Phi(rl, rr);
476 1 : EndBlock();
477 :
478 : // Outer diamond merge.
479 1 : StartBlock();
480 1 : auto phi = Phi(l_phi, r_phi);
481 1 : Return(Reg(phi));
482 1 : EndBlock();
483 :
484 1 : Allocate();
485 1 : }
486 :
487 15189 : TEST_F(RegisterAllocatorTest, RegressionSplitBeforeAndMove) {
488 1 : StartBlock();
489 :
490 : // Fill registers.
491 17 : VReg values[Register::kNumRegisters];
492 16 : for (size_t i = 0; i < arraysize(values); ++i) {
493 16 : if (i == 0 || i == 1) continue; // Leave a hole for c_1 to take.
494 28 : values[i] = Define(Reg(static_cast<int>(i)));
495 : }
496 :
497 2 : auto c_0 = DefineConstant();
498 2 : auto c_1 = DefineConstant();
499 :
500 1 : EmitOI(Reg(1), Reg(c_0, 0), UniqueReg(c_1));
501 :
502 : // Use previous values to force c_1 to split before the previous instruction.
503 17 : for (size_t i = 0; i < arraysize(values); ++i) {
504 16 : if (i == 0 || i == 1) continue;
505 28 : EmitI(Reg(values[i], static_cast<int>(i)));
506 : }
507 :
508 1 : EndBlock(Last());
509 :
510 1 : Allocate();
511 1 : }
512 :
513 15189 : TEST_F(RegisterAllocatorTest, RegressionSpillTwice) {
514 1 : StartBlock();
515 : auto p_0 = Parameter(Reg(1));
516 1 : EmitCall(Slot(-2), Unique(p_0), Reg(p_0, 1));
517 1 : EndBlock(Last());
518 :
519 1 : Allocate();
520 1 : }
521 :
522 15189 : TEST_F(RegisterAllocatorTest, RegressionLoadConstantBeforeSpill) {
523 1 : StartBlock();
524 : // Fill registers.
525 17 : VReg values[Register::kNumRegisters];
526 17 : for (size_t i = arraysize(values); i > 0; --i) {
527 32 : values[i - 1] = Define(Reg(static_cast<int>(i - 1)));
528 : }
529 2 : auto c = DefineConstant();
530 2 : auto to_spill = Define(Reg());
531 1 : EndBlock(Jump(1));
532 :
533 : {
534 1 : StartLoop(1);
535 :
536 1 : StartBlock();
537 : // Create a use for c in second half of prev block's last gap
538 1 : Phi(c);
539 18 : for (size_t i = arraysize(values); i > 0; --i) {
540 16 : Phi(values[i - 1]);
541 : }
542 1 : EndBlock(Jump(1));
543 :
544 1 : EndLoop();
545 : }
546 :
547 1 : StartBlock();
548 : // Force c to split within to_spill's definition.
549 1 : EmitI(Reg(c));
550 1 : EmitI(Reg(to_spill));
551 1 : EndBlock(Last());
552 :
553 1 : Allocate();
554 1 : }
555 :
556 15189 : TEST_F(RegisterAllocatorTest, DiamondWithCallFirstBlock) {
557 1 : StartBlock();
558 1 : auto x = EmitOI(Reg(0));
559 1 : EndBlock(Branch(Reg(x), 1, 2));
560 :
561 1 : StartBlock();
562 1 : EmitCall(Slot(-1));
563 1 : auto occupy = EmitOI(Reg(0));
564 1 : EndBlock(Jump(2));
565 :
566 1 : StartBlock();
567 1 : EndBlock(FallThrough());
568 :
569 1 : StartBlock();
570 : Use(occupy);
571 1 : Return(Reg(x));
572 1 : EndBlock();
573 1 : Allocate();
574 1 : }
575 :
576 15189 : TEST_F(RegisterAllocatorTest, DiamondWithCallSecondBlock) {
577 1 : StartBlock();
578 1 : auto x = EmitOI(Reg(0));
579 1 : EndBlock(Branch(Reg(x), 1, 2));
580 :
581 1 : StartBlock();
582 1 : EndBlock(Jump(2));
583 :
584 1 : StartBlock();
585 1 : EmitCall(Slot(-1));
586 1 : auto occupy = EmitOI(Reg(0));
587 1 : EndBlock(FallThrough());
588 :
589 1 : StartBlock();
590 : Use(occupy);
591 1 : Return(Reg(x));
592 1 : EndBlock();
593 1 : Allocate();
594 1 : }
595 :
596 15189 : TEST_F(RegisterAllocatorTest, SingleDeferredBlockSpill) {
597 1 : StartBlock(); // B0
598 1 : auto var = EmitOI(Reg(0));
599 1 : EndBlock(Branch(Reg(var), 1, 2));
600 :
601 1 : StartBlock(); // B1
602 1 : EndBlock(Jump(2));
603 :
604 1 : StartBlock(true); // B2
605 1 : EmitCall(Slot(-1), Slot(var));
606 1 : EndBlock();
607 :
608 1 : StartBlock(); // B3
609 1 : EmitNop();
610 1 : EndBlock();
611 :
612 1 : StartBlock(); // B4
613 1 : Return(Reg(var, 0));
614 1 : EndBlock();
615 :
616 1 : Allocate();
617 :
618 : const int var_def_index = 1;
619 : const int call_index = 3;
620 : int expect_no_moves =
621 1 : FLAG_turbo_preprocess_ranges ? var_def_index : call_index;
622 : int expect_spill_move =
623 1 : FLAG_turbo_preprocess_ranges ? call_index : var_def_index;
624 :
625 : // We should have no parallel moves at the "expect_no_moves" position.
626 2 : EXPECT_EQ(
627 0 : 0, GetParallelMoveCount(expect_no_moves, Instruction::START, sequence()));
628 :
629 : // The spill should be performed at the position expect_spill_move.
630 2 : EXPECT_TRUE(IsParallelMovePresent(expect_spill_move, Instruction::START,
631 0 : sequence(), Reg(0), Slot(0)));
632 1 : }
633 :
634 15189 : TEST_F(RegisterAllocatorTest, MultipleDeferredBlockSpills) {
635 1 : if (!FLAG_turbo_preprocess_ranges) return;
636 :
637 1 : StartBlock(); // B0
638 1 : auto var1 = EmitOI(Reg(0));
639 2 : auto var2 = EmitOI(Reg(1));
640 2 : auto var3 = EmitOI(Reg(2));
641 1 : EndBlock(Branch(Reg(var1, 0), 1, 2));
642 :
643 1 : StartBlock(true); // B1
644 1 : EmitCall(Slot(-2), Slot(var1));
645 1 : EndBlock(Jump(2));
646 :
647 1 : StartBlock(true); // B2
648 1 : EmitCall(Slot(-1), Slot(var2));
649 1 : EndBlock();
650 :
651 1 : StartBlock(); // B3
652 1 : EmitNop();
653 1 : EndBlock();
654 :
655 1 : StartBlock(); // B4
656 1 : Return(Reg(var3, 2));
657 1 : EndBlock();
658 :
659 : const int def_of_v2 = 3;
660 : const int call_in_b1 = 4;
661 : const int call_in_b2 = 6;
662 : const int end_of_b1 = 5;
663 : const int end_of_b2 = 7;
664 : const int start_of_b3 = 8;
665 :
666 1 : Allocate();
667 : // TODO(mtrofin): at the moment, the linear allocator spills var1 and var2,
668 : // so only var3 is spilled in deferred blocks.
669 : const int var3_reg = 2;
670 : const int var3_slot = 2;
671 :
672 2 : EXPECT_FALSE(IsParallelMovePresent(def_of_v2, Instruction::START, sequence(),
673 0 : Reg(var3_reg), Slot()));
674 2 : EXPECT_TRUE(IsParallelMovePresent(call_in_b1, Instruction::START, sequence(),
675 0 : Reg(var3_reg), Slot(var3_slot)));
676 2 : EXPECT_TRUE(IsParallelMovePresent(end_of_b1, Instruction::START, sequence(),
677 0 : Slot(var3_slot), Reg()));
678 :
679 2 : EXPECT_TRUE(IsParallelMovePresent(call_in_b2, Instruction::START, sequence(),
680 0 : Reg(var3_reg), Slot(var3_slot)));
681 2 : EXPECT_TRUE(IsParallelMovePresent(end_of_b2, Instruction::START, sequence(),
682 0 : Slot(var3_slot), Reg()));
683 :
684 2 : EXPECT_EQ(0,
685 0 : GetParallelMoveCount(start_of_b3, Instruction::START, sequence()));
686 : }
687 :
688 : namespace {
689 :
690 : enum class ParameterType { kFixedSlot, kSlot, kRegister, kFixedRegister };
691 :
692 : const ParameterType kParameterTypes[] = {
693 : ParameterType::kFixedSlot, ParameterType::kSlot, ParameterType::kRegister,
694 : ParameterType::kFixedRegister};
695 :
696 40 : class SlotConstraintTest : public RegisterAllocatorTest,
697 : public ::testing::WithParamInterface<
698 : ::testing::tuple<ParameterType, int>> {
699 : public:
700 : static const int kMaxVariant = 5;
701 :
702 : protected:
703 : ParameterType parameter_type() const {
704 20 : return ::testing::get<0>(B::GetParam());
705 : }
706 20 : int variant() const { return ::testing::get<1>(B::GetParam()); }
707 :
708 : private:
709 : typedef ::testing::WithParamInterface<::testing::tuple<ParameterType, int>> B;
710 : };
711 :
712 : } // namespace
713 :
714 18302 : TEST_P(SlotConstraintTest, SlotConstraint) {
715 20 : StartBlock();
716 : VReg p_0;
717 20 : switch (parameter_type()) {
718 : case ParameterType::kFixedSlot:
719 : p_0 = Parameter(Slot(-1));
720 5 : break;
721 : case ParameterType::kSlot:
722 : p_0 = Parameter(Slot(-1));
723 5 : break;
724 : case ParameterType::kRegister:
725 : p_0 = Parameter(Reg());
726 5 : break;
727 : case ParameterType::kFixedRegister:
728 : p_0 = Parameter(Reg(1));
729 5 : break;
730 : }
731 20 : switch (variant()) {
732 : case 0:
733 4 : EmitI(Slot(p_0), Reg(p_0));
734 4 : break;
735 : case 1:
736 4 : EmitI(Slot(p_0));
737 4 : break;
738 : case 2:
739 4 : EmitI(Reg(p_0));
740 4 : EmitI(Slot(p_0));
741 4 : break;
742 : case 3:
743 4 : EmitI(Slot(p_0));
744 4 : EmitI(Reg(p_0));
745 4 : break;
746 : case 4:
747 4 : EmitI(Slot(p_0, -1), Slot(p_0), Reg(p_0), Reg(p_0, 1));
748 4 : break;
749 : default:
750 0 : UNREACHABLE();
751 : break;
752 : }
753 20 : EndBlock(Last());
754 :
755 20 : Allocate();
756 20 : }
757 :
758 88073 : INSTANTIATE_TEST_SUITE_P(
759 : RegisterAllocatorTest, SlotConstraintTest,
760 : ::testing::Combine(::testing::ValuesIn(kParameterTypes),
761 : ::testing::Range(0, SlotConstraintTest::kMaxVariant)));
762 :
763 : } // namespace compiler
764 : } // namespace internal
765 9111 : } // namespace v8
|