// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_EXECUTION_FRAME_CONSTANTS_H_
#define V8_EXECUTION_FRAME_CONSTANTS_H_

#include "src/common/globals.h"
#include "src/flags/flags.h"

namespace v8 {
namespace internal {

// Every pointer in a frame has a slot id. On 32-bit platforms, doubles consume
// two slots.
//
// Stack slot indices >= 0 access the callee stack with slot 0 corresponding to
// the callee's saved return address and 1 corresponding to the saved frame
// pointer. Some frames have additional information stored in the fixed header,
// for example JSFunctions store the function context and marker in the fixed
// header, with slot index 2 corresponding to the current function context and 3
// corresponding to the frame marker/JSFunction.
//
//  slot      JS frame
//       +-----------------+--------------------------------
//  -n-1 |   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n   |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2   |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1   |   parameter 0   |                            v
//  -----+-----------------+--------------------------------
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | Fixed                      |
//       |- - - - - - - - -| Header <-- frame ptr       |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |Context/Frm. Type|   v   if a constant pool   |
//       |-----------------+----    is used, cp = 1,    |
// 3+cp  |                 |   ^   otherwise, cp = 0    |
//       |- - - - - - - - -|   |                        |
// 4+cp  |                 |   |                      Callee
//       |- - - - - - - - -|   |                   frame slots
//  ...  |                 | Frame slots           (slot >= 0)
//       |- - - - - - - - -|   |                        |
//       |                 |   v                        |
//  -----+-----------------+----- <-- stack ptr -------------
//
class CommonFrameConstants : public AllStatic {
 public:
  static constexpr int kCallerFPOffset = 0 * kSystemPointerSize;
  static constexpr int kCallerPCOffset = kCallerFPOffset + 1 * kFPOnStackSize;
  static constexpr int kCallerSPOffset = kCallerPCOffset + 1 * kPCOnStackSize;

