1// Copyright 2021 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#ifndef V8_CODEGEN_INTERFACE_DESCRIPTORS_INL_H_
6#define V8_CODEGEN_INTERFACE_DESCRIPTORS_INL_H_
7
8#include <utility>
9
10#include "src/base/logging.h"
11#include "src/codegen/interface-descriptors.h"
12#include "src/codegen/register.h"
13
14#if V8_TARGET_ARCH_X64
15#include "src/codegen/x64/interface-descriptors-x64-inl.h"
16#elif V8_TARGET_ARCH_ARM64
17#include "src/codegen/arm64/interface-descriptors-arm64-inl.h"
18#elif V8_TARGET_ARCH_IA32
19#include "src/codegen/ia32/interface-descriptors-ia32-inl.h"
20#elif V8_TARGET_ARCH_ARM
21#include "src/codegen/arm/interface-descriptors-arm-inl.h"
22#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
23#include "src/codegen/ppc/interface-descriptors-ppc-inl.h"
24#elif V8_TARGET_ARCH_S390
25#include "src/codegen/s390/interface-descriptors-s390-inl.h"
26#elif V8_TARGET_ARCH_MIPS64
27#include "src/codegen/mips64/interface-descriptors-mips64-inl.h"
28#elif V8_TARGET_ARCH_MIPS
29#include "src/codegen/mips/interface-descriptors-mips-inl.h"
30#elif V8_TARGET_ARCH_LOONG64
31#include "src/codegen/loong64/interface-descriptors-loong64-inl.h"
32#elif V8_TARGET_ARCH_RISCV64
33#include "src/codegen/riscv64/interface-descriptors-riscv64-inl.h"
34#else
35#error Unsupported target architecture.
36#endif
37
38namespace v8 {
39namespace internal {
40
41// static
42constexpr std::array<Register, kJSBuiltinRegisterParams>
43CallInterfaceDescriptor::DefaultJSRegisterArray() {
44  return RegisterArray(
45      kJavaScriptCallTargetRegister, kJavaScriptCallNewTargetRegister,
46      kJavaScriptCallArgCountRegister, kJavaScriptCallExtraArg1Register);
47}
48
49// static
50template <typename DerivedDescriptor>
51constexpr auto StaticCallInterfaceDescriptor<DerivedDescriptor>::registers() {
52  return CallInterfaceDescriptor::DefaultRegisterArray();
53}
54
55// static
56template <typename DerivedDescriptor>
57constexpr auto StaticJSCallInterfaceDescriptor<DerivedDescriptor>::registers() {
58  return CallInterfaceDescriptor::DefaultJSRegisterArray();
59}
60
61template <typename DerivedDescriptor>
62void StaticCallInterfaceDescriptor<DerivedDescriptor>::Initialize(
63    CallInterfaceDescriptorData* data) {
64  // Static local copy of the Registers array, for platform-specific
65  // initialization
66  static auto registers = DerivedDescriptor::registers();
67
68  // The passed pointer should be a modifiable pointer to our own data.
69  DCHECK_EQ(data, this->data());
70  DCHECK(!data->IsInitialized());
71
72  if (DerivedDescriptor::kRestrictAllocatableRegisters) {
73    data->RestrictAllocatableRegisters(registers.data(), registers.size());
74  } else {
75    DCHECK(!DerivedDescriptor::kCalleeSaveRegisters);
76  }
77
78  data->InitializeRegisters(
79      DerivedDescriptor::flags(), DerivedDescriptor::kReturnCount,
80      DerivedDescriptor::GetParameterCount(),
81      DerivedDescriptor::kStackArgumentOrder,
82      DerivedDescriptor::GetRegisterParameterCount(), registers.data());
83
84  // InitializeTypes is customizable by the DerivedDescriptor subclass.
85  DerivedDescriptor::InitializeTypes(data);
86
87  DCHECK(data->IsInitialized());
88  DCHECK(this->CheckFloatingPointParameters(data));
89#if DEBUG
90  DerivedDescriptor::Verify(data);
91#endif
92}
93// static
94template <typename DerivedDescriptor>
95constexpr int
96StaticCallInterfaceDescriptor<DerivedDescriptor>::GetReturnCount() {
97  static_assert(
98      DerivedDescriptor::kReturnCount >= 0,
99      "DerivedDescriptor subclass should override return count with a value "
100      "that is greater than 0");
101
102  return DerivedDescriptor::kReturnCount;
103}
104
105// static
106template <typename DerivedDescriptor>
107constexpr int
108StaticCallInterfaceDescriptor<DerivedDescriptor>::GetParameterCount() {
109  static_assert(
110      DerivedDescriptor::kParameterCount >= 0,
111      "DerivedDescriptor subclass should override parameter count with a "
112      "value that is greater than 0");
113
114  return DerivedDescriptor::kParameterCount;
115}
116
117namespace detail {
118
119// Helper trait for statically checking if a type is a std::array<Register,N>.
120template <typename T>
121struct IsRegisterArray : public std::false_type {};
122template <size_t N>
123struct IsRegisterArray<std::array<Register, N>> : public std::true_type {};
124template <>
125struct IsRegisterArray<EmptyRegisterArray> : public std::true_type {};
126
127// Helper for finding the index of the first invalid register in a register
128// array.
129template <size_t N, size_t Index>
130struct FirstInvalidRegisterHelper {
131  static constexpr int Call(std::array<Register, N> regs) {
132    if (!std::get<Index>(regs).is_valid()) {
133      // All registers after the first invalid one have to also be invalid (this
134      // DCHECK will be checked recursively).
135      DCHECK_EQ((FirstInvalidRegisterHelper<N, Index + 1>::Call(regs)),
136                Index + 1);
137      return Index;
138    }
139    return FirstInvalidRegisterHelper<N, Index + 1>::Call(regs);
140  }
141};
142template <size_t N>
143struct FirstInvalidRegisterHelper<N, N> {
144  static constexpr int Call(std::array<Register, N> regs) { return N; }
145};
146template <size_t N, size_t Index = 0>
147constexpr size_t FirstInvalidRegister(std::array<Register, N> regs) {
148  return FirstInvalidRegisterHelper<N, 0>::Call(regs);
149}
150constexpr size_t FirstInvalidRegister(EmptyRegisterArray regs) { return 0; }
151
152}  // namespace detail
153
154// static
155template <typename DerivedDescriptor>
156constexpr int
157StaticCallInterfaceDescriptor<DerivedDescriptor>::GetRegisterParameterCount() {
158  static_assert(
159      detail::IsRegisterArray<decltype(DerivedDescriptor::registers())>::value,
160      "DerivedDescriptor subclass should define a registers() function "
161      "returning a std::array<Register>");
162
163  // The register parameter count is the minimum of:
164  //   1. The number of named parameters in the descriptor, and
165  //   2. The number of valid registers the descriptor provides with its
166  //      registers() function, e.g. for {rax, rbx, no_reg} this number is 2.
167  //   3. The maximum number of register parameters allowed (
168  //      kMaxBuiltinRegisterParams for most builtins,
169  //      kMaxTFSBuiltinRegisterParams for TFS builtins, customizable by the
170  //      subclass otherwise).
171  return std::min<int>({DerivedDescriptor::GetParameterCount(),
172                        static_cast<int>(detail::FirstInvalidRegister(
173                            DerivedDescriptor::registers())),
174                        DerivedDescriptor::kMaxRegisterParams});
175}
176
177// static
178template <typename DerivedDescriptor>
179constexpr int
180StaticCallInterfaceDescriptor<DerivedDescriptor>::GetStackParameterCount() {
181  return DerivedDescriptor::GetParameterCount() -
182         DerivedDescriptor::GetRegisterParameterCount();
183}
184
185// static
186template <typename DerivedDescriptor>
187constexpr Register
188StaticCallInterfaceDescriptor<DerivedDescriptor>::GetRegisterParameter(int i) {
189  return DerivedDescriptor::registers()[i];
190}
191
192// static
193constexpr Register FastNewObjectDescriptor::TargetRegister() {
194  return kJSFunctionRegister;
195}
196
197// static
198constexpr Register FastNewObjectDescriptor::NewTargetRegister() {
199  return kJavaScriptCallNewTargetRegister;
200}
201
202// static
203constexpr Register WriteBarrierDescriptor::ObjectRegister() {
204  return std::get<kObject>(registers());
205}
206// static
207constexpr Register WriteBarrierDescriptor::SlotAddressRegister() {
208  return std::get<kSlotAddress>(registers());
209}
210
211// static
212constexpr Register WriteBarrierDescriptor::ValueRegister() {
213  return std::get<kSlotAddress + 1>(registers());
214}
215
216// static
217constexpr RegList WriteBarrierDescriptor::ComputeSavedRegisters(
218    Register object, Register slot_address) {
219  DCHECK(!AreAliased(object, slot_address));
220  RegList saved_registers;
221#if V8_TARGET_ARCH_X64
222  // Only push clobbered registers.
223  if (object != ObjectRegister()) saved_registers.set(ObjectRegister());
224  if (slot_address != no_reg && slot_address != SlotAddressRegister()) {
225    saved_registers.set(SlotAddressRegister());
226  }
227#elif V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_LOONG64 || \
228    V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_MIPS
229  if (object != ObjectRegister()) saved_registers.set(ObjectRegister());
230  // The slot address is always clobbered.
231  saved_registers.set(SlotAddressRegister());
232#else
233  // TODO(cbruni): Enable callee-saved registers for other platforms.
234  // This is a temporary workaround to prepare code for callee-saved registers.
235  constexpr auto allocated_registers = registers();
236  for (size_t i = 0; i < allocated_registers.size(); ++i) {
237    saved_registers.set(allocated_registers[i]);
238  }
239#endif
240  return saved_registers;
241}
242
243// static
244constexpr Register ApiGetterDescriptor::ReceiverRegister() {
245  return LoadDescriptor::ReceiverRegister();
246}
247
248// static
249constexpr Register LoadGlobalNoFeedbackDescriptor::ICKindRegister() {
250  return LoadDescriptor::SlotRegister();
251}
252
253// static
254constexpr Register LoadNoFeedbackDescriptor::ICKindRegister() {
255  return LoadGlobalNoFeedbackDescriptor::ICKindRegister();
256}
257
258#if V8_TARGET_ARCH_IA32
259// On ia32, LoadWithVectorDescriptor passes vector on the stack and thus we
260// need to choose a new register here.
261// static
262constexpr Register LoadGlobalWithVectorDescriptor::VectorRegister() {
263  STATIC_ASSERT(!LoadWithVectorDescriptor::VectorRegister().is_valid());
264  return LoadDescriptor::ReceiverRegister();
265}
266#else
267// static
268constexpr Register LoadGlobalWithVectorDescriptor::VectorRegister() {
269  return LoadWithVectorDescriptor::VectorRegister();
270}
271#endif
272
273// static
274constexpr auto LoadDescriptor::registers() {
275  return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
276}
277
278// static
279constexpr auto LoadBaselineDescriptor::registers() {
280  return LoadDescriptor::registers();
281}
282
283// static
284constexpr auto LoadGlobalDescriptor::registers() {
285  return RegisterArray(LoadDescriptor::NameRegister(),
286                       LoadDescriptor::SlotRegister());
287}
288
289// static
290constexpr auto LoadGlobalBaselineDescriptor::registers() {
291  return LoadGlobalDescriptor::registers();
292}
293
294// static
295constexpr auto StoreDescriptor::registers() {
296  return RegisterArray(ReceiverRegister(), NameRegister(), ValueRegister(),
297                       SlotRegister());
298}
299
300// static
301constexpr auto StoreBaselineDescriptor::registers() {
302  return StoreDescriptor::registers();
303}
304
305// static
306constexpr auto StoreGlobalDescriptor::registers() {
307  return RegisterArray(StoreDescriptor::NameRegister(),
308                       StoreDescriptor::ValueRegister(),
309                       StoreDescriptor::SlotRegister());
310}
311
312// static
313constexpr auto StoreGlobalBaselineDescriptor::registers() {
314  return StoreGlobalDescriptor::registers();
315}
316
317// static
318constexpr auto LoadWithReceiverBaselineDescriptor::registers() {
319  return RegisterArray(
320      LoadDescriptor::ReceiverRegister(),
321      LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister(),
322      LoadDescriptor::NameRegister(), LoadDescriptor::SlotRegister());
323}
324
325// static
326constexpr auto BaselineOutOfLinePrologueDescriptor::registers() {
327  // TODO(v8:11421): Implement on other platforms.
328#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM ||       \
329    V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390 ||      \
330    V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_MIPS || \
331    V8_TARGET_ARCH_LOONG64
332  return RegisterArray(
333      kContextRegister, kJSFunctionRegister, kJavaScriptCallArgCountRegister,
334      kJavaScriptCallExtraArg1Register, kJavaScriptCallNewTargetRegister,
335      kInterpreterBytecodeArrayRegister);
336#elif V8_TARGET_ARCH_IA32
337  STATIC_ASSERT(kJSFunctionRegister == kInterpreterBytecodeArrayRegister);
338  return RegisterArray(
339      kContextRegister, kJSFunctionRegister, kJavaScriptCallArgCountRegister,
340      kJavaScriptCallExtraArg1Register, kJavaScriptCallNewTargetRegister);
341#else
342  return DefaultRegisterArray();
343#endif
344}
345
346// static
347constexpr auto BaselineLeaveFrameDescriptor::registers() {
348  // TODO(v8:11421): Implement on other platforms.
349#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 ||      \
350    V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 ||       \
351    V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64 || \
352    V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_LOONG64
353  return RegisterArray(ParamsSizeRegister(), WeightRegister());
354#else
355  return DefaultRegisterArray();
356#endif
357}
358
359// static
360constexpr auto VoidDescriptor::registers() { return RegisterArray(); }
361
362// static
363constexpr auto AllocateDescriptor::registers() {
364  return RegisterArray(kAllocateSizeRegister);
365}
366
367// static
368constexpr auto CEntry1ArgvOnStackDescriptor::registers() {
369  return RegisterArray(kRuntimeCallArgCountRegister,
370                       kRuntimeCallFunctionRegister);
371}
372
373// static
374constexpr auto InterpreterCEntry1Descriptor::registers() {
375  return RegisterArray(kRuntimeCallArgCountRegister, kRuntimeCallArgvRegister,
376                       kRuntimeCallFunctionRegister);
377}
378
379// static
380constexpr auto InterpreterCEntry2Descriptor::registers() {
381  return RegisterArray(kRuntimeCallArgCountRegister, kRuntimeCallArgvRegister,
382                       kRuntimeCallFunctionRegister);
383}
384
385// static
386constexpr auto FastNewObjectDescriptor::registers() {
387  return RegisterArray(TargetRegister(), NewTargetRegister());
388}
389
390// static
391constexpr auto LoadNoFeedbackDescriptor::registers() {
392  return RegisterArray(LoadDescriptor::ReceiverRegister(),
393                       LoadDescriptor::NameRegister(), ICKindRegister());
394}
395
396// static
397constexpr auto LoadGlobalNoFeedbackDescriptor::registers() {
398  return RegisterArray(LoadDescriptor::NameRegister(), ICKindRegister());
399}
400
401// static
402constexpr auto LoadGlobalWithVectorDescriptor::registers() {
403  return RegisterArray(LoadDescriptor::NameRegister(),
404                       LoadDescriptor::SlotRegister(), VectorRegister());
405}
406
407// static
408constexpr auto LoadWithReceiverAndVectorDescriptor::registers() {
409  return RegisterArray(
410      LoadDescriptor::ReceiverRegister(), LookupStartObjectRegister(),
411      LoadDescriptor::NameRegister(), LoadDescriptor::SlotRegister(),
412      LoadWithVectorDescriptor::VectorRegister());
413}
414
415// static
416constexpr auto StoreGlobalWithVectorDescriptor::registers() {
417  return RegisterArray(StoreDescriptor::NameRegister(),
418                       StoreDescriptor::ValueRegister(),
419                       StoreDescriptor::SlotRegister(),
420                       StoreWithVectorDescriptor::VectorRegister());
421}
422
423// static
424constexpr auto StoreTransitionDescriptor::registers() {
425  return RegisterArray(StoreDescriptor::ReceiverRegister(),
426                       StoreDescriptor::NameRegister(), MapRegister(),
427                       StoreDescriptor::ValueRegister(),
428                       StoreDescriptor::SlotRegister(),
429                       StoreWithVectorDescriptor::VectorRegister());
430}
431
432// static
433constexpr auto TypeConversionDescriptor::registers() {
434  return RegisterArray(ArgumentRegister());
435}
436
437// static
438constexpr auto TypeConversionNoContextDescriptor::registers() {
439  return RegisterArray(TypeConversionDescriptor::ArgumentRegister());
440}
441
442// static
443constexpr auto SingleParameterOnStackDescriptor::registers() {
444  return RegisterArray();
445}
446
447// static
448constexpr auto AsyncFunctionStackParameterDescriptor::registers() {
449  return RegisterArray();
450}
451
452// static
453constexpr auto GetIteratorStackParameterDescriptor::registers() {
454  return RegisterArray();
455}
456
457// static
458constexpr auto LoadWithVectorDescriptor::registers() {
459  return RegisterArray(LoadDescriptor::ReceiverRegister(),
460                       LoadDescriptor::NameRegister(),
461                       LoadDescriptor::SlotRegister(), VectorRegister());
462}
463
464// static
465constexpr auto KeyedLoadBaselineDescriptor::registers() {
466  return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
467}
468
469// static
470constexpr auto KeyedLoadDescriptor::registers() {
471  return KeyedLoadBaselineDescriptor::registers();
472}
473
474// static
475constexpr auto KeyedLoadWithVectorDescriptor::registers() {
476  return RegisterArray(KeyedLoadBaselineDescriptor::ReceiverRegister(),
477                       KeyedLoadBaselineDescriptor::NameRegister(),
478                       KeyedLoadBaselineDescriptor::SlotRegister(),
479                       VectorRegister());
480}
481
482// static
483constexpr auto KeyedHasICBaselineDescriptor::registers() {
484  return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
485}
486
487// static
488constexpr auto KeyedHasICWithVectorDescriptor::registers() {
489  return RegisterArray(KeyedHasICBaselineDescriptor::ReceiverRegister(),
490                       KeyedHasICBaselineDescriptor::NameRegister(),
491                       KeyedHasICBaselineDescriptor::SlotRegister(),
492                       VectorRegister());
493}
494
495// static
496constexpr auto StoreWithVectorDescriptor::registers() {
497  return RegisterArray(StoreDescriptor::ReceiverRegister(),
498                       StoreDescriptor::NameRegister(),
499                       StoreDescriptor::ValueRegister(),
500                       StoreDescriptor::SlotRegister(), VectorRegister());
501}
502
503// static
504constexpr auto ApiGetterDescriptor::registers() {
505  return RegisterArray(ReceiverRegister(), HolderRegister(),
506                       CallbackRegister());
507}
508
509// static
510constexpr auto ContextOnlyDescriptor::registers() { return RegisterArray(); }
511
512// static
513constexpr auto NoContextDescriptor::registers() { return RegisterArray(); }
514
515// static
516constexpr auto GrowArrayElementsDescriptor::registers() {
517  return RegisterArray(ObjectRegister(), KeyRegister());
518}
519
520// static
521constexpr auto ArrayNArgumentsConstructorDescriptor::registers() {
522  // Keep the arguments on the same registers as they were in
523  // ArrayConstructorDescriptor to avoid unnecessary register moves.
524  // kFunction, kAllocationSite, kActualArgumentsCount
525  return RegisterArray(kJavaScriptCallTargetRegister,
526                       kJavaScriptCallExtraArg1Register,
527                       kJavaScriptCallArgCountRegister);
528}
529
530// static
531constexpr auto ArrayNoArgumentConstructorDescriptor::registers() {
532  // This descriptor must use the same set of registers as the
533  // ArrayNArgumentsConstructorDescriptor.
534  return ArrayNArgumentsConstructorDescriptor::registers();
535}
536
537// static
538constexpr auto ArraySingleArgumentConstructorDescriptor::registers() {
539  // This descriptor must use the same set of registers as the
540  // ArrayNArgumentsConstructorDescriptor.
541  return ArrayNArgumentsConstructorDescriptor::registers();
542}
543
544// static
545// static
546constexpr Register RunMicrotasksDescriptor::MicrotaskQueueRegister() {
547  return GetRegisterParameter(0);
548}
549
550#define DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER(Name, DescriptorName) \
551  template <>                                                         \
552  struct CallInterfaceDescriptorFor<Builtin::k##Name> {               \
553    using type = DescriptorName##Descriptor;                          \
554  };
555BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN,
556             /*TFC*/ DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER, IGNORE_BUILTIN,
557             /*TFH*/ DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER, IGNORE_BUILTIN,
558             /*ASM*/ DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER)
559#undef DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER
560#define DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER(Name, ...) \
561  template <>                                              \
562  struct CallInterfaceDescriptorFor<Builtin::k##Name> {    \
563    using type = Name##Descriptor;                         \
564  };
565BUILTIN_LIST_TFS(DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER)
566#undef DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER
567
568}  // namespace internal
569}  // namespace v8
570
571#endif  // V8_CODEGEN_INTERFACE_DESCRIPTORS_INL_H_
572