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/instruction-selector-unittest.h"
6
7#include "src/code-factory.h"
8#include "src/compiler/graph.h"
9#include "src/compiler/schedule.h"
10#include "src/flags.h"
11#include "test/unittests/compiler/compiler-test-utils.h"
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
17
18InstructionSelectorTest::InstructionSelectorTest() : rng_(FLAG_random_seed) {}
19
20
21InstructionSelectorTest::~InstructionSelectorTest() {}
22
23
24InstructionSelectorTest::Stream InstructionSelectorTest::StreamBuilder::Build(
25    InstructionSelector::Features features,
26    InstructionSelectorTest::StreamBuilderMode mode,
27    InstructionSelector::SourcePositionMode source_position_mode) {
28  Schedule* schedule = Export();
29  if (FLAG_trace_turbo) {
30    OFStream out(stdout);
31    out << "=== Schedule before instruction selection ===" << std::endl
32        << *schedule;
33  }
34  size_t const node_count = graph()->NodeCount();
35  EXPECT_NE(0u, node_count);
36  Linkage linkage(call_descriptor());
37  InstructionBlocks* instruction_blocks =
38      InstructionSequence::InstructionBlocksFor(test_->zone(), schedule);
39  InstructionSequence sequence(test_->isolate(), test_->zone(),
40                               instruction_blocks);
41  SourcePositionTable source_position_table(graph());
42  InstructionSelector selector(test_->zone(), node_count, &linkage, &sequence,
43                               schedule, &source_position_table, nullptr,
44                               source_position_mode, features);
45  selector.SelectInstructions();
46  if (FLAG_trace_turbo) {
47    OFStream out(stdout);
48    PrintableInstructionSequence printable = {RegisterConfiguration::Turbofan(),
49                                              &sequence};
50    out << "=== Code sequence after instruction selection ===" << std::endl
51        << printable;
52  }
53  Stream s;
54  s.virtual_registers_ = selector.GetVirtualRegistersForTesting();
55  // Map virtual registers.
56  for (Instruction* const instr : sequence) {
57    if (instr->opcode() < 0) continue;
58    if (mode == kTargetInstructions) {
59      switch (instr->arch_opcode()) {
60#define CASE(Name) \
61  case k##Name:    \
62    break;
63        TARGET_ARCH_OPCODE_LIST(CASE)
64#undef CASE
65        default:
66          continue;
67      }
68    }
69    if (mode == kAllExceptNopInstructions && instr->arch_opcode() == kArchNop) {
70      continue;
71    }
72    for (size_t i = 0; i < instr->OutputCount(); ++i) {
73      InstructionOperand* output = instr->OutputAt(i);
74      EXPECT_NE(InstructionOperand::IMMEDIATE, output->kind());
75      if (output->IsConstant()) {
76        int vreg = ConstantOperand::cast(output)->virtual_register();
77        s.constants_.insert(std::make_pair(vreg, sequence.GetConstant(vreg)));
78      }
79    }
80    for (size_t i = 0; i < instr->InputCount(); ++i) {
81      InstructionOperand* input = instr->InputAt(i);
82      EXPECT_NE(InstructionOperand::CONSTANT, input->kind());
83      if (input->IsImmediate()) {
84        auto imm = ImmediateOperand::cast(input);
85        if (imm->type() == ImmediateOperand::INDEXED) {
86          int index = imm->indexed_value();
87          s.immediates_.insert(
88              std::make_pair(index, sequence.GetImmediate(imm)));
89        }
90      }
91    }
92    s.instructions_.push_back(instr);
93  }
94  for (auto i : s.virtual_registers_) {
95    int const virtual_register = i.second;
96    if (sequence.IsFP(virtual_register)) {
97      EXPECT_FALSE(sequence.IsReference(virtual_register));
98      s.doubles_.insert(virtual_register);
99    }
100    if (sequence.IsReference(virtual_register)) {
101      EXPECT_FALSE(sequence.IsFP(virtual_register));
102      s.references_.insert(virtual_register);
103    }
104  }
105  for (int i = 0; i < sequence.GetFrameStateDescriptorCount(); i++) {
106    s.deoptimization_entries_.push_back(sequence.GetFrameStateDescriptor(
107        InstructionSequence::StateId::FromInt(i)));
108  }
109  return s;
110}
111
112
113int InstructionSelectorTest::Stream::ToVreg(const Node* node) const {
114  VirtualRegisters::const_iterator i = virtual_registers_.find(node->id());
115  CHECK(i != virtual_registers_.end());
116  return i->second;
117}
118
119
120bool InstructionSelectorTest::Stream::IsFixed(const InstructionOperand* operand,
121                                              Register reg) const {
122  if (!operand->IsUnallocated()) return false;
123  const UnallocatedOperand* unallocated = UnallocatedOperand::cast(operand);
124  if (!unallocated->HasFixedRegisterPolicy()) return false;
125  return unallocated->fixed_register_index() == reg.code();
126}
127
128
129bool InstructionSelectorTest::Stream::IsSameAsFirst(
130    const InstructionOperand* operand) const {
131  if (!operand->IsUnallocated()) return false;
132  const UnallocatedOperand* unallocated = UnallocatedOperand::cast(operand);
133  return unallocated->HasSameAsInputPolicy();
134}
135
136
137bool InstructionSelectorTest::Stream::IsUsedAtStart(
138    const InstructionOperand* operand) const {
139  if (!operand->IsUnallocated()) return false;
140  const UnallocatedOperand* unallocated = UnallocatedOperand::cast(operand);
141  return unallocated->IsUsedAtStart();
142}
143
144
145const FrameStateFunctionInfo*
146InstructionSelectorTest::StreamBuilder::GetFrameStateFunctionInfo(
147    int parameter_count, int local_count) {
148  return common()->CreateFrameStateFunctionInfo(
149      FrameStateType::kJavaScriptFunction, parameter_count, local_count,
150      Handle<SharedFunctionInfo>());
151}
152
153
154// -----------------------------------------------------------------------------
155// Return.
156
157
158TARGET_TEST_F(InstructionSelectorTest, ReturnFloat32Constant) {
159  const float kValue = 4.2f;
160  StreamBuilder m(this, MachineType::Float32());
161  m.Return(m.Float32Constant(kValue));
162  Stream s = m.Build(kAllInstructions);
163  ASSERT_EQ(3U, s.size());
164  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
165  ASSERT_EQ(InstructionOperand::CONSTANT, s[0]->OutputAt(0)->kind());
166  EXPECT_FLOAT_EQ(kValue, s.ToFloat32(s[0]->OutputAt(0)));
167  EXPECT_EQ(kArchRet, s[1]->arch_opcode());
168  EXPECT_EQ(1U, s[1]->InputCount());
169}
170
171
172TARGET_TEST_F(InstructionSelectorTest, ReturnParameter) {
173  StreamBuilder m(this, MachineType::Int32(), MachineType::Int32());
174  m.Return(m.Parameter(0));
175  Stream s = m.Build(kAllInstructions);
176  ASSERT_EQ(3U, s.size());
177  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
178  ASSERT_EQ(1U, s[0]->OutputCount());
179  EXPECT_EQ(kArchRet, s[1]->arch_opcode());
180  EXPECT_EQ(1U, s[1]->InputCount());
181}
182
183
184TARGET_TEST_F(InstructionSelectorTest, ReturnZero) {
185  StreamBuilder m(this, MachineType::Int32());
186  m.Return(m.Int32Constant(0));
187  Stream s = m.Build(kAllInstructions);
188  ASSERT_EQ(3U, s.size());
189  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
190  ASSERT_EQ(1U, s[0]->OutputCount());
191  EXPECT_EQ(InstructionOperand::CONSTANT, s[0]->OutputAt(0)->kind());
192  EXPECT_EQ(0, s.ToInt32(s[0]->OutputAt(0)));
193  EXPECT_EQ(kArchRet, s[1]->arch_opcode());
194  EXPECT_EQ(1U, s[1]->InputCount());
195}
196
197
198// -----------------------------------------------------------------------------
199// Conversions.
200
201TARGET_TEST_F(InstructionSelectorTest, TruncateFloat64ToWord32WithParameter) {
202  StreamBuilder m(this, MachineType::Int32(), MachineType::Float64());
203  m.Return(m.TruncateFloat64ToWord32(m.Parameter(0)));
204  Stream s = m.Build(kAllInstructions);
205  ASSERT_EQ(4U, s.size());
206  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
207  EXPECT_EQ(kArchTruncateDoubleToI, s[1]->arch_opcode());
208  EXPECT_EQ(1U, s[1]->InputCount());
209  EXPECT_EQ(1U, s[1]->OutputCount());
210  EXPECT_EQ(kArchRet, s[2]->arch_opcode());
211}
212
213
214// -----------------------------------------------------------------------------
215// Parameters.
216
217
218TARGET_TEST_F(InstructionSelectorTest, DoubleParameter) {
219  StreamBuilder m(this, MachineType::Float64(), MachineType::Float64());
220  Node* param = m.Parameter(0);
221  m.Return(param);
222  Stream s = m.Build(kAllInstructions);
223  EXPECT_TRUE(s.IsDouble(param));
224}
225
226
227TARGET_TEST_F(InstructionSelectorTest, ReferenceParameter) {
228  StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged());
229  Node* param = m.Parameter(0);
230  m.Return(param);
231  Stream s = m.Build(kAllInstructions);
232  EXPECT_TRUE(s.IsReference(param));
233}
234
235
236// -----------------------------------------------------------------------------
237// FinishRegion.
238
239
240TARGET_TEST_F(InstructionSelectorTest, FinishRegion) {
241  StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged());
242  Node* param = m.Parameter(0);
243  Node* finish =
244      m.AddNode(m.common()->FinishRegion(), param, m.graph()->start());
245  m.Return(finish);
246  Stream s = m.Build(kAllInstructions);
247  ASSERT_EQ(4U, s.size());
248  EXPECT_EQ(kArchNop, s[0]->arch_opcode());
249  ASSERT_EQ(1U, s[0]->OutputCount());
250  ASSERT_TRUE(s[0]->Output()->IsUnallocated());
251  EXPECT_EQ(s.ToVreg(param), s.ToVreg(s[0]->Output()));
252  EXPECT_EQ(kArchNop, s[1]->arch_opcode());
253  ASSERT_EQ(1U, s[1]->InputCount());
254  ASSERT_TRUE(s[1]->InputAt(0)->IsUnallocated());
255  EXPECT_EQ(s.ToVreg(param), s.ToVreg(s[1]->InputAt(0)));
256  ASSERT_EQ(1U, s[1]->OutputCount());
257  ASSERT_TRUE(s[1]->Output()->IsUnallocated());
258  EXPECT_TRUE(UnallocatedOperand::cast(s[1]->Output())->HasSameAsInputPolicy());
259  EXPECT_EQ(s.ToVreg(finish), s.ToVreg(s[1]->Output()));
260  EXPECT_TRUE(s.IsReference(finish));
261}
262
263
264// -----------------------------------------------------------------------------
265// Phi.
266
267
268typedef InstructionSelectorTestWithParam<MachineType>
269    InstructionSelectorPhiTest;
270
271
272TARGET_TEST_P(InstructionSelectorPhiTest, Doubleness) {
273  const MachineType type = GetParam();
274  StreamBuilder m(this, type, type, type);
275  Node* param0 = m.Parameter(0);
276  Node* param1 = m.Parameter(1);
277  RawMachineLabel a, b, c;
278  m.Branch(m.Int32Constant(0), &a, &b);
279  m.Bind(&a);
280  m.Goto(&c);
281  m.Bind(&b);
282  m.Goto(&c);
283  m.Bind(&c);
284  Node* phi = m.Phi(type.representation(), param0, param1);
285  m.Return(phi);
286  Stream s = m.Build(kAllInstructions);
287  EXPECT_EQ(s.IsDouble(phi), s.IsDouble(param0));
288  EXPECT_EQ(s.IsDouble(phi), s.IsDouble(param1));
289}
290
291
292TARGET_TEST_P(InstructionSelectorPhiTest, Referenceness) {
293  const MachineType type = GetParam();
294  StreamBuilder m(this, type, type, type);
295  Node* param0 = m.Parameter(0);
296  Node* param1 = m.Parameter(1);
297  RawMachineLabel a, b, c;
298  m.Branch(m.Int32Constant(1), &a, &b);
299  m.Bind(&a);
300  m.Goto(&c);
301  m.Bind(&b);
302  m.Goto(&c);
303  m.Bind(&c);
304  Node* phi = m.Phi(type.representation(), param0, param1);
305  m.Return(phi);
306  Stream s = m.Build(kAllInstructions);
307  EXPECT_EQ(s.IsReference(phi), s.IsReference(param0));
308  EXPECT_EQ(s.IsReference(phi), s.IsReference(param1));
309}
310
311
312INSTANTIATE_TEST_CASE_P(
313    InstructionSelectorTest, InstructionSelectorPhiTest,
314    ::testing::Values(MachineType::Float64(), MachineType::Int8(),
315                      MachineType::Uint8(), MachineType::Int16(),
316                      MachineType::Uint16(), MachineType::Int32(),
317                      MachineType::Uint32(), MachineType::Int64(),
318                      MachineType::Uint64(), MachineType::Pointer(),
319                      MachineType::AnyTagged()));
320
321
322// -----------------------------------------------------------------------------
323// ValueEffect.
324
325
326TARGET_TEST_F(InstructionSelectorTest, ValueEffect) {
327  StreamBuilder m1(this, MachineType::Int32(), MachineType::Pointer());
328  Node* p1 = m1.Parameter(0);
329  m1.Return(m1.Load(MachineType::Int32(), p1, m1.Int32Constant(0)));
330  Stream s1 = m1.Build(kAllInstructions);
331  StreamBuilder m2(this, MachineType::Int32(), MachineType::Pointer());
332  Node* p2 = m2.Parameter(0);
333  m2.Return(m2.AddNode(
334      m2.machine()->Load(MachineType::Int32()), p2, m2.Int32Constant(0),
335      m2.AddNode(m2.common()->BeginRegion(RegionObservability::kObservable),
336                 m2.graph()->start())));
337  Stream s2 = m2.Build(kAllInstructions);
338  EXPECT_LE(3U, s1.size());
339  ASSERT_EQ(s1.size(), s2.size());
340  TRACED_FORRANGE(size_t, i, 0, s1.size() - 1) {
341    const Instruction* i1 = s1[i];
342    const Instruction* i2 = s2[i];
343    EXPECT_EQ(i1->arch_opcode(), i2->arch_opcode());
344    EXPECT_EQ(i1->InputCount(), i2->InputCount());
345    EXPECT_EQ(i1->OutputCount(), i2->OutputCount());
346  }
347}
348
349
350// -----------------------------------------------------------------------------
351// Calls with deoptimization.
352
353
354TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
355  StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
356                  MachineType::AnyTagged(), MachineType::AnyTagged());
357
358  BailoutId bailout_id(42);
359
360  Node* function_node = m.Parameter(0);
361  Node* receiver = m.Parameter(1);
362  Node* context = m.Parameter(2);
363
364  ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
365  ZoneVector<MachineType> empty_types(zone());
366
367  CallDescriptor* descriptor = Linkage::GetJSCallDescriptor(
368      zone(), false, 1, CallDescriptor::kNeedsFrameState);
369
370  // Build frame state for the state before the call.
371  Node* parameters =
372      m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(1));
373  Node* locals = m.AddNode(m.common()->TypedStateValues(&empty_types));
374  Node* stack = m.AddNode(m.common()->TypedStateValues(&empty_types));
375  Node* context_sentinel = m.Int32Constant(0);
376  Node* state_node = m.AddNode(
377      m.common()->FrameState(bailout_id, OutputFrameStateCombine::Push(),
378                             m.GetFrameStateFunctionInfo(1, 0)),
379      parameters, locals, stack, context_sentinel, function_node,
380      m.UndefinedConstant());
381
382  // Build the call.
383  Node* args[] = {receiver, m.UndefinedConstant(), m.Int32Constant(1), context};
384  Node* call =
385      m.CallNWithFrameState(descriptor, function_node, args, state_node);
386  m.Return(call);
387
388  Stream s = m.Build(kAllExceptNopInstructions);
389
390  // Skip until kArchCallJSFunction.
391  size_t index = 0;
392  for (; index < s.size() && s[index]->arch_opcode() != kArchCallJSFunction;
393       index++) {
394  }
395  // Now we should have two instructions: call and return.
396  ASSERT_EQ(index + 2, s.size());
397
398  EXPECT_EQ(kArchCallJSFunction, s[index++]->arch_opcode());
399  EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
400
401  // TODO(jarin) Check deoptimization table.
402}
403
404
405TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeopt) {
406  StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
407                  MachineType::AnyTagged(), MachineType::AnyTagged());
408
409  BailoutId bailout_id_before(42);
410
411  // Some arguments for the call node.
412  Node* function_node = m.Parameter(0);
413  Node* receiver = m.Parameter(1);
414  Node* context = m.Int32Constant(1);  // Context is ignored.
415
416  ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
417  ZoneVector<MachineType> float64_type(1, MachineType::Float64(), zone());
418  ZoneVector<MachineType> tagged_type(1, MachineType::AnyTagged(), zone());
419
420  Callable callable = CodeFactory::ToObject(isolate());
421  CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
422      isolate(), zone(), callable.descriptor(), 1,
423      CallDescriptor::kNeedsFrameState, Operator::kNoProperties);
424
425  // Build frame state for the state before the call.
426  Node* parameters =
427      m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(43));
428  Node* locals = m.AddNode(m.common()->TypedStateValues(&float64_type),
429                           m.Float64Constant(0.5));
430  Node* stack = m.AddNode(m.common()->TypedStateValues(&tagged_type),
431                          m.UndefinedConstant());
432  Node* context_sentinel = m.Int32Constant(0);
433  Node* state_node = m.AddNode(
434      m.common()->FrameState(bailout_id_before, OutputFrameStateCombine::Push(),
435                             m.GetFrameStateFunctionInfo(1, 1)),
436      parameters, locals, stack, context_sentinel, function_node,
437      m.UndefinedConstant());
438
439  // Build the call.
440  Node* args[] = {function_node, receiver, context};
441  Node* stub_code = m.HeapConstant(callable.code());
442  Node* call = m.CallNWithFrameState(descriptor, stub_code, args, state_node);
443  m.Return(call);
444
445  Stream s = m.Build(kAllExceptNopInstructions);
446
447  // Skip until kArchCallJSFunction.
448  size_t index = 0;
449  for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
450       index++) {
451  }
452  // Now we should have two instructions: call, return.
453  ASSERT_EQ(index + 2, s.size());
454
455  // Check the call instruction
456  const Instruction* call_instr = s[index++];
457  EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
458  size_t num_operands =
459      1 +  // Code object.
460      1 +
461      5 +  // Frame state deopt id + one input for each value in frame state.
462      1 +  // Function.
463      1;   // Context.
464  ASSERT_EQ(num_operands, call_instr->InputCount());
465
466  // Code object.
467  EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
468
469  // Deoptimization id.
470  int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1));
471  FrameStateDescriptor* desc_before =
472      s.GetFrameStateDescriptor(deopt_id_before);
473  EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
474  EXPECT_EQ(OutputFrameStateCombine::kPushOutput,
475            desc_before->state_combine().kind());
476  EXPECT_EQ(1u, desc_before->parameters_count());
477  EXPECT_EQ(1u, desc_before->locals_count());
478  EXPECT_EQ(1u, desc_before->stack_count());
479  EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(3)));
480  EXPECT_EQ(0, s.ToInt32(call_instr->InputAt(4)));  // This should be a context.
481                                                    // We inserted 0 here.
482  EXPECT_EQ(0.5, s.ToFloat64(call_instr->InputAt(5)));
483  EXPECT_TRUE(s.ToHeapObject(call_instr->InputAt(6))->IsUndefined(isolate()));
484  EXPECT_EQ(MachineType::AnyTagged(),
485            desc_before->GetType(0));  // function is always
486                                       // tagged/any.
487  EXPECT_EQ(MachineType::Int32(), desc_before->GetType(1));
488  EXPECT_EQ(MachineType::AnyTagged(),
489            desc_before->GetType(2));  // context is always
490                                       // tagged/any.
491  EXPECT_EQ(MachineType::Float64(), desc_before->GetType(3));
492  EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(4));
493
494  // Function.
495  EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(7)));
496  // Context.
497  EXPECT_EQ(s.ToVreg(context), s.ToVreg(call_instr->InputAt(8)));
498
499  EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
500
501  EXPECT_EQ(index, s.size());
502}
503
504
505TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeoptRecursiveFrameState) {
506  StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
507                  MachineType::AnyTagged(), MachineType::AnyTagged());
508
509  BailoutId bailout_id_before(42);
510  BailoutId bailout_id_parent(62);
511
512  // Some arguments for the call node.
513  Node* function_node = m.Parameter(0);
514  Node* receiver = m.Parameter(1);
515  Node* context = m.Int32Constant(66);
516  Node* context2 = m.Int32Constant(46);
517
518  ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
519  ZoneVector<MachineType> int32x2_type(2, MachineType::Int32(), zone());
520  ZoneVector<MachineType> float64_type(1, MachineType::Float64(), zone());
521
522  Callable callable = CodeFactory::ToObject(isolate());
523  CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
524      isolate(), zone(), callable.descriptor(), 1,
525      CallDescriptor::kNeedsFrameState, Operator::kNoProperties);
526
527  // Build frame state for the state before the call.
528  Node* parameters =
529      m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(63));
530  Node* locals =
531      m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(64));
532  Node* stack =
533      m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(65));
534  Node* frame_state_parent = m.AddNode(
535      m.common()->FrameState(bailout_id_parent,
536                             OutputFrameStateCombine::Ignore(),
537                             m.GetFrameStateFunctionInfo(1, 1)),
538      parameters, locals, stack, context, function_node, m.UndefinedConstant());
539
540  Node* parameters2 =
541      m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(43));
542  Node* locals2 = m.AddNode(m.common()->TypedStateValues(&float64_type),
543                            m.Float64Constant(0.25));
544  Node* stack2 = m.AddNode(m.common()->TypedStateValues(&int32x2_type),
545                           m.Int32Constant(44), m.Int32Constant(45));
546  Node* state_node = m.AddNode(
547      m.common()->FrameState(bailout_id_before, OutputFrameStateCombine::Push(),
548                             m.GetFrameStateFunctionInfo(1, 1)),
549      parameters2, locals2, stack2, context2, function_node,
550      frame_state_parent);
551
552  // Build the call.
553  Node* args[] = {function_node, receiver, context2};
554  Node* stub_code = m.HeapConstant(callable.code());
555  Node* call = m.CallNWithFrameState(descriptor, stub_code, args, state_node);
556  m.Return(call);
557
558  Stream s = m.Build(kAllExceptNopInstructions);
559
560  // Skip until kArchCallJSFunction.
561  size_t index = 0;
562  for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
563       index++) {
564  }
565  // Now we should have three instructions: call, return.
566  EXPECT_EQ(index + 2, s.size());
567
568  // Check the call instruction
569  const Instruction* call_instr = s[index++];
570  EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
571  size_t num_operands =
572      1 +  // Code object.
573      1 +  // Frame state deopt id
574      6 +  // One input for each value in frame state + context.
575      5 +  // One input for each value in the parent frame state + context.
576      1 +  // Function.
577      1;   // Context.
578  EXPECT_EQ(num_operands, call_instr->InputCount());
579  // Code object.
580  EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
581
582  // Deoptimization id.
583  int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1));
584  FrameStateDescriptor* desc_before =
585      s.GetFrameStateDescriptor(deopt_id_before);
586  FrameStateDescriptor* desc_before_outer = desc_before->outer_state();
587  EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
588  EXPECT_EQ(1u, desc_before_outer->parameters_count());
589  EXPECT_EQ(1u, desc_before_outer->locals_count());
590  EXPECT_EQ(1u, desc_before_outer->stack_count());
591  // Values from parent environment.
592  EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(0));
593  EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(3)));
594  EXPECT_EQ(MachineType::Int32(), desc_before_outer->GetType(1));
595  // Context:
596  EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(4)));
597  EXPECT_EQ(MachineType::AnyTagged(), desc_before_outer->GetType(2));
598  EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(5)));
599  EXPECT_EQ(MachineType::Int32(), desc_before_outer->GetType(3));
600  EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(6)));
601  EXPECT_EQ(MachineType::Int32(), desc_before_outer->GetType(4));
602  // Values from the nested frame.
603  EXPECT_EQ(1u, desc_before->parameters_count());
604  EXPECT_EQ(1u, desc_before->locals_count());
605  EXPECT_EQ(2u, desc_before->stack_count());
606  EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(0));
607  EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(8)));
608  EXPECT_EQ(MachineType::Int32(), desc_before->GetType(1));
609  EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(9)));
610  EXPECT_EQ(MachineType::AnyTagged(), desc_before->GetType(2));
611  EXPECT_EQ(0.25, s.ToFloat64(call_instr->InputAt(10)));
612  EXPECT_EQ(MachineType::Float64(), desc_before->GetType(3));
613  EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(11)));
614  EXPECT_EQ(MachineType::Int32(), desc_before->GetType(4));
615  EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(12)));
616  EXPECT_EQ(MachineType::Int32(), desc_before->GetType(5));
617
618  // Function.
619  EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(13)));
620  // Context.
621  EXPECT_EQ(s.ToVreg(context2), s.ToVreg(call_instr->InputAt(14)));
622  // Continuation.
623
624  EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
625  EXPECT_EQ(index, s.size());
626}
627
628}  // namespace compiler
629}  // namespace internal
630}  // namespace v8
631