  // Fixed part of the frame consists of return address, caller fp,
  // constant pool (if V8_EMBEDDED_CONSTANT_POOL_BOOL), context, and
  // function. CommonFrame::IterateExpressions assumes that kLastObjectOffset
  // is the last object pointer.
  static constexpr int kFixedFrameSizeAboveFp = kPCOnStackSize + kFPOnStackSize;
  static constexpr int kFixedSlotCountAboveFp =
      kFixedFrameSizeAboveFp / kSystemPointerSize;
  static constexpr int kCPSlotSize =
      V8_EMBEDDED_CONSTANT_POOL_BOOL ? kSystemPointerSize : 0;
  static constexpr int kCPSlotCount = kCPSlotSize / kSystemPointerSize;
  static constexpr int kConstantPoolOffset =
      kCPSlotSize ? -1 * kSystemPointerSize : 0;
  static constexpr int kContextOrFrameTypeSize = kSystemPointerSize;
  static constexpr int kContextOrFrameTypeOffset =
      -(kCPSlotSize + kContextOrFrameTypeSize);
};

// StandardFrames are used for both unoptimized and optimized JavaScript
// frames. They always have a context below the saved fp/constant
// pool, below that the JSFunction of the executing function and below that an
// integer (not a Smi) containing the actual number of arguments passed to the
// JavaScript code.
//
//  slot      JS frame
//       +-----------------+--------------------------------
//  -n-1 |   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n   |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2   |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1   |   parameter 0   |                            v
//  -----+-----------------+--------------------------------
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | Fixed                      |
//       |- - - - - - - - -| Header <-- frame ptr       |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |     Context     |   |   if a constant pool   |
//       |- - - - - - - - -|   |    is used, cp = 1,    |
// 3+cp  |    JSFunction   |   |   otherwise, cp = 0    |
//       |- - - - - - - - -|   |                        |
// 4+cp  |      argc       |   v                        |
//       +-----------------+----                        |
// 5+cp  |  expressions or |   ^                      Callee
//       |- - - - - - - - -|   |                   frame slots
//  ...  |  pushed values  | Frame slots           (slot >= 0)
//       |- - - - - - - - -|   |                        |
//       |                 |   v                        |
//  -----+-----------------+----- <-- stack ptr -------------
//
class StandardFrameConstants : public CommonFrameConstants {
 public:
  static constexpr int kFixedFrameSizeFromFp =
      3 * kSystemPointerSize + kCPSlotSize;
  static constexpr int kFixedFrameSize =
      kFixedFrameSizeAboveFp + kFixedFrameSizeFromFp;
  static constexpr int kFixedSlotCountFromFp =
      kFixedFrameSizeFromFp / kSystemPointerSize;
  static constexpr int kFixedSlotCount = kFixedFrameSize / kSystemPointerSize;
  static constexpr int kContextOffset = kContextOrFrameTypeOffset;
  static constexpr int kFunctionOffset = -2 * kSystemPointerSize - kCPSlotSize;
  static constexpr int kArgCOffset = -3 * kSystemPointerSize - kCPSlotSize;
  static constexpr int kExpressionsOffset =
      -4 * kSystemPointerSize - kCPSlotSize;
  static constexpr int kFirstPushedFrameValueOffset = kExpressionsOffset;
  static constexpr int kLastObjectOffset = kContextOffset;
};

// TypedFrames have a type maker value below the saved FP/constant pool to
// distinguish them from StandardFrames, which have a context in that position
// instead.
//
//  slot      JS frame
//       +-----------------+--------------------------------
//  -n-1 |   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n   |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2   |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1   |   parameter 0   |                            v
//  -----+-----------------+--------------------------------
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | Fixed                      |
//       |- - - - - - - - -| Header <-- frame ptr       |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |Frame Type Marker|   v   if a constant pool   |
//       |-----------------+----    is used, cp = 1,    |
// 3+cp  |  pushed value 0 |   ^   otherwise, cp = 0    |
//       |- - - - - - - - -|   |                        |
// 4+cp  |  pushed value 1 |   |                      Callee
//       |- - - - - - - - -|   |                   frame slots
//  ...  |                 | Frame slots           (slot >= 0)
//       |- - - - - - - - -|   |                        |
//       |                 |   v                        |
//  -----+-----------------+----- <-- stack ptr -------------
//
class TypedFrameConstants : public CommonFrameConstants {
 public:
  // FP-relative.
  static constexpr int kFrameTypeSize = kContextOrFrameTypeSize;
  static constexpr int kFrameTypeOffset = kContextOrFrameTypeOffset;
  static constexpr int kFixedFrameSizeFromFp = kCPSlotSize + kFrameTypeSize;
  static constexpr int kFixedSlotCountFromFp =
      kFixedFrameSizeFromFp / kSystemPointerSize;
  static constexpr int kFixedFrameSize =
      StandardFrameConstants::kFixedFrameSizeAboveFp + kFixedFrameSizeFromFp;
  static constexpr int kFixedSlotCount = kFixedFrameSize / kSystemPointerSize;
  static constexpr int kFirstPushedFrameValueOffset =
      -kFixedFrameSizeFromFp - kSystemPointerSize;
};

#define FRAME_PUSHED_VALUE_OFFSET(parent, x) \
  (parent::kFirstPushedFrameValueOffset - (x)*kSystemPointerSize)
#define FRAME_SIZE(parent, count) \
  (parent::kFixedFrameSize + (count)*kSystemPointerSize)
#define FRAME_SIZE_FROM_FP(parent, count) \
  (parent::kFixedFrameSizeFromFp + (count)*kSystemPointerSize)
#define DEFINE_FRAME_SIZES(parent, count)                                      \
  static constexpr int kFixedFrameSize = FRAME_SIZE(parent, count);            \
  static constexpr int kFixedSlotCount = kFixedFrameSize / kSystemPointerSize; \
  static constexpr int kFixedFrameSizeFromFp =                                 \
      FRAME_SIZE_FROM_FP(parent, count);                                       \
  static constexpr int kFixedSlotCountFromFp =                                 \
      kFixedFrameSizeFromFp / kSystemPointerSize;                              \
  static constexpr int kFirstPushedFrameValueOffset =                          \
      parent::kFirstPushedFrameValueOffset - (count) * kSystemPointerSize;     \
  /* The number of slots added on top of given parent frame type. */           \
  template <typename TParentFrameConstants>                                    \
  static constexpr int getExtraSlotsCountFrom() {                              \
    return kFixedSlotCount - TParentFrameConstants::kFixedSlotCount;           \
  }                                                                            \
  /* TODO(ishell): remove in favour of getExtraSlotsCountFrom() because */     \
  /* it's not clear from which base should we count "extra" - from direct */   \
  /* parent or maybe from parent's parent? */                                  \
  static constexpr int kExtraSlotCount =                                       \
      kFixedSlotCount - parent::kFixedSlotCount

#define STANDARD_FRAME_EXTRA_PUSHED_VALUE_OFFSET(x) \
  FRAME_PUSHED_VALUE_OFFSET(StandardFrameConstants, x)
#define DEFINE_STANDARD_FRAME_SIZES(count) \
  DEFINE_FRAME_SIZES(StandardFrameConstants, count)

#define TYPED_FRAME_PUSHED_VALUE_OFFSET(x) \
  FRAME_PUSHED_VALUE_OFFSET(TypedFrameConstants, x)
#define DEFINE_TYPED_FRAME_SIZES(count) \
  DEFINE_FRAME_SIZES(TypedFrameConstants, count)

class BuiltinFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  DEFINE_TYPED_FRAME_SIZES(2);
};

class ConstructFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kContextOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kLengthOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  static constexpr int kConstructorOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
  static constexpr int kPaddingOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(3);
  static constexpr int kNewTargetOrImplicitReceiverOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(4);
  DEFINE_TYPED_FRAME_SIZES(5);
  static constexpr int kLastObjectOffset = kContextOffset;
};

class FastConstructFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kContextOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kImplicitReceiverOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  DEFINE_TYPED_FRAME_SIZES(2);
};

#if V8_ENABLE_WEBASSEMBLY
class CWasmEntryFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative:
  static constexpr int kCEntryFPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  DEFINE_TYPED_FRAME_SIZES(1);
};

class WasmFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kWasmInstanceDataOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  DEFINE_TYPED_FRAME_SIZES(1);

  // The WasmTrapHandlerLandingPad builtin gets called from the WebAssembly
  // trap handler when an out-of-bounds memory access happened or when a null
  // reference gets dereferenced. This builtin then fakes a call from the
  // instruction that triggered the signal to the runtime. This is done by
  // setting a return address and then jumping to a builtin which will call
  // further to the runtime. As the return address we use the fault address +
  // {kProtectedInstructionReturnAddressOffset}. Using the fault address itself
  // would cause problems with safepoints and source positions.
  //
  // The problem with safepoints is that a safepoint has to be registered at the
  // return address, and that at most one safepoint should be registered at a
  // location. However, there could already be a safepoint registered at the
  // fault address if the fault address is the return address of a call.
  //
  // The problem with source positions is that the stack trace code looks for
  // the source position of a call before the return address. The source
  // position of the faulty memory access, however, is recorded at the fault
  // address. Therefore the stack trace code would not find the source position
  // if we used the fault address as the return address.
  static constexpr int kProtectedInstructionReturnAddressOffset = 1;
};

#if V8_ENABLE_DRUMBRAKE
class WasmInterpreterFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kWasmInstanceObjectOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  DEFINE_TYPED_FRAME_SIZES(1);
};

// Fixed frame slots shared by the interpreter wasm-to-js wrapper.
class WasmToJSInterpreterFrameConstants : public TypedFrameConstants {
 public:
  // This slot contains the number of slots at the top of the frame that need to
  // be scanned by the GC.
  static constexpr int kGCScanSlotLimitOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(0);

  // The stack pointer at the moment of the JS function call.
  static constexpr int kGCSPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);

  // Returned for a successful completion of the WasmToJS call (no exception).
  static constexpr int kSuccess = 1;
};

class WasmInterpreterCWasmEntryConstants : public TypedFrameConstants {
 public:
  // FP-relative:
  static constexpr int kCEntryFPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kSPFPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  DEFINE_TYPED_FRAME_SIZES(2);
};
#endif  // V8_ENABLE_DRUMBRAKE

class WasmExitFrameConstants : public WasmFrameConstants {
 public:
  // FP-relative.
  static const int kCallingPCOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  DEFINE_TYPED_FRAME_SIZES(2);
};

