/*
* Copyright (c) 2011, 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.heap.gcx;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.heap.gcx.rset.ctbl.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.runtime.*;
import static com.sun.max.vm.heap.gcx.rset.ctbl.CardState.*;
import static com.sun.max.vm.heap.gcx.HeapFreeChunk.*;
public final class NoEvacuatedSpaceReferenceVerifier extends PointerIndexVisitor implements CellRangeVisitor, OverlappingCellVisitor {
final CardTableRSet cardTableRSet;
EvacuatingSpace evacuatedSpace;
/**
* Controls whether the whole iterable range passed to {@link #visitCells(Address, Address)} is verified or only dirty cards in the range.
*/
boolean dirtyCardsOnly;
public NoEvacuatedSpaceReferenceVerifier(CardTableRSet cardTableRSet, EvacuatingSpace evacuatedSpace) {
this.cardTableRSet = cardTableRSet;
this.evacuatedSpace = evacuatedSpace;
}
public void setEvacuatedSpace(EvacuatingSpace evacuatedSpace) {
this.evacuatedSpace = evacuatedSpace;
}
@Override
public void visitCells(Address start, Address end) {
if (dirtyCardsOnly) {
cardTableRSet.visitCards(start, end, DIRTY_CARD, this);
} else {
Pointer cell = start.asPointer();
do {
cell = visitCell(cell);
} while (cell.lessThan(end));
}
}
public void setVisitDirtyCardsOnly(boolean dirtyCardsOnly) {
this.dirtyCardsOnly = dirtyCardsOnly;
}
private Pointer visitCell(Pointer cell) {
final Pointer origin = Layout.cellToOrigin(cell);
checkNoRef(origin, Layout.hubIndex());
final Hub hub = Layout.getHub(origin);
if (hub == heapFreeChunkHub()) {
return cell.plus(toHeapFreeChunk(origin).size);
}
final SpecificLayout specificLayout = hub.specificLayout;
if (specificLayout.isTupleLayout()) {
TupleReferenceMap.visitReferences(hub, origin, this);
if (hub.isJLRReference) {
checkNoRef(origin, SpecialReferenceManager.referentIndex());
}
return cell.plus(hub.tupleSize);
}
if (specificLayout.isHybridLayout()) {
TupleReferenceMap.visitReferences(hub, origin, this);
} else if (specificLayout.isReferenceArrayLayout()) {
final int length = Layout.readArrayLength(origin) + Layout.firstElementIndex();
for (int index = Layout.firstElementIndex(); index < length; index++) {
checkNoRef(origin, index);
}
}
return cell.plus(Layout.size(origin));
}
@Override
public Pointer visitCell(Pointer cell, Address start, Address end) {
if (start.greaterEqual(cell) && cell.lessEqual(end)) {
return visitCell(cell);
}
final Pointer origin = Layout.cellToOrigin(cell);
final Pointer hubPointer = origin.plusWords(Layout.hubIndex());
if (hubPointer.greaterEqual(start) && hubPointer.lessThan(end)) {
checkNoRef(origin, Layout.hubIndex());
}
final Hub hub = Layout.getHub(origin);
if (hub == heapFreeChunkHub()) {
return cell.plus(toHeapFreeChunk(origin).size);
}
final SpecificLayout specificLayout = hub.specificLayout;
if (specificLayout.isTupleLayout()) {
TupleReferenceMap.visitReferences(hub, origin, this, start, end);
if (hub.isJLRReference) {
final Pointer referentFieldPointer = origin.plusWords(SpecialReferenceManager.referentIndex());
if (referentFieldPointer.greaterEqual(start) && referentFieldPointer.lessThan(end)) {
checkNoRef(origin, SpecialReferenceManager.referentIndex());
}
}
return cell.plus(hub.tupleSize);
}
if (specificLayout.isHybridLayout()) {
TupleReferenceMap.visitReferences(hub, origin, this, start, end);
} else if (specificLayout.isReferenceArrayLayout()) {
int length = Layout.readArrayLength(origin) + Layout.firstElementIndex();
int firstWordIndex = Layout.firstElementIndex();
Pointer p = origin.plusWords(Layout.firstElementIndex());
if (p.lessThan(start)) {
firstWordIndex += start.minus(p).unsignedShiftedRight(Word.widthValue().log2numberOfBytes).toInt();
}
p = p.plusWords(length);
if (end.lessThan(p)) {
length -= p.minus(end).unsignedShiftedRight(Word.widthValue().log2numberOfBytes).toInt();
}
for (int index = firstWordIndex; index < length; index++) {
checkNoRef(origin, index);
}
}
return cell.plus(Layout.size(origin));
}
private void checkNoRef(Pointer pointer, int wordIndex) {
final Pointer cell = pointer.getReference(wordIndex).toOrigin();
if (evacuatedSpace.contains(cell)) {
Log.print("Reference in ");
Log.print(pointer);
Log.print(" at ");
Log.print(pointer.plusWords(wordIndex));
Log.print(" holds pointer to evacuated location ");
Log.println(cell);
FatalError.breakpoint();
FatalError.crash("invariant violation");
}
if (!cell.isZero()) {
DarkMatter.checkNotDarkMatterRef(Layout.cellToOrigin(cell), Layout.hubIndex());
}
}
@Override
public void visit(Pointer pointer, int wordIndex) {
checkNoRef(pointer, wordIndex);
}
}