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 | |
15 | namespace v8 { |
16 | namespace 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 | |
91 | class 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 | |
197 | class 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 | |
226 | class 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. |
352 | constexpr int kMaxBuiltinRegisterParams = 4; |
353 | constexpr int kMaxTFSBuiltinRegisterParams = 3; |
354 | #else |
355 | constexpr int kMaxBuiltinRegisterParams = 5; |
356 | constexpr int kMaxTFSBuiltinRegisterParams = kMaxBuiltinRegisterParams; |
357 | #endif |
358 | STATIC_ASSERT(kMaxTFSBuiltinRegisterParams <= kMaxBuiltinRegisterParams); |
359 | |
360 | #define DECLARE_DEFAULT_DESCRIPTOR(name, base) \ |
361 | DECLARE_DESCRIPTOR_WITH_BASE(name, base) \ |
362 | protected: \ |
363 | static const int = \ |
364 | kParameterCount > kMaxTFSBuiltinRegisterParams \ |
365 | ? kMaxTFSBuiltinRegisterParams \ |
366 | : kParameterCount; \ |
367 | static const int = kParameterCount - kRegisterParams; \ |
368 | void (CallInterfaceDescriptorData* data) \ |
369 | override { \ |
370 | DefaultInitializePlatformSpecific(data, kRegisterParams); \ |
371 | } \ |
372 | void (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 = \ |
395 | CallInterfaceDescriptorData::kNoFlags; \ |
396 | static constexpr int = return_count; \ |
397 | enum { \ |
398 | = -1, /* to be able to pass zero arguments */ \ |
399 | ##__VA_ARGS__, \ |
400 | \ |
401 | |
---|