001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.mmtk.utility;
014
015import static org.mmtk.utility.Constants.*;
016
017import org.mmtk.vm.VM;
018
019import org.vmmagic.unboxed.*;
020import org.vmmagic.pragma.*;
021
022/**
023 * This class implements basic memory copying, setting and clearing
024 * operations.<p>
025 *
026 * NOTE: Most of the operations in this class are performed at the
027 * granularity of a Java integer (ie 4-byte units)<p>
028 *
029 * FIXME: Why can't these operations be performed at word-granularity?
030 */
031@Uninterruptible
032public class Memory {
033
034  /****************************************************************************
035   *
036   * Class variables
037   */
038
039  /** zero operations greater than this size are done using the
040   * underlying OS implementation of zero() */
041  private static final int SMALL_REGION_THRESHOLD = 1 << 8; // empirically chosen
042
043
044  /****************************************************************************
045   *
046   * Basic memory setting and zeroing operations
047   */
048
049  /**
050   * Zero a region of memory
051   *
052   * @param start The start of the region to be zeroed (must be 4-byte aligned)
053   * @param bytes The number of bytes to be zeroed (must be 4-byte aligned)
054   */
055  @Inline
056  public static void zero(Address start, Extent bytes) {
057    if (VM.VERIFY_ASSERTIONS) {
058      assertAligned(start);
059      assertAligned(bytes);
060    }
061    if (bytes.GT(Extent.fromIntZeroExtend(SMALL_REGION_THRESHOLD)))
062      VM.memory.zero(false, start, bytes);
063    else
064      zeroSmall(start, bytes);
065  }
066
067  /**
068   * Zero a small region of memory
069   *
070   * @param start The start of the region to be zeroed (must be 4-byte aligned)
071   * @param bytes The number of bytes to be zeroed (must be 4-byte aligned)
072   */
073  @Inline
074  public static void zeroSmall(Address start, Extent bytes) {
075    if (VM.VERIFY_ASSERTIONS) {
076      assertAligned(start);
077      assertAligned(bytes);
078    }
079    Address end = start.plus(bytes);
080    for (Address addr = start; addr.LT(end); addr = addr.plus(BYTES_IN_INT))
081      addr.store(0);
082  }
083
084  /**
085   * Set a region of memory
086   *
087   * @param start The start of the region to be zeroed (must be 4-byte aligned)
088   * @param bytes The number of bytes to be zeroed (must be 4-byte aligned)
089   * @param value The value to which the integers in the region should be set
090   */
091  @Inline
092  public static void set(Address start, int bytes, int value) {
093    if (VM.VERIFY_ASSERTIONS) {
094      assertAligned(start);
095      assertAligned(bytes);
096    }
097    Address end = start.plus(bytes);
098    for (Address addr = start; addr.LT(end); addr = addr.plus(BYTES_IN_INT))
099      addr.store(value);
100  }
101
102
103  /****************************************************************************
104   *
105   * Helper methods
106   */
107
108  /**
109   * Check that a memory range is zeroed
110   *
111   * @param start The start address of the range to be checked
112   * @param bytes The size of the region to be checked, in bytes
113   * @return {@code true} if the region is zeroed
114   */
115  @Inline
116  public static boolean isZeroed(Address start, int bytes) {
117    return isSet(start, bytes, false, 0);
118  }
119
120  /**
121   * Assert that a memory range is zeroed.  An assertion failure will
122   * occur if the region is not zeroed.<p>
123   *
124   * this is in the inline allocation sequence when
125   * VM.VERIFY_ASSERTIONS is {@code true}, it is carefully written to
126   * reduce the impact on code space.
127   *
128   * @param start The start address of the range to be checked
129   * @param bytes The size of the region to be checked, in bytes
130   */
131  @NoInline
132  public static void assertIsZeroed(Address start, int bytes) {
133    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isSet(start, bytes, true, 0));
134  }
135
136  /**
137   * Verbosely check and return {@code true} if a memory range is set to some
138   * integer value
139   *
140   * @param start The start address of the range to be checked
141   * @param bytes The size of the region to be checked, in bytes
142   * @param value The value to which this region should be set
143   * @return {@code true} if the region has been correctly set
144   */
145  @Inline
146  public static boolean isSet(Address start, int bytes, int value) {
147    return isSet(start, bytes, true, value);
148  }
149
150  /**
151   * Assert appropriate alignment, triggering an assertion failure if
152   * the value does not satisfy the alignment requirement of the
153   * memory operations.
154   *
155   * @param value The value to be tested
156   */
157  private static void assertAligned(int value) {
158    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((value & (BYTES_IN_INT - 1)) == 0);
159  }
160
161  private static void assertAligned(Word value) {
162    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(Word.fromIntSignExtend(BYTES_IN_INT - 1)).isZero());
163  }
164
165  private static void assertAligned(Extent value) {
166    assertAligned(value.toWord());
167  }
168
169  private static void assertAligned(Address value) {
170    assertAligned(value.toWord());
171  }
172
173  /**
174   * Test whether a memory range is set to a given integer value
175   *
176   * @param start The address to start checking at
177   * @param bytes The size of the region to check, in bytes
178   * @param verbose If {@code true}, produce verbose output
179   * @param value The value to which the memory should be set
180   * @return {@code true} if the memory range is set to the given value,
181   *  {@code false} if there's at least one address where another value
182   *  is saved
183   */
184  @NoInline
185  private static boolean isSet(Address start, int bytes, boolean verbose,
186      int value)
187    /* Inlining this loop into the uninterruptible code can
188     *  cause/encourage the GCP into moving a get_obj_tib into the
189     * interruptible region where the TIB is being installed via an
190     * int_store
191   */ {
192    if (VM.VERIFY_ASSERTIONS) assertAligned(bytes);
193    for (int i = 0; i < bytes; i += BYTES_IN_INT)
194      if (start.loadInt(Offset.fromIntSignExtend(i)) != value) {
195        if (verbose) {
196          Log.prependThreadId();
197          Log.write("VM range does not contain only value ");
198          Log.writeln(value);
199          Log.write("Non-zero range: "); Log.write(start);
200          Log.write(" .. "); Log.writeln(start.plus(bytes));
201          Log.write("First bad value at "); Log.writeln(start.plus(i));
202          dumpMemory(start, 0, bytes);
203        }
204        return false;
205      }
206    return true;
207  }
208
209  /**
210   * Dump the contents of memory around a given address
211   *
212   * @param addr The address around which the memory should be dumped
213   * @param beforeBytes The number of bytes before the address to be
214   * included in the dump
215   * @param afterBytes The number of bytes after the address to be
216   * included in the dump
217   */
218  public static void dumpMemory(Address addr, int beforeBytes, int afterBytes) {
219    VM.memory.dumpMemory(addr, beforeBytes, afterBytes);
220  }
221}