/*
 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2008, 2009, 2010 Red Hat, Inc.
 * 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 CPU_ZERO_VM_STACK_ZERO_HPP
#define CPU_ZERO_VM_STACK_ZERO_HPP

#include "utilities/sizes.hpp"

class ZeroStack {
 private:
  intptr_t *_base; // the last available word
  intptr_t *_top;  // the word past the end of the stack
  intptr_t *_sp;   // the top word on the stack

 private:
  int _shadow_pages_size; // how much ABI stack must we keep free?

 public:
  ZeroStack()
    : _base(NULL), _top(NULL), _sp(NULL) {
    _shadow_pages_size = StackShadowPages * os::vm_page_size();
  }

  bool needs_setup() const {
    return _base == NULL;
  }

  int suggest_size(Thread *thread) const;

  void setup(void *mem, size_t size) {
    assert(needs_setup(), "already set up");
    assert(!(size & WordAlignmentMask), "unaligned");

    _base = (intptr_t *) mem;
    _top  = _base + (size >> LogBytesPerWord);
    _sp   = _top;
  }
  void teardown() {
    assert(!needs_setup(), "not set up");
    assert(_sp == _top, "stuff on stack at teardown");

    _base = NULL;
    _top  = NULL;
    _sp   = NULL;
  }

  intptr_t *sp() const {
    return _sp;
  }
  void set_sp(intptr_t *new_sp) {
    assert(_top >= new_sp && new_sp >= _base, "bad stack pointer");
    _sp = new_sp;
  }

  int total_words() const {
    return _top - _base;
  }
  int available_words() const {
    return _sp - _base;
  }

  void push(intptr_t value) {
    assert(_sp > _base, "stack overflow");
    *(--_sp) = value;
  }
  intptr_t pop() {
    assert(_sp < _top, "stack underflow");
    return *(_sp++);
  }

  void *alloc(size_t size) {
    int count = align_size_up(size, wordSize) >> LogBytesPerWord;
    assert(count <= available_words(), "stack overflow");
    return _sp -= count;
  }

  int shadow_pages_size() const {
    return _shadow_pages_size;
  }
  int abi_stack_available(Thread *thread) const;

 public:
  void overflow_check(int required_words, TRAPS);
  static void handle_overflow(TRAPS);

 public:
  void zap(int c) PRODUCT_RETURN;

 public:
  static ByteSize base_offset() {
    return byte_offset_of(ZeroStack, _base);
  }
  static ByteSize top_offset() {
    return byte_offset_of(ZeroStack, _top);
  }
  static ByteSize sp_offset() {
    return byte_offset_of(ZeroStack, _sp);
  }
};


class EntryFrame;
class InterpreterFrame;
class SharkFrame;
class FakeStubFrame;

//
// |  ...               |
// +--------------------+  ------------------
// |  ...               |       low addresses
// | frame_type         |
// | next_frame         |      high addresses
// +--------------------+  ------------------
// |  ...               |

class ZeroFrame {
  friend class frame;
  friend class ZeroStackPrinter;

 protected:
  ZeroFrame() {
    ShouldNotCallThis();
  }

  enum Layout {
    next_frame_off,
    frame_type_off,
    jf_header_words
  };

  enum FrameType {
    ENTRY_FRAME = 1,
    INTERPRETER_FRAME,
    SHARK_FRAME,
    FAKE_STUB_FRAME
  };

 protected:
  intptr_t *addr_of_word(int offset) const {
    return (intptr_t *) this - offset;
  }
  intptr_t value_of_word(int offset) const {
    return *addr_of_word(offset);
  }

 public:
  ZeroFrame *next() const {
    return (ZeroFrame *) value_of_word(next_frame_off);
  }

 protected:
  FrameType type() const {
    return (FrameType) value_of_word(frame_type_off);
  }

 public:
  bool is_entry_frame() const {
    return type() == ENTRY_FRAME;
  }
  bool is_interpreter_frame() const {
    return type() == INTERPRETER_FRAME;
  }
  bool is_shark_frame() const {
    return type() == SHARK_FRAME;
  }
  bool is_fake_stub_frame() const {
    return type() == FAKE_STUB_FRAME;
  }

 public:
  EntryFrame *as_entry_frame() const {
    assert(is_entry_frame(), "should be");
    return (EntryFrame *) this;
  }
  InterpreterFrame *as_interpreter_frame() const {
    assert(is_interpreter_frame(), "should be");
    return (InterpreterFrame *) this;
  }
  SharkFrame *as_shark_frame() const {
    assert(is_shark_frame(), "should be");
    return (SharkFrame *) this;
  }
  FakeStubFrame *as_fake_stub_frame() const {
    assert(is_fake_stub_frame(), "should be");
    return (FakeStubFrame *) this;
  }

 public:
  void identify_word(int   frame_index,
                     int   offset,
                     char* fieldbuf,
                     char* valuebuf,
                     int   buflen) const;

 protected:
  void identify_vp_word(int       frame_index,
                        intptr_t* addr,
                        intptr_t* monitor_base,
                        intptr_t* stack_base,
                        char*     fieldbuf,
                        int       buflen) const;
};

#endif // CPU_ZERO_VM_STACK_ZERO_HPP