// Fixed frame slots used by the js-to-wasm wrapper.
class JSToWasmWrapperFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kResultArrayParamOffset = 2 * kSystemPointerSize;
  // A WasmTrustedInstanceData or WasmImportData depending on the callee.
  static constexpr int kImplicitArgOffset = 3 * kSystemPointerSize;

  // Contains RawPtr to stack-allocated buffer.
  static constexpr int kWrapperBufferOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(0);

  // Offsets into the wrapper buffer for values passed from Torque to the
  // assembly builtin.
  static constexpr size_t kWrapperBufferReturnCount = 0;
  static constexpr size_t kWrapperBufferRefReturnCount = 4;
  static constexpr size_t kWrapperBufferSigRepresentationArray = 8;
  static constexpr size_t kWrapperBufferStackReturnBufferSize = 16;
  static constexpr size_t kWrapperBufferCallTarget = 24;
  static constexpr size_t kWrapperBufferParamStart = 32;
  static constexpr size_t kWrapperBufferParamEnd = 40;

  // Offsets into the wrapper buffer for values passed from the assembly builtin
  // to Torque.
  static constexpr size_t kWrapperBufferStackReturnBufferStart = 16;
  static constexpr size_t kWrapperBufferFPReturnRegister1 = 24;
  static constexpr size_t kWrapperBufferFPReturnRegister2 = 32;
  static constexpr size_t kWrapperBufferGPReturnRegister1 = 40;
  static constexpr size_t kWrapperBufferGPReturnRegister2 =
      kWrapperBufferGPReturnRegister1 + kSystemPointerSize;

  // Size of the wrapper buffer
  static constexpr int kWrapperBufferSize =
      kWrapperBufferGPReturnRegister2 + kSystemPointerSize;
  static_assert(kWrapperBufferParamEnd + kSystemPointerSize <=
                kWrapperBufferSize);
};

// Fixed frame slots used by the ReturnPromiseOnSuspendAsm wrapper
// and the WasmResume wrapper.
class StackSwitchFrameConstants : public JSToWasmWrapperFrameConstants {
 public:
  //  StackSwitching stack layout
  //  ------+-----------------+----------------------
  //        |  return addr    |
  //    fp  |- - - - - - - - -|  -------------------|
  //        |       fp        |                     |
  //   fp-p |- - - - - - - - -|                     |
  //        |  frame marker   |                     | no GC scan
  //  fp-2p |- - - - - - - - -|                     |
  //        |   scan_count    |                     |
  //  fp-3p |- - - - - - - - -|  -------------------|
  //        |  wasm_instance  |                     |
  //  fp-4p |- - - - - - - - -|                     | fixed GC scan
  //        |  result_array   |                     |
  //  fp-5p |- - - - - - - - -|  -------------------|
  //        |      ....       | <- spill_slot_limit |
  //        |   spill slots   |                     | GC scan scan_count slots
  //        |      ....       | <- spill_slot_base--|
  //        |- - - - - - - - -|                     |
  // This slot contains the number of slots at the top of the frame that need to
  // be scanned by the GC.
  static constexpr int kGCScanSlotCountOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  // Tagged pointer to WasmTrustedInstanceData or WasmImportData.
  static constexpr int kImplicitArgOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
  // Tagged pointer to a JS Array for result values.
  static constexpr int kResultArrayOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(3);

  static constexpr int kLastSpillOffset = kResultArrayOffset;
  static constexpr int kNumSpillSlots = 4;
};

class WasmToJSWrapperConstants {
 public:
  // FP-relative.
  static constexpr size_t kSignatureOffset = 2 * kSystemPointerSize;
};

#if V8_ENABLE_DRUMBRAKE
class BuiltinWasmInterpreterWrapperConstants : public TypedFrameConstants {
 public:
  // This slot contains the number of slots at the top of the frame that need to
  // be scanned by the GC.
  static constexpr int kGCScanSlotCountOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  // The number of parameters passed to this function.
  static constexpr int kInParamCountOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  // The number of parameters according to the signature.
  static constexpr int kParamCountOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
  // The number of return values according to the siganture.
  static constexpr int kReturnCountOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(3);
  // `reps_` of wasm::FunctionSig.
  static constexpr int kSigRepsOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(4);
  // The current valuetype in the function signature being converted.
  static constexpr int kValueTypesArrayStartOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(5);
  // Array of arguments/return values.
  static constexpr int kArgRetsAddressOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(6);
  // Whether the array is for arguments or return values.
  static constexpr int kArgRetsIsArgsOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(7);
  // The index of the argument or return value being converted.
  static constexpr int kCurrentIndexOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(8);
  // Precomputed signature data.
  static constexpr int kSignatureDataOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(9);
};
#endif  // V8_ENABLE_DRUMBRAKE
#endif  // V8_ENABLE_WEBASSEMBLY

class BuiltinContinuationFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kFunctionOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kFrameSPtoFPDeltaAtDeoptimize =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(1);
  static constexpr int kBuiltinContextOffset =
      TYPED_FRAME_PUSHED_VALUE_OFFSET(2);
  static constexpr int kBuiltinIndexOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(3);

  // The argument count is in the first allocatable register, stored below the
  // fixed part of the frame and therefore is not part of the fixed frame size.
  static constexpr int kArgCOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(4);
  DEFINE_TYPED_FRAME_SIZES(4);

  // Returns the number of padding stack slots needed when we have
  // 'register_count' register slots.
  // This is needed on some architectures to ensure the stack pointer is
  // aligned.
  static int PaddingSlotCount(int register_count);
};

class ExitFrameConstants : public TypedFrameConstants {
 public:
  // FP-relative.
  static constexpr int kSPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kLastExitFrameField = kSPOffset;
  DEFINE_TYPED_FRAME_SIZES(1);

  // FP-relative displacement of the caller's SP.  It points just
  // below the saved PC.
  static constexpr int kCallerSPDisplacement = kCallerSPOffset;
};
#define EXIT_FRAME_PUSHED_VALUE_OFFSET(x) \
  FRAME_PUSHED_VALUE_OFFSET(ExitFrameConstants, x)
#define DEFINE_EXIT_FRAME_SIZES(x) DEFINE_FRAME_SIZES(ExitFrameConstants, x);

// Behaves like an exit frame but with extra arguments (target, new target and
// JS arguments count), followed by JS arguments passed to the JS function
// (receiver and etc.).
//
//  slot      JS frame
//       +-----------------+--------------------------------
// -n-1-k|   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n-k |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2-k |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1-k |    receiver     |                            v
//  -----+-----------------+--------------------------------
//  -k   |  extra arg k-1  |                            ^
//       |- - - - - - - - -|                            |
//  -k+1 |  extra arg k-2  |                  Extra arguments passed
//  ...  |       ...       |                      to CPP builtin
//  -2   |   extra arg 1   |                    k := kNumExtraArgs
//       |- - - - - - - - -|                            |
//  -1   |   extra arg 0   |                            v
//  -----+-----------------+--------------------------------
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | ExitFrame                  |
//       |- - - - - - - - -| Header     <-- frame ptr   |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |Frame Type Marker|   |   if a constant pool   |
//       |- - - - - - - - -|   |    is used, cp = 1,    |
// 3+cp  |    caller SP    |   v   otherwise, cp = 0    |
//       |-----------------+----                        |
// 4+cp  |       ...       |   ^                      Callee
//       |- - - - - - - - -|   |                   frame slots
//  ...  | C function args | Frame slots           (slot >= 0)
//       |- - - - - - - - -|   |                        |
//       |                 |   v                        |
//  -----+-----------------+----- <-- stack ptr -------------
//
class BuiltinExitFrameConstants : public ExitFrameConstants {
 public:
  // The following constants must be in sync with BuiltinArguments' extra
  // arguments layout. This is guaraneed by static_asserts elsewhere.
  static constexpr int kNewTargetIndex = 0;
  static constexpr int kTargetIndex = 1;
  static constexpr int kArgcIndex = 2;
  // TODO(ishell): this padding is required only on Arm64.
  static constexpr int kPaddingIndex = 3;
  static constexpr int kNumExtraArgs = 4;
  static constexpr int kNumExtraArgsWithReceiver = kNumExtraArgs + 1;

  // BuiltinArguments' arguments_ array.
  static constexpr int kArgumentsArrayOffset = kFixedFrameSizeAboveFp;
  static constexpr int kTargetOffset =
      kArgumentsArrayOffset + kTargetIndex * kSystemPointerSize;
  static constexpr int kNewTargetOffset =
      kArgumentsArrayOffset + kNewTargetIndex * kSystemPointerSize;
  static constexpr int kArgcOffset =
      kArgumentsArrayOffset + kArgcIndex * kSystemPointerSize;

  // JS arguments.
  static constexpr int kReceiverOffset =
      kArgumentsArrayOffset + kNumExtraArgs * kSystemPointerSize;

  static constexpr int kFirstArgumentOffset =
      kReceiverOffset + kSystemPointerSize;
};

