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