/*
 * Copyright (c) 1999, 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 SHARE_VM_SHARK_SHARKSTACK_HPP
#define SHARE_VM_SHARK_SHARKSTACK_HPP

#include "shark/llvmHeaders.hpp"
#include "shark/sharkInvariants.hpp"
#include "shark/sharkType.hpp"

class SharkFunction;
class SharkNativeWrapper;
class SharkStackWithNormalFrame;
class SharkStackWithNativeFrame;

class SharkStack : public SharkCompileInvariants {
 public:
  static SharkStack* CreateBuildAndPushFrame(
    SharkFunction* function, llvm::Value* method);
  static SharkStack* CreateBuildAndPushFrame(
    SharkNativeWrapper* wrapper, llvm::Value* method);

 protected:
  SharkStack(const SharkCompileInvariants* parent)
    : SharkCompileInvariants(parent) {}

 protected:
  void initialize(llvm::Value* method);

 protected:
  void CreateStackOverflowCheck(llvm::Value* sp);

  // Properties of the method being compiled
 protected:
  virtual int arg_size() const = 0;
  virtual int max_locals() const = 0;
  virtual int max_stack() const = 0;
  virtual int max_monitors() const = 0;

  // BasicBlock creation
 protected:
  virtual llvm::BasicBlock* CreateBlock(const char* name = "") const = 0;

  // Interpreter entry point for bailouts
 protected:
  virtual address interpreter_entry_point() const = 0;

  // Interface with the Zero stack
 private:
  llvm::Value* zero_stack() const {
    return builder()->CreateAddressOfStructEntry(
      thread(),
      JavaThread::zero_stack_offset(),
      SharkType::zeroStack_type(),
      "zero_stack");
  }
  llvm::Value* stack_base() const {
    return builder()->CreateValueOfStructEntry(
      zero_stack(),
      ZeroStack::base_offset(),
      SharkType::intptr_type(),
      "stack_base");
  }
  llvm::Value* stack_pointer_addr() const {
    return builder()->CreateAddressOfStructEntry(
      zero_stack(),
      ZeroStack::sp_offset(),
      llvm::PointerType::getUnqual(SharkType::intptr_type()),
      "stack_pointer_addr");
  }
  llvm::Value* frame_pointer_addr() const {
    return builder()->CreateAddressOfStructEntry(
      thread(),
      JavaThread::top_zero_frame_offset(),
      llvm::PointerType::getUnqual(SharkType::intptr_type()),
      "frame_pointer_addr");
  }

 public:
  llvm::LoadInst* CreateLoadStackPointer(const char *name = "") {
    return builder()->CreateLoad(stack_pointer_addr(), name);
  }
  llvm::StoreInst* CreateStoreStackPointer(llvm::Value* value) {
    return builder()->CreateStore(value, stack_pointer_addr());
  }
  llvm::LoadInst* CreateLoadFramePointer(const char *name = "") {
    return builder()->CreateLoad(frame_pointer_addr(), name);
  }
  llvm::StoreInst* CreateStoreFramePointer(llvm::Value* value) {
    return builder()->CreateStore(value, frame_pointer_addr());
  }
  llvm::Value* CreatePopFrame(int result_slots);

  // Interface with the frame anchor
 private:
  llvm::Value* last_Java_sp_addr() const {
    return builder()->CreateAddressOfStructEntry(
      thread(),
      JavaThread::last_Java_sp_offset(),
      llvm::PointerType::getUnqual(SharkType::intptr_type()),
      "last_Java_sp_addr");
  }
  llvm::Value* last_Java_fp_addr() const {
    return builder()->CreateAddressOfStructEntry(
      thread(),
      JavaThread::last_Java_fp_offset(),
      llvm::PointerType::getUnqual(SharkType::intptr_type()),
      "last_Java_fp_addr");
  }

 public:
  void CreateSetLastJavaFrame() {
    // Note that whenever _last_Java_sp != NULL other anchor fields
    // must be valid.  The profiler apparently depends on this.
    NOT_PRODUCT(CreateAssertLastJavaSPIsNull());
    builder()->CreateStore(CreateLoadFramePointer(), last_Java_fp_addr());
    // XXX There's last_Java_pc as well, but I don't think anything uses it
    // Also XXX: should we fence here?  Zero doesn't...
    builder()->CreateStore(CreateLoadStackPointer(), last_Java_sp_addr());
    // Also also XXX: we could probably cache the sp (and the fp we know??)
  }
  void CreateResetLastJavaFrame() {
    builder()->CreateStore(LLVMValue::intptr_constant(0), last_Java_sp_addr());
  }

 private:
  void CreateAssertLastJavaSPIsNull() const PRODUCT_RETURN;

  // Our method's frame
 private:
  llvm::Value* _frame;
  int          _extended_frame_size;
  int          _stack_slots_offset;

 public:
  int extended_frame_size() const {
    return _extended_frame_size;
  }
  int oopmap_frame_size() const {
    return extended_frame_size() - arg_size();
  }

  // Offsets of things in the frame
 private:
  int _monitors_slots_offset;
  int _oop_tmp_slot_offset;
  int _method_slot_offset;
  int _pc_slot_offset;
  int _locals_slots_offset;

 public:
  int stack_slots_offset() const {
    return _stack_slots_offset;
  }
  int oop_tmp_slot_offset() const {
    return _oop_tmp_slot_offset;
  }
  int method_slot_offset() const {
    return _method_slot_offset;
  }
  int pc_slot_offset() const {
    return _pc_slot_offset;
  }
  int locals_slots_offset() const {
    return _locals_slots_offset;
  }
  int monitor_offset(int index) const {
    assert(index >= 0 && index < max_monitors(), "invalid monitor index");
    return _monitors_slots_offset +
      (max_monitors() - 1 - index) * frame::interpreter_frame_monitor_size();
  }
  int monitor_object_offset(int index) const {
    return monitor_offset(index) +
      (BasicObjectLock::obj_offset_in_bytes() >> LogBytesPerWord);
  }
  int monitor_header_offset(int index) const {
    return monitor_offset(index) +
      ((BasicObjectLock::lock_offset_in_bytes() +
        BasicLock::displaced_header_offset_in_bytes()) >> LogBytesPerWord);
  }

  // Addresses of things in the frame
 public:
  llvm::Value* slot_addr(int               offset,
                         llvm::Type* type = NULL,
                         const char*       name = "") const;

  llvm::Value* monitor_addr(int index) const {
    return slot_addr(
      monitor_offset(index),
      SharkType::monitor_type(),
      "monitor");
  }
  llvm::Value* monitor_object_addr(int index) const {
    return slot_addr(
      monitor_object_offset(index),
      SharkType::oop_type(),
      "object_addr");
  }
  llvm::Value* monitor_header_addr(int index) const {
    return slot_addr(
      monitor_header_offset(index),
      SharkType::intptr_type(),
      "displaced_header_addr");
  }

  // oopmap helpers
 public:
  static int oopmap_slot_munge(int offset) {
    return offset << (LogBytesPerWord - LogBytesPerInt);
  }
  static VMReg slot2reg(int offset) {
    return VMRegImpl::stack2reg(oopmap_slot_munge(offset));
  }
};

class SharkStackWithNormalFrame : public SharkStack {
  friend class SharkStack;

 protected:
  SharkStackWithNormalFrame(SharkFunction* function, llvm::Value* method);

 private:
  SharkFunction* _function;

 private:
  SharkFunction* function() const {
    return _function;
  }

  // Properties of the method being compiled
 private:
  int arg_size() const;
  int max_locals() const;
  int max_stack() const;
  int max_monitors() const;

  // BasicBlock creation
 private:
  llvm::BasicBlock* CreateBlock(const char* name = "") const;

  // Interpreter entry point for bailouts
 private:
  address interpreter_entry_point() const;
};

class SharkStackWithNativeFrame : public SharkStack {
  friend class SharkStack;

 protected:
  SharkStackWithNativeFrame(SharkNativeWrapper* wrapper, llvm::Value* method);

 private:
  SharkNativeWrapper* _wrapper;

 private:
  SharkNativeWrapper* wrapper() const {
    return _wrapper;
  }

  // Properties of the method being compiled
 private:
  int arg_size() const;
  int max_locals() const;
  int max_stack() const;
  int max_monitors() const;

  // BasicBlock creation
 private:
  llvm::BasicBlock* CreateBlock(const char* name = "") const;

  // Interpreter entry point for bailouts
 private:
  address interpreter_entry_point() const;
};

#endif // SHARE_VM_SHARK_SHARKSTACK_HPP
