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
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 {
Callv8::internal::detail::FirstInvalidRegisterHelper131 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> {
Callv8::internal::detail::FirstInvalidRegisterHelper144 static constexpr int Call(std::array<Register, N> regs) { return N; }
145 };
146 template <size_t N, size_t Index = 0>
FirstInvalidRegister(std::array<Register, N> regs)147 constexpr size_t FirstInvalidRegister(std::array<Register, N> regs) {
148 return FirstInvalidRegisterHelper<N, 0>::Call(regs);
149 }
FirstInvalidRegister(EmptyRegisterArray regs)150 constexpr size_t FirstInvalidRegister(EmptyRegisterArray regs) { return 0; }
151
152 } // namespace detail
153
154 // static
155 template <typename DerivedDescriptor>
156 constexpr int
GetRegisterParameterCount()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
GetStackParameterCount()180 StaticCallInterfaceDescriptor<DerivedDescriptor>::GetStackParameterCount() {
181 return DerivedDescriptor::GetParameterCount() -
182 DerivedDescriptor::GetRegisterParameterCount();
183 }
184
185 // static
186 template <typename DerivedDescriptor>
187 constexpr Register
GetRegisterParameter(int i)188 StaticCallInterfaceDescriptor<DerivedDescriptor>::GetRegisterParameter(int i) {
189 return DerivedDescriptor::registers()[i];
190 }
191
192 // static
TargetRegister()193 constexpr Register FastNewObjectDescriptor::TargetRegister() {
194 return kJSFunctionRegister;
195 }
196
197 // static
NewTargetRegister()198 constexpr Register FastNewObjectDescriptor::NewTargetRegister() {
199 return kJavaScriptCallNewTargetRegister;
200 }
201
202 // static
ObjectRegister()203 constexpr Register WriteBarrierDescriptor::ObjectRegister() {
204 return std::get<kObject>(registers());
205 }
206 // static
SlotAddressRegister()207 constexpr Register WriteBarrierDescriptor::SlotAddressRegister() {
208 return std::get<kSlotAddress>(registers());
209 }
210
211 // static
ValueRegister()212 constexpr Register WriteBarrierDescriptor::ValueRegister() {
213 return std::get<kSlotAddress + 1>(registers());
214 }
215
216 // static
ComputeSavedRegisters( Register object, Register slot_address)217 constexpr 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
ReceiverRegister()244 constexpr Register ApiGetterDescriptor::ReceiverRegister() {
245 return LoadDescriptor::ReceiverRegister();
246 }
247
248 // static
ICKindRegister()249 constexpr Register LoadGlobalNoFeedbackDescriptor::ICKindRegister() {
250 return LoadDescriptor::SlotRegister();
251 }
252
253 // static
ICKindRegister()254 constexpr 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
VectorRegister()262 constexpr Register LoadGlobalWithVectorDescriptor::VectorRegister() {
263 STATIC_ASSERT(!LoadWithVectorDescriptor::VectorRegister().is_valid());
264 return LoadDescriptor::ReceiverRegister();
265 }
266 #else
267 // static
VectorRegister()268 constexpr Register LoadGlobalWithVectorDescriptor::VectorRegister() {
269 return LoadWithVectorDescriptor::VectorRegister();
270 }
271 #endif
272
273 // static
registers()274 constexpr auto LoadDescriptor::registers() {
275 return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
276 }
277
278 // static
registers()279 constexpr auto LoadBaselineDescriptor::registers() {
280 return LoadDescriptor::registers();
281 }
282
283 // static
registers()284 constexpr auto LoadGlobalDescriptor::registers() {
285 return RegisterArray(LoadDescriptor::NameRegister(),
286 LoadDescriptor::SlotRegister());
287 }
288
289 // static
registers()290 constexpr auto LoadGlobalBaselineDescriptor::registers() {
291 return LoadGlobalDescriptor::registers();
292 }
293
294 // static
registers()295 constexpr auto StoreDescriptor::registers() {
296 return RegisterArray(ReceiverRegister(), NameRegister(), ValueRegister(),
297 SlotRegister());
298 }
299
300 // static
registers()301 constexpr auto StoreBaselineDescriptor::registers() {
302 return StoreDescriptor::registers();
303 }
304
305 // static
registers()306 constexpr auto StoreGlobalDescriptor::registers() {
307 return RegisterArray(StoreDescriptor::NameRegister(),
308 StoreDescriptor::ValueRegister(),
309 StoreDescriptor::SlotRegister());
310 }
311
312 // static
registers()313 constexpr auto StoreGlobalBaselineDescriptor::registers() {
314 return StoreGlobalDescriptor::registers();
315 }
316
317 // static
registers()318 constexpr auto LoadWithReceiverBaselineDescriptor::registers() {
319 return RegisterArray(
320 LoadDescriptor::ReceiverRegister(),
321 LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister(),
322 LoadDescriptor::NameRegister(), LoadDescriptor::SlotRegister());
323 }
324
325 // static
registers()326 constexpr 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
registers()347 constexpr 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
registers()360 constexpr auto VoidDescriptor::registers() { return RegisterArray(); }
361
362 // static
registers()363 constexpr auto AllocateDescriptor::registers() {
364 return RegisterArray(kAllocateSizeRegister);
365 }
366
367 // static
registers()368 constexpr auto CEntry1ArgvOnStackDescriptor::registers() {
369 return RegisterArray(kRuntimeCallArgCountRegister,
370 kRuntimeCallFunctionRegister);
371 }
372
373 // static
registers()374 constexpr auto InterpreterCEntry1Descriptor::registers() {
375 return RegisterArray(kRuntimeCallArgCountRegister, kRuntimeCallArgvRegister,
376 kRuntimeCallFunctionRegister);
377 }
378
379 // static
registers()380 constexpr auto InterpreterCEntry2Descriptor::registers() {
381 return RegisterArray(kRuntimeCallArgCountRegister, kRuntimeCallArgvRegister,
382 kRuntimeCallFunctionRegister);
383 }
384
385 // static
registers()386 constexpr auto FastNewObjectDescriptor::registers() {
387 return RegisterArray(TargetRegister(), NewTargetRegister());
388 }
389
390 // static
registers()391 constexpr auto LoadNoFeedbackDescriptor::registers() {
392 return RegisterArray(LoadDescriptor::ReceiverRegister(),
393 LoadDescriptor::NameRegister(), ICKindRegister());
394 }
395
396 // static
registers()397 constexpr auto LoadGlobalNoFeedbackDescriptor::registers() {
398 return RegisterArray(LoadDescriptor::NameRegister(), ICKindRegister());
399 }
400
401 // static
registers()402 constexpr auto LoadGlobalWithVectorDescriptor::registers() {
403 return RegisterArray(LoadDescriptor::NameRegister(),
404 LoadDescriptor::SlotRegister(), VectorRegister());
405 }
406
407 // static
registers()408 constexpr auto LoadWithReceiverAndVectorDescriptor::registers() {
409 return RegisterArray(
410 LoadDescriptor::ReceiverRegister(), LookupStartObjectRegister(),
411 LoadDescriptor::NameRegister(), LoadDescriptor::SlotRegister(),
412 LoadWithVectorDescriptor::VectorRegister());
413 }
414
415 // static
registers()416 constexpr auto StoreGlobalWithVectorDescriptor::registers() {
417 return RegisterArray(StoreDescriptor::NameRegister(),
418 StoreDescriptor::ValueRegister(),
419 StoreDescriptor::SlotRegister(),
420 StoreWithVectorDescriptor::VectorRegister());
421 }
422
423 // static
registers()424 constexpr 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
registers()433 constexpr auto TypeConversionDescriptor::registers() {
434 return RegisterArray(ArgumentRegister());
435 }
436
437 // static
registers()438 constexpr auto TypeConversionNoContextDescriptor::registers() {
439 return RegisterArray(TypeConversionDescriptor::ArgumentRegister());
440 }
441
442 // static
registers()443 constexpr auto SingleParameterOnStackDescriptor::registers() {
444 return RegisterArray();
445 }
446
447 // static
registers()448 constexpr auto AsyncFunctionStackParameterDescriptor::registers() {
449 return RegisterArray();
450 }
451
452 // static
registers()453 constexpr auto GetIteratorStackParameterDescriptor::registers() {
454 return RegisterArray();
455 }
456
457 // static
registers()458 constexpr auto LoadWithVectorDescriptor::registers() {
459 return RegisterArray(LoadDescriptor::ReceiverRegister(),
460 LoadDescriptor::NameRegister(),
461 LoadDescriptor::SlotRegister(), VectorRegister());
462 }
463
464 // static
registers()465 constexpr auto KeyedLoadBaselineDescriptor::registers() {
466 return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
467 }
468
469 // static
registers()470 constexpr auto KeyedLoadDescriptor::registers() {
471 return KeyedLoadBaselineDescriptor::registers();
472 }
473
474 // static
registers()475 constexpr auto KeyedLoadWithVectorDescriptor::registers() {
476 return RegisterArray(KeyedLoadBaselineDescriptor::ReceiverRegister(),
477 KeyedLoadBaselineDescriptor::NameRegister(),
478 KeyedLoadBaselineDescriptor::SlotRegister(),
479 VectorRegister());
480 }
481
482 // static
registers()483 constexpr auto KeyedHasICBaselineDescriptor::registers() {
484 return RegisterArray(ReceiverRegister(), NameRegister(), SlotRegister());
485 }
486
487 // static
registers()488 constexpr auto KeyedHasICWithVectorDescriptor::registers() {
489 return RegisterArray(KeyedHasICBaselineDescriptor::ReceiverRegister(),
490 KeyedHasICBaselineDescriptor::NameRegister(),
491 KeyedHasICBaselineDescriptor::SlotRegister(),
492 VectorRegister());
493 }
494
495 // static
registers()496 constexpr auto StoreWithVectorDescriptor::registers() {
497 return RegisterArray(StoreDescriptor::ReceiverRegister(),
498 StoreDescriptor::NameRegister(),
499 StoreDescriptor::ValueRegister(),
500 StoreDescriptor::SlotRegister(), VectorRegister());
501 }
502
503 // static
registers()504 constexpr auto ApiGetterDescriptor::registers() {
505 return RegisterArray(ReceiverRegister(), HolderRegister(),
506 CallbackRegister());
507 }
508
509 // static
registers()510 constexpr auto ContextOnlyDescriptor::registers() { return RegisterArray(); }
511
512 // static
registers()513 constexpr auto NoContextDescriptor::registers() { return RegisterArray(); }
514
515 // static
registers()516 constexpr auto GrowArrayElementsDescriptor::registers() {
517 return RegisterArray(ObjectRegister(), KeyRegister());
518 }
519
520 // static
registers()521 constexpr 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
registers()531 constexpr 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
registers()538 constexpr 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
MicrotaskQueueRegister()546 constexpr 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 };
555 BUILTIN_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 };
565 BUILTIN_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