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-arch.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 
38 namespace v8 {
39 namespace internal {
40 
41 // static
42 constexpr std::array<Register, kJSBuiltinRegisterParams>
DefaultJSRegisterArray()43 CallInterfaceDescriptor::DefaultJSRegisterArray() {
44   return RegisterArray(
45       kJavaScriptCallTargetRegister, kJavaScriptCallNewTargetRegister,
46       kJavaScriptCallArgCountRegister, kJavaScriptCallExtraArg1Register);
47 }
48 
49 // static
50 template <typename DerivedDescriptor>
registers()51 constexpr auto StaticCallInterfaceDescriptor<DerivedDescriptor>::registers() {
52   return CallInterfaceDescriptor::DefaultRegisterArray();
53 }
54 
55 // static
56 template <typename DerivedDescriptor>
registers()57 constexpr auto StaticJSCallInterfaceDescriptor<DerivedDescriptor>::registers() {
58   return CallInterfaceDescriptor::DefaultJSRegisterArray();
59 }
60 
61 template <typename DerivedDescriptor>
Initialize(CallInterfaceDescriptorData * data)62 void 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
94 template <typename DerivedDescriptor>
95 constexpr int
GetReturnCount()96 StaticCallInterfaceDescriptor<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
106 template <typename DerivedDescriptor>
107 constexpr int
GetParameterCount()108 StaticCallInterfaceDescriptor<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 
117 namespace detail {
118 
119 // Helper trait for statically checking if a type is a std::array<Register,N>.
120 template <typename T>
121 struct IsRegisterArray : public std::false_type {};
122 template <size_t N>
123 struct IsRegisterArray<std::array<Register, N>> : public std::true_type {};
124 template <>
125 struct IsRegisterArray<EmptyRegisterArray> : public std::true_type {};
126 
127 // Helper for finding the index of the first invalid register in a register
128 // array.
129 template <size_t N, size_t Index>
130 struct 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 };
142 template <size_t N>
143 struct FirstInvalidRegisterHelper<N, N> {
144   static constexpr int Call(std::array<Register, N> regs) { return N; }
145 };
146 template <size_t N, size_t Index = 0>
147 constexpr size_t FirstInvalidRegister(std::array<Register, N> regs) {
148   return FirstInvalidRegisterHelper<N, 0>::Call(regs);
149 }
150 constexpr size_t FirstInvalidRegister(EmptyRegisterArray regs) { return 0; }
151 
152 }  // namespace detail
153 
154 // static
155 template <typename DerivedDescriptor>
156 constexpr int
157 StaticCallInterfaceDescriptor<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
178 template <typename DerivedDescriptor>
179 constexpr int
180 StaticCallInterfaceDescriptor<DerivedDescriptor>::GetStackParameterCount() {
181   return DerivedDescriptor::GetParameterCount() -
182          DerivedDescriptor::GetRegisterParameterCount();
183 }
184 
185 // static
186 template <typename DerivedDescriptor>
187 constexpr Register
188 StaticCallInterfaceDescriptor<DerivedDescriptor>::GetRegisterParameter(int i) {
189   return DerivedDescriptor::registers()[i];
190 }
191 
192 // static
193 constexpr Register FastNewObjectDescriptor::TargetRegister() {
194   return kJSFunctionRegister;
195 }
196 
197 // static
198 constexpr Register FastNewObjectDescriptor::NewTargetRegister() {
199   return kJavaScriptCallNewTargetRegister;
200 }
201 
202 // static
203 constexpr Register WriteBarrierDescriptor::ObjectRegister() {
204   return std::get<kObject>(registers());
205 }
206 // static
207 constexpr Register WriteBarrierDescriptor::SlotAddressRegister() {
208   return std::get<kSlotAddress>(registers());
209 }
210 
211 // static
212 constexpr Register WriteBarrierDescriptor::ValueRegister() {
213   return std::get<kSlotAddress + 1>(registers());
214 }
215 
216 // static
217 constexpr RegList WriteBarrierDescriptor::ComputeSavedRegisters(
218     Register object, Register slot_address) {
219   DCHECK(!AreAliased(object, slot_address));
220   RegList saved_registers = 0;
221 #if V8_TARGET_ARCH_X64
222   // Only push clobbered registers.
223   if (object != ObjectRegister()) saved_registers |= ObjectRegister().bit();
224   if (slot_address != no_reg && slot_address != SlotAddressRegister()) {
225     saved_registers |= SlotAddressRegister().bit();
226   }
227 #else
228   // TODO(cbruni): Enable callee-saved registers for other platforms.
229   // This is a temporary workaround to prepare code for callee-saved registers.
230   constexpr auto allocated_registers = registers();
231   for (size_t i = 0; i < allocated_registers.size(); ++i) {
232     saved_registers |= allocated_registers[i].bit();
233   }
234 #endif
235   return saved_registers;
236 }
237 
238 // static
239 constexpr Register ApiGetterDescriptor::ReceiverRegister() {
240   return LoadDescriptor::ReceiverRegister();
241 }
242 
243 // static
244 constexpr Register LoadGlobalNoFeedbackDescriptor::ICKindRegister() {
245   return LoadDescriptor::SlotRegister();
246 }
247 
248 // static
249 constexpr Register LoadNoFeedbackDescriptor::ICKindRegister() {
250   return LoadGlobalNoFeedbackDescriptor::ICKindRegister();
251 }
252 
253 #if V8_TARGET_ARCH_IA32
254 // On ia32, LoadWithVectorDescriptor passes vector on the stack and thus we
255 // need to choose a new register here.
256 // static
257 constexpr Register LoadGlobalWithVectorDescriptor::VectorRegister() {
258   STATIC_ASSERT(!LoadWithVectorDescriptor::VectorRegister().is_valid());
259   return LoadDescriptor::ReceiverRegister();
260 }
261 #else
262 // static
263 constexpr Register LoadGlobalWithVectorDescriptor::VectorRegister() {
264   return LoadWithVectorDescriptor::VectorRegister();
265 }
266 #endif
267 
268 // static
269 constexpr auto LoadDescriptor::registers() {
270   return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
271 }
272 
273 // static
274 constexpr auto LoadBaselineDescriptor::registers() {
275   return LoadDescriptor::registers();
276 }
277 
278 // static
279 constexpr auto LoadGlobalDescriptor::registers() {
280   return RegisterArray(LoadDescriptor::NameRegister(),
281                        LoadDescriptor::SlotRegister());
282 }
283 
284 // static
285 constexpr auto LoadGlobalBaselineDescriptor::registers() {
286   return LoadGlobalDescriptor::registers();
287 }
288 
289 // static
290 constexpr auto StoreDescriptor::registers() {
291   return RegisterArray(ReceiverRegister(), NameRegister(), ValueRegister(),
292                        SlotRegister());
293 }
294 
295 // static
296 constexpr auto StoreBaselineDescriptor::registers() {
297   return StoreDescriptor::registers();
298 }
299 
300 // static
301 constexpr auto StoreGlobalDescriptor::registers() {
302   return RegisterArray(StoreDescriptor::NameRegister(),
303                        StoreDescriptor::ValueRegister(),
304                        StoreDescriptor::SlotRegister());
305 }
306 
307 // static
308 constexpr auto StoreGlobalBaselineDescriptor::registers() {
309   return StoreGlobalDescriptor::registers();
310 }
311 
312 // static
313 constexpr auto LoadWithReceiverBaselineDescriptor::registers() {
314   return RegisterArray(
315       LoadDescriptor::ReceiverRegister(),
316       LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister(),
317       LoadDescriptor::NameRegister(), LoadDescriptor::SlotRegister());
318 }
319 
320 // static
321 constexpr auto BaselineOutOfLinePrologueDescriptor::registers() {
322   // TODO(v8:11421): Implement on other platforms.
323 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_ARM ||       \
324     V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_S390 ||      \
325     V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_MIPS || \
326     V8_TARGET_ARCH_LOONG64
327   return RegisterArray(
328       kContextRegister, kJSFunctionRegister, kJavaScriptCallArgCountRegister,
329       kJavaScriptCallExtraArg1Register, kJavaScriptCallNewTargetRegister,
330       kInterpreterBytecodeArrayRegister);
331 #elif V8_TARGET_ARCH_IA32
332   STATIC_ASSERT(kJSFunctionRegister == kInterpreterBytecodeArrayRegister);
333   return RegisterArray(
334       kContextRegister, kJSFunctionRegister, kJavaScriptCallArgCountRegister,
335       kJavaScriptCallExtraArg1Register, kJavaScriptCallNewTargetRegister);
336 #else
337   return DefaultRegisterArray();
338 #endif
339 }
340 
341 // static
342 constexpr auto BaselineLeaveFrameDescriptor::registers() {
343   // TODO(v8:11421): Implement on other platforms.
344 #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64 ||      \
345     V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 ||       \
346     V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64 || \
347     V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_LOONG64
348   return RegisterArray(ParamsSizeRegister(), WeightRegister());
349 #else
350   return DefaultRegisterArray();
351 #endif
352 }
353 
354 // static
355 constexpr auto VoidDescriptor::registers() { return RegisterArray(); }
356 
357 // static
358 constexpr auto AllocateDescriptor::registers() {
359   return RegisterArray(kAllocateSizeRegister);
360 }
361 
362 // static
363 constexpr auto CEntry1ArgvOnStackDescriptor::registers() {
364   return RegisterArray(kRuntimeCallArgCountRegister,
365                        kRuntimeCallFunctionRegister);
366 }
367 
368 // static
369 constexpr auto InterpreterCEntry1Descriptor::registers() {
370   return RegisterArray(kRuntimeCallArgCountRegister, kRuntimeCallArgvRegister,
371                        kRuntimeCallFunctionRegister);
372 }
373 
374 // static
375 constexpr auto InterpreterCEntry2Descriptor::registers() {
376   return RegisterArray(kRuntimeCallArgCountRegister, kRuntimeCallArgvRegister,
377                        kRuntimeCallFunctionRegister);
378 }
379 
380 // static
381 constexpr auto FastNewObjectDescriptor::registers() {
382   return RegisterArray(TargetRegister(), NewTargetRegister());
383 }
384 
385 // static
386 constexpr auto LoadNoFeedbackDescriptor::registers() {
387   return RegisterArray(LoadDescriptor::ReceiverRegister(),
388                        LoadDescriptor::NameRegister(), ICKindRegister());
389 }
390 
391 // static
392 constexpr auto LoadGlobalNoFeedbackDescriptor::registers() {
393   return RegisterArray(LoadDescriptor::NameRegister(), ICKindRegister());
394 }
395 
396 // static
397 constexpr auto LoadGlobalWithVectorDescriptor::registers() {
398   return RegisterArray(LoadDescriptor::NameRegister(),
399                        LoadDescriptor::SlotRegister(), VectorRegister());
400 }
401 
402 // static
403 constexpr auto LoadWithReceiverAndVectorDescriptor::registers() {
404   return RegisterArray(
405       LoadDescriptor::ReceiverRegister(), LookupStartObjectRegister(),
406       LoadDescriptor::NameRegister(), LoadDescriptor::SlotRegister(),
407       LoadWithVectorDescriptor::VectorRegister());
408 }
409 
410 // static
411 constexpr auto StoreGlobalWithVectorDescriptor::registers() {
412   return RegisterArray(StoreDescriptor::NameRegister(),
413                        StoreDescriptor::ValueRegister(),
414                        StoreDescriptor::SlotRegister(),
415                        StoreWithVectorDescriptor::VectorRegister());
416 }
417 
418 // static
419 constexpr auto StoreTransitionDescriptor::registers() {
420   return RegisterArray(StoreDescriptor::ReceiverRegister(),
421                        StoreDescriptor::NameRegister(), MapRegister(),
422                        StoreDescriptor::ValueRegister(),
423                        StoreDescriptor::SlotRegister(),
424                        StoreWithVectorDescriptor::VectorRegister());
425 }
426 
427 // static
428 constexpr auto TypeConversionDescriptor::registers() {
429   return RegisterArray(ArgumentRegister());
430 }
431 
432 // static
433 constexpr auto TypeConversionNoContextDescriptor::registers() {
434   return RegisterArray(TypeConversionDescriptor::ArgumentRegister());
435 }
436 
437 // static
438 constexpr auto SingleParameterOnStackDescriptor::registers() {
439   return RegisterArray();
440 }
441 
442 // static
443 constexpr auto AsyncFunctionStackParameterDescriptor::registers() {
444   return RegisterArray();
445 }
446 
447 // static
448 constexpr auto GetIteratorStackParameterDescriptor::registers() {
449   return RegisterArray();
450 }
451 
452 // static
453 constexpr auto LoadWithVectorDescriptor::registers() {
454   return RegisterArray(LoadDescriptor::ReceiverRegister(),
455                        LoadDescriptor::NameRegister(),
456                        LoadDescriptor::SlotRegister(), VectorRegister());
457 }
458 
459 // static
460 constexpr auto StoreWithVectorDescriptor::registers() {
461   return RegisterArray(StoreDescriptor::ReceiverRegister(),
462                        StoreDescriptor::NameRegister(),
463                        StoreDescriptor::ValueRegister(),
464                        StoreDescriptor::SlotRegister(), VectorRegister());
465 }
466 
467 // static
468 constexpr auto ApiGetterDescriptor::registers() {
469   return RegisterArray(ReceiverRegister(), HolderRegister(),
470                        CallbackRegister());
471 }
472 
473 // static
474 constexpr auto ContextOnlyDescriptor::registers() { return RegisterArray(); }
475 
476 // static
477 constexpr auto NoContextDescriptor::registers() { return RegisterArray(); }
478 
479 // static
480 constexpr auto GrowArrayElementsDescriptor::registers() {
481   return RegisterArray(ObjectRegister(), KeyRegister());
482 }
483 
484 // static
485 constexpr auto ArrayNArgumentsConstructorDescriptor::registers() {
486   // Keep the arguments on the same registers as they were in
487   // ArrayConstructorDescriptor to avoid unnecessary register moves.
488   // kFunction, kAllocationSite, kActualArgumentsCount
489   return RegisterArray(kJavaScriptCallTargetRegister,
490                        kJavaScriptCallExtraArg1Register,
491                        kJavaScriptCallArgCountRegister);
492 }
493 
494 // static
495 constexpr auto ArrayNoArgumentConstructorDescriptor::registers() {
496   // This descriptor must use the same set of registers as the
497   // ArrayNArgumentsConstructorDescriptor.
498   return ArrayNArgumentsConstructorDescriptor::registers();
499 }
500 
501 // static
502 constexpr auto ArraySingleArgumentConstructorDescriptor::registers() {
503   // This descriptor must use the same set of registers as the
504   // ArrayNArgumentsConstructorDescriptor.
505   return ArrayNArgumentsConstructorDescriptor::registers();
506 }
507 
508 // static
509 // static
510 constexpr Register RunMicrotasksDescriptor::MicrotaskQueueRegister() {
511   return GetRegisterParameter(0);
512 }
513 
514 #define DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER(Name, DescriptorName) \
515   template <>                                                         \
516   struct CallInterfaceDescriptorFor<Builtin::k##Name> {               \
517     using type = DescriptorName##Descriptor;                          \
518   };
519 BUILTIN_LIST(IGNORE_BUILTIN, IGNORE_BUILTIN,
520              /*TFC*/ DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER, IGNORE_BUILTIN,
521              /*TFH*/ DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER, IGNORE_BUILTIN,
522              /*ASM*/ DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER)
523 #undef DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER
524 #define DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER(Name, ...) \
525   template <>                                              \
526   struct CallInterfaceDescriptorFor<Builtin::k##Name> {    \
527     using type = Name##Descriptor;                         \
528   };
529 BUILTIN_LIST_TFS(DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER)
530 #undef DEFINE_STATIC_BUILTIN_DESCRIPTOR_GETTER
531 
532 }  // namespace internal
533 }  // namespace v8
534 
535 #endif  // V8_CODEGEN_INTERFACE_DESCRIPTORS_INL_H_
536