// Behaves like an exit frame but with v8::FunctionCallbackInfo's implicit
// arguments (FCI), followed by JS arguments passed to the JS function
// (receiver and etc.).
//
//  slot      JS frame
//       +-----------------+--------------------------------
// -n-1-k|   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n-k |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2-k |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1-k |    receiver     |                            v
//  -----+-----------------+--------------------------------
//  -k   |   FCI slot k-1  |                            ^
//       |- - - - - - - - -|                            |
//  -k+1 |   FCI slot k-2  |                 v8::FunctionCallbackInfo's
//  ...  |       ...       |                   FCI::implicit_args[k]
//  -2   |   FCI slot 1    |                   k := FCI::kArgsLength
//       |- - - - - - - - -|                            |
//  -1   |   FCI slot 0    |                            v
//  -----+-----------------+--------------------------------
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | ExitFrame                  |
//       |- - - - - - - - -| Header     <-- frame ptr   |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |Frame Type Marker|   |   if a constant pool   |
//       |- - - - - - - - -|   |    is used, cp = 1,    |
// 3+cp  |    caller SP    |   v   otherwise, cp = 0    |
//       |-----------------+----                        |
// 4+cp  | FCI::argc_      |   ^                      Callee
//       |- - - - - - - - -|   |                   frame slots
// 5+cp  | FCI::values_    |   |                   (slot >= 0)
//       |- - - - - - - - -|   |                        |
// 6+cp  | FCI::imp._args_ | Frame slots                |
//       |- - - - - - - - -|   |                        |
//  ...  | C function args |   |                        |
//       |- - - - - - - - -|   |                        |
//       |                 |   v                        |
//  -----+-----------------+----- <-- stack ptr -------------
//
class ApiCallbackExitFrameConstants : public ExitFrameConstants {
 public:
  // The following constants must be in sync with v8::FunctionCallbackInfo's
  // layout. This is guaraneed by static_asserts elsewhere.
  static constexpr int kFunctionCallbackInfoContextIndex = 2;
  static constexpr int kFunctionCallbackInfoReturnValueIndex = 3;
  static constexpr int kFunctionCallbackInfoTargetIndex = 4;
  static constexpr int kFunctionCallbackInfoNewTargetIndex = 5;
  static constexpr int kFunctionCallbackInfoArgsLength = 6;

  // FP-relative.
  // v8::FunctionCallbackInfo struct (implicit_args_, args_, argc_) is pushed
  // on top of the ExitFrame.
  static constexpr int kFCIArgcOffset = EXIT_FRAME_PUSHED_VALUE_OFFSET(0);
  static constexpr int kFCIValuesOffset = EXIT_FRAME_PUSHED_VALUE_OFFSET(1);
  static constexpr int kFCIImplicitArgsOffset =
      EXIT_FRAME_PUSHED_VALUE_OFFSET(2);

  DEFINE_EXIT_FRAME_SIZES(3)
  static_assert(kSPOffset - kSystemPointerSize == kFCIArgcOffset);

  // v8::FunctionCallbackInfo's struct allocated right below the exit frame.
  static constexpr int kFunctionCallbackInfoOffset = kFCIImplicitArgsOffset;

  // v8::FunctionCallbackInfo's implicit_args array.
  static constexpr int kImplicitArgsArrayOffset = kFixedFrameSizeAboveFp;
  static constexpr int kTargetOffset =
      kImplicitArgsArrayOffset +
      kFunctionCallbackInfoTargetIndex * kSystemPointerSize;
  static constexpr int kNewTargetOffset =
      kImplicitArgsArrayOffset +
      kFunctionCallbackInfoNewTargetIndex * kSystemPointerSize;
  static constexpr int kContextOffset =
      kImplicitArgsArrayOffset +
      kFunctionCallbackInfoContextIndex * kSystemPointerSize;
  static constexpr int kReturnValueOffset =
      kImplicitArgsArrayOffset +
      kFunctionCallbackInfoReturnValueIndex * kSystemPointerSize;

  // JS arguments.
  static constexpr int kReceiverOffset =
      kImplicitArgsArrayOffset +
      kFunctionCallbackInfoArgsLength * kSystemPointerSize;

  static constexpr int kFirstArgumentOffset =
      kReceiverOffset + kSystemPointerSize;
};

