package com.oracle.svm.core.genscavenge;
import org.graalvm.compiler.options.Option;
import org.graalvm.compiler.word.Word;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;
import com.oracle.svm.core.annotate.AlwaysInline;
import com.oracle.svm.core.heap.ObjectReferenceVisitor;
import com.oracle.svm.core.heap.ReferenceAccess;
import com.oracle.svm.core.hub.LayoutEncoding;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.option.HostedOptionKey;
final class GreyToBlackObjRefVisitor implements ObjectReferenceVisitor {
private final Counters counters;
@Platforms(Platform.HOSTED_ONLY.class)
GreyToBlackObjRefVisitor() {
if (Options.GreyToBlackObjRefDemographics.getValue()) {
counters = new RealCounters();
} else {
counters = new NoopCounters();
}
}
@Override
public boolean visitObjectReference(Pointer objRef, boolean compressed) {
return visitObjectReferenceInline(objRef, 0, compressed, null);
}
@Override
@AlwaysInline("GC performance")
public boolean visitObjectReferenceInline(Pointer objRef, boolean compressed, Object holderObject) {
return visitObjectReferenceInline(objRef, 0, compressed, holderObject);
}
@Override
@AlwaysInline("GC performance")
public boolean visitObjectReferenceInline(Pointer objRef, int innerOffset, boolean compressed) {
return visitObjectReferenceInline(objRef, innerOffset, compressed, null);
}
@Override
@AlwaysInline("GC performance")
public boolean visitObjectReferenceInline(Pointer objRef, int innerOffset, boolean compressed, Object holderObject) {
assert innerOffset >= 0;
assert !objRef.isNull();
counters.noteObjRef();
Pointer offsetP = ReferenceAccess.singleton().readObjectAsUntrackedPointer(objRef, compressed);
assert offsetP.isNonNull() || innerOffset == 0;
Pointer p = offsetP.subtract(innerOffset);
if (p.isNull()) {
counters.noteNullReferent();
return true;
}
if (HeapImpl.getHeapImpl().isInImageHeap(p)) {
counters.noteNonHeapReferent();
return true;
}
UnsignedWord header = ObjectHeaderImpl.readHeaderFromPointer(p);
if (GCImpl.getGCImpl().isCompleteCollection() || !ObjectHeaderImpl.hasRememberedSet(header)) {
if (ObjectHeaderImpl.isForwardedHeader(header)) {
counters.noteForwardedReferent();
Object obj = ObjectHeaderImpl.getForwardedObject(p);
Object offsetObj = (innerOffset == 0) ? obj : Word.objectToUntrackedPointer(obj).add(innerOffset).toObject();
ReferenceAccess.singleton().writeObjectAt(objRef, offsetObj, compressed);
HeapImpl.getHeapImpl().dirtyCardIfNecessary(holderObject, obj);
return true;
}
Object obj = p.toObject();
assert innerOffset < LayoutEncoding.getSizeFromObject(obj).rawValue();
Object copy = HeapImpl.getHeapImpl().promoteObject(obj, header);
if (copy != obj) {
counters.noteCopiedReferent();
Object offsetCopy = (innerOffset == 0) ? copy : Word.objectToUntrackedPointer(copy).add(innerOffset).toObject();
ReferenceAccess.singleton().writeObjectAt(objRef, offsetCopy, compressed);
} else {
counters.noteUnmodifiedReference();
}
HeapImpl.getHeapImpl().dirtyCardIfNecessary(holderObject, copy);
}
return true;
}
public Counters openCounters() {
return counters.open();
}
public static class Options {
@Option(help = "Develop demographics of the object references visited.")
public static final HostedOptionKey<Boolean> GreyToBlackObjRefDemographics = new HostedOptionKey<>(false);
}
public interface Counters extends AutoCloseable {
Counters open();
@Override
void close();
void noteObjRef();
void noteNullReferent();
void noteForwardedReferent();
void noteNonHeapReferent();
void noteCopiedReferent();
void noteUnmodifiedReference();
void toLog();
void reset();
}
public static class RealCounters implements Counters {
private long objRef;
private long nullObjRef;
private long nullReferent;
private long forwardedReferent;
private long nonHeapReferent;
private long copiedReferent;
private long unmodifiedReference;
RealCounters() {
reset();
}
@Override
public void reset() {
objRef = 0L;
nullObjRef = 0L;
nullReferent = 0L;
forwardedReferent = 0L;
nonHeapReferent = 0L;
copiedReferent = 0L;
unmodifiedReference = 0L;
}
@Override
public RealCounters open() {
reset();
return this;
}
@Override
public void close() {
toLog();
reset();
}
@Override
public void noteObjRef() {
objRef += 1L;
}
@Override
public void noteNullReferent() {
nullReferent += 1L;
}
@Override
public void noteForwardedReferent() {
forwardedReferent += 1L;
}
@Override
public void noteNonHeapReferent() {
nonHeapReferent += 1L;
}
@Override
public void noteCopiedReferent() {
copiedReferent += 1L;
}
@Override
public void noteUnmodifiedReference() {
unmodifiedReference += 1L;
}
@Override
public void toLog() {
Log log = Log.log();
log.string("[GreyToBlackObjRefVisitor.counters:");
log.string(" objRef: ").signed(objRef);
log.string(" nullObjRef: ").signed(nullObjRef);
log.string(" nullReferent: ").signed(nullReferent);
log.string(" forwardedReferent: ").signed(forwardedReferent);
log.string(" nonHeapReferent: ").signed(nonHeapReferent);
log.string(" copiedReferent: ").signed(copiedReferent);
log.string(" unmodifiedReference: ").signed(unmodifiedReference);
log.string("]").newline();
}
}
public static class NoopCounters implements Counters {
NoopCounters() {
}
@Override
public NoopCounters open() {
return this;
}
@Override
public void close() {
}
@Override
public void noteObjRef() {
}
@Override
public void noteNullReferent() {
}
@Override
public void noteForwardedReferent() {
}
@Override
public void noteNonHeapReferent() {
}
@Override
public void noteCopiedReferent() {
}
@Override
public void noteUnmodifiedReference() {
}
@Override
public void toLog() {
}
@Override
public void reset() {
}
}
}