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 "test/unittests/compiler/backend/instruction-selector-unittest.h"
6 :
7 : #include "src/code-factory.h"
8 : #include "src/compiler/compiler-source-position-table.h"
9 : #include "src/compiler/graph.h"
10 : #include "src/compiler/schedule.h"
11 : #include "src/flags.h"
12 : #include "src/objects-inl.h"
13 : #include "test/unittests/compiler/compiler-test-utils.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 : namespace compiler {
18 :
19 342 : InstructionSelectorTest::InstructionSelectorTest() : rng_(FLAG_random_seed) {}
20 :
21 : InstructionSelectorTest::~InstructionSelectorTest() = default;
22 :
23 249 : InstructionSelectorTest::Stream InstructionSelectorTest::StreamBuilder::Build(
24 : InstructionSelector::Features features,
25 : InstructionSelectorTest::StreamBuilderMode mode,
26 : InstructionSelector::SourcePositionMode source_position_mode) {
27 249 : Schedule* schedule = Export();
28 249 : if (FLAG_trace_turbo) {
29 0 : StdoutStream{} << "=== Schedule before instruction selection ==="
30 : << std::endl
31 0 : << *schedule;
32 : }
33 249 : size_t const node_count = graph()->NodeCount();
34 249 : EXPECT_NE(0u, node_count);
35 : Linkage linkage(call_descriptor());
36 : InstructionBlocks* instruction_blocks =
37 498 : InstructionSequence::InstructionBlocksFor(test_->zone(), schedule);
38 249 : InstructionSequence sequence(test_->isolate(), test_->zone(),
39 498 : instruction_blocks);
40 249 : SourcePositionTable source_position_table(graph());
41 : InstructionSelector selector(
42 249 : test_->zone(), node_count, &linkage, &sequence, schedule,
43 : &source_position_table, nullptr,
44 : InstructionSelector::kEnableSwitchJumpTable, source_position_mode,
45 : features, InstructionSelector::kDisableScheduling,
46 : InstructionSelector::kEnableRootsRelativeAddressing,
47 498 : PoisoningMitigationLevel::kPoisonAll);
48 249 : selector.SelectInstructions();
49 249 : if (FLAG_trace_turbo) {
50 0 : StdoutStream{} << "=== Code sequence after instruction selection ==="
51 : << std::endl
52 0 : << sequence;
53 : }
54 498 : Stream s;
55 498 : s.virtual_registers_ = selector.GetVirtualRegistersForTesting();
56 : // Map virtual registers.
57 1537 : for (Instruction* const instr : sequence) {
58 1288 : if (instr->opcode() < 0) continue;
59 1288 : if (mode == kTargetInstructions) {
60 1015 : switch (instr->arch_opcode()) {
61 : #define CASE(Name) \
62 : case k##Name: \
63 : break;
64 : TARGET_ARCH_OPCODE_LIST(CASE)
65 : #undef CASE
66 : default:
67 : continue;
68 : }
69 : }
70 528 : if (mode == kAllExceptNopInstructions && instr->arch_opcode() == kArchNop) {
71 : continue;
72 : }
73 1885 : for (size_t i = 0; i < instr->OutputCount(); ++i) {
74 : InstructionOperand* output = instr->OutputAt(i);
75 301 : EXPECT_NE(InstructionOperand::IMMEDIATE, output->kind());
76 301 : if (output->IsConstant()) {
77 : int vreg = ConstantOperand::cast(output)->virtual_register();
78 24 : s.constants_.insert(std::make_pair(vreg, sequence.GetConstant(vreg)));
79 : }
80 : }
81 3268 : for (size_t i = 0; i < instr->InputCount(); ++i) {
82 : InstructionOperand* input = instr->InputAt(i);
83 762 : EXPECT_NE(InstructionOperand::CONSTANT, input->kind());
84 762 : if (input->IsImmediate()) {
85 : auto imm = ImmediateOperand::cast(input);
86 319 : if (imm->type() == ImmediateOperand::INDEXED) {
87 : int index = imm->indexed_value();
88 : s.immediates_.insert(
89 410 : std::make_pair(index, sequence.GetImmediate(imm)));
90 : }
91 : }
92 : }
93 491 : s.instructions_.push_back(instr);
94 : }
95 976 : for (auto i : s.virtual_registers_) {
96 727 : int const virtual_register = i.second;
97 727 : if (sequence.IsFP(virtual_register)) {
98 114 : EXPECT_FALSE(sequence.IsReference(virtual_register));
99 : s.doubles_.insert(virtual_register);
100 : }
101 1454 : if (sequence.IsReference(virtual_register)) {
102 60 : EXPECT_FALSE(sequence.IsFP(virtual_register));
103 : s.references_.insert(virtual_register);
104 : }
105 : }
106 255 : for (int i = 0; i < sequence.GetDeoptimizationEntryCount(); i++) {
107 6 : s.deoptimization_entries_.push_back(
108 3 : sequence.GetDeoptimizationEntry(i).descriptor());
109 : }
110 498 : return s;
111 : }
112 :
113 363 : int InstructionSelectorTest::Stream::ToVreg(const Node* node) const {
114 : VirtualRegisters::const_iterator i = virtual_registers_.find(node->id());
115 363 : CHECK(i != virtual_registers_.end());
116 363 : return i->second;
117 : }
118 :
119 4 : bool InstructionSelectorTest::Stream::IsFixed(const InstructionOperand* operand,
120 : Register reg) const {
121 4 : if (!operand->IsUnallocated()) return false;
122 : const UnallocatedOperand* unallocated = UnallocatedOperand::cast(operand);
123 4 : if (!unallocated->HasFixedRegisterPolicy()) return false;
124 4 : return unallocated->fixed_register_index() == reg.code();
125 : }
126 :
127 68 : bool InstructionSelectorTest::Stream::IsSameAsFirst(
128 : const InstructionOperand* operand) const {
129 68 : if (!operand->IsUnallocated()) return false;
130 : const UnallocatedOperand* unallocated = UnallocatedOperand::cast(operand);
131 68 : return unallocated->HasSameAsInputPolicy();
132 : }
133 :
134 2 : bool InstructionSelectorTest::Stream::IsUsedAtStart(
135 : const InstructionOperand* operand) const {
136 2 : if (!operand->IsUnallocated()) return false;
137 : const UnallocatedOperand* unallocated = UnallocatedOperand::cast(operand);
138 2 : return unallocated->IsUsedAtStart();
139 : }
140 :
141 : const FrameStateFunctionInfo*
142 0 : InstructionSelectorTest::StreamBuilder::GetFrameStateFunctionInfo(
143 : int parameter_count, int local_count) {
144 4 : return common()->CreateFrameStateFunctionInfo(
145 : FrameStateType::kInterpretedFunction, parameter_count, local_count,
146 4 : Handle<SharedFunctionInfo>());
147 : }
148 :
149 : // -----------------------------------------------------------------------------
150 : // Return.
151 :
152 15444 : TARGET_TEST_F(InstructionSelectorTest, ReturnFloat32Constant) {
153 : const float kValue = 4.2f;
154 1 : StreamBuilder m(this, MachineType::Float32());
155 1 : m.Return(m.Float32Constant(kValue));
156 1 : Stream s = m.Build(kAllInstructions);
157 2 : ASSERT_EQ(3U, s.size());
158 3 : EXPECT_EQ(kArchNop, s[0]->arch_opcode());
159 3 : ASSERT_EQ(InstructionOperand::CONSTANT, s[0]->OutputAt(0)->kind());
160 2 : EXPECT_FLOAT_EQ(kValue, s.ToFloat32(s[0]->OutputAt(0)));
161 3 : EXPECT_EQ(kArchRet, s[1]->arch_opcode());
162 3 : EXPECT_EQ(2U, s[1]->InputCount());
163 : }
164 :
165 15444 : TARGET_TEST_F(InstructionSelectorTest, ReturnParameter) {
166 1 : StreamBuilder m(this, MachineType::Int32(), MachineType::Int32());
167 1 : m.Return(m.Parameter(0));
168 1 : Stream s = m.Build(kAllInstructions);
169 2 : ASSERT_EQ(3U, s.size());
170 3 : EXPECT_EQ(kArchNop, s[0]->arch_opcode());
171 3 : ASSERT_EQ(1U, s[0]->OutputCount());
172 3 : EXPECT_EQ(kArchRet, s[1]->arch_opcode());
173 3 : EXPECT_EQ(2U, s[1]->InputCount());
174 : }
175 :
176 15444 : TARGET_TEST_F(InstructionSelectorTest, ReturnZero) {
177 1 : StreamBuilder m(this, MachineType::Int32());
178 1 : m.Return(m.Int32Constant(0));
179 1 : Stream s = m.Build(kAllInstructions);
180 2 : ASSERT_EQ(3U, s.size());
181 3 : EXPECT_EQ(kArchNop, s[0]->arch_opcode());
182 3 : ASSERT_EQ(1U, s[0]->OutputCount());
183 3 : EXPECT_EQ(InstructionOperand::CONSTANT, s[0]->OutputAt(0)->kind());
184 3 : EXPECT_EQ(0, s.ToInt32(s[0]->OutputAt(0)));
185 3 : EXPECT_EQ(kArchRet, s[1]->arch_opcode());
186 3 : EXPECT_EQ(2U, s[1]->InputCount());
187 : }
188 :
189 : // -----------------------------------------------------------------------------
190 : // Conversions.
191 :
192 15444 : TARGET_TEST_F(InstructionSelectorTest, TruncateFloat64ToWord32WithParameter) {
193 1 : StreamBuilder m(this, MachineType::Int32(), MachineType::Float64());
194 1 : m.Return(m.TruncateFloat64ToWord32(m.Parameter(0)));
195 1 : Stream s = m.Build(kAllInstructions);
196 2 : ASSERT_EQ(4U, s.size());
197 3 : EXPECT_EQ(kArchNop, s[0]->arch_opcode());
198 3 : EXPECT_EQ(kArchTruncateDoubleToI, s[1]->arch_opcode());
199 3 : EXPECT_EQ(1U, s[1]->InputCount());
200 3 : EXPECT_EQ(1U, s[1]->OutputCount());
201 3 : EXPECT_EQ(kArchRet, s[2]->arch_opcode());
202 : }
203 :
204 : // -----------------------------------------------------------------------------
205 : // Parameters.
206 :
207 15444 : TARGET_TEST_F(InstructionSelectorTest, DoubleParameter) {
208 1 : StreamBuilder m(this, MachineType::Float64(), MachineType::Float64());
209 1 : Node* param = m.Parameter(0);
210 1 : m.Return(param);
211 1 : Stream s = m.Build(kAllInstructions);
212 2 : EXPECT_TRUE(s.IsDouble(param));
213 1 : }
214 :
215 15444 : TARGET_TEST_F(InstructionSelectorTest, ReferenceParameter) {
216 1 : StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged());
217 1 : Node* param = m.Parameter(0);
218 1 : m.Return(param);
219 1 : Stream s = m.Build(kAllInstructions);
220 2 : EXPECT_TRUE(s.IsReference(param));
221 1 : }
222 :
223 : // -----------------------------------------------------------------------------
224 : // FinishRegion.
225 :
226 15444 : TARGET_TEST_F(InstructionSelectorTest, FinishRegion) {
227 1 : StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged());
228 1 : Node* param = m.Parameter(0);
229 : Node* finish =
230 1 : m.AddNode(m.common()->FinishRegion(), param, m.graph()->start());
231 1 : m.Return(finish);
232 1 : Stream s = m.Build(kAllInstructions);
233 2 : ASSERT_EQ(3U, s.size());
234 3 : EXPECT_EQ(kArchNop, s[0]->arch_opcode());
235 3 : ASSERT_EQ(1U, s[0]->OutputCount());
236 2 : ASSERT_TRUE(s[0]->Output()->IsUnallocated());
237 3 : EXPECT_EQ(kArchRet, s[1]->arch_opcode());
238 3 : EXPECT_EQ(s.ToVreg(param), s.ToVreg(s[0]->Output()));
239 3 : EXPECT_EQ(s.ToVreg(param), s.ToVreg(s[1]->InputAt(1)));
240 2 : EXPECT_TRUE(s.IsReference(finish));
241 : }
242 :
243 : // -----------------------------------------------------------------------------
244 : // Phi.
245 :
246 : typedef InstructionSelectorTestWithParam<MachineType>
247 : InstructionSelectorPhiTest;
248 :
249 18572 : TARGET_TEST_P(InstructionSelectorPhiTest, Doubleness) {
250 11 : const MachineType type = GetParam();
251 11 : StreamBuilder m(this, type, type, type);
252 11 : Node* param0 = m.Parameter(0);
253 11 : Node* param1 = m.Parameter(1);
254 11 : RawMachineLabel a, b, c;
255 11 : m.Branch(m.Int32Constant(0), &a, &b);
256 11 : m.Bind(&a);
257 11 : m.Goto(&c);
258 11 : m.Bind(&b);
259 11 : m.Goto(&c);
260 11 : m.Bind(&c);
261 11 : Node* phi = m.Phi(type.representation(), param0, param1);
262 11 : m.Return(phi);
263 11 : Stream s = m.Build(kAllInstructions);
264 22 : EXPECT_EQ(s.IsDouble(phi), s.IsDouble(param0));
265 22 : EXPECT_EQ(s.IsDouble(phi), s.IsDouble(param1));
266 11 : }
267 :
268 18572 : TARGET_TEST_P(InstructionSelectorPhiTest, Referenceness) {
269 11 : const MachineType type = GetParam();
270 11 : StreamBuilder m(this, type, type, type);
271 11 : Node* param0 = m.Parameter(0);
272 11 : Node* param1 = m.Parameter(1);
273 11 : RawMachineLabel a, b, c;
274 11 : m.Branch(m.Int32Constant(1), &a, &b);
275 11 : m.Bind(&a);
276 11 : m.Goto(&c);
277 11 : m.Bind(&b);
278 11 : m.Goto(&c);
279 11 : m.Bind(&c);
280 11 : Node* phi = m.Phi(type.representation(), param0, param1);
281 11 : m.Return(phi);
282 11 : Stream s = m.Build(kAllInstructions);
283 22 : EXPECT_EQ(s.IsReference(phi), s.IsReference(param0));
284 22 : EXPECT_EQ(s.IsReference(phi), s.IsReference(param1));
285 11 : }
286 :
287 163664 : INSTANTIATE_TEST_SUITE_P(
288 : InstructionSelectorTest, InstructionSelectorPhiTest,
289 : ::testing::Values(MachineType::Float64(), MachineType::Int8(),
290 : MachineType::Uint8(), MachineType::Int16(),
291 : MachineType::Uint16(), MachineType::Int32(),
292 : MachineType::Uint32(), MachineType::Int64(),
293 : MachineType::Uint64(), MachineType::Pointer(),
294 : MachineType::AnyTagged()));
295 :
296 : // -----------------------------------------------------------------------------
297 : // ValueEffect.
298 :
299 15444 : TARGET_TEST_F(InstructionSelectorTest, ValueEffect) {
300 1 : StreamBuilder m1(this, MachineType::Int32(), MachineType::Pointer());
301 1 : Node* p1 = m1.Parameter(0);
302 1 : m1.Return(m1.Load(MachineType::Int32(), p1, m1.Int32Constant(0)));
303 1 : Stream s1 = m1.Build(kAllInstructions);
304 1 : StreamBuilder m2(this, MachineType::Int32(), MachineType::Pointer());
305 1 : Node* p2 = m2.Parameter(0);
306 2 : m2.Return(m2.AddNode(
307 : m2.machine()->Load(MachineType::Int32()), p2, m2.Int32Constant(0),
308 : m2.AddNode(m2.common()->BeginRegion(RegionObservability::kObservable),
309 1 : m2.graph()->start())));
310 1 : Stream s2 = m2.Build(kAllInstructions);
311 1 : EXPECT_LE(3U, s1.size());
312 3 : ASSERT_EQ(s1.size(), s2.size());
313 17 : TRACED_FORRANGE(size_t, i, 0, s1.size() - 1) {
314 4 : const Instruction* i1 = s1[i];
315 4 : const Instruction* i2 = s2[i];
316 12 : EXPECT_EQ(i1->arch_opcode(), i2->arch_opcode());
317 12 : EXPECT_EQ(i1->InputCount(), i2->InputCount());
318 12 : EXPECT_EQ(i1->OutputCount(), i2->OutputCount());
319 : }
320 : }
321 :
322 : // -----------------------------------------------------------------------------
323 : // Calls with deoptimization.
324 :
325 15444 : TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
326 : StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
327 1 : MachineType::AnyTagged(), MachineType::AnyTagged());
328 :
329 : BailoutId bailout_id(42);
330 :
331 1 : Node* function_node = m.Parameter(0);
332 1 : Node* receiver = m.Parameter(1);
333 1 : Node* context = m.Parameter(2);
334 :
335 : ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
336 : ZoneVector<MachineType> empty_types(zone());
337 :
338 : auto call_descriptor = Linkage::GetJSCallDescriptor(
339 : zone(), false, 1,
340 1 : CallDescriptor::kNeedsFrameState | CallDescriptor::kCanUseRoots);
341 :
342 : // Build frame state for the state before the call.
343 1 : Node* parameters = m.AddNode(
344 : m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
345 : m.Int32Constant(1));
346 1 : Node* locals = m.AddNode(
347 : m.common()->TypedStateValues(&empty_types, SparseInputMask::Dense()));
348 1 : Node* stack = m.AddNode(
349 : m.common()->TypedStateValues(&empty_types, SparseInputMask::Dense()));
350 1 : Node* context_sentinel = m.Int32Constant(0);
351 2 : Node* state_node = m.AddNode(
352 : m.common()->FrameState(bailout_id, OutputFrameStateCombine::PokeAt(0),
353 : m.GetFrameStateFunctionInfo(1, 0)),
354 : parameters, locals, stack, context_sentinel, function_node,
355 : m.UndefinedConstant());
356 :
357 : // Build the call.
358 1 : Node* nodes[] = {function_node, receiver, m.UndefinedConstant(),
359 2 : m.Int32Constant(1), context, state_node};
360 1 : Node* call = m.CallNWithFrameState(call_descriptor, arraysize(nodes), nodes);
361 1 : m.Return(call);
362 :
363 1 : Stream s = m.Build(kAllExceptNopInstructions);
364 :
365 : // Skip until kArchCallJSFunction.
366 : size_t index = 0;
367 4 : for (; index < s.size() && s[index]->arch_opcode() != kArchCallJSFunction;
368 : index++) {
369 : }
370 : // Now we should have two instructions: call and return.
371 2 : ASSERT_EQ(index + 2, s.size());
372 :
373 3 : EXPECT_EQ(kArchCallJSFunction, s[index++]->arch_opcode());
374 3 : EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
375 :
376 : // TODO(jarin) Check deoptimization table.
377 : }
378 :
379 15444 : TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeopt) {
380 : StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
381 1 : MachineType::AnyTagged(), MachineType::AnyTagged());
382 :
383 : BailoutId bailout_id_before(42);
384 :
385 : // Some arguments for the call node.
386 1 : Node* function_node = m.Parameter(0);
387 1 : Node* receiver = m.Parameter(1);
388 1 : Node* context = m.Int32Constant(1); // Context is ignored.
389 :
390 : ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
391 : ZoneVector<MachineType> float64_type(1, MachineType::Float64(), zone());
392 : ZoneVector<MachineType> tagged_type(1, MachineType::AnyTagged(), zone());
393 :
394 1 : Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
395 1 : auto call_descriptor = Linkage::GetStubCallDescriptor(
396 1 : zone(), callable.descriptor(), 1, CallDescriptor::kNeedsFrameState,
397 1 : Operator::kNoProperties);
398 :
399 : // Build frame state for the state before the call.
400 1 : Node* parameters = m.AddNode(
401 : m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
402 : m.Int32Constant(43));
403 1 : Node* locals = m.AddNode(
404 : m.common()->TypedStateValues(&float64_type, SparseInputMask::Dense()),
405 : m.Float64Constant(0.5));
406 1 : Node* stack = m.AddNode(
407 : m.common()->TypedStateValues(&tagged_type, SparseInputMask::Dense()),
408 : m.UndefinedConstant());
409 1 : Node* context_sentinel = m.Int32Constant(0);
410 : Node* state_node =
411 2 : m.AddNode(m.common()->FrameState(bailout_id_before,
412 : OutputFrameStateCombine::PokeAt(0),
413 : m.GetFrameStateFunctionInfo(1, 1)),
414 : parameters, locals, stack, context_sentinel, function_node,
415 : m.UndefinedConstant());
416 :
417 : // Build the call.
418 1 : Node* stub_code = m.HeapConstant(callable.code());
419 1 : Node* nodes[] = {stub_code, function_node, receiver, context, state_node};
420 1 : Node* call = m.CallNWithFrameState(call_descriptor, arraysize(nodes), nodes);
421 1 : m.Return(call);
422 :
423 1 : Stream s = m.Build(kAllExceptNopInstructions);
424 :
425 : // Skip until kArchCallJSFunction.
426 1 : size_t index = 0;
427 4 : for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
428 : index++) {
429 : }
430 : // Now we should have two instructions: call, return.
431 2 : ASSERT_EQ(index + 2, s.size());
432 :
433 : // Check the call instruction
434 1 : const Instruction* call_instr = s[index++];
435 2 : EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
436 : size_t num_operands =
437 : 1 + // Code object.
438 : 1 + // Poison index
439 : 6 + // Frame state deopt id + one input for each value in frame state.
440 : 1 + // Function.
441 1 : 1; // Context.
442 2 : ASSERT_EQ(num_operands, call_instr->InputCount());
443 :
444 : // Code object.
445 1 : EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
446 :
447 : // Deoptimization id.
448 : int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(2));
449 : FrameStateDescriptor* desc_before =
450 1 : s.GetFrameStateDescriptor(deopt_id_before);
451 2 : EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
452 2 : EXPECT_EQ(1u, desc_before->parameters_count());
453 2 : EXPECT_EQ(1u, desc_before->locals_count());
454 2 : EXPECT_EQ(1u, desc_before->stack_count());
455 2 : EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(4)));
456 2 : EXPECT_EQ(0, s.ToInt32(call_instr->InputAt(5))); // This should be a context.
457 : // We inserted 0 here.
458 2 : EXPECT_EQ(0.5, s.ToFloat64(call_instr->InputAt(6)));
459 1 : EXPECT_TRUE(s.ToHeapObject(call_instr->InputAt(7))->IsUndefined(isolate()));
460 :
461 : // Function.
462 2 : EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(8)));
463 : // Context.
464 2 : EXPECT_EQ(s.ToVreg(context), s.ToVreg(call_instr->InputAt(9)));
465 :
466 3 : EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
467 :
468 2 : EXPECT_EQ(index, s.size());
469 : }
470 :
471 15444 : TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeoptRecursiveFrameState) {
472 : StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
473 1 : MachineType::AnyTagged(), MachineType::AnyTagged());
474 :
475 : BailoutId bailout_id_before(42);
476 : BailoutId bailout_id_parent(62);
477 :
478 : // Some arguments for the call node.
479 1 : Node* function_node = m.Parameter(0);
480 1 : Node* receiver = m.Parameter(1);
481 1 : Node* context = m.Int32Constant(66);
482 1 : Node* context2 = m.Int32Constant(46);
483 :
484 : ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
485 : ZoneVector<MachineType> int32x2_type(2, MachineType::Int32(), zone());
486 : ZoneVector<MachineType> float64_type(1, MachineType::Float64(), zone());
487 :
488 1 : Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject);
489 1 : auto call_descriptor = Linkage::GetStubCallDescriptor(
490 1 : zone(), callable.descriptor(), 1, CallDescriptor::kNeedsFrameState,
491 1 : Operator::kNoProperties);
492 :
493 : // Build frame state for the state before the call.
494 1 : Node* parameters = m.AddNode(
495 : m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
496 : m.Int32Constant(63));
497 1 : Node* locals = m.AddNode(
498 : m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
499 : m.Int32Constant(64));
500 1 : Node* stack = m.AddNode(
501 : m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
502 : m.Int32Constant(65));
503 2 : Node* frame_state_parent = m.AddNode(
504 : m.common()->FrameState(bailout_id_parent,
505 : OutputFrameStateCombine::Ignore(),
506 : m.GetFrameStateFunctionInfo(1, 1)),
507 : parameters, locals, stack, context, function_node, m.UndefinedConstant());
508 :
509 1 : Node* parameters2 = m.AddNode(
510 : m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
511 : m.Int32Constant(43));
512 1 : Node* locals2 = m.AddNode(
513 : m.common()->TypedStateValues(&float64_type, SparseInputMask::Dense()),
514 : m.Float64Constant(0.25));
515 1 : Node* stack2 = m.AddNode(
516 : m.common()->TypedStateValues(&int32x2_type, SparseInputMask::Dense()),
517 : m.Int32Constant(44), m.Int32Constant(45));
518 : Node* state_node =
519 1 : m.AddNode(m.common()->FrameState(bailout_id_before,
520 : OutputFrameStateCombine::PokeAt(0),
521 : m.GetFrameStateFunctionInfo(1, 1)),
522 : parameters2, locals2, stack2, context2, function_node,
523 : frame_state_parent);
524 :
525 : // Build the call.
526 1 : Node* stub_code = m.HeapConstant(callable.code());
527 1 : Node* nodes[] = {stub_code, function_node, receiver, context2, state_node};
528 1 : Node* call = m.CallNWithFrameState(call_descriptor, arraysize(nodes), nodes);
529 1 : m.Return(call);
530 :
531 1 : Stream s = m.Build(kAllExceptNopInstructions);
532 :
533 : // Skip until kArchCallJSFunction.
534 1 : size_t index = 0;
535 4 : for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
536 : index++) {
537 : }
538 : // Now we should have three instructions: call, return.
539 2 : EXPECT_EQ(index + 2, s.size());
540 :
541 : // Check the call instruction
542 1 : const Instruction* call_instr = s[index++];
543 2 : EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
544 : size_t num_operands =
545 : 1 + // Code object.
546 : 1 + // Poison index.
547 : 1 + // Frame state deopt id
548 : 6 + // One input for each value in frame state + context.
549 : 5 + // One input for each value in the parent frame state + context.
550 : 1 + // Function.
551 1 : 1; // Context.
552 2 : EXPECT_EQ(num_operands, call_instr->InputCount());
553 : // Code object.
554 1 : EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
555 :
556 : // Deoptimization id.
557 : int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(2));
558 : FrameStateDescriptor* desc_before =
559 1 : s.GetFrameStateDescriptor(deopt_id_before);
560 : FrameStateDescriptor* desc_before_outer = desc_before->outer_state();
561 2 : EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
562 2 : EXPECT_EQ(1u, desc_before_outer->parameters_count());
563 2 : EXPECT_EQ(1u, desc_before_outer->locals_count());
564 2 : EXPECT_EQ(1u, desc_before_outer->stack_count());
565 : // Values from parent environment.
566 2 : EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(4)));
567 : // Context:
568 2 : EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(5)));
569 2 : EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(6)));
570 2 : EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(7)));
571 : // Values from the nested frame.
572 2 : EXPECT_EQ(1u, desc_before->parameters_count());
573 2 : EXPECT_EQ(1u, desc_before->locals_count());
574 2 : EXPECT_EQ(2u, desc_before->stack_count());
575 2 : EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(9)));
576 2 : EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(10)));
577 2 : EXPECT_EQ(0.25, s.ToFloat64(call_instr->InputAt(11)));
578 2 : EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(12)));
579 2 : EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(13)));
580 :
581 : // Function.
582 2 : EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(14)));
583 : // Context.
584 2 : EXPECT_EQ(s.ToVreg(context2), s.ToVreg(call_instr->InputAt(15)));
585 : // Continuation.
586 :
587 3 : EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
588 2 : EXPECT_EQ(index, s.size());
589 1 : }
590 :
591 : // Helper to make calls to private InstructionSelector shuffle functions.
592 14 : class InstructionSelectorShuffleTest : public ::testing::Test {
593 : public:
594 : using Shuffle = std::array<uint8_t, kSimd128Size>;
595 :
596 : struct TestShuffle {
597 : Shuffle non_canonical;
598 : Shuffle canonical;
599 : bool needs_swap;
600 : bool is_swizzle;
601 : };
602 :
603 : // Call testing members in InstructionSelector.
604 : static void CanonicalizeShuffle(bool inputs_equal, Shuffle* shuffle,
605 : bool* needs_swap, bool* is_swizzle) {
606 : InstructionSelector::CanonicalizeShuffleForTesting(
607 : inputs_equal, &(*shuffle)[0], needs_swap, is_swizzle);
608 : }
609 :
610 : static bool TryMatchIdentity(const Shuffle& shuffle) {
611 : return InstructionSelector::TryMatchIdentityForTesting(&shuffle[0]);
612 : }
613 : template <int LANES>
614 : static bool TryMatchDup(const Shuffle& shuffle, int* index) {
615 : return InstructionSelector::TryMatchDupForTesting<LANES>(&shuffle[0],
616 : index);
617 : }
618 : static bool TryMatch32x4Shuffle(const Shuffle& shuffle,
619 : uint8_t* shuffle32x4) {
620 : return InstructionSelector::TryMatch32x4ShuffleForTesting(&shuffle[0],
621 : shuffle32x4);
622 : }
623 : static bool TryMatch16x8Shuffle(const Shuffle& shuffle,
624 : uint8_t* shuffle16x8) {
625 : return InstructionSelector::TryMatch16x8ShuffleForTesting(&shuffle[0],
626 : shuffle16x8);
627 : }
628 : static bool TryMatchConcat(const Shuffle& shuffle, uint8_t* offset) {
629 : return InstructionSelector::TryMatchConcatForTesting(&shuffle[0], offset);
630 : }
631 : static bool TryMatchBlend(const Shuffle& shuffle) {
632 : return InstructionSelector::TryMatchBlendForTesting(&shuffle[0]);
633 : }
634 : };
635 :
636 0 : bool operator==(const InstructionSelectorShuffleTest::Shuffle& a,
637 : const InstructionSelectorShuffleTest::Shuffle& b) {
638 0 : for (int i = 0; i < kSimd128Size; ++i) {
639 0 : if (a[i] != b[i]) return false;
640 : }
641 : return true;
642 : }
643 :
644 15444 : TEST_F(InstructionSelectorShuffleTest, CanonicalizeShuffle) {
645 : const bool kInputsEqual = true;
646 : const bool kNeedsSwap = true;
647 : const bool kIsSwizzle = true;
648 :
649 : bool needs_swap;
650 : bool is_swizzle;
651 :
652 : // Test canonicalization driven by input shuffle.
653 : TestShuffle test_shuffles[] = {
654 : // Identity is canonical.
655 : {{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
656 : {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
657 : !kNeedsSwap,
658 : kIsSwizzle},
659 : // Non-canonical identity requires a swap.
660 : {{{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}},
661 : {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
662 : kNeedsSwap,
663 : kIsSwizzle},
664 : // General shuffle, canonical is unchanged.
665 : {{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
666 : {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
667 : !kNeedsSwap,
668 : !kIsSwizzle},
669 : // Non-canonical shuffle requires a swap.
670 : {{{16, 0, 17, 1, 18, 2, 19, 3, 20, 4, 21, 5, 22, 6, 23, 7}},
671 : {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
672 : kNeedsSwap,
673 : !kIsSwizzle},
674 1 : };
675 9 : for (size_t i = 0; i < arraysize(test_shuffles); ++i) {
676 4 : Shuffle shuffle = test_shuffles[i].non_canonical;
677 : CanonicalizeShuffle(!kInputsEqual, &shuffle, &needs_swap, &is_swizzle);
678 8 : EXPECT_EQ(shuffle, test_shuffles[i].canonical);
679 8 : EXPECT_EQ(needs_swap, test_shuffles[i].needs_swap);
680 8 : EXPECT_EQ(is_swizzle, test_shuffles[i].is_swizzle);
681 : }
682 :
683 : // Test canonicalization when inputs are equal (explicit swizzle).
684 : TestShuffle test_swizzles[] = {
685 : // Identity is canonical.
686 : {{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
687 : {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
688 : !kNeedsSwap,
689 : kIsSwizzle},
690 : // Non-canonical identity requires a swap.
691 : {{{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}},
692 : {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
693 : !kNeedsSwap,
694 : kIsSwizzle},
695 : // Canonicalized to swizzle.
696 : {{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
697 : {{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}},
698 : !kNeedsSwap,
699 : kIsSwizzle},
700 : // Canonicalized to swizzle.
701 : {{{16, 0, 17, 1, 18, 2, 19, 3, 20, 4, 21, 5, 22, 6, 23, 7}},
702 : {{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}},
703 : !kNeedsSwap,
704 : kIsSwizzle},
705 1 : };
706 9 : for (size_t i = 0; i < arraysize(test_swizzles); ++i) {
707 4 : Shuffle shuffle = test_swizzles[i].non_canonical;
708 : CanonicalizeShuffle(kInputsEqual, &shuffle, &needs_swap, &is_swizzle);
709 8 : EXPECT_EQ(shuffle, test_swizzles[i].canonical);
710 8 : EXPECT_EQ(needs_swap, test_swizzles[i].needs_swap);
711 8 : EXPECT_EQ(is_swizzle, test_swizzles[i].is_swizzle);
712 : }
713 1 : }
714 :
715 15444 : TEST_F(InstructionSelectorShuffleTest, TryMatchIdentity) {
716 : // Match shuffle that returns first source operand.
717 2 : EXPECT_TRUE(TryMatchIdentity(
718 0 : {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}));
719 : // The non-canonicalized identity shuffle doesn't match.
720 3 : EXPECT_FALSE(TryMatchIdentity(
721 0 : {{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}}));
722 : // Even one lane out of place is not an identity shuffle.
723 3 : EXPECT_FALSE(TryMatchIdentity(
724 0 : {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31}}));
725 1 : }
726 :
727 15444 : TEST_F(InstructionSelectorShuffleTest, TryMatchDup) {
728 : int index;
729 : // All lanes from the same 32 bit source lane.
730 2 : EXPECT_TRUE(TryMatchDup<4>({{4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}},
731 0 : &index));
732 2 : EXPECT_EQ(1, index);
733 : // It shouldn't match for other vector shapes.
734 3 : EXPECT_FALSE(TryMatchDup<8>(
735 0 : {{4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}}, &index));
736 3 : EXPECT_FALSE(TryMatchDup<16>(
737 0 : {{4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}}, &index));
738 : // All lanes from the same 16 bit source lane.
739 2 : EXPECT_TRUE(TryMatchDup<8>(
740 : {{16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17}},
741 0 : &index));
742 2 : EXPECT_EQ(8, index);
743 : // It shouldn't match for other vector shapes.
744 3 : EXPECT_FALSE(TryMatchDup<4>(
745 : {{16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17}},
746 0 : &index));
747 3 : EXPECT_FALSE(TryMatchDup<16>(
748 : {{16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17}},
749 0 : &index));
750 : // All lanes from the same 8 bit source lane.
751 2 : EXPECT_TRUE(TryMatchDup<16>(
752 0 : {{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}, &index));
753 2 : EXPECT_EQ(7, index);
754 : // It shouldn't match for other vector shapes.
755 3 : EXPECT_FALSE(TryMatchDup<4>(
756 0 : {{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}, &index));
757 3 : EXPECT_FALSE(TryMatchDup<8>(
758 0 : {{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}, &index));
759 1 : }
760 :
761 15444 : TEST_F(InstructionSelectorShuffleTest, TryMatchConcat) {
762 : uint8_t offset;
763 : // Ascending indices, jump at end to same input (concatenating swizzle).
764 2 : EXPECT_TRUE(TryMatchConcat(
765 0 : {{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2}}, &offset));
766 2 : EXPECT_EQ(3, offset);
767 : // Ascending indices, jump at end to other input (concatenating shuffle).
768 2 : EXPECT_TRUE(TryMatchConcat(
769 0 : {{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}}, &offset));
770 2 : EXPECT_EQ(4, offset);
771 :
772 : // Shuffles that should not match:
773 : // Ascending indices, but jump isn't at end/beginning.
774 3 : EXPECT_FALSE(TryMatchConcat(
775 0 : {{3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6}}, &offset));
776 : // Ascending indices, but multiple jumps.
777 3 : EXPECT_FALSE(TryMatchConcat(
778 0 : {{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}}, &offset));
779 1 : }
780 :
781 15444 : TEST_F(InstructionSelectorShuffleTest, TryMatch32x4Shuffle) {
782 : uint8_t shuffle32x4[4];
783 : // Match if each group of 4 bytes is from the same 32 bit lane.
784 2 : EXPECT_TRUE(TryMatch32x4Shuffle(
785 : {{12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19}},
786 0 : shuffle32x4));
787 2 : EXPECT_EQ(3, shuffle32x4[0]);
788 2 : EXPECT_EQ(2, shuffle32x4[1]);
789 2 : EXPECT_EQ(1, shuffle32x4[2]);
790 2 : EXPECT_EQ(4, shuffle32x4[3]);
791 : // Bytes must be in order in the 32 bit lane.
792 3 : EXPECT_FALSE(TryMatch32x4Shuffle(
793 : {{12, 13, 14, 14, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19}},
794 0 : shuffle32x4));
795 : // Each group must start with the first byte in the 32 bit lane.
796 3 : EXPECT_FALSE(TryMatch32x4Shuffle(
797 : {{13, 14, 15, 12, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19}},
798 0 : shuffle32x4));
799 1 : }
800 :
801 15444 : TEST_F(InstructionSelectorShuffleTest, TryMatch16x8Shuffle) {
802 : uint8_t shuffle16x8[8];
803 : // Match if each group of 2 bytes is from the same 16 bit lane.
804 2 : EXPECT_TRUE(TryMatch16x8Shuffle(
805 : {{12, 13, 30, 31, 8, 9, 26, 27, 4, 5, 22, 23, 16, 17, 2, 3}},
806 0 : shuffle16x8));
807 2 : EXPECT_EQ(6, shuffle16x8[0]);
808 2 : EXPECT_EQ(15, shuffle16x8[1]);
809 2 : EXPECT_EQ(4, shuffle16x8[2]);
810 2 : EXPECT_EQ(13, shuffle16x8[3]);
811 2 : EXPECT_EQ(2, shuffle16x8[4]);
812 2 : EXPECT_EQ(11, shuffle16x8[5]);
813 2 : EXPECT_EQ(8, shuffle16x8[6]);
814 2 : EXPECT_EQ(1, shuffle16x8[7]);
815 : // Bytes must be in order in the 16 bit lane.
816 3 : EXPECT_FALSE(TryMatch16x8Shuffle(
817 : {{12, 13, 30, 30, 8, 9, 26, 27, 4, 5, 22, 23, 16, 17, 2, 3}},
818 0 : shuffle16x8));
819 : // Each group must start with the first byte in the 16 bit lane.
820 3 : EXPECT_FALSE(TryMatch16x8Shuffle(
821 : {{12, 13, 31, 30, 8, 9, 26, 27, 4, 5, 22, 23, 16, 17, 2, 3}},
822 0 : shuffle16x8));
823 1 : }
824 :
825 15444 : TEST_F(InstructionSelectorShuffleTest, TryMatchBlend) {
826 : // Match if each byte remains in place.
827 2 : EXPECT_TRUE(TryMatchBlend(
828 0 : {{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}}));
829 : // Identity is a blend.
830 2 : EXPECT_TRUE(
831 0 : TryMatchBlend({{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}));
832 : // Even one lane out of place is not a blend.
833 3 : EXPECT_FALSE(TryMatchBlend(
834 0 : {{1, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}}));
835 1 : }
836 :
837 : } // namespace compiler
838 : } // namespace internal
839 9264 : } // namespace v8
|