xref: /third_party/node/deps/v8/src/maglev/maglev-ir.cc (revision 1cb0ef41)
1// Copyright 2022 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/maglev/maglev-ir.h"
6
7#include "src/base/bits.h"
8#include "src/base/logging.h"
9#include "src/codegen/interface-descriptors-inl.h"
10#include "src/codegen/macro-assembler-inl.h"
11#include "src/codegen/register.h"
12#include "src/compiler/backend/instruction.h"
13#include "src/ic/handler-configuration.h"
14#include "src/maglev/maglev-code-gen-state.h"
15#include "src/maglev/maglev-compilation-unit.h"
16#include "src/maglev/maglev-graph-labeller.h"
17#include "src/maglev/maglev-graph-printer.h"
18#include "src/maglev/maglev-graph-processor.h"
19#include "src/maglev/maglev-interpreter-frame-state.h"
20#include "src/maglev/maglev-vreg-allocator.h"
21
22namespace v8 {
23namespace internal {
24namespace maglev {
25
26const char* ToString(Opcode opcode) {
27#define DEF_NAME(Name) #Name,
28  static constexpr const char* const names[] = {NODE_BASE_LIST(DEF_NAME)};
29#undef DEF_NAME
30  return names[static_cast<int>(opcode)];
31}
32
33#define __ code_gen_state->masm()->
34
35// TODO(v8:7700): Clean up after all code paths are supported.
36static bool g_this_field_will_be_unused_once_all_code_paths_are_supported;
37#define UNSUPPORTED(REASON)                                                \
38  do {                                                                     \
39    std::cerr << "Maglev: Can't compile, unsuppored codegen path (" REASON \
40                 ")\n";                                                    \
41    code_gen_state->set_found_unsupported_code_paths(true);                \
42    g_this_field_will_be_unused_once_all_code_paths_are_supported = true;  \
43  } while (false)
44
45namespace {
46
47// ---
48// Vreg allocation helpers.
49// ---
50
51int GetVirtualRegister(Node* node) {
52  return compiler::UnallocatedOperand::cast(node->result().operand())
53      .virtual_register();
54}
55
56void DefineAsRegister(MaglevVregAllocationState* vreg_state, Node* node) {
57  node->result().SetUnallocated(
58      compiler::UnallocatedOperand::MUST_HAVE_REGISTER,
59      vreg_state->AllocateVirtualRegister());
60}
61
62void DefineAsFixed(MaglevVregAllocationState* vreg_state, Node* node,
63                   Register reg) {
64  node->result().SetUnallocated(compiler::UnallocatedOperand::FIXED_REGISTER,
65                                reg.code(),
66                                vreg_state->AllocateVirtualRegister());
67}
68
69void DefineSameAsFirst(MaglevVregAllocationState* vreg_state, Node* node) {
70  node->result().SetUnallocated(vreg_state->AllocateVirtualRegister(), 0);
71}
72
73void UseRegister(Input& input) {
74  input.SetUnallocated(compiler::UnallocatedOperand::MUST_HAVE_REGISTER,
75                       compiler::UnallocatedOperand::USED_AT_START,
76                       GetVirtualRegister(input.node()));
77}
78void UseAny(Input& input) {
79  input.SetUnallocated(
80      compiler::UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT,
81      compiler::UnallocatedOperand::USED_AT_START,
82      GetVirtualRegister(input.node()));
83}
84void UseFixed(Input& input, Register reg) {
85  input.SetUnallocated(compiler::UnallocatedOperand::FIXED_REGISTER, reg.code(),
86                       GetVirtualRegister(input.node()));
87}
88
89// ---
90// Code gen helpers.
91// ---
92
93void PushInput(MaglevCodeGenState* code_gen_state, const Input& input) {
94  // TODO(leszeks): Consider special casing the value. (Toon: could possibly
95  // be done through Input directly?)
96  const compiler::AllocatedOperand& operand =
97      compiler::AllocatedOperand::cast(input.operand());
98
99  if (operand.IsRegister()) {
100    __ Push(operand.GetRegister());
101  } else {
102    DCHECK(operand.IsStackSlot());
103    __ Push(GetStackSlot(operand));
104  }
105}
106
107// ---
108// Deferred code handling.
109// ---
110
111// Base case provides an error.
112template <typename T, typename Enable = void>
113struct CopyForDeferredHelper {
114  template <typename U>
115  struct No_Copy_Helper_Implemented_For_Type;
116  static void Copy(MaglevCompilationUnit* compilation_unit,
117                   No_Copy_Helper_Implemented_For_Type<T>);
118};
119
120// Helper for copies by value.
121template <typename T, typename Enable = void>
122struct CopyForDeferredByValue {
123  static T Copy(MaglevCompilationUnit* compilation_unit, T node) {
124    return node;
125  }
126};
127
128// Node pointers are copied by value.
129template <typename T>
130struct CopyForDeferredHelper<
131    T*, typename std::enable_if<std::is_base_of<NodeBase, T>::value>::type>
132    : public CopyForDeferredByValue<T*> {};
133// Arithmetic values and enums are copied by value.
134template <typename T>
135struct CopyForDeferredHelper<
136    T, typename std::enable_if<std::is_arithmetic<T>::value>::type>
137    : public CopyForDeferredByValue<T> {};
138template <typename T>
139struct CopyForDeferredHelper<
140    T, typename std::enable_if<std::is_enum<T>::value>::type>
141    : public CopyForDeferredByValue<T> {};
142// MaglevCompilationUnits are copied by value.
143template <>
144struct CopyForDeferredHelper<MaglevCompilationUnit*>
145    : public CopyForDeferredByValue<MaglevCompilationUnit*> {};
146// Machine registers are copied by value.
147template <>
148struct CopyForDeferredHelper<Register>
149    : public CopyForDeferredByValue<Register> {};
150// Bytecode offsets are copied by value.
151template <>
152struct CopyForDeferredHelper<BytecodeOffset>
153    : public CopyForDeferredByValue<BytecodeOffset> {};
154
155// InterpreterFrameState is cloned.
156template <>
157struct CopyForDeferredHelper<const InterpreterFrameState*> {
158  static const InterpreterFrameState* Copy(
159      MaglevCompilationUnit* compilation_unit,
160      const InterpreterFrameState* frame_state) {
161    return compilation_unit->zone()->New<InterpreterFrameState>(
162        *compilation_unit, *frame_state);
163  }
164};
165// EagerDeoptInfo pointers are copied by value.
166template <>
167struct CopyForDeferredHelper<EagerDeoptInfo*>
168    : public CopyForDeferredByValue<EagerDeoptInfo*> {};
169
170template <typename T>
171T CopyForDeferred(MaglevCompilationUnit* compilation_unit, T&& value) {
172  return CopyForDeferredHelper<T>::Copy(compilation_unit,
173                                        std::forward<T>(value));
174}
175
176template <typename T>
177T CopyForDeferred(MaglevCompilationUnit* compilation_unit, T& value) {
178  return CopyForDeferredHelper<T>::Copy(compilation_unit, value);
179}
180
181template <typename T>
182T CopyForDeferred(MaglevCompilationUnit* compilation_unit, const T& value) {
183  return CopyForDeferredHelper<T>::Copy(compilation_unit, value);
184}
185
186template <typename Function, typename FunctionPointer = Function>
187struct FunctionArgumentsTupleHelper
188    : FunctionArgumentsTupleHelper<Function,
189                                   decltype(&FunctionPointer::operator())> {};
190
191template <typename T, typename C, typename R, typename... A>
192struct FunctionArgumentsTupleHelper<T, R (C::*)(A...) const> {
193  using FunctionPointer = R (*)(A...);
194  using Tuple = std::tuple<A...>;
195  static constexpr size_t kSize = sizeof...(A);
196};
197
198template <typename T>
199struct StripFirstTwoTupleArgs;
200
201template <typename T1, typename T2, typename... T>
202struct StripFirstTwoTupleArgs<std::tuple<T1, T2, T...>> {
203  using Stripped = std::tuple<T...>;
204};
205
206template <typename Function>
207class DeferredCodeInfoImpl final : public DeferredCodeInfo {
208 public:
209  using FunctionPointer =
210      typename FunctionArgumentsTupleHelper<Function>::FunctionPointer;
211  using Tuple = typename StripFirstTwoTupleArgs<
212      typename FunctionArgumentsTupleHelper<Function>::Tuple>::Stripped;
213  static constexpr size_t kSize = FunctionArgumentsTupleHelper<Function>::kSize;
214
215  template <typename... InArgs>
216  explicit DeferredCodeInfoImpl(MaglevCompilationUnit* compilation_unit,
217                                FunctionPointer function, InArgs&&... args)
218      : function(function),
219        args(CopyForDeferred(compilation_unit, std::forward<InArgs>(args))...) {
220  }
221
222  DeferredCodeInfoImpl(DeferredCodeInfoImpl&&) = delete;
223  DeferredCodeInfoImpl(const DeferredCodeInfoImpl&) = delete;
224
225  void Generate(MaglevCodeGenState* code_gen_state,
226                Label* return_label) override {
227    DoCall(code_gen_state, return_label, std::make_index_sequence<kSize - 2>{});
228  }
229
230 private:
231  template <size_t... I>
232  auto DoCall(MaglevCodeGenState* code_gen_state, Label* return_label,
233              std::index_sequence<I...>) {
234    // TODO(leszeks): This could be replaced with std::apply in C++17.
235    return function(code_gen_state, return_label, std::get<I>(args)...);
236  }
237
238  FunctionPointer function;
239  Tuple args;
240};
241
242template <typename Function, typename... Args>
243void JumpToDeferredIf(Condition cond, MaglevCodeGenState* code_gen_state,
244                      Function&& deferred_code_gen, Args&&... args) {
245  using DeferredCodeInfoT = DeferredCodeInfoImpl<Function>;
246  DeferredCodeInfoT* deferred_code =
247      code_gen_state->compilation_unit()->zone()->New<DeferredCodeInfoT>(
248          code_gen_state->compilation_unit(), deferred_code_gen,
249          std::forward<Args>(args)...);
250
251  code_gen_state->PushDeferredCode(deferred_code);
252  if (FLAG_code_comments) {
253    __ RecordComment("-- Jump to deferred code");
254  }
255  __ j(cond, &deferred_code->deferred_code_label);
256  __ bind(&deferred_code->return_label);
257}
258
259// ---
260// Deopt
261// ---
262
263void RegisterEagerDeopt(MaglevCodeGenState* code_gen_state,
264                        EagerDeoptInfo* deopt_info) {
265  if (deopt_info->deopt_entry_label.is_unused()) {
266    code_gen_state->PushEagerDeopt(deopt_info);
267  }
268}
269
270void EmitEagerDeoptIf(Condition cond, MaglevCodeGenState* code_gen_state,
271                      EagerDeoptInfo* deopt_info) {
272  RegisterEagerDeopt(code_gen_state, deopt_info);
273  __ RecordComment("-- Jump to eager deopt");
274  __ j(cond, &deopt_info->deopt_entry_label);
275}
276
277template <typename NodeT>
278void EmitEagerDeoptIf(Condition cond, MaglevCodeGenState* code_gen_state,
279                      NodeT* node) {
280  STATIC_ASSERT(NodeT::kProperties.can_eager_deopt());
281  EmitEagerDeoptIf(cond, code_gen_state, node->eager_deopt_info());
282}
283
284// ---
285// Print
286// ---
287
288void PrintInputs(std::ostream& os, MaglevGraphLabeller* graph_labeller,
289                 const NodeBase* node) {
290  if (!node->has_inputs()) return;
291
292  os << " [";
293  for (int i = 0; i < node->input_count(); i++) {
294    if (i != 0) os << ", ";
295    graph_labeller->PrintInput(os, node->input(i));
296  }
297  os << "]";
298}
299
300void PrintResult(std::ostream& os, MaglevGraphLabeller* graph_labeller,
301                 const NodeBase* node) {}
302
303void PrintResult(std::ostream& os, MaglevGraphLabeller* graph_labeller,
304                 const ValueNode* node) {
305  os << " → " << node->result().operand();
306  if (node->has_valid_live_range()) {
307    os << ", live range: [" << node->live_range().start << "-"
308       << node->live_range().end << "]";
309  }
310}
311
312void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
313                  const NodeBase* node) {}
314
315void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
316                  const UnconditionalControlNode* node) {
317  os << " b" << graph_labeller->BlockId(node->target());
318}
319
320void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
321                  const ConditionalControlNode* node) {
322  os << " b" << graph_labeller->BlockId(node->if_true()) << " b"
323     << graph_labeller->BlockId(node->if_false());
324}
325
326template <typename NodeT>
327void PrintImpl(std::ostream& os, MaglevGraphLabeller* graph_labeller,
328               const NodeT* node) {
329  os << node->opcode();
330  node->PrintParams(os, graph_labeller);
331  PrintInputs(os, graph_labeller, node);
332  PrintResult(os, graph_labeller, node);
333  PrintTargets(os, graph_labeller, node);
334}
335
336}  // namespace
337
338void NodeBase::Print(std::ostream& os,
339                     MaglevGraphLabeller* graph_labeller) const {
340  switch (opcode()) {
341#define V(Name)         \
342  case Opcode::k##Name: \
343    return PrintImpl(os, graph_labeller, this->Cast<Name>());
344    NODE_BASE_LIST(V)
345#undef V
346  }
347  UNREACHABLE();
348}
349
350DeoptInfo::DeoptInfo(Zone* zone, const MaglevCompilationUnit& compilation_unit,
351                     CheckpointedInterpreterState state)
352    : state(state),
353      input_locations(zone->NewArray<InputLocation>(
354          state.register_frame->size(compilation_unit))) {
355  // Default initialise if we're printing the graph, to avoid printing junk
356  // values.
357  if (FLAG_print_maglev_graph) {
358    for (size_t i = 0; i < state.register_frame->size(compilation_unit); ++i) {
359      new (&input_locations[i]) InputLocation();
360    }
361  }
362}
363
364// ---
365// Nodes
366// ---
367void SmiConstant::AllocateVreg(MaglevVregAllocationState* vreg_state,
368                               const ProcessingState& state) {
369  DefineAsRegister(vreg_state, this);
370}
371void SmiConstant::GenerateCode(MaglevCodeGenState* code_gen_state,
372                               const ProcessingState& state) {
373  __ Move(ToRegister(result()), Immediate(value()));
374}
375void SmiConstant::PrintParams(std::ostream& os,
376                              MaglevGraphLabeller* graph_labeller) const {
377  os << "(" << value() << ")";
378}
379
380void Constant::AllocateVreg(MaglevVregAllocationState* vreg_state,
381                            const ProcessingState& state) {
382  DefineAsRegister(vreg_state, this);
383}
384void Constant::GenerateCode(MaglevCodeGenState* code_gen_state,
385                            const ProcessingState& state) {
386  __ Move(ToRegister(result()), object_.object());
387}
388void Constant::PrintParams(std::ostream& os,
389                           MaglevGraphLabeller* graph_labeller) const {
390  os << "(" << object_ << ")";
391}
392
393void InitialValue::AllocateVreg(MaglevVregAllocationState* vreg_state,
394                                const ProcessingState& state) {
395  // TODO(leszeks): Make this nicer.
396  result().SetUnallocated(compiler::UnallocatedOperand::FIXED_SLOT,
397                          (StandardFrameConstants::kExpressionsOffset -
398                           UnoptimizedFrameConstants::kRegisterFileFromFp) /
399                                  kSystemPointerSize +
400                              source().index(),
401                          vreg_state->AllocateVirtualRegister());
402}
403void InitialValue::GenerateCode(MaglevCodeGenState* code_gen_state,
404                                const ProcessingState& state) {
405  // No-op, the value is already in the appropriate slot.
406}
407void InitialValue::PrintParams(std::ostream& os,
408                               MaglevGraphLabeller* graph_labeller) const {
409  os << "(" << source().ToString() << ")";
410}
411
412void LoadGlobal::AllocateVreg(MaglevVregAllocationState* vreg_state,
413                              const ProcessingState& state) {
414  UseFixed(context(), kContextRegister);
415  DefineAsFixed(vreg_state, this, kReturnRegister0);
416}
417void LoadGlobal::GenerateCode(MaglevCodeGenState* code_gen_state,
418                              const ProcessingState& state) {
419  // TODO(leszeks): Port the nice Sparkplug CallBuiltin helper.
420
421  DCHECK_EQ(ToRegister(context()), kContextRegister);
422
423  // TODO(jgruber): Detect properly.
424  const int ic_kind =
425      static_cast<int>(FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
426
427  __ Move(LoadGlobalNoFeedbackDescriptor::GetRegisterParameter(
428              LoadGlobalNoFeedbackDescriptor::kName),
429          name().object());
430  __ Move(LoadGlobalNoFeedbackDescriptor::GetRegisterParameter(
431              LoadGlobalNoFeedbackDescriptor::kICKind),
432          Immediate(Smi::FromInt(ic_kind)));
433
434  // TODO(jgruber): Implement full LoadGlobal handling.
435  __ CallBuiltin(Builtin::kLoadGlobalIC_NoFeedback);
436}
437void LoadGlobal::PrintParams(std::ostream& os,
438                             MaglevGraphLabeller* graph_labeller) const {
439  os << "(" << name() << ")";
440}
441
442void RegisterInput::AllocateVreg(MaglevVregAllocationState* vreg_state,
443                                 const ProcessingState& state) {
444  DefineAsFixed(vreg_state, this, input());
445}
446void RegisterInput::GenerateCode(MaglevCodeGenState* code_gen_state,
447                                 const ProcessingState& state) {
448  // Nothing to be done, the value is already in the register.
449}
450void RegisterInput::PrintParams(std::ostream& os,
451                                MaglevGraphLabeller* graph_labeller) const {
452  os << "(" << input() << ")";
453}
454
455void RootConstant::AllocateVreg(MaglevVregAllocationState* vreg_state,
456                                const ProcessingState& state) {
457  DefineAsRegister(vreg_state, this);
458}
459void RootConstant::GenerateCode(MaglevCodeGenState* code_gen_state,
460                                const ProcessingState& state) {
461  if (!has_valid_live_range()) return;
462
463  Register reg = ToRegister(result());
464  __ LoadRoot(reg, index());
465}
466void RootConstant::PrintParams(std::ostream& os,
467                               MaglevGraphLabeller* graph_labeller) const {
468  os << "(" << RootsTable::name(index()) << ")";
469}
470
471void CheckMaps::AllocateVreg(MaglevVregAllocationState* vreg_state,
472                             const ProcessingState& state) {
473  UseRegister(actual_map_input());
474  set_temporaries_needed(1);
475}
476void CheckMaps::GenerateCode(MaglevCodeGenState* code_gen_state,
477                             const ProcessingState& state) {
478  Register object = ToRegister(actual_map_input());
479  RegList temps = temporaries();
480  Register map_tmp = temps.PopFirst();
481
482  __ LoadMap(map_tmp, object);
483  __ Cmp(map_tmp, map().object());
484
485  // TODO(leszeks): Encode as a bit on CheckMaps.
486  if (map().is_migration_target()) {
487    JumpToDeferredIf(
488        not_equal, code_gen_state,
489        [](MaglevCodeGenState* code_gen_state, Label* return_label,
490           Register object, CheckMaps* node, EagerDeoptInfo* deopt_info,
491           Register map_tmp) {
492          RegisterEagerDeopt(code_gen_state, deopt_info);
493
494          // If the map is not deprecated, deopt straight away.
495          __ movl(kScratchRegister,
496                  FieldOperand(map_tmp, Map::kBitField3Offset));
497          __ testl(kScratchRegister,
498                   Immediate(Map::Bits3::IsDeprecatedBit::kMask));
499          __ j(zero, &deopt_info->deopt_entry_label);
500
501          // Otherwise, try migrating the object. If the migration returns Smi
502          // zero, then it failed and we should deopt.
503          __ Push(object);
504          __ Move(kContextRegister,
505                  code_gen_state->broker()->target_native_context().object());
506          // TODO(verwaest): We're calling so we need to spill around it.
507          __ CallRuntime(Runtime::kTryMigrateInstance);
508          __ cmpl(kReturnRegister0, Immediate(0));
509          __ j(equal, &deopt_info->deopt_entry_label);
510
511          // The migrated object is returned on success, retry the map check.
512          __ Move(object, kReturnRegister0);
513          __ LoadMap(map_tmp, object);
514          __ Cmp(map_tmp, node->map().object());
515          __ j(equal, return_label);
516          __ jmp(&deopt_info->deopt_entry_label);
517        },
518        object, this, eager_deopt_info(), map_tmp);
519  } else {
520    EmitEagerDeoptIf(not_equal, code_gen_state, this);
521  }
522}
523void CheckMaps::PrintParams(std::ostream& os,
524                            MaglevGraphLabeller* graph_labeller) const {
525  os << "(" << *map().object() << ")";
526}
527
528void LoadField::AllocateVreg(MaglevVregAllocationState* vreg_state,
529                             const ProcessingState& state) {
530  UseRegister(object_input());
531  DefineAsRegister(vreg_state, this);
532}
533void LoadField::GenerateCode(MaglevCodeGenState* code_gen_state,
534                             const ProcessingState& state) {
535  // os << "kField, is in object = "
536  //    << LoadHandler::IsInobjectBits::decode(raw_handler)
537  //    << ", is double = " << LoadHandler::IsDoubleBits::decode(raw_handler)
538  //    << ", field index = " <<
539  //    LoadHandler::FieldIndexBits::decode(raw_handler);
540
541  Register object = ToRegister(object_input());
542  Register res = ToRegister(result());
543  int handler = this->handler();
544
545  if (LoadHandler::IsInobjectBits::decode(handler)) {
546    Operand input_field_operand = FieldOperand(
547        object, LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize);
548    __ DecompressAnyTagged(res, input_field_operand);
549  } else {
550    Operand property_array_operand =
551        FieldOperand(object, JSReceiver::kPropertiesOrHashOffset);
552    __ DecompressAnyTagged(res, property_array_operand);
553
554    __ AssertNotSmi(res);
555
556    Operand input_field_operand = FieldOperand(
557        res, LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize);
558    __ DecompressAnyTagged(res, input_field_operand);
559  }
560
561  if (LoadHandler::IsDoubleBits::decode(handler)) {
562    // TODO(leszeks): Copy out the value, either as a double or a HeapNumber.
563    UNSUPPORTED("LoadField double property");
564  }
565}
566void LoadField::PrintParams(std::ostream& os,
567                            MaglevGraphLabeller* graph_labeller) const {
568  os << "(" << std::hex << handler() << std::dec << ")";
569}
570
571void StoreField::AllocateVreg(MaglevVregAllocationState* vreg_state,
572                              const ProcessingState& state) {
573  UseRegister(object_input());
574  UseRegister(value_input());
575}
576void StoreField::GenerateCode(MaglevCodeGenState* code_gen_state,
577                              const ProcessingState& state) {
578  Register object = ToRegister(object_input());
579  Register value = ToRegister(value_input());
580
581  if (StoreHandler::IsInobjectBits::decode(this->handler())) {
582    Operand operand = FieldOperand(
583        object,
584        StoreHandler::FieldIndexBits::decode(this->handler()) * kTaggedSize);
585    __ StoreTaggedField(operand, value);
586  } else {
587    // TODO(victorgomes): Out-of-object properties.
588    UNSUPPORTED("StoreField out-of-object property");
589  }
590}
591
592void StoreField::PrintParams(std::ostream& os,
593                             MaglevGraphLabeller* graph_labeller) const {
594  os << "(" << std::hex << handler() << std::dec << ")";
595}
596
597void LoadNamedGeneric::AllocateVreg(MaglevVregAllocationState* vreg_state,
598                                    const ProcessingState& state) {
599  using D = LoadWithVectorDescriptor;
600  UseFixed(context(), kContextRegister);
601  UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
602  DefineAsFixed(vreg_state, this, kReturnRegister0);
603}
604void LoadNamedGeneric::GenerateCode(MaglevCodeGenState* code_gen_state,
605                                    const ProcessingState& state) {
606  using D = LoadWithVectorDescriptor;
607  DCHECK_EQ(ToRegister(context()), kContextRegister);
608  DCHECK_EQ(ToRegister(object_input()), D::GetRegisterParameter(D::kReceiver));
609  __ Move(D::GetRegisterParameter(D::kName), name().object());
610  __ Move(D::GetRegisterParameter(D::kSlot),
611          Smi::FromInt(feedback().slot.ToInt()));
612  __ Move(D::GetRegisterParameter(D::kVector), feedback().vector);
613  __ CallBuiltin(Builtin::kLoadIC);
614}
615void LoadNamedGeneric::PrintParams(std::ostream& os,
616                                   MaglevGraphLabeller* graph_labeller) const {
617  os << "(" << name_ << ")";
618}
619
620void GapMove::AllocateVreg(MaglevVregAllocationState* vreg_state,
621                           const ProcessingState& state) {
622  UNREACHABLE();
623}
624void GapMove::GenerateCode(MaglevCodeGenState* code_gen_state,
625                           const ProcessingState& state) {
626  if (source().IsAnyRegister()) {
627    Register source_reg = ToRegister(source());
628    if (target().IsAnyRegister()) {
629      __ movq(ToRegister(target()), source_reg);
630    } else {
631      __ movq(ToMemOperand(target()), source_reg);
632    }
633  } else {
634    MemOperand source_op = ToMemOperand(source());
635    if (target().IsAnyRegister()) {
636      __ movq(ToRegister(target()), source_op);
637    } else {
638      __ movq(kScratchRegister, source_op);
639      __ movq(ToMemOperand(target()), kScratchRegister);
640    }
641  }
642}
643void GapMove::PrintParams(std::ostream& os,
644                          MaglevGraphLabeller* graph_labeller) const {
645  os << "(" << source() << " → " << target() << ")";
646}
647
648namespace {
649
650constexpr Builtin BuiltinFor(Operation operation) {
651  switch (operation) {
652#define CASE(name)         \
653  case Operation::k##name: \
654    return Builtin::k##name##_WithFeedback;
655    OPERATION_LIST(CASE)
656#undef CASE
657  }
658}
659
660}  // namespace
661
662template <class Derived, Operation kOperation>
663void UnaryWithFeedbackNode<Derived, kOperation>::AllocateVreg(
664    MaglevVregAllocationState* vreg_state, const ProcessingState& state) {
665  using D = UnaryOp_WithFeedbackDescriptor;
666  UseFixed(operand_input(), D::GetRegisterParameter(D::kValue));
667  DefineAsFixed(vreg_state, this, kReturnRegister0);
668}
669
670template <class Derived, Operation kOperation>
671void UnaryWithFeedbackNode<Derived, kOperation>::GenerateCode(
672    MaglevCodeGenState* code_gen_state, const ProcessingState& state) {
673  using D = UnaryOp_WithFeedbackDescriptor;
674  DCHECK_EQ(ToRegister(operand_input()), D::GetRegisterParameter(D::kValue));
675  __ Move(kContextRegister, code_gen_state->native_context().object());
676  __ Move(D::GetRegisterParameter(D::kSlot), Immediate(feedback().index()));
677  __ Move(D::GetRegisterParameter(D::kFeedbackVector), feedback().vector);
678  __ CallBuiltin(BuiltinFor(kOperation));
679}
680
681template <class Derived, Operation kOperation>
682void BinaryWithFeedbackNode<Derived, kOperation>::AllocateVreg(
683    MaglevVregAllocationState* vreg_state, const ProcessingState& state) {
684  using D = BinaryOp_WithFeedbackDescriptor;
685  UseFixed(left_input(), D::GetRegisterParameter(D::kLeft));
686  UseFixed(right_input(), D::GetRegisterParameter(D::kRight));
687  DefineAsFixed(vreg_state, this, kReturnRegister0);
688}
689
690template <class Derived, Operation kOperation>
691void BinaryWithFeedbackNode<Derived, kOperation>::GenerateCode(
692    MaglevCodeGenState* code_gen_state, const ProcessingState& state) {
693  using D = BinaryOp_WithFeedbackDescriptor;
694  DCHECK_EQ(ToRegister(left_input()), D::GetRegisterParameter(D::kLeft));
695  DCHECK_EQ(ToRegister(right_input()), D::GetRegisterParameter(D::kRight));
696  __ Move(kContextRegister, code_gen_state->native_context().object());
697  __ Move(D::GetRegisterParameter(D::kSlot), Immediate(feedback().index()));
698  __ Move(D::GetRegisterParameter(D::kFeedbackVector), feedback().vector);
699  __ CallBuiltin(BuiltinFor(kOperation));
700}
701
702#define DEF_OPERATION(Name)                                      \
703  void Name::AllocateVreg(MaglevVregAllocationState* vreg_state, \
704                          const ProcessingState& state) {        \
705    Base::AllocateVreg(vreg_state, state);                       \
706  }                                                              \
707  void Name::GenerateCode(MaglevCodeGenState* code_gen_state,    \
708                          const ProcessingState& state) {        \
709    Base::GenerateCode(code_gen_state, state);                   \
710  }
711GENERIC_OPERATIONS_NODE_LIST(DEF_OPERATION)
712#undef DEF_OPERATION
713
714void CheckedSmiUntag::AllocateVreg(MaglevVregAllocationState* vreg_state,
715                                   const ProcessingState& state) {
716  UseRegister(input());
717  DefineSameAsFirst(vreg_state, this);
718}
719
720void CheckedSmiUntag::GenerateCode(MaglevCodeGenState* code_gen_state,
721                                   const ProcessingState& state) {
722  Register value = ToRegister(input());
723  // TODO(leszeks): Consider optimizing away this test and using the carry bit
724  // of the `sarl` for cases where the deopt uses the value from a different
725  // register.
726  __ testb(value, Immediate(1));
727  EmitEagerDeoptIf(not_zero, code_gen_state, this);
728  __ sarl(value, Immediate(1));
729}
730
731void CheckedSmiTag::AllocateVreg(MaglevVregAllocationState* vreg_state,
732                                 const ProcessingState& state) {
733  UseRegister(input());
734  DefineSameAsFirst(vreg_state, this);
735}
736
737void CheckedSmiTag::GenerateCode(MaglevCodeGenState* code_gen_state,
738                                 const ProcessingState& state) {
739  Register reg = ToRegister(input());
740  __ addl(reg, reg);
741  EmitEagerDeoptIf(overflow, code_gen_state, this);
742}
743
744void Int32Constant::AllocateVreg(MaglevVregAllocationState* vreg_state,
745                                 const ProcessingState& state) {
746  DefineAsRegister(vreg_state, this);
747}
748void Int32Constant::GenerateCode(MaglevCodeGenState* code_gen_state,
749                                 const ProcessingState& state) {
750  __ Move(ToRegister(result()), Immediate(value()));
751}
752void Int32Constant::PrintParams(std::ostream& os,
753                                MaglevGraphLabeller* graph_labeller) const {
754  os << "(" << value() << ")";
755}
756
757void Int32AddWithOverflow::AllocateVreg(MaglevVregAllocationState* vreg_state,
758                                        const ProcessingState& state) {
759  UseRegister(left_input());
760  UseRegister(right_input());
761  DefineSameAsFirst(vreg_state, this);
762}
763
764void Int32AddWithOverflow::GenerateCode(MaglevCodeGenState* code_gen_state,
765                                        const ProcessingState& state) {
766  Register left = ToRegister(left_input());
767  Register right = ToRegister(right_input());
768  __ addl(left, right);
769  EmitEagerDeoptIf(overflow, code_gen_state, this);
770}
771
772void Phi::AllocateVreg(MaglevVregAllocationState* vreg_state,
773                       const ProcessingState& state) {
774  // Phi inputs are processed in the post-process, once loop phis' inputs'
775  // v-regs are allocated.
776  result().SetUnallocated(
777      compiler::UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT,
778      vreg_state->AllocateVirtualRegister());
779}
780// TODO(verwaest): Remove after switching the register allocator.
781void Phi::AllocateVregInPostProcess(MaglevVregAllocationState* vreg_state) {
782  for (Input& input : *this) {
783    UseAny(input);
784  }
785}
786void Phi::GenerateCode(MaglevCodeGenState* code_gen_state,
787                       const ProcessingState& state) {}
788void Phi::PrintParams(std::ostream& os,
789                      MaglevGraphLabeller* graph_labeller) const {
790  os << "(" << owner().ToString() << ")";
791}
792
793void Call::AllocateVreg(MaglevVregAllocationState* vreg_state,
794                        const ProcessingState& state) {
795  UseFixed(function(), CallTrampolineDescriptor::GetRegisterParameter(
796                           CallTrampolineDescriptor::kFunction));
797  UseFixed(context(), kContextRegister);
798  for (int i = 0; i < num_args(); i++) {
799    UseAny(arg(i));
800  }
801  DefineAsFixed(vreg_state, this, kReturnRegister0);
802}
803void Call::GenerateCode(MaglevCodeGenState* code_gen_state,
804                        const ProcessingState& state) {
805  // TODO(leszeks): Port the nice Sparkplug CallBuiltin helper.
806
807  DCHECK_EQ(ToRegister(function()),
808            CallTrampolineDescriptor::GetRegisterParameter(
809                CallTrampolineDescriptor::kFunction));
810  DCHECK_EQ(ToRegister(context()), kContextRegister);
811
812  for (int i = num_args() - 1; i >= 0; --i) {
813    PushInput(code_gen_state, arg(i));
814  }
815
816  uint32_t arg_count = num_args();
817  __ Move(CallTrampolineDescriptor::GetRegisterParameter(
818              CallTrampolineDescriptor::kActualArgumentsCount),
819          Immediate(arg_count));
820
821  // TODO(leszeks): This doesn't collect feedback yet, either pass in the
822  // feedback vector by Handle.
823  switch (receiver_mode_) {
824    case ConvertReceiverMode::kNullOrUndefined:
825      __ CallBuiltin(Builtin::kCall_ReceiverIsNullOrUndefined);
826      break;
827    case ConvertReceiverMode::kNotNullOrUndefined:
828      __ CallBuiltin(Builtin::kCall_ReceiverIsNotNullOrUndefined);
829      break;
830    case ConvertReceiverMode::kAny:
831      __ CallBuiltin(Builtin::kCall_ReceiverIsAny);
832      break;
833  }
834
835  lazy_deopt_info()->deopting_call_return_pc = __ pc_offset_for_safepoint();
836  code_gen_state->PushLazyDeopt(lazy_deopt_info());
837
838  SafepointTableBuilder::Safepoint safepoint =
839      code_gen_state->safepoint_table_builder()->DefineSafepoint(
840          code_gen_state->masm());
841  code_gen_state->DefineSafepointStackSlots(safepoint);
842}
843
844// ---
845// Control nodes
846// ---
847void Return::AllocateVreg(MaglevVregAllocationState* vreg_state,
848                          const ProcessingState& state) {
849  UseFixed(value_input(), kReturnRegister0);
850}
851void Return::GenerateCode(MaglevCodeGenState* code_gen_state,
852                          const ProcessingState& state) {
853  DCHECK_EQ(ToRegister(value_input()), kReturnRegister0);
854
855  // We're not going to continue execution, so we can use an arbitrary register
856  // here instead of relying on temporaries from the register allocator.
857  Register actual_params_size = r8;
858
859  // Compute the size of the actual parameters + receiver (in bytes).
860  // TODO(leszeks): Consider making this an input into Return to re-use the
861  // incoming argc's register (if it's still valid).
862  __ movq(actual_params_size,
863          MemOperand(rbp, StandardFrameConstants::kArgCOffset));
864
865  // Leave the frame.
866  // TODO(leszeks): Add a new frame maker for Maglev.
867  __ LeaveFrame(StackFrame::BASELINE);
868
869  // If actual is bigger than formal, then we should use it to free up the stack
870  // arguments.
871  Label drop_dynamic_arg_size;
872  __ cmpq(actual_params_size, Immediate(code_gen_state->parameter_count()));
873  __ j(greater, &drop_dynamic_arg_size);
874
875  // Drop receiver + arguments according to static formal arguments size.
876  __ Ret(code_gen_state->parameter_count() * kSystemPointerSize,
877         kScratchRegister);
878
879  __ bind(&drop_dynamic_arg_size);
880  // Drop receiver + arguments according to dynamic arguments size.
881  __ DropArguments(actual_params_size, r9, TurboAssembler::kCountIsInteger,
882                   TurboAssembler::kCountIncludesReceiver);
883  __ Ret();
884}
885
886void Deopt::AllocateVreg(MaglevVregAllocationState* vreg_state,
887                         const ProcessingState& state) {}
888void Deopt::GenerateCode(MaglevCodeGenState* code_gen_state,
889                         const ProcessingState& state) {
890  EmitEagerDeoptIf(always, code_gen_state, this);
891}
892
893void Jump::AllocateVreg(MaglevVregAllocationState* vreg_state,
894                        const ProcessingState& state) {}
895void Jump::GenerateCode(MaglevCodeGenState* code_gen_state,
896                        const ProcessingState& state) {
897  // Avoid emitting a jump to the next block.
898  if (target() != state.next_block()) {
899    __ jmp(target()->label());
900  }
901}
902
903void JumpLoop::AllocateVreg(MaglevVregAllocationState* vreg_state,
904                            const ProcessingState& state) {}
905void JumpLoop::GenerateCode(MaglevCodeGenState* code_gen_state,
906                            const ProcessingState& state) {
907  __ jmp(target()->label());
908}
909
910void BranchIfTrue::AllocateVreg(MaglevVregAllocationState* vreg_state,
911                                const ProcessingState& state) {
912  UseRegister(condition_input());
913}
914void BranchIfTrue::GenerateCode(MaglevCodeGenState* code_gen_state,
915                                const ProcessingState& state) {
916  Register value = ToRegister(condition_input());
917
918  auto* next_block = state.next_block();
919
920  // We don't have any branch probability information, so try to jump
921  // over whatever the next block emitted is.
922  if (if_false() == next_block) {
923    // Jump over the false block if true, otherwise fall through into it.
924    __ JumpIfRoot(value, RootIndex::kTrueValue, if_true()->label());
925  } else {
926    // Jump to the false block if true.
927    __ JumpIfNotRoot(value, RootIndex::kTrueValue, if_false()->label());
928    // Jump to the true block if it's not the next block.
929    if (if_true() != next_block) {
930      __ jmp(if_true()->label());
931    }
932  }
933}
934
935void BranchIfCompare::AllocateVreg(MaglevVregAllocationState* vreg_state,
936                                   const ProcessingState& state) {}
937void BranchIfCompare::GenerateCode(MaglevCodeGenState* code_gen_state,
938                                   const ProcessingState& state) {
939  USE(operation_);
940  UNREACHABLE();
941}
942
943void BranchIfToBooleanTrue::AllocateVreg(MaglevVregAllocationState* vreg_state,
944                                         const ProcessingState& state) {
945  UseFixed(condition_input(),
946           ToBooleanForBaselineJumpDescriptor::GetRegisterParameter(0));
947}
948void BranchIfToBooleanTrue::GenerateCode(MaglevCodeGenState* code_gen_state,
949                                         const ProcessingState& state) {
950  DCHECK_EQ(ToRegister(condition_input()),
951            ToBooleanForBaselineJumpDescriptor::GetRegisterParameter(0));
952
953  // ToBooleanForBaselineJump returns the ToBoolean value into return reg 1, and
954  // the original value into kInterpreterAccumulatorRegister, so we don't have
955  // to worry about it getting clobbered.
956  __ CallBuiltin(Builtin::kToBooleanForBaselineJump);
957  __ SmiCompare(kReturnRegister1, Smi::zero());
958
959  auto* next_block = state.next_block();
960
961  // We don't have any branch probability information, so try to jump
962  // over whatever the next block emitted is.
963  if (if_false() == next_block) {
964    // Jump over the false block if non zero, otherwise fall through into it.
965    __ j(not_equal, if_true()->label());
966  } else {
967    // Jump to the false block if zero.
968    __ j(equal, if_false()->label());
969    // Fall through or jump to the true block.
970    if (if_true() != next_block) {
971      __ jmp(if_true()->label());
972    }
973  }
974}
975
976}  // namespace maglev
977}  // namespace internal
978}  // namespace v8
979