package org.graalvm.compiler.hotspot.replacements;
import static jdk.vm.ci.code.MemoryBarriers.LOAD_STORE;
import static jdk.vm.ci.code.MemoryBarriers.STORE_STORE;
import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_OPTIONVALUES;
import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_VMCONFIG;
import static org.graalvm.compiler.hotspot.nodes.AcquiredCASLockNode.mark;
import static org.graalvm.compiler.hotspot.nodes.BeginLockScopeNode.beginLockScope;
import static org.graalvm.compiler.hotspot.nodes.EndLockScopeNode.endLockScope;
import static org.graalvm.compiler.hotspot.nodes.VMErrorNode.vmError;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.DISPLACED_MARK_WORD_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.MARK_WORD_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.OBJECT_MONITOR_CXQ_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.OBJECT_MONITOR_ENTRY_LIST_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.OBJECT_MONITOR_OWNER_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.OBJECT_MONITOR_RECURSION_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.PROTOTYPE_MARK_WORD_LOCATION;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.ageMaskInPlace;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.biasedLockMaskInPlace;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.biasedLockPattern;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.epochMaskInPlace;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.loadWordFromObject;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.lockDisplacedMarkOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.markOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.monitorMask;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.objectMonitorCxqOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.objectMonitorEntryListOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.objectMonitorOwnerOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.objectMonitorRecursionsOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.pageSize;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.prototypeMarkWordOffset;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.registerAsWord;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.stackBias;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.unlockedMask;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.useBiasedLocking;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.verifyOop;
import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.wordSize;
import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.ProfileMonitors;
import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.SimpleFastInflatedLocking;
import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.TraceMonitorsMethodFilter;
import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.TraceMonitorsTypeFilter;
import static org.graalvm.compiler.hotspot.replacements.HotspotSnippetsOptions.VerifyBalancedMonitors;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FAST_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.FREQUENT_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_LIKELY_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.SLOW_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.VERY_FAST_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
import static org.graalvm.compiler.nodes.extended.MembarNode.memoryBarrier;
import static org.graalvm.compiler.replacements.SnippetTemplate.DEFAULT_REPLACER;
import static jdk.internal.vm.compiler.word.WordFactory.unsigned;
import static jdk.internal.vm.compiler.word.WordFactory.zero;
import java.util.List;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
import org.graalvm.compiler.bytecode.Bytecode;
import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
import org.graalvm.compiler.core.common.type.ObjectStamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.core.common.type.StampPair;
import org.graalvm.compiler.debug.DebugHandlersFactory;
import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
import org.graalvm.compiler.graph.Node.NodeIntrinsic;
import org.graalvm.compiler.graph.iterators.NodeIterable;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
import org.graalvm.compiler.hotspot.meta.HotSpotRegistersProvider;
import org.graalvm.compiler.hotspot.nodes.CurrentLockNode;
import org.graalvm.compiler.hotspot.nodes.FastAcquireBiasedLockNode;
import org.graalvm.compiler.hotspot.nodes.MonitorCounterNode;
import org.graalvm.compiler.hotspot.word.KlassPointer;
import org.graalvm.compiler.nodes.BreakpointNode;
import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.DeoptimizeNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.InvokeNode;
import org.graalvm.compiler.nodes.NamedLocationIdentity;
import org.graalvm.compiler.nodes.NodeView;
import org.graalvm.compiler.nodes.ReturnNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.debug.DynamicCounterNode;
import org.graalvm.compiler.nodes.extended.ForeignCallNode;
import org.graalvm.compiler.nodes.extended.MembarNode;
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
import org.graalvm.compiler.nodes.java.MonitorExitNode;
import org.graalvm.compiler.nodes.java.RawMonitorEnterNode;
import org.graalvm.compiler.nodes.spi.LoweringTool;
import org.graalvm.compiler.nodes.type.StampTool;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.phases.common.inlining.InliningUtil;
import org.graalvm.compiler.replacements.Log;
import org.graalvm.compiler.replacements.SnippetCounter;
import org.graalvm.compiler.replacements.SnippetTemplate.AbstractTemplates;
import org.graalvm.compiler.replacements.SnippetTemplate.Arguments;
import org.graalvm.compiler.replacements.SnippetTemplate.SnippetInfo;
import org.graalvm.compiler.replacements.Snippets;
import org.graalvm.compiler.word.Word;
import jdk.internal.vm.compiler.word.LocationIdentity;
import jdk.internal.vm.compiler.word.Pointer;
import jdk.internal.vm.compiler.word.WordBase;
import jdk.internal.vm.compiler.word.WordFactory;
import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.meta.DeoptimizationAction;
import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.ResolvedJavaType;
public class MonitorSnippets implements Snippets {
private static final boolean PROFILE_CONTEXT = false;
@Fold
static boolean doProfile(@Fold.InjectedParameter OptionValues options) {
return ProfileMonitors.getValue(options);
}
@Snippet
public static void monitorenter(Object object, KlassPointer hub, @ConstantParameter int lockDepth, @ConstantParameter Register threadRegister, @ConstantParameter Register stackPointerRegister,
@ConstantParameter boolean trace, @ConstantParameter Counters counters) {
verifyOop(object);
final Word mark = loadWordFromObject(object, markOffset(INJECTED_VMCONFIG));
final Word lock = beginLockScope(lockDepth);
Pointer objectPointer = Word.objectToTrackedPointer(object);
trace(trace, " object: 0x%016lx\n", objectPointer);
trace(trace, " lock: 0x%016lx\n", lock);
trace(trace, " mark: 0x%016lx\n", mark);
incCounter();
if (useBiasedLocking(INJECTED_VMCONFIG)) {
if (tryEnterBiased(object, hub, lock, mark, threadRegister, trace, counters)) {
return;
}
}
if (inlineFastLockSupported() && probability(SLOW_PATH_PROBABILITY, mark.and(monitorMask(INJECTED_VMCONFIG)).notEqual(0))) {
if (tryEnterInflated(object, lock, mark, threadRegister, trace, counters)) {
return;
}
} else {
Word unlockedMark = mark.or(unlockedMask(INJECTED_VMCONFIG));
trace(trace, " unlockedMark: 0x%016lx\n", unlockedMark);
lock.writeWord(lockDisplacedMarkOffset(INJECTED_VMCONFIG), unlockedMark, DISPLACED_MARK_WORD_LOCATION);
MembarNode.memoryBarrier(STORE_STORE);
Word currentMark = objectPointer.compareAndSwapWord(markOffset(INJECTED_VMCONFIG), unlockedMark, lock, MARK_WORD_LOCATION);
if (probability(FAST_PATH_PROBABILITY, currentMark.equal(unlockedMark))) {
traceObject(trace, "+lock{cas}", object, true);
counters.lockCas.inc();
mark(object);
return;
} else {
trace(trace, " currentMark: 0x%016lx\n", currentMark);
final Word alignedMask = unsigned(wordSize() - 1);
final Word stackPointer = registerAsWord(stackPointerRegister).add(stackBias(INJECTED_VMCONFIG));
if (probability(FAST_PATH_PROBABILITY, currentMark.subtract(stackPointer).and(alignedMask.subtract(pageSize(INJECTED_VMCONFIG))).equal(0))) {
lock.writeWord(lockDisplacedMarkOffset(INJECTED_VMCONFIG), zero(), DISPLACED_MARK_WORD_LOCATION);
traceObject(trace, "+lock{cas:recursive}", object, true);
counters.lockCasRecursive.inc();
return;
}
traceObject(trace, "+lock{stub:failed-cas/stack}", object, true);
counters.lockStubFailedCas.inc();
}
}
monitorenterStubC(MONITORENTER, object, lock);
}
private static boolean tryEnterBiased(Object object, KlassPointer hub, Word lock, Word mark, Register threadRegister, boolean trace, Counters counters) {
final Word biasableLockBits = mark.and(biasedLockMaskInPlace(INJECTED_VMCONFIG));
final Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(INJECTED_VMCONFIG), PROTOTYPE_MARK_WORD_LOCATION);
final Word thread = registerAsWord(threadRegister);
final Word tmp = prototypeMarkWord.or(thread).xor(mark).and(~ageMaskInPlace(INJECTED_VMCONFIG));
trace(trace, "prototypeMarkWord: 0x%016lx\n", prototypeMarkWord);
trace(trace, " thread: 0x%016lx\n", thread);
trace(trace, " tmp: 0x%016lx\n", tmp);
if (probability(FAST_PATH_PROBABILITY, tmp.equal(0))) {
traceObject(trace, "+lock{bias:existing}", object, true);
counters.lockBiasExisting.inc();
FastAcquireBiasedLockNode.mark(object);
return true;
}
if (probability(NOT_FREQUENT_PROBABILITY, biasableLockBits.equal(WordFactory.unsigned(biasedLockPattern(INJECTED_VMCONFIG))))) {
Pointer objectPointer = Word.objectToTrackedPointer(object);
if (probability(FREQUENT_PROBABILITY, tmp.and(biasedLockMaskInPlace(INJECTED_VMCONFIG)).equal(0))) {
if (probability(FREQUENT_PROBABILITY, tmp.and(epochMaskInPlace(INJECTED_VMCONFIG)).equal(0))) {
Word unbiasedMark = mark.and(biasedLockMaskInPlace(INJECTED_VMCONFIG) | ageMaskInPlace(INJECTED_VMCONFIG) | epochMaskInPlace(INJECTED_VMCONFIG));
Word biasedMark = unbiasedMark.or(thread);
trace(trace, " unbiasedMark: 0x%016lx\n", unbiasedMark);
trace(trace, " biasedMark: 0x%016lx\n", biasedMark);
if (probability(VERY_FAST_PATH_PROBABILITY, objectPointer.logicCompareAndSwapWord(markOffset(INJECTED_VMCONFIG), unbiasedMark, biasedMark, MARK_WORD_LOCATION))) {
traceObject(trace, "+lock{bias:acquired}", object, true);
counters.lockBiasAcquired.inc();
return true;
}
traceObject(trace, "+lock{stub:revoke}", object, true);
counters.lockStubRevoke.inc();
} else {
Word biasedMark = prototypeMarkWord.or(thread);
trace(trace, " biasedMark: 0x%016lx\n", biasedMark);
if (probability(VERY_FAST_PATH_PROBABILITY, objectPointer.logicCompareAndSwapWord(markOffset(INJECTED_VMCONFIG), mark, biasedMark, MARK_WORD_LOCATION))) {
traceObject(trace, "+lock{bias:transfer}", object, true);
counters.lockBiasTransfer.inc();
return true;
}
traceObject(trace, "+lock{stub:epoch-expired}", object, true);
counters.lockStubEpochExpired.inc();
}
monitorenterStubC(MONITORENTER, object, lock);
return true;
} else {
Word result = objectPointer.compareAndSwapWord(markOffset(INJECTED_VMCONFIG), mark, prototypeMarkWord, MARK_WORD_LOCATION);
if (ENABLE_BREAKPOINT) {
bkpt(object, mark, tmp, result);
}
counters.revokeBias.inc();
return false;
}
} else {
counters.unbiasable.inc();
return false;
}
}
@Fold
public static boolean useFastInflatedLocking(@Fold.InjectedParameter OptionValues options) {
return SimpleFastInflatedLocking.getValue(options);
}
private static boolean inlineFastLockSupported() {
return inlineFastLockSupported(INJECTED_VMCONFIG, INJECTED_OPTIONVALUES);
}
private static boolean inlineFastLockSupported(GraalHotSpotVMConfig config, OptionValues options) {
return useFastInflatedLocking(options) && monitorMask(config) >= 0 && objectMonitorOwnerOffset(config) >= 0;
}
private static boolean tryEnterInflated(Object object, Word lock, Word mark, Register threadRegister, boolean trace, Counters counters) {
lock.writeWord(lockDisplacedMarkOffset(INJECTED_VMCONFIG), lock, DISPLACED_MARK_WORD_LOCATION);
Word monitor = mark.subtract(monitorMask(INJECTED_VMCONFIG));
int ownerOffset = objectMonitorOwnerOffset(INJECTED_VMCONFIG);
Word owner = monitor.readWord(ownerOffset, OBJECT_MONITOR_OWNER_LOCATION);
if (probability(FREQUENT_PROBABILITY, owner.equal(0))) {
if (probability(FREQUENT_PROBABILITY, monitor.logicCompareAndSwapWord(ownerOffset, owner, registerAsWord(threadRegister), OBJECT_MONITOR_OWNER_LOCATION))) {
traceObject(trace, "+lock{inflated:cas}", object, true);
counters.inflatedCas.inc();
return true;
} else {
traceObject(trace, "+lock{stub:inflated:failed-cas}", object, true);
counters.inflatedFailedCas.inc();
}
} else {
traceObject(trace, "+lock{stub:inflated:owned}", object, true);
counters.inflatedOwned.inc();
}
return false;
}
@Snippet
public static void monitorenterStub(Object object, @ConstantParameter int lockDepth, @ConstantParameter boolean trace) {
verifyOop(object);
incCounter();
if (object == null) {
DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException);
}
final Word lock = beginLockScope(lockDepth);
traceObject(trace, "+lock{stub}", object, true);
monitorenterStubC(MONITORENTER, object, lock);
}
@Snippet
public static void monitorexit(Object object, @ConstantParameter int lockDepth, @ConstantParameter Register threadRegister, @ConstantParameter boolean trace,
@ConstantParameter Counters counters) {
trace(trace, " object: 0x%016lx\n", Word.objectToTrackedPointer(object));
final Word mark = loadWordFromObject(object, markOffset(INJECTED_VMCONFIG));
if (useBiasedLocking(INJECTED_VMCONFIG)) {
trace(trace, " mark: 0x%016lx\n", mark);
if (probability(FREQUENT_PROBABILITY, mark.and(biasedLockMaskInPlace(INJECTED_VMCONFIG)).equal(WordFactory.unsigned(biasedLockPattern(INJECTED_VMCONFIG))))) {
endLockScope();
decCounter();
traceObject(trace, "-lock{bias}", object, false);
counters.unlockBias.inc();
return;
}
}
final Word lock = CurrentLockNode.currentLock(lockDepth);
final Word displacedMark = lock.readWord(lockDisplacedMarkOffset(INJECTED_VMCONFIG), DISPLACED_MARK_WORD_LOCATION);
trace(trace, " displacedMark: 0x%016lx\n", displacedMark);
if (probability(NOT_LIKELY_PROBABILITY, displacedMark.equal(0))) {
traceObject(trace, "-lock{recursive}", object, false);
counters.unlockCasRecursive.inc();
} else {
if (!tryExitInflated(object, mark, lock, threadRegister, trace, counters)) {
verifyOop(object);
Pointer objectPointer = Word.objectToTrackedPointer(object);
if (probability(VERY_FAST_PATH_PROBABILITY, objectPointer.logicCompareAndSwapWord(markOffset(INJECTED_VMCONFIG), lock, displacedMark, MARK_WORD_LOCATION))) {
traceObject(trace, "-lock{cas}", object, false);
counters.unlockCas.inc();
} else {
traceObject(trace, "-lock{stub}", object, false);
counters.unlockStub.inc();
monitorexitStubC(MONITOREXIT, object, lock);
}
}
}
endLockScope();
decCounter();
}
private static boolean inlineFastUnlockSupported(OptionValues options) {
return inlineFastUnlockSupported(INJECTED_VMCONFIG, options);
}
private static boolean inlineFastUnlockSupported(GraalHotSpotVMConfig config, OptionValues options) {
return useFastInflatedLocking(options) && objectMonitorEntryListOffset(config) >= 0 && objectMonitorCxqOffset(config) >= 0 && monitorMask(config) >= 0 &&
objectMonitorOwnerOffset(config) >= 0 && objectMonitorRecursionsOffset(config) >= 0;
}
private static boolean tryExitInflated(Object object, Word mark, Word lock, Register threadRegister, boolean trace, Counters counters) {
if (!inlineFastUnlockSupported(INJECTED_OPTIONVALUES)) {
return false;
}
if (probability(SLOW_PATH_PROBABILITY, mark.and(monitorMask(INJECTED_VMCONFIG)).notEqual(0))) {
Word monitor = mark.subtract(monitorMask(INJECTED_VMCONFIG));
int ownerOffset = objectMonitorOwnerOffset(INJECTED_VMCONFIG);
Word owner = monitor.readWord(ownerOffset, OBJECT_MONITOR_OWNER_LOCATION);
int recursionsOffset = objectMonitorRecursionsOffset(INJECTED_VMCONFIG);
Word recursions = monitor.readWord(recursionsOffset, OBJECT_MONITOR_RECURSION_LOCATION);
Word thread = registerAsWord(threadRegister);
if (probability(FAST_PATH_PROBABILITY, owner.xor(thread).or(recursions).equal(0))) {
int cxqOffset = objectMonitorCxqOffset(INJECTED_VMCONFIG);
Word cxq = monitor.readWord(cxqOffset, OBJECT_MONITOR_CXQ_LOCATION);
int entryListOffset = objectMonitorEntryListOffset(INJECTED_VMCONFIG);
Word entryList = monitor.readWord(entryListOffset, OBJECT_MONITOR_ENTRY_LIST_LOCATION);
if (probability(FREQUENT_PROBABILITY, cxq.or(entryList).equal(0))) {
memoryBarrier(LOAD_STORE | STORE_STORE);
monitor.writeWord(ownerOffset, zero());
traceObject(trace, "-lock{inflated:simple}", object, false);
counters.unlockInflatedSimple.inc();
return true;
}
}
counters.unlockStubInflated.inc();
traceObject(trace, "-lock{stub:inflated}", object, false);
monitorexitStubC(MONITOREXIT, object, lock);
return true;
}
return false;
}
@Snippet
public static void monitorexitStub(Object object, @ConstantParameter int lockDepth, @ConstantParameter boolean trace) {
verifyOop(object);
traceObject(trace, "-lock{stub}", object, false);
final Word lock = CurrentLockNode.currentLock(lockDepth);
monitorexitStubC(MONITOREXIT, object, lock);
endLockScope();
decCounter();
}
public static void traceObject(boolean enabled, String action, Object object, boolean enter) {
if (doProfile(INJECTED_OPTIONVALUES)) {
DynamicCounterNode.counter(enter ? "number of monitor enters" : "number of monitor exits", action, 1, PROFILE_CONTEXT);
}
if (enabled) {
Log.print(action);
Log.print(' ');
Log.printlnObject(object);
}
}
public static void trace(boolean enabled, String format, WordBase value) {
if (enabled) {
Log.printf(format, value.rawValue());
}
}
private static final boolean ENABLE_BREAKPOINT = false;
private static final LocationIdentity MONITOR_COUNTER_LOCATION = NamedLocationIdentity.mutable("MonitorCounter");
@NodeIntrinsic(BreakpointNode.class)
static native void bkpt(Object object, Word mark, Word tmp, Word value);
@Fold
static boolean verifyBalancedMonitors(@Fold.InjectedParameter OptionValues options) {
return VerifyBalancedMonitors.getValue(options);
}
static void incCounter() {
if (verifyBalancedMonitors(INJECTED_OPTIONVALUES)) {
final Word counter = MonitorCounterNode.counter();
final int count = counter.readInt(0, MONITOR_COUNTER_LOCATION);
counter.writeInt(0, count + 1, MONITOR_COUNTER_LOCATION);
}
}
public static void decCounter() {
if (verifyBalancedMonitors(INJECTED_OPTIONVALUES)) {
final Word counter = MonitorCounterNode.counter();
final int count = counter.readInt(0, MONITOR_COUNTER_LOCATION);
counter.writeInt(0, count - 1, MONITOR_COUNTER_LOCATION);
}
}
@Snippet
private static void initCounter() {
final Word counter = MonitorCounterNode.counter();
counter.writeInt(0, 0, MONITOR_COUNTER_LOCATION);
}
@Snippet
private static void checkCounter(@ConstantParameter String errMsg) {
final Word counter = MonitorCounterNode.counter();
final int count = counter.readInt(0, MONITOR_COUNTER_LOCATION);
if (count != 0) {
vmError(errMsg, count);
}
}
public static class Counters {
public final SnippetCounter lockBiasExisting;
public final SnippetCounter lockBiasAcquired;
public final SnippetCounter lockBiasTransfer;
public final SnippetCounter lockCas;
public final SnippetCounter lockCasRecursive;
public final SnippetCounter lockStubEpochExpired;
public final SnippetCounter lockStubRevoke;
public final SnippetCounter lockStubFailedCas;
public final SnippetCounter inflatedCas;
public final SnippetCounter inflatedFailedCas;
public final SnippetCounter inflatedOwned;
public final SnippetCounter unbiasable;
public final SnippetCounter revokeBias;
public final SnippetCounter unlockBias;
public final SnippetCounter unlockCas;
public final SnippetCounter unlockCasRecursive;
public final SnippetCounter unlockStub;
public final SnippetCounter unlockStubInflated;
public final SnippetCounter unlockInflatedSimple;
public Counters(SnippetCounter.Group.Factory factory) {
SnippetCounter.Group enter = factory.createSnippetCounterGroup("MonitorEnters");
SnippetCounter.Group exit = factory.createSnippetCounterGroup("MonitorExits");
lockBiasExisting = new SnippetCounter(enter, "lock{bias:existing}", "bias-locked previously biased object");
lockBiasAcquired = new SnippetCounter(enter, "lock{bias:acquired}", "bias-locked newly biased object");
lockBiasTransfer = new SnippetCounter(enter, "lock{bias:transfer}", "bias-locked, biased transferred");
lockCas = new SnippetCounter(enter, "lock{cas}", "cas-locked an object");
lockCasRecursive = new SnippetCounter(enter, "lock{cas:recursive}", "cas-locked, recursive");
lockStubEpochExpired = new SnippetCounter(enter, "lock{stub:epoch-expired}", "stub-locked, epoch expired");
lockStubRevoke = new SnippetCounter(enter, "lock{stub:revoke}", "stub-locked, biased revoked");
lockStubFailedCas = new SnippetCounter(enter, "lock{stub:failed-cas/stack}", "stub-locked, failed cas and stack locking");
inflatedCas = new SnippetCounter(enter, "lock{inflated:cas}", "heavyweight-locked, cas-locked");
inflatedFailedCas = new SnippetCounter(enter, "lock{inflated:failed-cas}", "heavyweight-locked, failed cas");
inflatedOwned = new SnippetCounter(enter, "lock{inflated:owned}", "heavyweight-locked, already owned");
unbiasable = new SnippetCounter(enter, "unbiasable", "object with unbiasable type");
revokeBias = new SnippetCounter(enter, "revokeBias", "object had bias revoked");
unlockBias = new SnippetCounter(exit, "unlock{bias}", "bias-unlocked an object");
unlockCas = new SnippetCounter(exit, "unlock{cas}", "cas-unlocked an object");
unlockCasRecursive = new SnippetCounter(exit, "unlock{cas:recursive}", "cas-unlocked an object, recursive");
unlockStub = new SnippetCounter(exit, "unlock{stub}", "stub-unlocked an object");
unlockStubInflated = new SnippetCounter(exit, "unlock{stub:inflated}", "stub-unlocked an object with inflated monitor");
unlockInflatedSimple = new SnippetCounter(exit, "unlock{inflated}", "unlocked an object monitor");
}
}
public static class Templates extends AbstractTemplates {
private final SnippetInfo monitorenter = snippet(MonitorSnippets.class, "monitorenter");
private final SnippetInfo monitorexit = snippet(MonitorSnippets.class, "monitorexit");
private final SnippetInfo monitorenterStub = snippet(MonitorSnippets.class, "monitorenterStub");
private final SnippetInfo monitorexitStub = snippet(MonitorSnippets.class, "monitorexitStub");
private final SnippetInfo initCounter = snippet(MonitorSnippets.class, "initCounter");
private final SnippetInfo checkCounter = snippet(MonitorSnippets.class, "checkCounter");
private final boolean useFastLocking;
public final Counters counters;
public Templates(OptionValues options, Iterable<DebugHandlersFactory> factories, SnippetCounter.Group.Factory factory, HotSpotProviders providers, TargetDescription target,
boolean useFastLocking) {
super(options, factories, providers, providers.getSnippetReflection(), target);
this.useFastLocking = useFastLocking;
this.counters = new Counters(factory);
}
public void lower(RawMonitorEnterNode monitorenterNode, HotSpotRegistersProvider registers, LoweringTool tool) {
StructuredGraph graph = monitorenterNode.graph();
checkBalancedMonitors(graph, tool);
assert ((ObjectStamp) monitorenterNode.object().stamp(NodeView.DEFAULT)).nonNull();
Arguments args;
if (useFastLocking) {
args = new Arguments(monitorenter, graph.getGuardsStage(), tool.getLoweringStage());
args.add("object", monitorenterNode.object());
args.add("hub", monitorenterNode.getHub());
args.addConst("lockDepth", monitorenterNode.getMonitorId().getLockDepth());
args.addConst("threadRegister", registers.getThreadRegister());
args.addConst("stackPointerRegister", registers.getStackPointerRegister());
args.addConst("trace", isTracingEnabledForType(monitorenterNode.object()) || isTracingEnabledForMethod(graph));
args.addConst("counters", counters);
} else {
args = new Arguments(monitorenterStub, graph.getGuardsStage(), tool.getLoweringStage());
args.add("object", monitorenterNode.object());
args.addConst("lockDepth", monitorenterNode.getMonitorId().getLockDepth());
args.addConst("trace", isTracingEnabledForType(monitorenterNode.object()) || isTracingEnabledForMethod(graph));
args.addConst("counters", counters);
}
template(monitorenterNode, args).instantiate(providers.getMetaAccess(), monitorenterNode, DEFAULT_REPLACER, args);
}
public void lower(MonitorExitNode monitorexitNode, HotSpotRegistersProvider registers, LoweringTool tool) {
StructuredGraph graph = monitorexitNode.graph();
Arguments args;
if (useFastLocking) {
args = new Arguments(monitorexit, graph.getGuardsStage(), tool.getLoweringStage());
} else {
args = new Arguments(monitorexitStub, graph.getGuardsStage(), tool.getLoweringStage());
}
args.add("object", monitorexitNode.object());
args.addConst("lockDepth", monitorexitNode.getMonitorId().getLockDepth());
args.addConst("threadRegister", registers.getThreadRegister());
args.addConst("trace", isTracingEnabledForType(monitorexitNode.object()) || isTracingEnabledForMethod(graph));
args.addConst("counters", counters);
template(monitorexitNode, args).instantiate(providers.getMetaAccess(), monitorexitNode, DEFAULT_REPLACER, args);
}
public static boolean isTracingEnabledForType(ValueNode object) {
ResolvedJavaType type = StampTool.typeOrNull(object.stamp(NodeView.DEFAULT));
String filter = TraceMonitorsTypeFilter.getValue(object.getOptions());
if (filter == null) {
return false;
} else {
if (filter.length() == 0) {
return true;
}
if (type == null) {
return false;
}
return (type.getName().contains(filter));
}
}
public static boolean isTracingEnabledForMethod(StructuredGraph graph) {
String filter = TraceMonitorsMethodFilter.getValue(graph.getOptions());
if (filter == null) {
return false;
} else {
if (filter.length() == 0) {
return true;
}
if (graph.method() == null) {
return false;
}
return (graph.method().format("%H.%n").contains(filter));
}
}
private void checkBalancedMonitors(StructuredGraph graph, LoweringTool tool) {
if (VerifyBalancedMonitors.getValue(options)) {
NodeIterable<MonitorCounterNode> nodes = graph.getNodes().filter(MonitorCounterNode.class);
if (nodes.isEmpty()) {
JavaType returnType = initCounter.getMethod().getSignature().getReturnType(initCounter.getMethod().getDeclaringClass());
StampPair returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false);
MethodCallTargetNode callTarget = graph.add(new MethodCallTargetNode(InvokeKind.Static, initCounter.getMethod(), new ValueNode[0], returnStamp, null));
InvokeNode invoke = graph.add(new InvokeNode(callTarget, 0));
invoke.setStateAfter(graph.start().stateAfter());
graph.addAfterFixed(graph.start(), invoke);
StructuredGraph inlineeGraph = providers.getReplacements().getSnippet(initCounter.getMethod(), null, invoke.graph().trackNodeSourcePosition(), invoke.getNodeSourcePosition());
InliningUtil.inline(invoke, inlineeGraph, false, null);
List<ReturnNode> rets = graph.getNodes(ReturnNode.TYPE).snapshot();
for (ReturnNode ret : rets) {
returnType = checkCounter.getMethod().getSignature().getReturnType(checkCounter.getMethod().getDeclaringClass());
String msg = "unbalanced monitors in " + graph.method().format("%H.%n(%p)") + ", count = %d";
ConstantNode errMsg = ConstantNode.forConstant(tool.getConstantReflection().forString(msg), providers.getMetaAccess(), graph);
returnStamp = StampFactory.forDeclaredType(graph.getAssumptions(), returnType, false);
callTarget = graph.add(new MethodCallTargetNode(InvokeKind.Static, checkCounter.getMethod(), new ValueNode[]{errMsg}, returnStamp, null));
invoke = graph.add(new InvokeNode(callTarget, 0));
Bytecode code = new ResolvedJavaMethodBytecode(graph.method());
FrameState stateAfter = new FrameState(null, code, BytecodeFrame.AFTER_BCI, new ValueNode[0], new ValueNode[0], 0, new ValueNode[0], null, false, false);
invoke.setStateAfter(graph.add(stateAfter));
graph.addBeforeFixed(ret, invoke);
Arguments args = new Arguments(checkCounter, graph.getGuardsStage(), tool.getLoweringStage());
args.addConst("errMsg", msg);
inlineeGraph = template(invoke, args).copySpecializedGraph(graph.getDebug());
InliningUtil.inline(invoke, inlineeGraph, false, null);
}
}
}
}
}
public static final ForeignCallDescriptor MONITORENTER = new ForeignCallDescriptor("monitorenter", void.class, Object.class, Word.class);
public static final ForeignCallDescriptor MONITOREXIT = new ForeignCallDescriptor("monitorexit", void.class, Object.class, Word.class);
@NodeIntrinsic(ForeignCallNode.class)
private static native void monitorenterStubC(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object, Word lock);
@NodeIntrinsic(ForeignCallNode.class)
public static native void monitorexitStubC(@ConstantNodeParameter ForeignCallDescriptor descriptor, Object object, Word lock);
}