/*
 * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef SHARE_VM_RUNTIME_CONTINUATIONENTRY_HPP
#define SHARE_VM_RUNTIME_CONTINUATIONENTRY_HPP

#include "oops/oop.hpp"
#include "oops/oopsHierarchy.hpp"
#include "runtime/continuation.hpp"
#include "utilities/sizes.hpp"

#include CPU_HEADER(continuationEntry)

class JavaThread;
class nmethod;
class OopMap;
class RegisterMap;

// Metadata stored in the continuation entry frame
class ContinuationEntry {
  friend class VMStructs;
  friend class JVMCIVMStructs;
  ContinuationEntryPD _pd;
#ifdef ASSERT
 private:
  static const int COOKIE_VALUE = 0x1234;
  int cookie;

 public:
  static int cookie_value() { return COOKIE_VALUE; }
  static ByteSize cookie_offset() { return byte_offset_of(ContinuationEntry, cookie); }

  void verify_cookie() {
    assert(cookie == COOKIE_VALUE, "Bad cookie: %#x, expected: %#x", cookie, COOKIE_VALUE);
  }
#endif

 public:
  static int _return_pc_offset; // friend gen_continuation_enter
  static int _thaw_call_pc_offset;
  static int _cleanup_offset;

  static void set_enter_code(nmethod* nm, int interpreted_entry_offset);
  static bool is_interpreted_call(address call_address);

 private:
  static address _return_pc;
  static address _thaw_call_pc;
  static address _cleanup_pc;
  static nmethod* _enter_special;
  static int _interpreted_entry_offset;

 private:
  ContinuationEntry* _parent;
  oopDesc* _cont;
  oopDesc* _chunk;
  int _flags;
  // Size in words of the stack arguments of the bottom frame on stack if compiled 0 otherwise.
  // The caller (if there is one) is the still frozen top frame in the StackChunk.
  int _argsize;
  intptr_t* _parent_cont_fastpath;
#ifdef _LP64
  int64_t   _parent_held_monitor_count;
#else
  int32_t   _parent_held_monitor_count;
#endif
  uint32_t _pin_count;

 public:
  static ByteSize parent_offset()   { return byte_offset_of(ContinuationEntry, _parent); }
  static ByteSize cont_offset()     { return byte_offset_of(ContinuationEntry, _cont); }
  static ByteSize chunk_offset()    { return byte_offset_of(ContinuationEntry, _chunk); }
  static ByteSize flags_offset()    { return byte_offset_of(ContinuationEntry, _flags); }
  static ByteSize argsize_offset()  { return byte_offset_of(ContinuationEntry, _argsize); }
  static ByteSize pin_count_offset(){ return byte_offset_of(ContinuationEntry, _pin_count); }
  static ByteSize parent_cont_fastpath_offset()      { return byte_offset_of(ContinuationEntry, _parent_cont_fastpath); }
  static ByteSize parent_held_monitor_count_offset() { return byte_offset_of(ContinuationEntry, _parent_held_monitor_count); }

  static address return_pc() { return _return_pc; }
  static address return_pc_address() { return (address)&_return_pc; }

 public:
  static size_t size() { return align_up((int)sizeof(ContinuationEntry), 2*wordSize); }

  ContinuationEntry* parent() const { return _parent; }
  int64_t parent_held_monitor_count() const { return (int64_t)_parent_held_monitor_count; }

  static address entry_pc() { return _return_pc; }
  intptr_t* entry_sp() const { return (intptr_t*)this; }
  intptr_t* entry_fp() const;

  static address thaw_call_pc_address() { return (address)&_thaw_call_pc; }
  static address cleanup_pc() { return _cleanup_pc; }

  static address compiled_entry();
  static address interpreted_entry();

  int argsize() const { return _argsize; }
  void set_argsize(int value) { _argsize = value; }

  bool is_pinned() { return _pin_count > 0; }
  bool pin() {
    if (_pin_count == UINT32_MAX) return false;
    _pin_count++;
    return true;
  }
  bool unpin() {
    if (_pin_count == 0) return false;
    _pin_count--;
    return true;
  }

  intptr_t* parent_cont_fastpath() const { return _parent_cont_fastpath; }
  void set_parent_cont_fastpath(intptr_t* x) { _parent_cont_fastpath = x; }

  static ContinuationEntry* from_frame(const frame& f);
  frame to_frame() const;
  void update_register_map(RegisterMap* map) const;
  void flush_stack_processing(JavaThread* thread) const;

  inline intptr_t* bottom_sender_sp() const;
  inline oop cont_oop(const JavaThread* thread) const;
  inline oop scope(const JavaThread* thread) const;
  inline static oop cont_oop_or_null(const ContinuationEntry* ce, const JavaThread* thread);

  oop* cont_addr() { return (oop*)&_cont; }
  oop* chunk_addr() { return (oop*)&_chunk; }

  bool is_virtual_thread() const { return _flags != 0; }

#ifndef PRODUCT
  void describe(FrameValues& values, int frame_no) const;
#endif

#ifdef ASSERT
  static bool assert_entry_frame_laid_out(JavaThread* thread);
#endif
};

#endif // SHARE_VM_RUNTIME_CONTINUATIONENTRY_HPP
