/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* 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.
*/
package com.sun.max.vm.code;
import java.util.*;
import com.sun.max.annotate.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.heap.*;
/**
* A code region that encapsulates a contiguous, fixed-sized memory area in the VM
* for storing code and data structures relating to code.
*/
public class CodeRegion extends LinearAllocatorRegion {
public static final int DEFAULT_CAPACITY = 10;
/**
* Inspectable counter of the number of code evictions in this region that have <strong>begun</strong>.
* It is intended that this counter increment just before eviction starts to modify the contents of the
* code cache, after any preliminary steps that do not modify the contents.
* <p>
* The count will remain 0 in any unmanaged code region.
* <p>
* Used by the Inspector to determine how many evictions have happened and whether an eviction
* is currently in progress.
* <p>
* {@code 0 <= codeEvictionCompletedCounter <= codeEvictionStartedCounter}
*/
@INSPECTED
private long evictionStartedCount = 0;
/**
* Inspectable counter of the number of code cache evictions in this region that have <strong>completed</strong>.
* It is intended that this counter increment just after eviction finishes modifying the contents of the
* code cache, before any subsequent steps that do not modify the contents.
* <p>
* The count will remain 0 in any unmanaged code region.
* <p>
* Used by the Inspector to determine if the VM is performing a code eviction in this region.
*/
@INSPECTED
private long evictionCompletedCount = 0;
/**
* Creates a code region that is not yet bound to any memory.
*
* @param description a description of this code region. This value may be used by a debugger.
*/
public CodeRegion(String description) {
super(description);
targetMethods = new TargetMethod[DEFAULT_CAPACITY];
findIndex = new int[DEFAULT_CAPACITY];
}
/**
* Constructs a new code region that begins at the specified address and has the specified fixed size.
*
* This constructor is only used for creating the {@linkplain Code#bootCodeRegion boot} code region.
*
* @param start the starting memory address
* @param size the size of the code region in bytes
*/
@HOSTED_ONLY
public CodeRegion(Address start, Size size, String description) {
super(start, size, description);
targetMethods = new TargetMethod[DEFAULT_CAPACITY];
findIndex = new int[DEFAULT_CAPACITY];
}
/**
* Binds this code region to some allocated memory range.
*
* @param start the start address of the range
* @param size the size of the memory range
*/
public void bind(Address start, Size size) {
this.start = start;
this.size = size;
this.mark.set(start);
}
/**
* Gets the address at which the GC should start traversing this region.
* In case it is managed, this may be different from the value obtained from {@link #start()}.
*/
public Address gcstart() {
return start();
}
/**
* A sorted list of the target methods allocated within this code region.
*/
@INSPECTED
protected TargetMethod[] targetMethods;
/**
* The number of target methods allocated within this code region.
*/
@INSPECTED
protected int length;
/**
* The number of times that an addition to the array
* of target methods has been started. During the addition
* the array may not be in a usefully inspectable state.
*/
@INSPECTED
private int additionStartedCount = 0;
/**
* The number of times that an addition to the array
* of target methods has completed.
*/
@INSPECTED
private int additionCompletedCount = 0;
public static final int FIND_INDEX_ALIGN_SHIFT = 9;
public static final int FIND_INDEX_ALIGN = 1 << FIND_INDEX_ALIGN_SHIFT;
/**
* Index into {@link #targetMethods} that allows a constant-time implementation of {@link #find(Address)}.
* The code region is divided in pages of size {@link #FIND_INDEX_ALIGN}, and this array stores the index
* of the method in the {@link #targetMethods} that covers the beginning of the page.
* Since {@link #targetMethods} is sorted, a linear search with this starting point quickly finds the method
* for an arbitrary address.
*/
protected int[] findIndex;
/**
* Number of target methods in the code regions.
*/
public int numTargetMethods() {
return length;
}
/**
* Gets a copy of the sorted target method list.
*/
public TargetMethod[] copyOfTargetMethods() {
int length = this.length;
assert length <= targetMethods.length;
TargetMethod[] result = new TargetMethod[length];
System.arraycopy(targetMethods, 0, result, 0, length);
return result;
}
/**
* Adds a target method to this sorted list of target methods.
*/
public void add(TargetMethod targetMethod) {
additionStartedCount++; // The array becomes not inspectable
if (length == targetMethods.length) {
int newCapacity = (targetMethods.length * 3) / 2 + 1;
targetMethods = Arrays.copyOf(targetMethods, newCapacity);
}
int insertionPoint;
if (length == 0 || COMPARATOR.compare(targetMethods[length - 1], targetMethod) < 0) {
// follows any existing entries in the array so it can simply be appended.
insertionPoint = length;
} else {
// Out-of-order addition: create an open array entry for the insertion
int index = Arrays.binarySearch(targetMethods, 0, length, targetMethod, COMPARATOR);
assert index < 0 : targetMethod + " overlaps " + targetMethods[index];
insertionPoint = -(index + 1);
System.arraycopy(targetMethods, insertionPoint, targetMethods, insertionPoint + 1, length - insertionPoint);
}
targetMethods[insertionPoint] = targetMethod;
length++;
additionCompletedCount++; // The array becomes once again inspectable
assert start().alignUp(FIND_INDEX_ALIGN).equals(start());
int startIdx = targetMethod.start().plus(FIND_INDEX_ALIGN - 1).minus(start()).unsignedShiftedRight(FIND_INDEX_ALIGN_SHIFT).toInt();
int endIdx = targetMethod.end().minus(1).minus(start()).unsignedShiftedRight(FIND_INDEX_ALIGN_SHIFT).toInt();
if (endIdx >= findIndex.length) {
findIndex = Arrays.copyOf(findIndex, (endIdx * 3) / 2 + 1);
}
for (int i = startIdx; i <= endIdx; i++) {
assert findIndex[i] == 0;
findIndex[i] = insertionPoint;
}
}
/**
* Looks up the target method containing a particular address, using the index.
*
* @param cp the address to lookup in this region
* @return a reference to the target method containing the specified address, if it exists; {@code null} otherwise
*/
public TargetMethod find(Address cp) {
return find0(cp, start(), findIndex, targetMethods);
}
protected final TargetMethod find0(Address cp, Address start, int[] index, TargetMethod[] tms) {
int pageIndex = cp.minus(start).unsignedShiftedRight(FIND_INDEX_ALIGN_SHIFT).toInt();
if (pageIndex < 0 || pageIndex >= index.length) {
return null;
}
int methodIdx = index[pageIndex];
while (true) {
TargetMethod method = tms[methodIdx];
if (method == null) {
return null;
}
assert validMethodStart(method, cp);
if (methodFound(method, cp)) {
return method;
}
methodIdx++;
}
}
protected boolean validMethodStart(TargetMethod tm, Address address) {
return tm.start().lessEqual(address);
}
protected boolean methodFound(TargetMethod tm, Address address) {
return tm.end().greaterThan(address);
}
/**
* Process each target method in this region with a given closure. This iteration operates on a snapshot of the
* methods in this region.
*
* @return {@code false} if {@code c} returned {@code false} when processing a target method (and thus aborted
* processing of any subsequent methods in the snapshot)
*/
public boolean doAllTargetMethods(TargetMethod.Closure c) {
TargetMethod[] targetMethods = this.targetMethods;
assert length <= targetMethods.length;
for (int i = 0; i < length; i++) {
TargetMethod targetMethod = targetMethods[i];
if (targetMethod != null && !c.doTargetMethod(targetMethod)) {
return false;
}
}
return true;
}
/**
* Receives notification that a code eviction in this region is just about to start.
* The intention is that the notification is received just before actual modifications
* to the contents of the region begin.
*/
public void notifyEvictionStarted() {
evictionStartedCount++;
}
/**
* Receives notification that a code eviction in this region has just concluded.
* The intention is that the notification is received just after the last actual modifications
* to the contents of the region have ended.
*/
public void notifyEvictionCompleted() {
evictionCompletedCount++;
}
public static final Comparator<TargetMethod> COMPARATOR = new Comparator<TargetMethod>() {
@Override
public int compare(TargetMethod o1, TargetMethod o2) {
Address o1Start = o1.start();
Address o2Start = o2.start();
if (o1Start.lessThan(o2Start)) {
assert o1.end().lessEqual(o2Start) : "intersecting regions";
return -1;
}
if (o1Start.equals(o2Start)) {
assert o1.end().equals(o2.end()) : "intersecting regions";
return 0;
}
assert o2.end().lessEqual(o1Start) : "intersecting regions";
return 1;
}
};
}