// Copyright 2018 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.

type FrameType extends Smi
    generates 'TNode<Smi>' constexpr 'StackFrame::Type';
const ARGUMENTS_ADAPTOR_FRAME: constexpr FrameType
    generates 'StackFrame::ARGUMENTS_ADAPTOR';
const STUB_FRAME: constexpr FrameType
    generates 'StackFrame::STUB';
const kFrameTypeCount:
    constexpr int31 generates 'StackFrame::NUMBER_OF_TYPES';

FromConstexpr<FrameType, constexpr FrameType>(t: constexpr FrameType):
    FrameType {
  // Note that althought FrameTypes sometimes masquerade as Smis (their
  // LSB is a zero), they are not. For efficiency in storing them as a
  // constant into a frame, they are simply the FrameType value shifted
  // up by a single bit.
  const i: constexpr uintptr = %RawConstexprCast<constexpr uintptr>(t)
      << kSmiTagSize;
  return %RawDownCast<FrameType>(BitcastWordToTaggedSigned(i));
}
Cast<FrameType>(o: Object): FrameType
    labels CastError {
  if (TaggedIsNotSmi(o)) goto CastError;
  assert(
      (Convert<uintptr>(BitcastTaggedToWord(o)) >>> kSmiTagSize) <
      kFrameTypeCount);
  return %RawDownCast<FrameType>(o);
}

type FrameBase extends RawPtr
    generates 'TNode<RawPtrT>' constexpr 'void*';
type StandardFrame extends FrameBase
    generates 'TNode<RawPtrT>' constexpr 'void*';
type ArgumentsAdaptorFrame extends FrameBase
    generates 'TNode<RawPtrT>' constexpr 'void*';
type StubFrame extends FrameBase
    generates 'TNode<RawPtrT>' constexpr 'void*';
type Frame = ArgumentsAdaptorFrame | StandardFrame | StubFrame;

extern macro LoadFramePointer(): Frame;
extern macro LoadParentFramePointer(): Frame;

// Load values from a specified frame by given offset in bytes.
macro LoadObjectFromFrame(f: Frame, o: constexpr int32): Object {
  return LoadBufferObject(f, o);
}
macro LoadPointerFromFrame(f: Frame, o: constexpr int32): RawPtr {
  return LoadBufferPointer(f, o);
}
macro LoadSmiFromFrame(f: Frame, o: constexpr int32): Smi {
  return LoadBufferSmi(f, o);
}

const kStandardFrameFunctionOffset: constexpr int31
    generates 'StandardFrameConstants::kFunctionOffset';
operator '.function' macro LoadFunctionFromFrame(f: Frame): JSFunction {
  // TODO(danno): Use RawDownCast here in order to avoid passing the implicit
  // context, since this accessor is used in legacy CSA code through
  // LoadTargetFromFrame
  const result: Object = LoadObjectFromFrame(f, kStandardFrameFunctionOffset);
  return %RawDownCast<JSFunction>(result);
}

const kStandardFrameCallerFPOffset: constexpr int31
    generates 'StandardFrameConstants::kCallerFPOffset';
operator '.caller' macro LoadCallerFromFrame(f: Frame): Frame {
  const result: RawPtr = LoadPointerFromFrame(f, kStandardFrameCallerFPOffset);
  return %RawDownCast<Frame>(result);
}

type ContextOrFrameType = Context | FrameType;
Cast<ContextOrFrameType>(implicit context: Context)(o: Object):
    ContextOrFrameType
    labels CastError {
  typeswitch (o) {
    case (c: Context): {
      return c;
    }
    case (t: FrameType): {
      return t;
    }
    case (Object): {
      goto CastError;
    }
  }
}

const kStandardFrameContextOrFrameTypeOffset: constexpr int31
    generates 'StandardFrameConstants::kContextOrFrameTypeOffset';
operator '.context_or_frame_type'
macro LoadContextOrFrameTypeFromFrame(implicit context: Context)(f: Frame):
    ContextOrFrameType {
  return UnsafeCast<ContextOrFrameType>(
      LoadObjectFromFrame(f, kStandardFrameContextOrFrameTypeOffset));
}

const kArgumentsAdaptorFrameLengthOffset: constexpr int31
    generates 'ArgumentsAdaptorFrameConstants::kLengthOffset';
operator '.length'
macro LoadLengthFromAdapterFrame(implicit context: Context)(
    f: ArgumentsAdaptorFrame): Smi {
  return LoadSmiFromFrame(f, kArgumentsAdaptorFrameLengthOffset);
}

operator '==' macro FrameTypeEquals(f1: FrameType, f2: FrameType): bool {
  return WordEqual(f1, f2);
}

macro Cast<A: type>(implicit context: Context)(o: Frame): A labels CastError;
Cast<StandardFrame>(implicit context: Context)(f: Frame):
    StandardFrame labels CastError {
  const o: HeapObject =
      Cast<HeapObject>(f.context_or_frame_type) otherwise CastError;
  // StandardFrames (which include interpreted and JIT-compiled frames),
  // unlike other frame types, don't have their own type marker stored in
  // the frame, but rather have the function's context stored where the
  // type marker is stored for other frame types. From Torque, it would
  // be quite expensive to do the test required to distinguish interpreter
  // frames from JITted ones (and other StandardFrame types), so
  // StandardFrame is the level of granularity support when iterating the
  // stack from generated code.
  // See the descriptions and frame layouts in src/frame-constants.h.
  if (IsContext(o)) {
    return %RawDownCast<StandardFrame>(f);
  }
  goto CastError;
}

Cast<ArgumentsAdaptorFrame>(implicit context: Context)(f: Frame):
    ArgumentsAdaptorFrame labels CastError {
  const t: FrameType =
      Cast<FrameType>(f.context_or_frame_type) otherwise CastError;
  if (t == ARGUMENTS_ADAPTOR_FRAME) {
    return %RawDownCast<ArgumentsAdaptorFrame>(f);
  }
  goto CastError;
}

// Load target function from the current JS frame.
// This is an alternative way of getting the target function in addition to
// Parameter(Descriptor::kJSTarget). The latter should be used near the
// beginning of builtin code while the target value is still in the register
// and the former should be used in slow paths in order to reduce register
// pressure on the fast path.
macro LoadTargetFromFrame(): JSFunction {
  return LoadFramePointer().function;
}