xref: /third_party/node/deps/v8/src/compiler/linkage.h (revision 1cb0ef41)
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 "src/base/compiler-specific.h"
9#include "src/base/flags.h"
10#include "src/codegen/interface-descriptors.h"
11#include "src/codegen/machine-type.h"
12#include "src/codegen/register.h"
13#include "src/codegen/reglist.h"
14#include "src/codegen/signature.h"
15#include "src/common/globals.h"
16#include "src/compiler/frame.h"
17#include "src/compiler/operator.h"
18#include "src/execution/encoded-c-signature.h"
19#include "src/runtime/runtime.h"
20#include "src/zone/zone.h"
21
22#if !defined(__clang__) && defined(_M_ARM64)
23// _M_ARM64 is an MSVC-specific macro that clang-cl emulates.
24#define NO_INLINE_FOR_ARM64_MSVC __declspec(noinline)
25#else
26#define NO_INLINE_FOR_ARM64_MSVC
27#endif
28
29namespace v8 {
30class CFunctionInfo;
31
32namespace internal {
33
34class CallInterfaceDescriptor;
35class OptimizedCompilationInfo;
36
37namespace compiler {
38
39constexpr RegList kNoCalleeSaved;
40constexpr DoubleRegList kNoCalleeSavedFp;
41
42class OsrHelper;
43
44// Describes the location for a parameter or a return value to a call.
45class LinkageLocation {
46 public:
47  bool operator==(const LinkageLocation& other) const {
48    return bit_field_ == other.bit_field_ &&
49           machine_type_ == other.machine_type_;
50  }
51
52  bool operator!=(const LinkageLocation& other) const {
53    return !(*this == other);
54  }
55
56  static bool IsSameLocation(const LinkageLocation& a,
57                             const LinkageLocation& b) {
58    // Different MachineTypes may end up at the same physical location. With the
59    // sub-type check we make sure that types like {AnyTagged} and
60    // {TaggedPointer} which would end up with the same physical location are
61    // considered equal here.
62    return (a.bit_field_ == b.bit_field_) &&
63           (IsSubtype(a.machine_type_.representation(),
64                      b.machine_type_.representation()) ||
65            IsSubtype(b.machine_type_.representation(),
66                      a.machine_type_.representation()));
67  }
68
69  static LinkageLocation ForAnyRegister(
70      MachineType type = MachineType::None()) {
71    return LinkageLocation(REGISTER, ANY_REGISTER, type);
72  }
73
74  static LinkageLocation ForRegister(int32_t reg,
75                                     MachineType type = MachineType::None()) {
76    DCHECK_LE(0, reg);
77    return LinkageLocation(REGISTER, reg, type);
78  }
79
80  static LinkageLocation ForCallerFrameSlot(int32_t slot, MachineType type) {
81    DCHECK_GT(0, slot);
82    return LinkageLocation(STACK_SLOT, slot, type);
83  }
84
85  static LinkageLocation ForCalleeFrameSlot(int32_t slot, MachineType type) {
86    // TODO(titzer): bailout instead of crashing here.
87    DCHECK(slot >= 0 && slot < LinkageLocation::MAX_STACK_SLOT);
88    return LinkageLocation(STACK_SLOT, slot, type);
89  }
90
91  static LinkageLocation ForSavedCallerReturnAddress() {
92    return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
93                               StandardFrameConstants::kCallerPCOffset) /
94                                  kSystemPointerSize,
95                              MachineType::Pointer());
96  }
97
98  static LinkageLocation ForSavedCallerFramePtr() {
99    return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
100                               StandardFrameConstants::kCallerFPOffset) /
101                                  kSystemPointerSize,
102                              MachineType::Pointer());
103  }
104
105  static LinkageLocation ForSavedCallerConstantPool() {
106    DCHECK(V8_EMBEDDED_CONSTANT_POOL);
107    return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
108                               StandardFrameConstants::kConstantPoolOffset) /
109                                  kSystemPointerSize,
110                              MachineType::AnyTagged());
111  }
112
113  static LinkageLocation ForSavedCallerFunction() {
114    return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
115                               StandardFrameConstants::kFunctionOffset) /
116                                  kSystemPointerSize,
117                              MachineType::AnyTagged());
118  }
119
120  static LinkageLocation ConvertToTailCallerLocation(
121      LinkageLocation caller_location, int stack_param_delta) {
122    if (!caller_location.IsRegister()) {
123      return LinkageLocation(STACK_SLOT,
124                             caller_location.GetLocation() + stack_param_delta,
125                             caller_location.GetType());
126    }
127    return caller_location;
128  }
129
130  MachineType GetType() const { return machine_type_; }
131
132  int GetSizeInPointers() const {
133    return ElementSizeInPointers(GetType().representation());
134  }
135
136  int32_t GetLocation() const {
137    // We can't use LocationField::decode here because it doesn't work for
138    // negative values!
139    return static_cast<int32_t>(bit_field_ & LocationField::kMask) >>
140           LocationField::kShift;
141  }
142
143  NO_INLINE_FOR_ARM64_MSVC bool IsRegister() const {
144    return TypeField::decode(bit_field_) == REGISTER;
145  }
146  bool IsAnyRegister() const {
147    return IsRegister() && GetLocation() == ANY_REGISTER;
148  }
149  bool IsCallerFrameSlot() const { return !IsRegister() && GetLocation() < 0; }
150  bool IsCalleeFrameSlot() const { return !IsRegister() && GetLocation() >= 0; }
151
152  int32_t AsRegister() const {
153    DCHECK(IsRegister());
154    return GetLocation();
155  }
156  int32_t AsCallerFrameSlot() const {
157    DCHECK(IsCallerFrameSlot());
158    return GetLocation();
159  }
160  int32_t AsCalleeFrameSlot() const {
161    DCHECK(IsCalleeFrameSlot());
162    return GetLocation();
163  }
164
165 private:
166  enum LocationType { REGISTER, STACK_SLOT };
167
168  using TypeField = base::BitField<LocationType, 0, 1>;
169  using LocationField = TypeField::Next<int32_t, 31>;
170
171  static constexpr int32_t ANY_REGISTER = -1;
172  static constexpr int32_t MAX_STACK_SLOT = 32767;
173
174  LinkageLocation(LocationType type, int32_t location,
175                  MachineType machine_type) {
176    bit_field_ = TypeField::encode(type) |
177                 // {location} can be -1 (ANY_REGISTER).
178                 ((static_cast<uint32_t>(location) << LocationField::kShift) &
179                  LocationField::kMask);
180    machine_type_ = machine_type;
181  }
182
183  int32_t bit_field_;
184  MachineType machine_type_;
185};
186
187using LocationSignature = Signature<LinkageLocation>;
188
189// Describes a call to various parts of the compiler. Every call has the notion
190// of a "target", which is the first input to the call.
191class V8_EXPORT_PRIVATE CallDescriptor final
192    : public NON_EXPORTED_BASE(ZoneObject) {
193 public:
194  // Describes the kind of this call, which determines the target.
195  enum Kind {
196    kCallCodeObject,         // target is a Code object
197    kCallJSFunction,         // target is a JSFunction object
198    kCallAddress,            // target is a machine pointer
199#if V8_ENABLE_WEBASSEMBLY    // ↓ WebAssembly only
200    kCallWasmCapiFunction,   // target is a Wasm C API function
201    kCallWasmFunction,       // target is a wasm function
202    kCallWasmImportWrapper,  // target is a wasm import wrapper
203#endif                       // ↑ WebAssembly only
204    kCallBuiltinPointer,     // target is a builtin pointer
205  };
206
207  // NOTE: The lowest 10 bits of the Flags field are encoded in InstructionCode
208  // (for use in the code generator). All higher bits are lost.
209  static constexpr int kFlagsBitsEncodedInInstructionCode = 10;
210  enum Flag {
211    kNoFlags = 0u,
212    kNeedsFrameState = 1u << 0,
213    kHasExceptionHandler = 1u << 1,
214    kCanUseRoots = 1u << 2,
215    // Causes the code generator to initialize the root register.
216    kInitializeRootRegister = 1u << 3,
217    // Does not ever try to allocate space on our heap.
218    kNoAllocate = 1u << 4,
219    // Use the kJavaScriptCallCodeStartRegister (fixed) register for the
220    // indirect target address when calling.
221    kFixedTargetRegister = 1u << 5,
222    kCallerSavedRegisters = 1u << 6,
223    // The kCallerSavedFPRegisters only matters (and set) when the more general
224    // flag for kCallerSavedRegisters above is also set.
225    kCallerSavedFPRegisters = 1u << 7,
226    // Tail calls for tier up are special (in fact they are different enough
227    // from normal tail calls to warrant a dedicated opcode; but they also have
228    // enough similar aspects that reusing the TailCall opcode is pragmatic).
229    // Specifically:
230    //
231    // 1. Caller and callee are both JS-linkage Code objects.
232    // 2. JS runtime arguments are passed unchanged from caller to callee.
233    // 3. JS runtime arguments are not attached as inputs to the TailCall node.
234    // 4. Prior to the tail call, frame and register state is torn down to just
235    //    before the caller frame was constructed.
236    // 5. Unlike normal tail calls, arguments adaptor frames (if present) are
237    //    *not* torn down.
238    //
239    // In other words, behavior is identical to a jmp instruction prior caller
240    // frame construction.
241    kIsTailCallForTierUp = 1u << 8,
242
243    // AIX has a function descriptor by default but it can be disabled for a
244    // certain CFunction call (only used for Kind::kCallAddress).
245    kNoFunctionDescriptor = 1u << 9,
246
247    // Flags past here are *not* encoded in InstructionCode and are thus not
248    // accessible from the code generator. See also
249    // kFlagsBitsEncodedInInstructionCode.
250  };
251  using Flags = base::Flags<Flag>;
252
253  CallDescriptor(Kind kind, MachineType target_type, LinkageLocation target_loc,
254                 LocationSignature* location_sig, size_t param_slot_count,
255                 Operator::Properties properties,
256                 RegList callee_saved_registers,
257                 DoubleRegList callee_saved_fp_registers, Flags flags,
258                 const char* debug_name = "",
259                 StackArgumentOrder stack_order = StackArgumentOrder::kDefault,
260#if V8_ENABLE_WEBASSEMBLY
261                 const wasm::FunctionSig* wasm_sig = nullptr,
262#endif
263                 const RegList allocatable_registers = {},
264                 size_t return_slot_count = 0)
265      : kind_(kind),
266        target_type_(target_type),
267        target_loc_(target_loc),
268        location_sig_(location_sig),
269        param_slot_count_(param_slot_count),
270        return_slot_count_(return_slot_count),
271        properties_(properties),
272        callee_saved_registers_(callee_saved_registers),
273        callee_saved_fp_registers_(callee_saved_fp_registers),
274        allocatable_registers_(allocatable_registers),
275        flags_(flags),
276        stack_order_(stack_order),
277#if V8_ENABLE_WEBASSEMBLY
278        wasm_sig_(wasm_sig),
279#endif
280        debug_name_(debug_name) {
281  }
282
283  CallDescriptor(const CallDescriptor&) = delete;
284  CallDescriptor& operator=(const CallDescriptor&) = delete;
285
286  // Returns the kind of this call.
287  Kind kind() const { return kind_; }
288
289  // Returns {true} if this descriptor is a call to a C function.
290  bool IsCFunctionCall() const { return kind_ == kCallAddress; }
291
292  // Returns {true} if this descriptor is a call to a JSFunction.
293  bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }
294
295#if V8_ENABLE_WEBASSEMBLY
296  // Returns {true} if this descriptor is a call to a WebAssembly function.
297  bool IsWasmFunctionCall() const { return kind_ == kCallWasmFunction; }
298
299  // Returns {true} if this descriptor is a call to a WebAssembly function.
300  bool IsWasmImportWrapper() const { return kind_ == kCallWasmImportWrapper; }
301
302  // Returns {true} if this descriptor is a call to a Wasm C API function.
303  bool IsWasmCapiFunction() const { return kind_ == kCallWasmCapiFunction; }
304
305  // Returns the wasm signature for this call based on the real parameter types.
306  const wasm::FunctionSig* wasm_sig() const { return wasm_sig_; }
307#endif  // V8_ENABLE_WEBASSEMBLY
308
309  bool RequiresFrameAsIncoming() const {
310    if (IsCFunctionCall() || IsJSFunctionCall()) return true;
311#if V8_ENABLE_WEBASSEMBLY
312    if (IsWasmFunctionCall()) return true;
313#endif  // V8_ENABLE_WEBASSEMBLY
314    if (CalleeSavedRegisters() != kNoCalleeSaved) return true;
315    return false;
316  }
317
318  // The number of return values from this call.
319  size_t ReturnCount() const { return location_sig_->return_count(); }
320
321  // The number of C parameters to this call. The following invariant
322  // should hold true:
323  // ParameterCount() == GPParameterCount() + FPParameterCount()
324  size_t ParameterCount() const { return location_sig_->parameter_count(); }
325
326  // The number of general purpose C parameters to this call.
327  size_t GPParameterCount() const {
328    if (!gp_param_count_) {
329      ComputeParamCounts();
330    }
331    return gp_param_count_.value();
332  }
333
334  // The number of floating point C parameters to this call.
335  size_t FPParameterCount() const {
336    if (!fp_param_count_) {
337      ComputeParamCounts();
338    }
339    return fp_param_count_.value();
340  }
341
342  // The number of stack parameter slots to the call.
343  size_t ParameterSlotCount() const { return param_slot_count_; }
344
345  // The number of stack return value slots from the call.
346  size_t ReturnSlotCount() const { return return_slot_count_; }
347
348  // The number of parameters to the JS function call.
349  size_t JSParameterCount() const {
350    DCHECK(IsJSFunctionCall());
351    return param_slot_count_;
352  }
353
354  int GetStackIndexFromSlot(int slot_index) const {
355    switch (GetStackArgumentOrder()) {
356      case StackArgumentOrder::kDefault:
357        return -slot_index - 1;
358      case StackArgumentOrder::kJS:
359        return slot_index + static_cast<int>(ParameterSlotCount());
360    }
361  }
362
363  // The total number of inputs to this call, which includes the target,
364  // receiver, context, etc.
365  // TODO(titzer): this should input the framestate input too.
366  size_t InputCount() const { return 1 + location_sig_->parameter_count(); }
367
368  size_t FrameStateCount() const { return NeedsFrameState() ? 1 : 0; }
369
370  Flags flags() const { return flags_; }
371
372  bool NeedsFrameState() const { return flags() & kNeedsFrameState; }
373  bool InitializeRootRegister() const {
374    return flags() & kInitializeRootRegister;
375  }
376  bool NeedsCallerSavedRegisters() const {
377    return flags() & kCallerSavedRegisters;
378  }
379  bool NeedsCallerSavedFPRegisters() const {
380    return flags() & kCallerSavedFPRegisters;
381  }
382  bool IsTailCallForTierUp() const { return flags() & kIsTailCallForTierUp; }
383  bool NoFunctionDescriptor() const { return flags() & kNoFunctionDescriptor; }
384
385  LinkageLocation GetReturnLocation(size_t index) const {
386    return location_sig_->GetReturn(index);
387  }
388
389  LinkageLocation GetInputLocation(size_t index) const {
390    if (index == 0) return target_loc_;
391    return location_sig_->GetParam(index - 1);
392  }
393
394  MachineSignature* GetMachineSignature(Zone* zone) const;
395
396  MachineType GetReturnType(size_t index) const {
397    return location_sig_->GetReturn(index).GetType();
398  }
399
400  MachineType GetInputType(size_t index) const {
401    if (index == 0) return target_type_;
402    return location_sig_->GetParam(index - 1).GetType();
403  }
404
405  MachineType GetParameterType(size_t index) const {
406    return location_sig_->GetParam(index).GetType();
407  }
408
409  StackArgumentOrder GetStackArgumentOrder() const { return stack_order_; }
410
411  // Operator properties describe how this call can be optimized, if at all.
412  Operator::Properties properties() const { return properties_; }
413
414  // Get the callee-saved registers, if any, across this call.
415  RegList CalleeSavedRegisters() const { return callee_saved_registers_; }
416
417  // Get the callee-saved FP registers, if any, across this call.
418  DoubleRegList CalleeSavedFPRegisters() const {
419    return callee_saved_fp_registers_;
420  }
421
422  const char* debug_name() const { return debug_name_; }
423
424  // Difference between the number of parameter slots of *this* and
425  // *tail_caller* (callee minus caller).
426  int GetStackParameterDelta(const CallDescriptor* tail_caller) const;
427
428  // Returns the offset to the area below the parameter slots on the stack,
429  // relative to callee slot 0, the return address. If there are no parameter
430  // slots, returns +1.
431  int GetOffsetToFirstUnusedStackSlot() const;
432
433  // Returns the offset to the area above the return slots on the stack,
434  // relative to callee slot 0, the return address. If there are no return
435  // slots, returns the offset to the lowest slot of the parameter area.
436  // If there are no parameter slots, returns 0.
437  int GetOffsetToReturns() const;
438
439  // Returns two 16-bit numbers packed together: (first slot << 16) | num_slots.
440  uint32_t GetTaggedParameterSlots() const;
441
442  bool CanTailCall(const CallDescriptor* callee) const;
443
444  int CalculateFixedFrameSize(CodeKind code_kind) const;
445
446  RegList AllocatableRegisters() const { return allocatable_registers_; }
447
448  bool HasRestrictedAllocatableRegisters() const {
449    return !allocatable_registers_.is_empty();
450  }
451
452  EncodedCSignature ToEncodedCSignature() const;
453
454 private:
455  void ComputeParamCounts() const;
456
457  friend class Linkage;
458
459  const Kind kind_;
460  const MachineType target_type_;
461  const LinkageLocation target_loc_;
462  const LocationSignature* const location_sig_;
463  const size_t param_slot_count_;
464  const size_t return_slot_count_;
465  const Operator::Properties properties_;
466  const RegList callee_saved_registers_;
467  const DoubleRegList callee_saved_fp_registers_;
468  // Non-zero value means restricting the set of allocatable registers for
469  // register allocator to use.
470  const RegList allocatable_registers_;
471  const Flags flags_;
472  const StackArgumentOrder stack_order_;
473#if V8_ENABLE_WEBASSEMBLY
474  const wasm::FunctionSig* wasm_sig_;
475#endif
476  const char* const debug_name_;
477
478  mutable base::Optional<size_t> gp_param_count_;
479  mutable base::Optional<size_t> fp_param_count_;
480};
481
482DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags)
483
484std::ostream& operator<<(std::ostream& os, const CallDescriptor& d);
485V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
486                                           const CallDescriptor::Kind& k);
487
488// Defines the linkage for a compilation, including the calling conventions
489// for incoming parameters and return value(s) as well as the outgoing calling
490// convention for any kind of call. Linkage is generally architecture-specific.
491//
492// Can be used to translate {arg_index} (i.e. index of the call node input) as
493// well as {param_index} (i.e. as stored in parameter nodes) into an operator
494// representing the architecture-specific location. The following call node
495// layouts are supported (where {n} is the number of value inputs):
496//
497//                        #0          #1     #2     [...]             #n
498// Call[CodeStub]         code,       arg 1, arg 2, [...],            context
499// Call[JSFunction]       function,   rcvr,  arg 1, [...], new, #arg, context
500// Call[Runtime]          CEntry,     arg 1, arg 2, [...], fun, #arg, context
501// Call[BytecodeDispatch] address,    arg 1, arg 2, [...]
502class V8_EXPORT_PRIVATE Linkage : public NON_EXPORTED_BASE(ZoneObject) {
503 public:
504  explicit Linkage(CallDescriptor* incoming) : incoming_(incoming) {}
505  Linkage(const Linkage&) = delete;
506  Linkage& operator=(const Linkage&) = delete;
507
508  static CallDescriptor* ComputeIncoming(Zone* zone,
509                                         OptimizedCompilationInfo* info);
510
511  // The call descriptor for this compilation unit describes the locations
512  // of incoming parameters and the outgoing return value(s).
513  CallDescriptor* GetIncomingDescriptor() const { return incoming_; }
514  static CallDescriptor* GetJSCallDescriptor(Zone* zone, bool is_osr,
515                                             int parameter_count,
516                                             CallDescriptor::Flags flags);
517
518  static CallDescriptor* GetRuntimeCallDescriptor(
519      Zone* zone, Runtime::FunctionId function, int js_parameter_count,
520      Operator::Properties properties, CallDescriptor::Flags flags);
521
522  static CallDescriptor* GetCEntryStubCallDescriptor(
523      Zone* zone, int return_count, int js_parameter_count,
524      const char* debug_name, Operator::Properties properties,
525      CallDescriptor::Flags flags,
526      StackArgumentOrder stack_order = StackArgumentOrder::kDefault);
527
528  static CallDescriptor* GetStubCallDescriptor(
529      Zone* zone, const CallInterfaceDescriptor& descriptor,
530      int stack_parameter_count, CallDescriptor::Flags flags,
531      Operator::Properties properties = Operator::kNoProperties,
532      StubCallMode stub_mode = StubCallMode::kCallCodeObject);
533
534  static CallDescriptor* GetBytecodeDispatchCallDescriptor(
535      Zone* zone, const CallInterfaceDescriptor& descriptor,
536      int stack_parameter_count);
537
538  // Creates a call descriptor for simplified C calls that is appropriate
539  // for the host platform. This simplified calling convention only supports
540  // integers and pointers of one word size each, i.e. no floating point,
541  // structs, pointers to members, etc.
542  static CallDescriptor* GetSimplifiedCDescriptor(
543      Zone* zone, const MachineSignature* sig,
544      CallDescriptor::Flags flags = CallDescriptor::kNoFlags);
545
546  // Get the location of an (incoming) parameter to this function.
547  LinkageLocation GetParameterLocation(int index) const {
548    return incoming_->GetInputLocation(index + 1);  // + 1 to skip target.
549  }
550
551  // Get the machine type of an (incoming) parameter to this function.
552  MachineType GetParameterType(int index) const {
553    return incoming_->GetInputType(index + 1);  // + 1 to skip target.
554  }
555
556  // Get the location where this function should place its return value.
557  LinkageLocation GetReturnLocation(size_t index = 0) const {
558    return incoming_->GetReturnLocation(index);
559  }
560
561  // Get the machine type of this function's return value.
562  MachineType GetReturnType(size_t index = 0) const {
563    return incoming_->GetReturnType(index);
564  }
565
566  bool ParameterHasSecondaryLocation(int index) const;
567  LinkageLocation GetParameterSecondaryLocation(int index) const;
568
569  static bool NeedsFrameStateInput(Runtime::FunctionId function);
570
571  // Get the location where an incoming OSR value is stored.
572  LinkageLocation GetOsrValueLocation(int index) const;
573
574  // A special {Parameter} index for Stub Calls that represents context.
575  static int GetStubCallContextParamIndex(int parameter_count) {
576    return parameter_count + 0;  // Parameter (arity + 0) is special.
577  }
578
579  // A special {Parameter} index for JSCalls that represents the new target.
580  static constexpr int GetJSCallNewTargetParamIndex(int parameter_count) {
581    return parameter_count + 0;  // Parameter (arity + 0) is special.
582  }
583
584  // A special {Parameter} index for JSCalls that represents the argument count.
585  static constexpr int GetJSCallArgCountParamIndex(int parameter_count) {
586    return parameter_count + 1;  // Parameter (arity + 1) is special.
587  }
588
589  // A special {Parameter} index for JSCalls that represents the context.
590  static constexpr int GetJSCallContextParamIndex(int parameter_count) {
591    return parameter_count + 2;  // Parameter (arity + 2) is special.
592  }
593
594  // A special {Parameter} index for JSCalls that represents the closure.
595  static constexpr int kJSCallClosureParamIndex = -1;
596
597  // A special {OsrValue} index to indicate the context spill slot.
598  static const int kOsrContextSpillSlotIndex = -1;
599
600  // A special {OsrValue} index to indicate the accumulator register.
601  static const int kOsrAccumulatorRegisterIndex = -1;
602
603 private:
604  CallDescriptor* const incoming_;
605};
606
607}  // namespace compiler
608}  // namespace internal
609}  // namespace v8
610#undef NO_INLINE_FOR_ARM64_MSVC
611
612#endif  // V8_COMPILER_LINKAGE_H_
613