| /* |
| * This file is part of the Jikes RVM project (http://jikesrvm.org). |
| * |
| * This file is licensed to You under the Eclipse Public License (EPL); |
| * 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/eclipse-1.0.php |
| * |
| * See the COPYRIGHT.txt file distributed with this work for information |
| * regarding copyright ownership. |
| */ |
| package org.mmtk.utility; |
| |
| import org.mmtk.vm.VM; |
| |
| import org.vmmagic.unboxed.*; |
| import org.vmmagic.pragma.*; |
| |
| /** |
| * This class implements basic memory copying, setting and clearing |
| * operations. |
| * |
| * NOTE: Most of the operations in this class are performed at teh |
| * granularity of a Java integer (ie 4-byte units) |
| * |
| * FIXME: Why can't these operations be performed at word-granularity? |
| */ |
| @Uninterruptible |
| public class Memory implements Constants { |
| |
| /**************************************************************************** |
| * |
| * Class variables |
| */ |
| |
| /** zero operations greater than this size are done using the |
| * underlying OS implementation of zero() */ |
| private static final int SMALL_REGION_THRESHOLD = 1<<8; // empirically chosen |
| |
| |
| /**************************************************************************** |
| * |
| * Basic memory setting and zeroing operations |
| */ |
| |
| /** |
| * Zero a region of memory |
| * |
| * @param start The start of the region to be zeroed (must be 4-byte aligned) |
| * @param bytes The number of bytes to be zeroed (must be 4-byte aligned) |
| */ |
| @Inline |
| public static void zero(Address start, Extent bytes) { |
| if (VM.VERIFY_ASSERTIONS) { |
| assertAligned(start); |
| assertAligned(bytes); |
| } |
| if (bytes.GT(Extent.fromIntZeroExtend(SMALL_REGION_THRESHOLD))) |
| VM.memory.zero(start, bytes); |
| else |
| zeroSmall(start, bytes); |
| } |
| |
| /** |
| * Zero a small region of memory |
| * |
| * @param start The start of the region to be zeroed (must be 4-byte aligned) |
| * @param bytes The number of bytes to be zeroed (must be 4-byte aligned) |
| */ |
| @Inline |
| public static void zeroSmall(Address start, Extent bytes) { |
| if (VM.VERIFY_ASSERTIONS) { |
| assertAligned(start); |
| assertAligned(bytes); |
| } |
| Address end = start.plus(bytes); |
| for (Address addr = start; addr.LT(end); addr = addr.plus(BYTES_IN_INT)) |
| addr.store(0); |
| } |
| |
| /** |
| * Set a region of memory |
| * |
| * @param start The start of the region to be zeroed (must be 4-byte aligned) |
| * @param bytes The number of bytes to be zeroed (must be 4-byte aligned) |
| * @param value The value to which the integers in the region should be set |
| */ |
| @Inline |
| public static void set(Address start, int bytes, int value) { |
| if (VM.VERIFY_ASSERTIONS) { |
| assertAligned(start); |
| assertAligned(bytes); |
| } |
| Address end = start.plus(bytes); |
| for (Address addr = start; addr.LT(end); addr = addr.plus(BYTES_IN_INT)) |
| addr.store(value); |
| } |
| |
| |
| /**************************************************************************** |
| * |
| * Helper methods |
| */ |
| |
| /** |
| * Check that a memory range is zeroed |
| * |
| * @param start The start address of the range to be checked |
| * @param bytes The size of the region to be checked, in bytes |
| * @return True if the region is zeroed |
| */ |
| @Inline |
| public static boolean IsZeroed(Address start, int bytes) { |
| return isSet(start, bytes, false, 0); |
| } |
| |
| /** |
| * Assert that a memory range is zeroed. An assertion failure will |
| * occur if the region is not zeroed. |
| * |
| * this is in the inline allocation sequence when |
| * VM.VERIFY_ASSERTIONS is true, it is carefully written to |
| * reduce the impact on code space. |
| * |
| * @param start The start address of the range to be checked |
| * @param bytes The size of the region to be checked, in bytes |
| */ |
| @NoInline |
| public static void assertIsZeroed(Address start, int bytes) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isSet(start, bytes, true, 0)); |
| } |
| |
| /** |
| * Verbosely check and return true if a memory range is set to some |
| * integer value |
| * |
| * @param start The start address of the range to be checked |
| * @param bytes The size of the region to be checked, in bytes |
| * @param value The value to which this region should be set |
| * @return True if the region has been correctly set |
| */ |
| @Inline |
| public static boolean isSet(Address start, int bytes, int value) { |
| return isSet(start, bytes, true, value); |
| } |
| |
| /** |
| * Assert appropriate alignment, triggering an assertion failure if |
| * the value does not satisify the alignment requirement of the |
| * memory operations. |
| * |
| * @param value The value to be tested |
| */ |
| private static void assertAligned(int value) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((value & (BYTES_IN_INT - 1)) == 0); |
| } |
| |
| private static void assertAligned(Word value) { |
| if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(value.and(Word.fromIntSignExtend(BYTES_IN_INT-1)).isZero()); |
| } |
| |
| private static void assertAligned(Extent value) { |
| assertAligned(value.toWord()); |
| } |
| |
| private static void assertAligned(Address value) { |
| assertAligned(value.toWord()); |
| } |
| |
| /** |
| * Test whether a memory range is set to a given integer value |
| * |
| * @param start The address to start checking at |
| * @param bytes The size of the region to check, in bytes |
| * @param verbose If true, produce verbose output |
| * @param value The value to which the memory should be set |
| */ |
| @NoInline |
| private static boolean isSet(Address start, int bytes, boolean verbose, |
| int value) |
| /* Inlining this loop into the uninterruptible code can |
| * cause/encourage the GCP into moving a get_obj_tib into the |
| * interruptible region where the tib is being installed via an |
| * int_store |
| */ { |
| if (VM.VERIFY_ASSERTIONS) assertAligned(bytes); |
| for (int i = 0; i < bytes; i += BYTES_IN_INT) |
| if (start.loadInt(Offset.fromIntSignExtend(i)) != value) { |
| if (verbose) { |
| Log.prependThreadId(); |
| Log.write("VM range does not contain only value "); |
| Log.writeln(value); |
| Log.write("Non-zero range: "); Log.write(start); |
| Log.write(" .. "); Log.writeln(start.plus(bytes)); |
| Log.write("First bad value at "); Log.writeln(start.plus(i)); |
| dumpMemory(start, 0, bytes); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Dump the contents of memory around a given address |
| * |
| * @param addr The address around which the memory should be dumped |
| * @param beforeBytes The number of bytes before the address to be |
| * included in the dump |
| * @param afterBytes The number of bytes after the address to be |
| * included in the dump |
| */ |
| public static void dumpMemory(Address addr, int beforeBytes, int afterBytes) { |
| VM.memory.dumpMemory(addr, beforeBytes, afterBytes); |
| } |
| } |