/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.mmtk.harness.vm;

import org.mmtk.policy.ImmortalSpace;
import org.mmtk.utility.heap.VMRequest;

import org.vmmagic.unboxed.*;
import org.vmmagic.pragma.*;

@Uninterruptible
public class Memory extends org.mmtk.vm.Memory {

  /**
   * Allows for the VM to reserve space between HEAP_START()
   * and AVAILABLE_START() for its own purposes.  MMTk should
   * expect to encounter objects in this range, but may not
   * allocate in this range.
   *
   * MMTk expects the virtual address space between AVAILABLE_START()
   * and AVAILABLE_END() to be contiguous and unmapped.
   * Allows for the VM to reserve space between HEAP_END()
   * and AVAILABLE_END() for its own purposes.  MMTk should
   * expect to encounter objects in this range, but may not
   * allocate in this range.
   *
   * MMTk expects the virtual address space between AVAILABLE_START()
   * and AVAILABLE_END() to be contiguous and unmapped.
   *
   * @return The high bound of the memory that MMTk can allocate.
   */
   private static ImmortalSpace vmSpace = null;
   private static Extent SIZE = Extent.fromIntZeroExtend(0x10000000);

  /**
   * Return the space associated with/reserved for the VM.  In the
   * case of Jikes RVM this is the boot image space.<p>
   *
   * @return The space managed by the virtual machine.
   */
  @Interruptible
  public ImmortalSpace getVMSpace() {
    if (vmSpace == null) {
      vmSpace = new ImmortalSpace("vm", 0, VMRequest.create(SIZE, false));
    }
    return vmSpace;
  }

  /** Global preparation for a collection. */
  public void globalPrepareVMSpace() {
    // Nothing in vmSpace
  }

  /** Per-collector preparation for a collection. */
  public void collectorPrepareVMSpace() {
    // Nothing in vmSpace
  }

  /** Per-collector post-collection work. */
  public void collectorReleaseVMSpace() {
    // Nothing in vmSpace
  }

  /** Global post-collection work. */
  public void globalReleaseVMSpace() {
    // Nothing in vmSpace
  }

  /**
   * Sets the range of addresses associated with a heap.
   *
   * @param id the heap identifier
   * @param start the address of the start of the heap
   * @param end the address of the end of the heap
   */
  public void setHeapRange(int id, Address start, Address end) {
    // TODO: More checking possible
  }

  /**
   * Demand zero mmaps an area of virtual memory.
   *
   * @param start the address of the start of the area to be mapped
   * @param size the size, in bytes, of the area to be mapped
   * @return 0 if successful, otherwise the system errno
   */
  public int dzmmap(Address start, int size) {
    if (SimulatedMemory.map(start, size)) {
      return 0;
    }
    return -1;
  }

  /**
   * Protects access to an area of virtual memory.
   *
   * @param start the address of the start of the area to be mapped
   * @param size the size, in bytes, of the area to be mapped
   * @return <code>true</code> if successful, otherwise
   * <code>false</code>
   */
  public boolean mprotect(Address start, int size) {
    return SimulatedMemory.protect(start, size);
  }

  /**
   * Allows access to an area of virtual memory.
   *
   * @param start the address of the start of the area to be mapped
   * @param size the size, in bytes, of the area to be mapped
   * @return <code>true</code> if successful, otherwise
   * <code>false</code>
   */
  public boolean munprotect(Address start, int size) {
    return SimulatedMemory.unprotect(start, size);
  }

  /**
   * Zero a region of memory.
   * @param start Start of address range (inclusive)
   * @param len Length in bytes of range to zero
   * Returned: nothing
   */
  public void zero(Address start, Extent len) {
    SimulatedMemory.zero(start, len);
  }

  /**
   * Zero a range of pages of memory.
   * @param start Start of address range (must be a page address)
   * @param len Length in bytes of range (must be multiple of page size)
   */
  public void zeroPages(Address start, int len) {
    SimulatedMemory.zeroPages(start, len);
  }

  /**
   * Logs the contents of an address and the surrounding memory to the
   * error output.
   *
   * @param start the address of the memory to be dumped
   * @param beforeBytes the number of bytes before the address to be
   * included
   * @param afterBytes the number of bytes after the address to be
   * included
   */
  public void dumpMemory(Address start, int beforeBytes, int afterBytes) {
    SimulatedMemory.dumpMemory(start, beforeBytes, afterBytes);
  }

  /**
   * Wait for preceeding cache flush/invalidate instructions to complete
   * on all processors.  Ensures that all memory writes before this
   * point are visible to all processors.
   */
  @Inline
  public void sync() {
    // Nothing required
  }

  /**
   * Wait for all preceeding instructions to complete and discard any
   * prefetched instructions on this processor.  Also prevents the
   * compiler from performing code motion across this point.
   */
  @Inline
  public void isync() {
    // Nothing required
  }

  /*
   * NOTE: The following methods must be implemented by subclasses of this
   * class, but are internal to the VM<->MM interface glue, so are never
   * called by MMTk users.
   */
  /** @return The lowest address in the virtual address space known to MMTk */
  protected Address getHeapStartConstant() { return SimulatedMemory.HEAP_START; }
  /** @return The highest address in the virtual address space known to MMTk */
  protected Address getHeapEndConstant() { return SimulatedMemory.HEAP_END; }
  /** @return The lowest address in the contiguous address space available to MMTk  */
  protected Address getAvailableStartConstant() { return SimulatedMemory.HEAP_START.plus(SIZE); }
  /** @return The highest address in the contiguous address space available to MMTk */
  protected Address getAvailableEndConstant()  { return SimulatedMemory.HEAP_END; }
  /** @return The log base two of the size of an address */
  protected byte getLogBytesInAddressConstant() { return SimulatedMemory.LOG_BYTES_IN_WORD; }
  /** @return The log base two of the size of a word */
  protected byte getLogBytesInWordConstant() { return SimulatedMemory.LOG_BYTES_IN_WORD; }
  /** @return The log base two of the size of an OS page */
  protected byte getLogBytesInPageConstant() { return SimulatedMemory.LOG_BYTES_IN_PAGE; }
  /** @return The log base two of the minimum allocation alignment */
  protected byte getLogMinAlignmentConstant()  { return SimulatedMemory.LOG_BYTES_IN_WORD; }
  /** @return The log base two of (MAX_ALIGNMENT/MIN_ALIGNMENT) */
  protected byte getMaxAlignmentShiftConstant() { return 1; }
  /** @return The maximum number of bytes of padding to prepend to an object */
  protected int getMaxBytesPaddingConstant() { return SimulatedMemory.BYTES_IN_WORD; }
  /** @return The value to store in alignment holes */
  protected int getAlignmentValueConstant() { return ObjectModel.ALIGNMENT_VALUE; }
}