// Behaves like an exit frame but with v8::PropertyCallbackInfo's (PCI)
// fields allocated in GC-ed area of the exit frame, followed by zero or
// more parameters (required by some callback kinds).
//
//  slot      JS frame
//       +-----------------+--------------------------------
// -n-1-k|   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n-k |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2-k |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1-k |   parameter 0   |                            v
//  -----+-----------------+--------------------------------
//  -k   |   PCI slot k-1  |                            ^
//       |- - - - - - - - -|                            |
//  -k+1 |   PCI slot k-2  |                 v8::PropertyCallbackInfo's
//  ...  |       ...       |                       PCI::args[k]
//  -2   |   PCI slot 1    |                   k := PCI::kArgsLength
//       |- - - - - - - - -|                            |
//  -1   |   PCI slot 0    |                            v
//  -----+-----------------+--------------------------------   <-- PCI object
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | ExitFrame                  |
//       |- - - - - - - - -| Header     <-- frame ptr   |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |Frame Type Marker|   |   if a constant pool   |
//       |- - - - - - - - -|   |    is used, cp = 1,    |
// 3+cp  |    caller SP    |   v   otherwise, cp = 0    |
//       |-----------------+----                        |
// 4+cp  |                 |   ^                      Callee
//       |- - - - - - - - -|   |                   frame slots
//  ...  | C function args | Frame slots           (slot >= 0)
//       |- - - - - - - - -|   |                        |
//       |                 |   v                        |
//  -----+-----------------+----- <-- stack ptr -------------
//
class ApiAccessorExitFrameConstants : public ExitFrameConstants {
 public:
  // The following constants must be in sync with v8::PropertyCallbackInfo's
  // layout. This is guaraneed by static_asserts elsewhere.
  static constexpr int kPropertyCallbackInfoPropertyKeyIndex = 0;
  static constexpr int kPropertyCallbackInfoHolderIndex = 2;
  static constexpr int kPropertyCallbackInfoReturnValueIndex = 5;
  static constexpr int kPropertyCallbackInfoReceiverIndex = 7;
  static constexpr int kPropertyCallbackInfoArgsLength = 8;

  // FP-relative.

  // v8::PropertyCallbackInfo's args array.
  static constexpr int kArgsArrayOffset = kFixedFrameSizeAboveFp;
  static constexpr int kPropertyNameOffset =
      kArgsArrayOffset +
      kPropertyCallbackInfoPropertyKeyIndex * kSystemPointerSize;
  static constexpr int kReturnValueOffset =
      kArgsArrayOffset +
      kPropertyCallbackInfoReturnValueIndex * kSystemPointerSize;
  static constexpr int kReceiverOffset =
      kArgsArrayOffset +
      kPropertyCallbackInfoReceiverIndex * kSystemPointerSize;
  static constexpr int kHolderOffset =
      kArgsArrayOffset + kPropertyCallbackInfoHolderIndex * kSystemPointerSize;

  // v8::PropertyCallbackInfo's address is equal to address of the args_ array.
  static constexpr int kPropertyCallbackInfoOffset = kArgsArrayOffset;
};

// Unoptimized frames are used for interpreted and baseline-compiled JavaScript
// frames. They are a "standard" frame, with an additional fixed header for the
// BytecodeArray, bytecode offset (if running interpreted), feedback vector (if
// running baseline code), and then the interpreter register file.
//
//  slot      JS frame
//       +-----------------+--------------------------------
//  -n-1 |   parameter n   |                            ^
//       |- - - - - - - - -|                            |
//  -n   |  parameter n-1  |                          Caller
//  ...  |       ...       |                       frame slots
//  -2   |   parameter 1   |                       (slot < 0)
//       |- - - - - - - - -|                            |
//  -1   |   parameter 0   |                            v
//  -----+-----------------+--------------------------------
//   0   |   return addr   |   ^                        ^
//       |- - - - - - - - -|   |                        |
//   1   | saved frame ptr | Fixed                      |
//       |- - - - - - - - -| Header <-- frame ptr       |
//   2   | [Constant Pool] |   |                        |
//       |- - - - - - - - -|   |                        |
// 2+cp  |     Context     |   |   if a constant pool   |
//       |- - - - - - - - -|   |    is used, cp = 1,    |
// 3+cp  |    JSFunction   |   |   otherwise, cp = 0    |
//       |- - - - - - - - -|   |                        |
// 4+cp  |      argc       |   v                        |
//       +-----------------+----                        |
// 5+cp  |  BytecodeArray  |   ^                        |
//       |- - - - - - - - -|   |                        |
// 6+cp  |  offset / cell  | Unoptimized code header    |
//       |- - - - - - - - -|   |                        |
// 7+cp  |      FBV        |   v                        |
//       +-----------------+----                        |
// 8+cp  |   register 0    |   ^                     Callee
//       |- - - - - - - - -|   |                   frame slots
// 9+cp  |   register 1    | Register file         (slot >= 0)
//  ...  |       ...       |   |                        |
//       |  register n-1   |   |                        |
//       |- - - - - - - - -|   |                        |
// 9+cp+n|   register n    |   v                        v
//  -----+-----------------+----- <-- stack ptr -------------
//
class UnoptimizedFrameConstants : public StandardFrameConstants {
 public:
  // FP-relative.
  static constexpr int kBytecodeArrayFromFp =
      STANDARD_FRAME_EXTRA_PUSHED_VALUE_OFFSET(0);
  static constexpr int kBytecodeOffsetOrFeedbackCellFromFp =
      STANDARD_FRAME_EXTRA_PUSHED_VALUE_OFFSET(1);
  static constexpr int kFeedbackVectorFromFp =
      STANDARD_FRAME_EXTRA_PUSHED_VALUE_OFFSET(2);
  DEFINE_STANDARD_FRAME_SIZES(3);

