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