1// Copyright 2014 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_INTERFACE_DESCRIPTORS_H_
6#define V8_INTERFACE_DESCRIPTORS_H_
7
8#include <memory>
9
10#include "src/globals.h"
11#include "src/isolate.h"
12#include "src/machine-type.h"
13#include "src/register-arch.h"
14
15namespace v8 {
16namespace internal {
17
18#define INTERFACE_DESCRIPTOR_LIST(V) \
19 V(Abort) \
20 V(Allocate) \
21 V(AllocateHeapNumber) \
22 V(ApiCallback) \
23 V(ApiGetter) \
24 V(ArgumentsAdaptor) \
25 V(ArrayConstructor) \
26 V(ArrayNArgumentsConstructor) \
27 V(ArrayNoArgumentConstructor) \
28 V(ArraySingleArgumentConstructor) \
29 V(AsyncFunctionStackParameter) \
30 V(BigIntToI64) \
31 V(I64ToBigInt) \
32 V(BinaryOp) \
33 V(CallForwardVarargs) \
34 V(CallFunctionTemplate) \
35 V(CallTrampoline) \
36 V(CallVarargs) \
37 V(CallWithArrayLike) \
38 V(CallWithSpread) \
39 V(CEntry1ArgvOnStack) \
40 V(CloneObjectWithVector) \
41 V(Compare) \
42 V(ConstructForwardVarargs) \
43 V(ConstructStub) \
44 V(ConstructVarargs) \
45 V(ConstructWithArrayLike) \
46 V(ConstructWithSpread) \
47 V(ContextOnly) \
48 V(CppBuiltinAdaptor) \
49 V(EphemeronKeyBarrier) \
50 V(FastNewFunctionContext) \
51 V(FastNewObject) \
52 V(FrameDropperTrampoline) \
53 V(GetProperty) \
54 V(GrowArrayElements) \
55 V(InterpreterCEntry1) \
56 V(InterpreterCEntry2) \
57 V(InterpreterDispatch) \
58 V(InterpreterPushArgsThenCall) \
59 V(InterpreterPushArgsThenConstruct) \
60 V(JSTrampoline) \
61 V(Load) \
62 V(LoadGlobal) \
63 V(LoadGlobalWithVector) \
64 V(LoadWithVector) \
65 V(NewArgumentsElements) \
66 V(NoContext) \
67 V(RecordWrite) \
68 V(ResumeGenerator) \
69 V(RunMicrotasksEntry) \
70 V(RunMicrotasks) \
71 V(Store) \
72 V(StoreGlobal) \
73 V(StoreGlobalWithVector) \
74 V(StoreTransition) \
75 V(StoreWithVector) \
76 V(StringAt) \
77 V(StringSubstring) \
78 V(TypeConversion) \
79 V(TypeConversionStackParameter) \
80 V(Typeof) \
81 V(Void) \
82 V(WasmAtomicNotify) \
83 V(WasmI32AtomicWait) \
84 V(WasmI64AtomicWait) \
85 V(WasmMemoryGrow) \
86 V(WasmTableGet) \
87 V(WasmTableSet) \
88 V(WasmThrow) \
89 BUILTIN_LIST_TFS(V)
90
91class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
92 public:
93 enum Flag {
94 kNoFlags = 0u,
95 kNoContext = 1u << 0,
96
97 // This indicates that the code uses a special frame that does not scan the
98 // stack arguments, e.g. EntryFrame. And this allows the code to use
99 // untagged stack arguments.
100 kNoStackScan = 1u << 1,
101 };
102 typedef base::Flags<Flag> Flags;
103
104 CallInterfaceDescriptorData() = default;
105
106 // A copy of the passed in registers and param_representations is made
107 // and owned by the CallInterfaceDescriptorData.
108
109 void InitializePlatformSpecific(int register_parameter_count,
110 const Register* registers);
111
112 // if machine_types is null, then an array of size
113 // (return_count + parameter_count) will be created with
114 // MachineType::AnyTagged() for each member.
115 //
116 // if machine_types is not null, then it should be of the size
117 // (return_count + parameter_count). Those members of the parameter array will
118 // be initialized from {machine_types}, and the rest initialized to
119 // MachineType::AnyTagged().
120 void InitializePlatformIndependent(Flags flags, int return_count,
121 int parameter_count,
122 const MachineType* machine_types,
123 int machine_types_length);
124
125 void Reset();
126
127 bool IsInitialized() const {
128 return IsInitializedPlatformSpecific() &&
129 IsInitializedPlatformIndependent();
130 }
131
132 Flags flags() const { return flags_; }
133 int return_count() const { return return_count_; }
134 int param_count() const { return param_count_; }
135 int register_param_count() const { return register_param_count_; }
136 Register register_param(int index) const { return register_params_[index]; }
137 Register* register_params() const { return register_params_; }
138 MachineType return_type(int index) const {
139 DCHECK_LT(index, return_count_);
140 return machine_types_[index];
141 }
142 MachineType param_type(int index) const {
143 DCHECK_LT(index, param_count_);
144 return machine_types_[return_count_ + index];
145 }
146
147 void RestrictAllocatableRegisters(const Register* registers, int num) {
148 DCHECK_EQ(allocatable_registers_, 0);
149 for (int i = 0; i < num; ++i) {
150 allocatable_registers_ |= registers[i].bit();
151 }
152 DCHECK_GT(NumRegs(allocatable_registers_), 0);
153 }
154
155 RegList allocatable_registers() const { return allocatable_registers_; }
156
157 private:
158 bool IsInitializedPlatformSpecific() const {
159 const bool initialized =
160 (register_param_count_ == 0 && register_params_ == nullptr) ||
161 (register_param_count_ > 0 && register_params_ != nullptr);
162 // Platform-specific initialization happens before platform-independent.
163 return initialized;
164 }
165 bool IsInitializedPlatformIndependent() const {
166 const bool initialized =
167 return_count_ >= 0 && param_count_ >= 0 && machine_types_ != nullptr;
168 // Platform-specific initialization happens before platform-independent.
169 return initialized;
170 }
171
172#ifdef DEBUG
173 bool AllStackParametersAreTagged() const;
174#endif // DEBUG
175
176 int register_param_count_ = -1;
177 int return_count_ = -1;
178 int param_count_ = -1;
179 Flags flags_ = kNoFlags;
180
181 // Specifying the set of registers that could be used by the register
182 // allocator. Currently, it's only used by RecordWrite code stub.
183 RegList allocatable_registers_ = 0;
184
185 // |registers_params_| defines registers that are used for parameter passing.
186 // |machine_types_| defines machine types for resulting values and incomping
187 // parameters.
188 // Both arrays are allocated dynamically by the InterfaceDescriptor and
189 // freed on destruction. This is because static arrays cause creation of
190 // runtime static initializers which we don't want.
191 Register* register_params_ = nullptr;
192 MachineType* machine_types_ = nullptr;
193
194 DISALLOW_COPY_AND_ASSIGN(CallInterfaceDescriptorData);
195};
196
197class V8_EXPORT_PRIVATE CallDescriptors : public AllStatic {
198 public:
199 enum Key {
200#define DEF_ENUM(name, ...) name,
201 INTERFACE_DESCRIPTOR_LIST(DEF_ENUM)
202#undef DEF_ENUM
203 NUMBER_OF_DESCRIPTORS
204 };
205
206 static void InitializeOncePerProcess();
207 static void TearDown();
208
209 static CallInterfaceDescriptorData* call_descriptor_data(
210 CallDescriptors::Key key) {
211 return &call_descriptor_data_[key];
212 }
213
214 static Key GetKey(const CallInterfaceDescriptorData* data) {
215 ptrdiff_t index = data - call_descriptor_data_;
216 DCHECK_LE(0, index);
217 DCHECK_LT(index, CallDescriptors::NUMBER_OF_DESCRIPTORS);
218 return static_cast<CallDescriptors::Key>(index);
219 }
220
221 private:
222 static CallInterfaceDescriptorData
223 call_descriptor_data_[NUMBER_OF_DESCRIPTORS];
224};
225
226class V8_EXPORT_PRIVATE CallInterfaceDescriptor {
227 public:
228 typedef CallInterfaceDescriptorData::Flags Flags;
229
230 CallInterfaceDescriptor() : data_(nullptr) {}
231 virtual ~CallInterfaceDescriptor() = default;
232
233 explicit CallInterfaceDescriptor(CallDescriptors::Key key)
234 : data_(CallDescriptors::call_descriptor_data(key)) {}
235
236 Flags flags() const { return data()->flags(); }
237
238 bool HasContextParameter() const {
239 return (flags() & CallInterfaceDescriptorData::kNoContext) == 0;
240 }
241
242 int GetReturnCount() const { return data()->return_count(); }
243
244 MachineType GetReturnType(int index) const {
245 DCHECK_LT(index, data()->return_count());
246 return data()->return_type(index);
247 }
248
249 int GetParameterCount() const { return data()->param_count(); }
250
251 int GetRegisterParameterCount() const {
252 return data()->register_param_count();
253 }
254
255 int GetStackParameterCount() const {
256 return data()->param_count() - data()->register_param_count();
257 }
258
259 Register GetRegisterParameter(int index) const {
260 return data()->register_param(index);
261 }
262
263 MachineType GetParameterType(int index) const {
264 DCHECK_LT(index, data()->param_count());
265 return data()->param_type(index);
266 }
267
268 RegList allocatable_registers() const {
269 return data()->allocatable_registers();
270 }
271
272 static const Register ContextRegister();
273
274 const char* DebugName() const;
275
276 protected:
277 const CallInterfaceDescriptorData* data() const { return data_; }
278
279 virtual void InitializePlatformSpecific(CallInterfaceDescriptorData* data) {
280 UNREACHABLE();
281 }
282
283 virtual void InitializePlatformIndependent(
284 CallInterfaceDescriptorData* data) {
285 // Default descriptor configuration: one result, all parameters are passed
286 // in registers and all parameters have MachineType::AnyTagged() type.
287 data->InitializePlatformIndependent(CallInterfaceDescriptorData::kNoFlags,
288 1, data->register_param_count(),
289 nullptr, 0);
290 }
291
292 // Initializes |data| using the platform dependent default set of registers.
293 // It is intended to be used for TurboFan stubs when particular set of
294 // registers does not matter.
295 static void DefaultInitializePlatformSpecific(
296 CallInterfaceDescriptorData* data, int register_parameter_count);
297
298 // Initializes |data| using the platform dependent default set of registers
299 // for JavaScript-compatible calling convention.
300 // It is intended to be used for TurboFan stubs being called with JavaScript
301 // linkage + additional parameters on registers and stack.
302 static void JSDefaultInitializePlatformSpecific(
303 CallInterfaceDescriptorData* data, int non_js_register_parameter_count);
304
305 // Checks if float parameters are not assigned invalid registers.
306 bool CheckFloatingPointParameters(CallInterfaceDescriptorData* data) {
307 for (int i = 0; i < data->register_param_count(); i++) {
308 if (IsFloatingPoint(data->param_type(i).representation())) {
309 if (!IsValidFloatParameterRegister(data->register_param(i))) {
310 return false;
311 }
312 }
313 }
314 return true;
315 }
316
317 bool IsValidFloatParameterRegister(Register reg);
318
319 private:
320 // {CallDescriptors} is allowed to call the private {Initialize} method.
321 friend class CallDescriptors;
322
323 const CallInterfaceDescriptorData* data_;
324
325 void Initialize(CallInterfaceDescriptorData* data) {
326 // The passed pointer should be a modifiable pointer to our own data.
327 DCHECK_EQ(data, data_);
328 DCHECK(!data->IsInitialized());
329 InitializePlatformSpecific(data);
330 InitializePlatformIndependent(data);
331 DCHECK(data->IsInitialized());
332 DCHECK(CheckFloatingPointParameters(data));
333 }
334};
335
336#define DECLARE_DESCRIPTOR_WITH_BASE(name, base) \
337 public: \
338 explicit name() : base(key()) {} \
339 static inline CallDescriptors::Key key();
340
341#if defined(V8_TARGET_ARCH_IA32)
342// To support all possible cases, we must limit the number of register args for
343// TFS builtins on ia32 to 3. Out of the 6 allocatable registers, esi is taken
344// as the context register and ebx is the root register. One register must
345// remain available to store the jump/call target. Thus 3 registers remain for
346// arguments. The reason this applies to TFS builtins specifically is because
347// this becomes relevant for builtins used as targets of Torque function
348// pointers (which must have a register available to store the target).
349// TODO(jgruber): Ideally we should just decrement kMaxBuiltinRegisterParams but
350// that comes with its own set of complications. It's possible, but requires
351// refactoring the calling convention of other existing stubs.
352constexpr int kMaxBuiltinRegisterParams = 4;
353constexpr int kMaxTFSBuiltinRegisterParams = 3;
354#else
355constexpr int kMaxBuiltinRegisterParams = 5;
356constexpr int kMaxTFSBuiltinRegisterParams = kMaxBuiltinRegisterParams;
357#endif
358STATIC_ASSERT(kMaxTFSBuiltinRegisterParams <= kMaxBuiltinRegisterParams);
359
360#define DECLARE_DEFAULT_DESCRIPTOR(name, base) \
361 DECLARE_DESCRIPTOR_WITH_BASE(name, base) \
362 protected: \
363 static const int kRegisterParams = \
364 kParameterCount > kMaxTFSBuiltinRegisterParams \
365 ? kMaxTFSBuiltinRegisterParams \
366 : kParameterCount; \
367 static const int kStackParams = kParameterCount - kRegisterParams; \
368 void InitializePlatformSpecific(CallInterfaceDescriptorData* data) \
369 override { \
370 DefaultInitializePlatformSpecific(data, kRegisterParams); \
371 } \
372 void InitializePlatformIndependent(CallInterfaceDescriptorData* data) \
373 override { \
374 data->InitializePlatformIndependent(Flags(kDescriptorFlags), kReturnCount, \
375 kParameterCount, nullptr, 0); \
376 } \
377 name(CallDescriptors::Key key) : base(key) {} \
378 \
379 public:
380
381#define DECLARE_JS_COMPATIBLE_DESCRIPTOR(name, base, \
382 non_js_reg_parameters_count) \
383 DECLARE_DESCRIPTOR_WITH_BASE(name, base) \
384 protected: \
385 void InitializePlatformSpecific(CallInterfaceDescriptorData* data) \
386 override { \
387 JSDefaultInitializePlatformSpecific(data, non_js_reg_parameters_count); \
388 } \
389 name(CallDescriptors::Key key) : base(key) {} \
390 \
391 public:
392
393#define DEFINE_RESULT_AND_PARAMETERS(return_count, ...) \
394 static constexpr int kDescriptorFlags = \
395 CallInterfaceDescriptorData::kNoFlags; \
396 static constexpr int kReturnCount = return_count; \
397 enum ParameterIndices { \
398 __dummy = -1, /* to be able to pass zero arguments */ \
399 ##__VA_ARGS__, \
400 \
401