  static constexpr int kFirstParamFromFp =
      StandardFrameConstants::kCallerSPOffset;
  static constexpr int kRegisterFileFromFp =
      -kFixedFrameSizeFromFp - kSystemPointerSize;
  static constexpr int kExpressionsOffset = kRegisterFileFromFp;

  // Expression index for {JavaScriptFrame::GetExpressionAddress}.
  static constexpr int kBytecodeArrayExpressionIndex = -3;
  static constexpr int kBytecodeOffsetOrFeedbackCellExpressionIndex = -2;
  static constexpr int kFeedbackVectorExpressionIndex = -1;
  static constexpr int kRegisterFileExpressionIndex = 0;

  // Returns the number of stack slots needed for 'register_count' registers.
  // This is needed because some architectures must pad the stack frame with
  // additional stack slots to ensure the stack pointer is aligned.
  static int RegisterStackSlotCount(int register_count);
};

// Interpreter frames are unoptimized frames that are being executed by the
// interpreter. In this case, the "offset or cell" slot contains the bytecode
// offset of the currently executing bytecode.
class InterpreterFrameConstants : public UnoptimizedFrameConstants {
 public:
  static constexpr int kBytecodeOffsetExpressionIndex =
      kBytecodeOffsetOrFeedbackCellExpressionIndex;

  static constexpr int kBytecodeOffsetFromFp =
      kBytecodeOffsetOrFeedbackCellFromFp;
};

// Sparkplug frames are unoptimized frames that are being executed by
// sparkplug-compiled baseline code. base. In this case, the "offset or cell"
// slot contains the closure feedback cell.
class BaselineFrameConstants : public UnoptimizedFrameConstants {
 public:
  static constexpr int kFeedbackCellExpressionIndex =
      kBytecodeOffsetOrFeedbackCellExpressionIndex;

  static constexpr int kFeedbackCellFromFp =
      kBytecodeOffsetOrFeedbackCellFromFp;
};

inline static int FPOffsetToFrameSlot(int frame_offset) {
  return StandardFrameConstants::kFixedSlotCountAboveFp - 1 -
         frame_offset / kSystemPointerSize;
}

inline static int FrameSlotToFPOffset(int slot) {
  return (StandardFrameConstants::kFixedSlotCountAboveFp - 1 - slot) *
         kSystemPointerSize;
}

}  // namespace internal
}  // namespace v8

#if V8_TARGET_ARCH_IA32
#include "src/execution/ia32/frame-constants-ia32.h"
#elif V8_TARGET_ARCH_X64
#include "src/execution/x64/frame-constants-x64.h"
#elif V8_TARGET_ARCH_ARM64
#include "src/execution/arm64/frame-constants-arm64.h"
#elif V8_TARGET_ARCH_ARM
#include "src/execution/arm/frame-constants-arm.h"
#elif V8_TARGET_ARCH_PPC64
#include "src/execution/ppc/frame-constants-ppc.h"
#elif V8_TARGET_ARCH_MIPS64
#include "src/execution/mips64/frame-constants-mips64.h"
#elif V8_TARGET_ARCH_LOONG64
#include "src/execution/loong64/frame-constants-loong64.h"
#elif V8_TARGET_ARCH_S390X
#include "src/execution/s390/frame-constants-s390.h"
#elif V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64
#include "src/execution/riscv/frame-constants-riscv.h"
#else
#error Unsupported target architecture.
#endif

#endif  // V8_EXECUTION_FRAME_CONSTANTS_H_
