v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
linkage.h
Go to the documentation of this file.
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_COMPILER_LINKAGE_H_
6#define V8_COMPILER_LINKAGE_H_
7
8#include <optional>
9
11#include "src/base/flags.h"
16#include "src/codegen/reglist.h"
18#include "src/common/globals.h"
19#include "src/compiler/frame.h"
23#include "src/runtime/runtime.h"
24#include "src/zone/zone.h"
25
26namespace v8 {
27class CFunctionInfo;
28
29namespace internal {
30
31class CallInterfaceDescriptor;
32class OptimizedCompilationInfo;
33
34namespace compiler {
35
38
39class OsrHelper;
40
41// Describes a call to various parts of the compiler. Every call has the notion
42// of a "target", which is the first input to the call.
44 : public NON_EXPORTED_BASE(ZoneObject) {
45 public:
46 // Describes the kind of this call, which determines the target.
47 enum Kind {
48 kCallCodeObject, // target is a Code object
49 kCallJSFunction, // target is a JSFunction object
50 kCallAddress, // target is a machine pointer
51#if V8_ENABLE_WEBASSEMBLY // ↓ WebAssembly only
52 kCallWasmCapiFunction, // target is a Wasm C API function
53 kCallWasmFunction, // target is a wasm function
54 kCallWasmFunctionIndirect, // target is a wasm function that will be called
55 // indirectly
56 kCallWasmImportWrapper, // target is a wasm import wrapper
57#endif // ↑ WebAssembly only
58 kCallBuiltinPointer, // target is a builtin pointer
59 };
60
61 // NOTE: The lowest 10 bits of the Flags field are encoded in InstructionCode
62 // (for use in the code generator). All higher bits are lost.
63 static constexpr int kFlagsBitsEncodedInInstructionCode = 10;
64 enum Flag {
66 kNeedsFrameState = 1u << 0,
67 kHasExceptionHandler = 1u << 1,
68 kCanUseRoots = 1u << 2,
69 // Causes the code generator to initialize the root register.
70 kInitializeRootRegister = 1u << 3,
71 // Does not ever try to allocate space on our heap.
72 kNoAllocate = 1u << 4,
73 // Use the kJavaScriptCallCodeStartRegister (fixed) register for the
74 // indirect target address when calling.
75 kFixedTargetRegister = 1u << 5,
76 kCallerSavedRegisters = 1u << 6,
77 // The kCallerSavedFPRegisters only matters (and set) when the more general
78 // flag for kCallerSavedRegisters above is also set.
79 kCallerSavedFPRegisters = 1u << 7,
80 // Tail calls for tier up are special (in fact they are different enough
81 // from normal tail calls to warrant a dedicated opcode; but they also have
82 // enough similar aspects that reusing the TailCall opcode is pragmatic).
83 // Specifically:
84 //
85 // 1. Caller and callee are both JS-linkage Code objects.
86 // 2. JS runtime arguments are passed unchanged from caller to callee.
87 // 3. JS runtime arguments are not attached as inputs to the TailCall node.
88 // 4. Prior to the tail call, frame and register state is torn down to just
89 // before the caller frame was constructed.
90 // 5. Unlike normal tail calls, inlined arguments frames (if present) are
91 // *not* torn down.
92 //
93 // In other words, behavior is identical to a jmp instruction prior caller
94 // frame construction.
95 kIsTailCallForTierUp = 1u << 8,
96
97 // AIX has a function descriptor by default but it can be disabled for a
98 // certain CFunction call (only used for Kind::kCallAddress).
99 kNoFunctionDescriptor = 1u << 9,
100
101 // Flags past here are *not* encoded in InstructionCode and are thus not
102 // accessible from the code generator. See also
103 // kFlagsBitsEncodedInInstructionCode.
104 };
106
108 LinkageLocation target_loc, LocationSignature* location_sig,
109 size_t param_slot_count, Operator::Properties properties,
110 RegList callee_saved_registers,
111 DoubleRegList callee_saved_fp_registers, Flags flags,
112 const char* debug_name = "",
113 StackArgumentOrder stack_order = StackArgumentOrder::kDefault,
114 const RegList allocatable_registers = {},
115 size_t return_slot_count = 0,
116 uint64_t signature_hash = kInvalidWasmSignatureHash)
117 : kind_(kind),
118 tag_(tag),
119 target_type_(target_type),
120 target_loc_(target_loc),
121 location_sig_(location_sig),
122 param_slot_count_(param_slot_count),
123 return_slot_count_(return_slot_count),
124 properties_(properties),
125 callee_saved_registers_(callee_saved_registers),
126 callee_saved_fp_registers_(callee_saved_fp_registers),
127 allocatable_registers_(allocatable_registers),
128 flags_(flags),
129 stack_order_(stack_order),
130 debug_name_(debug_name),
131 signature_hash_(signature_hash) {
132#ifdef V8_ENABLE_WEBASSEMBLY
133 if (kind == Kind::kCallWasmFunctionIndirect) {
134 CHECK_NE(signature_hash, kInvalidWasmSignatureHash);
135 }
136#endif
137 }
138
141
142 // Returns the kind of this call.
143 Kind kind() const { return kind_; }
144
145 // Returns the entrypoint tag for this call.
146 CodeEntrypointTag tag() const { return tag_; }
147
148 uint64_t signature_hash() const;
149
150 // Returns the entrypoint tag for this call, shifted to the right by
151 // kCodeEntrypointTagShift so that it fits into a 32-bit immediate.
152 uint32_t shifted_tag() const {
153 static_assert(kCodeEntrypointTagShift >= 32);
154 return tag_ >> kCodeEntrypointTagShift;
155 }
156
157 // Returns {true} if this descriptor is a call to a Code object.
158 bool IsCodeObjectCall() const { return kind_ == kCallCodeObject; }
159
160 // Returns {true} if this descriptor is a call to a C function.
161 bool IsCFunctionCall() const { return kind_ == kCallAddress; }
162
163 // Returns {true} if this descriptor is a call to a JSFunction.
164 bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }
165
166#if V8_ENABLE_WEBASSEMBLY
167 // Returns {true} if this descriptor is a direct call to a WebAssembly
168 // function.
169 bool IsDirectWasmFunctionCall() const { return kind_ == kCallWasmFunction; }
170
171 // Returns {true} if this descriptor is a indirect call to a WebAssembly
172 // function.
173 bool IsIndirectWasmFunctionCall() const {
174 return kind_ == kCallWasmFunctionIndirect;
175 }
176
177 // Returns {true} if this descriptor is either a direct or an indirect call to
178 // a WebAssembly function.
179 bool IsAnyWasmFunctionCall() const {
180 return IsDirectWasmFunctionCall() || IsIndirectWasmFunctionCall();
181 }
182
183 // Returns {true} if this descriptor is a call to a WebAssembly function.
184 bool IsWasmImportWrapper() const { return kind_ == kCallWasmImportWrapper; }
185
186 // Returns {true} if this descriptor is a call to a Wasm C API function.
187 bool IsWasmCapiFunction() const { return kind_ == kCallWasmCapiFunction; }
188#endif // V8_ENABLE_WEBASSEMBLY
189
191
193 if (IsCFunctionCall() || IsJSFunctionCall()) return true;
194#if V8_ENABLE_WEBASSEMBLY
195 if (IsAnyWasmFunctionCall()) return true;
196#endif // V8_ENABLE_WEBASSEMBLY
197 if (CalleeSavedRegisters() != kNoCalleeSaved) return true;
198 return false;
199 }
200
201 bool RequiresEntrypointTagForCall() const { return IsCodeObjectCall(); }
202
203 // The number of return values from this call.
204 size_t ReturnCount() const { return location_sig_->return_count(); }
205
206 // The number of C parameters to this call. The following invariant
207 // should hold true:
208 // ParameterCount() == GPParameterCount() + FPParameterCount()
209 size_t ParameterCount() const { return location_sig_->parameter_count(); }
210
211 // The number of general purpose C parameters to this call.
212 size_t GPParameterCount() const {
213 if (!gp_param_count_) {
214 ComputeParamCounts();
215 }
216 return gp_param_count_.value();
217 }
218
219 // The number of floating point C parameters to this call.
220 size_t FPParameterCount() const {
221 if (!fp_param_count_) {
222 ComputeParamCounts();
223 }
224 return fp_param_count_.value();
225 }
226
227 // The number of stack parameter slots to the call.
228 size_t ParameterSlotCount() const { return param_slot_count_; }
229
230 // The number of stack return value slots from the call.
231 size_t ReturnSlotCount() const { return return_slot_count_; }
232
233 // The number of parameters to the JS function call.
234 size_t JSParameterCount() const {
235 DCHECK(IsJSFunctionCall());
236 return param_slot_count_;
237 }
238
239 int GetStackIndexFromSlot(int slot_index) const {
240 switch (GetStackArgumentOrder()) {
241 case StackArgumentOrder::kDefault:
242 return -slot_index - 1;
243 case StackArgumentOrder::kJS:
244 return slot_index + static_cast<int>(ParameterSlotCount());
245 }
246 }
247
248 // The total number of inputs to this call, which includes the target,
249 // receiver, context, etc.
250 // TODO(titzer): this should input the framestate input too.
251 size_t InputCount() const { return 1 + location_sig_->parameter_count(); }
252
253 size_t FrameStateCount() const { return NeedsFrameState() ? 1 : 0; }
254
255 Flags flags() const { return flags_; }
256
257 bool NeedsFrameState() const { return flags() & kNeedsFrameState; }
259 return flags() & kInitializeRootRegister;
260 }
262 return flags() & kCallerSavedRegisters;
263 }
265 return flags() & kCallerSavedFPRegisters;
266 }
267 bool IsTailCallForTierUp() const { return flags() & kIsTailCallForTierUp; }
268 bool NoFunctionDescriptor() const { return flags() & kNoFunctionDescriptor; }
269
270 LinkageLocation GetReturnLocation(size_t index) const {
271 return location_sig_->GetReturn(index);
272 }
273
274 LinkageLocation GetInputLocation(size_t index) const {
275 if (index == 0) return target_loc_;
276 return location_sig_->GetParam(index - 1);
277 }
278
279 MachineSignature* GetMachineSignature(Zone* zone) const;
280
281 MachineType GetReturnType(size_t index) const {
282 return location_sig_->GetReturn(index).GetType();
283 }
284
285 MachineType GetInputType(size_t index) const {
286 if (index == 0) return target_type_;
287 return location_sig_->GetParam(index - 1).GetType();
288 }
289
290 MachineType GetParameterType(size_t index) const {
291 return location_sig_->GetParam(index).GetType();
292 }
293
294 StackArgumentOrder GetStackArgumentOrder() const { return stack_order_; }
295
296 // Operator properties describe how this call can be optimized, if at all.
297 Operator::Properties properties() const { return properties_; }
298
299 // Get the callee-saved registers, if any, across this call.
300 RegList CalleeSavedRegisters() const { return callee_saved_registers_; }
301
302 // Get the callee-saved FP registers, if any, across this call.
304 return callee_saved_fp_registers_;
305 }
306
307 const char* debug_name() const { return debug_name_; }
308
309 // Difference between the number of parameter slots of *this* and
310 // *tail_caller* (callee minus caller).
311 int GetStackParameterDelta(const CallDescriptor* tail_caller) const;
312
313 // Returns the offset to the area below the parameter slots on the stack,
314 // relative to callee slot 0, the return address. If there are no parameter
315 // slots, returns +1.
316 int GetOffsetToFirstUnusedStackSlot() const;
317
318 // Returns the offset to the area above the return slots on the stack,
319 // relative to callee slot 0, the return address. If there are no return
320 // slots, returns the offset to the lowest slot of the parameter area.
321 // If there are no parameter slots, returns 0.
322 int GetOffsetToReturns() const;
323
324 // Returns two 16-bit numbers packed together: (first slot << 16) | num_slots.
325 uint32_t GetTaggedParameterSlots() const;
326
327 bool CanTailCall(const CallDescriptor* callee) const;
328
329 int CalculateFixedFrameSize(CodeKind code_kind) const;
330
331 RegList AllocatableRegisters() const { return allocatable_registers_; }
332
334 return !allocatable_registers_.is_empty();
335 }
336
337 EncodedCSignature ToEncodedCSignature() const;
338
339 private:
340 void ComputeParamCounts() const;
341
342 friend class Linkage;
343
344 const Kind kind_;
349 const size_t param_slot_count_;
350 const size_t return_slot_count_;
354 // Non-zero value means restricting the set of allocatable registers for
355 // register allocator to use.
359 const char* const debug_name_;
360
362
363 mutable std::optional<size_t> gp_param_count_;
364 mutable std::optional<size_t> fp_param_count_;
365};
366
368
369std::ostream& operator<<(std::ostream& os, const CallDescriptor& d);
370V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
371 const CallDescriptor::Kind& k);
372
373#if V8_ENABLE_WEBASSEMBLY
374// Lowers a wasm CallDescriptor for 32 bit platforms by replacing i64 parameters
375// and returns with two i32s each.
376V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptor(
377 Zone* zone, const CallDescriptor* call_descriptor);
378#endif
379
380// Defines the linkage for a compilation, including the calling conventions
381// for incoming parameters and return value(s) as well as the outgoing calling
382// convention for any kind of call. Linkage is generally architecture-specific.
383//
384// Can be used to translate {arg_index} (i.e. index of the call node input) as
385// well as {param_index} (i.e. as stored in parameter nodes) into an operator
386// representing the architecture-specific location. The following call node
387// layouts are supported (where {n} is the number of value inputs):
388//
389// #0 #1 #2 [...] #n
390// Call[CodeStub] code, arg 1, arg 2, [...], context
391// Call[JSFunction] function, rcvr, arg 1, [...], new, #arg, context
392// Call[Runtime] CEntry, arg 1, arg 2, [...], fun, #arg, context
393// Call[BytecodeDispatch] address, arg 1, arg 2, [...]
394class V8_EXPORT_PRIVATE Linkage : public NON_EXPORTED_BASE(ZoneObject) {
395 public:
396 explicit Linkage(CallDescriptor* incoming) : incoming_(incoming) {}
397 Linkage(const Linkage&) = delete;
398 Linkage& operator=(const Linkage&) = delete;
399
400 static CallDescriptor* ComputeIncoming(Zone* zone,
402
403 // The call descriptor for this compilation unit describes the locations
404 // of incoming parameters and the outgoing return value(s).
405 CallDescriptor* GetIncomingDescriptor() const { return incoming_; }
406 // Calls to JSFunctions should never overwrite the {properties}, but calls to
407 // known builtins might.
408 static CallDescriptor* GetJSCallDescriptor(
409 Zone* zone, bool is_osr, int parameter_count, CallDescriptor::Flags flags,
410 Operator::Properties properties =
411 Operator::kNoProperties /* use with care! */);
412
413 static CallDescriptor* GetRuntimeCallDescriptor(
414 Zone* zone, Runtime::FunctionId function, int js_parameter_count,
416 LazyDeoptOnThrow lazy_deopt_on_throw = LazyDeoptOnThrow::kNo);
417
418 static CallDescriptor* GetCEntryStubCallDescriptor(
419 Zone* zone, int return_count, int js_parameter_count,
420 const char* debug_name, Operator::Properties properties,
422 StackArgumentOrder stack_order = StackArgumentOrder::kDefault);
423
424 static CallDescriptor* GetStubCallDescriptor(
425 Zone* zone, const CallInterfaceDescriptor& descriptor,
426 int stack_parameter_count, CallDescriptor::Flags flags,
427 Operator::Properties properties = Operator::kNoProperties,
428 StubCallMode stub_mode = StubCallMode::kCallCodeObject);
429
430 static CallDescriptor* GetBytecodeDispatchCallDescriptor(
431 Zone* zone, const CallInterfaceDescriptor& descriptor,
432 int stack_parameter_count);
433
434 // Creates a call descriptor for simplified C calls that is appropriate
435 // for the host platform. This simplified calling convention only supports
436 // integers and pointers of one word size each, i.e. no floating point,
437 // structs, pointers to members, etc.
438 static CallDescriptor* GetSimplifiedCDescriptor(
439 Zone* zone, const MachineSignature* sig,
440 CallDescriptor::Flags flags = CallDescriptor::kNoFlags,
441 Operator::Properties properties = Operator::kNoThrow);
442
443 // Get the location of an (incoming) parameter to this function.
445 return incoming_->GetInputLocation(index + 1); // + 1 to skip target.
446 }
447
448 // Get the machine type of an (incoming) parameter to this function.
449 MachineType GetParameterType(int index) const {
450 return incoming_->GetInputType(index + 1); // + 1 to skip target.
451 }
452
453 // Get the location where this function should place its return value.
454 LinkageLocation GetReturnLocation(size_t index = 0) const {
455 return incoming_->GetReturnLocation(index);
456 }
457
458 // Get the machine type of this function's return value.
459 MachineType GetReturnType(size_t index = 0) const {
460 return incoming_->GetReturnType(index);
461 }
462
463 bool ParameterHasSecondaryLocation(int index) const;
464 LinkageLocation GetParameterSecondaryLocation(int index) const;
465
466 static bool NeedsFrameStateInput(Runtime::FunctionId function);
467
468 // Get the location where an incoming OSR value is stored.
469 LinkageLocation GetOsrValueLocation(int index) const;
470
471 // A special {Parameter} index for Stub Calls that represents context.
473 return parameter_count + 0;
474 }
475
476 // A special {Parameter} index for JSCalls that represents the new target.
478 return parameter_count + 0;
479 }
480
481 // A special {Parameter} index for JSCalls that represents the argument count.
483 return GetJSCallNewTargetParamIndex(parameter_count) + 1;
484 }
485
486#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
487 // A special {Parameter} index for JSCalls that represents the dispatch
488 // handle.
489 static constexpr int GetJSCallDispatchHandleParamIndex(int parameter_count) {
490 return GetJSCallArgCountParamIndex(parameter_count) + 1;
491 }
492#endif
493
494 // A special {Parameter} index for JSCalls that represents the context.
495 static constexpr int GetJSCallContextParamIndex(int parameter_count) {
496#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
497 return GetJSCallDispatchHandleParamIndex(parameter_count) + 1;
498#else
499 return GetJSCallArgCountParamIndex(parameter_count) + 1;
500#endif
501 }
502
503 // A special {Parameter} index for JSCalls that represents the closure.
504 static constexpr int kJSCallClosureParamIndex = kJSCallClosureParameterIndex;
505 static_assert(kJSCallClosureParamIndex == -1);
506
507 // A special {OsrValue} index to indicate the context spill slot.
508 static const int kOsrContextSpillSlotIndex = -1;
509
510 // A special {OsrValue} index to indicate the accumulator register.
511 static const int kOsrAccumulatorRegisterIndex = -1;
512
513 private:
515};
516
517} // namespace compiler
518} // namespace internal
519} // namespace v8
520#undef NO_INLINE_FOR_ARM64_MSVC
521
522#endif // V8_COMPILER_LINKAGE_H_
#define DEFINE_OPERATORS_FOR_FLAGS(Type)
Definition flags.h:100
int16_t parameter_count
Definition builtins.cc:67
Builtins::Kind kind
Definition builtins.cc:40
const CodeEntrypointTag tag_
Definition linkage.h:345
CodeEntrypointTag tag() const
Definition linkage.h:146
CallDescriptor(Kind kind, CodeEntrypointTag tag, MachineType target_type, LinkageLocation target_loc, LocationSignature *location_sig, size_t param_slot_count, Operator::Properties properties, RegList callee_saved_registers, DoubleRegList callee_saved_fp_registers, Flags flags, const char *debug_name="", StackArgumentOrder stack_order=StackArgumentOrder::kDefault, const RegList allocatable_registers={}, size_t return_slot_count=0, uint64_t signature_hash=kInvalidWasmSignatureHash)
Definition linkage.h:107
const LocationSignature *const location_sig_
Definition linkage.h:348
std::optional< size_t > gp_param_count_
Definition linkage.h:363
const StackArgumentOrder stack_order_
Definition linkage.h:358
CallDescriptor & operator=(const CallDescriptor &)=delete
int GetStackIndexFromSlot(int slot_index) const
Definition linkage.h:239
const Operator::Properties properties_
Definition linkage.h:351
MachineType GetInputType(size_t index) const
Definition linkage.h:285
DoubleRegList CalleeSavedFPRegisters() const
Definition linkage.h:303
std::optional< size_t > fp_param_count_
Definition linkage.h:364
MachineType GetParameterType(size_t index) const
Definition linkage.h:290
const DoubleRegList callee_saved_fp_registers_
Definition linkage.h:353
const LinkageLocation target_loc_
Definition linkage.h:347
CallDescriptor(const CallDescriptor &)=delete
StackArgumentOrder GetStackArgumentOrder() const
Definition linkage.h:294
MachineType GetReturnType(size_t index) const
Definition linkage.h:281
LinkageLocation GetReturnLocation(size_t index) const
Definition linkage.h:270
Operator::Properties properties() const
Definition linkage.h:297
LinkageLocation GetInputLocation(size_t index) const
Definition linkage.h:274
const char * debug_name() const
Definition linkage.h:307
Linkage(const Linkage &)=delete
CallDescriptor *const incoming_
Definition linkage.h:514
static constexpr int GetJSCallNewTargetParamIndex(int parameter_count)
Definition linkage.h:477
static constexpr int GetJSCallContextParamIndex(int parameter_count)
Definition linkage.h:495
Linkage & operator=(const Linkage &)=delete
LinkageLocation GetReturnLocation(size_t index=0) const
Definition linkage.h:454
static constexpr int GetJSCallArgCountParamIndex(int parameter_count)
Definition linkage.h:482
static int GetStubCallContextParamIndex(int parameter_count)
Definition linkage.h:472
Linkage(CallDescriptor *incoming)
Definition linkage.h:396
MachineType GetParameterType(int index) const
Definition linkage.h:449
LinkageLocation GetParameterLocation(int index) const
Definition linkage.h:444
MachineType GetReturnType(size_t index=0) const
Definition linkage.h:459
CallDescriptor * GetIncomingDescriptor() const
Definition linkage.h:405
JSRegExp::Flags flags_
const PropertyKind kind_
other heap size flags(e.g. initial_heap_size) take precedence") DEFINE_SIZE_T( max_shared_heap_size
constexpr RegList kNoCalleeSaved
Definition linkage.h:36
std::ostream & operator<<(std::ostream &os, AccessMode access_mode)
constexpr DoubleRegList kNoCalleeSavedFp
Definition linkage.h:37
constexpr int kJSCallClosureParameterIndex
Definition globals.h:2789
constexpr uint64_t kInvalidWasmSignatureHash
Definition globals.h:2896
constexpr int kCodeEntrypointTagShift
#define NON_EXPORTED_BASE(code)
#define CHECK_NE(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460