src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotSpeculationLog.java
Index
Unified diffs
Context diffs
Sdiffs
Frames
Patch
New
Old
Previous File
Next File
*** old/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotSpeculationLog.java Thu Mar 28 11:24:13 2019
--- new/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotSpeculationLog.java Thu Mar 28 11:24:13 2019
*** 1,7 ****
--- 1,7 ----
/*
! * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
! * Copyright (c) 2014, 2019, 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.
*** 20,119 ****
--- 20,361 ----
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.vm.ci.hotspot;
! import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
! import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Formatter;
+ import java.util.List;
+
+ import jdk.vm.ci.code.BailoutException;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.SpeculationLog;
+ /**
+ * Implements a {@link SpeculationLog} that can be used to:
+ * <ul>
+ * <li>Query failed speculations recorded in a native linked list of {@code FailedSpeculation}s (see
+ * methodData.hpp).</li>
+ * <li>Make speculations during compilation and record them in compiled code. This must only be done
+ * on compilation-local {@link HotSpotSpeculationLog} objects.</li>
+ * </ul>
+ *
+ * The choice of constructor determines whether the native failed speculations list is
+ * {@linkplain #managesFailedSpeculations() managed} by a {@link HotSpotSpeculationLog} object.
+ */
public class HotSpotSpeculationLog implements SpeculationLog {
+
+ private static final byte[] NO_FLATTENED_SPECULATIONS = {};
+
+ /**
+ * Creates a speculation log that manages a failed speculation list. That is, when this object
+ * dies, the native resources of the list are freed.
+ *
+ * @see #managesFailedSpeculations()
+ * @see #getFailedSpeculationsAddress()
+ */
+ public HotSpotSpeculationLog() {
+ managesFailedSpeculations = true;
+ }
+
+ /**
+ * Creates a speculation log that reads from an externally managed failed speculation list. That
+ * is, the lifetime of the list is independent of this object.
+ *
+ * @param failedSpeculationsAddress an address in native memory at which the pointer to the
+ * externally managed sailed speculation list resides
+ */
+ public HotSpotSpeculationLog(long failedSpeculationsAddress) {
+ if (failedSpeculationsAddress == 0) {
+ throw new IllegalArgumentException("failedSpeculationsAddress cannot be 0");
+ }
+ this.failedSpeculationsAddress = failedSpeculationsAddress;
+ managesFailedSpeculations = false;
+ }
+
+ /**
+ * Gets the address of the pointer to the native failed speculations list.
+ *
+ * @see #managesFailedSpeculations()
+ */
+ public long getFailedSpeculationsAddress() {
+ if (managesFailedSpeculations) {
+ synchronized (this) {
+ if (failedSpeculationsAddress == 0L) {
+ failedSpeculationsAddress = UnsafeAccess.UNSAFE.allocateMemory(HotSpotJVMCIRuntime.getHostWordKind().getByteCount());
+ UnsafeAccess.UNSAFE.putAddress(failedSpeculationsAddress, 0L);
+ LogCleaner c = new LogCleaner(this, failedSpeculationsAddress);
+ assert c.address == failedSpeculationsAddress;
+ }
+ }
+ }
+ return failedSpeculationsAddress;
+ }
+
+ /**
+ * Adds {@code speculation} to the native list of failed speculations. To update this object's
+ * view of the failed speculations, {@link #collectFailedSpeculations()} must be called after
+ * this method returns.
+ *
+ * This method exists primarily for testing purposes. Speculations are normally only added to
+ * the list by HotSpot during deoptimization.
+ *
+ * @return {@code false} if the speculation could not be appended to the list
+ */
+ public boolean addFailedSpeculation(Speculation speculation) {
+ return compilerToVM().addFailedSpeculation(getFailedSpeculationsAddress(), ((HotSpotSpeculation) speculation).encoding);
+ }
+
+ /**
+ * Returns {@code true} if the value returned by {@link #getFailedSpeculationsAddress()} is only
+ * valid only as long as this object is alive, {@code false} otherwise.
+ */
+ public boolean managesFailedSpeculations() {
+ return managesFailedSpeculations;
+ }
+
public static final class HotSpotSpeculation extends Speculation {
private JavaConstant encoding;
HotSpotSpeculation(SpeculationReason reason, JavaConstant encoding) {
+ /**
+ * A speculation id is a long encoding an offset (high 32 bits) and a length (low 32 bts).
+ * Combined, the index and length denote where the {@linkplain #encoding encoded
+ * speculation} is in a {@linkplain HotSpotSpeculationLog#getFlattenedSpeculations
+ * flattened} speculations array.
+ */
+ private final JavaConstant id;
+
+ private final byte[] encoding;
+
+ HotSpotSpeculation(SpeculationReason reason, JavaConstant id, byte[] encoding) {
super(reason);
+ this.id = id;
this.encoding = encoding;
}
public JavaConstant getEncoding() {
! return encoding;
! return id;
+ }
+
+ @Override
+ public String toString() {
+ long indexAndLength = id.asLong();
+ int index = decodeIndex(indexAndLength);
+ int length = decodeLength(indexAndLength);
+ return String.format("{0x%016x[index: %d, len: %d, hash: 0x%x]: %s}", indexAndLength, index, length, Arrays.hashCode(encoding), getReason());
}
}
/** Written by the C++ code that performs deoptimization. */
private volatile long lastFailed;
+ /**
+ * Address of a pointer to a set of failed speculations. The address is recorded in the nmethod
+ * compiled with this speculation log such that when it fails a speculation, the speculation is
+ * added to the list.
+ */
+ private long failedSpeculationsAddress;
/** All speculations that have caused a deoptimization. */
private Set<SpeculationReason> failedSpeculations;
+ private final boolean managesFailedSpeculations;
/** Strong references to all reasons embedded in the current nmethod. */
private HashMap<SpeculationReason, JavaConstant> speculations;
+ /**
+ * The list of failed speculations read from native memory via
+ * {@link CompilerToVM#getFailedSpeculations}.
+ */
+ private byte[][] failedSpeculations;
private long currentSpeculationID;
+ /**
+ * Speculations made during the compilation associated with this log.
+ */
+ private List<byte[]> speculations;
+ private List<SpeculationReason> speculationReasons;
@Override
- public synchronized void collectFailedSpeculations() {
! if (lastFailed != 0) {
if (failedSpeculations == null) {
! failedSpeculations = new HashSet<>(2);
! if (failedSpeculationsAddress != 0 && UnsafeAccess.UNSAFE.getLong(failedSpeculationsAddress) != 0) {
+ failedSpeculations = compilerToVM().getFailedSpeculations(failedSpeculationsAddress, failedSpeculations);
! assert failedSpeculations.getClass() == byte[][].class;
}
if (speculations != null) {
SpeculationReason lastFailedSpeculation = lookupSpeculation(this.lastFailed);
if (lastFailedSpeculation != null) {
failedSpeculations.add(lastFailedSpeculation);
}
lastFailed = 0;
speculations = null;
+
+ byte[] getFlattenedSpeculations(boolean validate) {
+ if (speculations == null) {
+ return NO_FLATTENED_SPECULATIONS;
+ }
+ if (validate) {
+ int newFailuresStart = failedSpeculations == null ? 0 : failedSpeculations.length;
+ collectFailedSpeculations();
+ if (failedSpeculations != null && failedSpeculations.length != newFailuresStart) {
+ for (SpeculationReason reason : speculationReasons) {
+ byte[] encoding = encode(reason);
+ // Only check against new failures
+ if (contains(failedSpeculations, newFailuresStart, encoding)) {
+ throw new BailoutException(false, "Speculation failed: " + reason);
}
}
}
private SpeculationReason lookupSpeculation(long value) {
for (Map.Entry<SpeculationReason, JavaConstant> entry : speculations.entrySet()) {
if (value == entry.getValue().asLong()) {
return entry.getKey();
}
+ int size = 0;
+ for (byte[] s : speculations) {
+ size += s.length;
}
! return null;
! byte[] result = new byte[size];
+ size = 0;
+ for (byte[] s : speculations) {
+ System.arraycopy(s, 0, result, size, s.length);
+ size += s.length;
+ }
+ return result;
}
@Override
- public synchronized boolean maySpeculate(SpeculationReason reason) {
! if (failedSpeculations != null && failedSpeculations.contains(reason)) {
! return false;
! if (failedSpeculations == null) {
! collectFailedSpeculations();
+ }
+ if (failedSpeculations != null && failedSpeculations.length != 0) {
+ byte[] encoding = encode(reason);
+ return !contains(failedSpeculations, 0, encoding);
+ }
+ return true;
}
+
+ /**
+ * @return {@code true} if {@code needle} is in {@code haystack[fromIndex..haystack.length-1]}
+ */
+ private static boolean contains(byte[][] haystack, int fromIndex, byte[] needle) {
+ for (int i = fromIndex; i < haystack.length; i++) {
+ byte[] fs = haystack[i];
+
+ if (Arrays.equals(fs, needle)) {
return true;
}
+ }
+ return false;
+ }
+
+ private static long encodeIndexAndLength(int index, int length) {
+ return ((long) index) << 32 | length;
+ }
+
+ private static int decodeIndex(long indexAndLength) {
+ return (int) (indexAndLength >>> 32);
+ }
+
+ private static int decodeLength(long indexAndLength) {
+ return (int) indexAndLength & 0xFFFFFFFF;
+ }
@Override
- public synchronized Speculation speculate(SpeculationReason reason) {
+ byte[] encoding = encode(reason);
+ JavaConstant id;
if (speculations == null) {
! speculations = new HashMap<>();
! speculations = new ArrayList<>();
+ speculationReasons = new ArrayList<>();
+ id = JavaConstant.forLong(encodeIndexAndLength(0, encoding.length));
+ speculations.add(encoding);
+ speculationReasons.add(reason);
+ } else {
+ id = null;
+ int flattenedIndex = 0;
+ for (byte[] fs : speculations) {
+ if (Arrays.equals(fs, encoding)) {
+ id = JavaConstant.forLong(encodeIndexAndLength(flattenedIndex, fs.length));
+ break;
+ }
+ flattenedIndex += fs.length;
}
JavaConstant id = speculations.get(reason);
if (id == null) {
! id = JavaConstant.forLong(++currentSpeculationID);
! speculations.put(reason, id);
! id = JavaConstant.forLong(encodeIndexAndLength(flattenedIndex, encoding.length));
! speculations.add(encoding);
+ speculationReasons.add(reason);
}
return new HotSpotSpeculation(reason, id);
+ }
+
+ return new HotSpotSpeculation(reason, id, encoding);
+ }
+
+ private static byte[] encode(SpeculationReason reason) {
+ HotSpotSpeculationEncoding encoding = (HotSpotSpeculationEncoding) reason.encode(HotSpotSpeculationEncoding::new);
+ byte[] result = encoding == null ? null : encoding.getByteArray();
+ if (result == null) {
+ throw new IllegalArgumentException(HotSpotSpeculationLog.class.getName() + " expects " + reason.getClass().getName() + ".encode() to return a non-empty encoding");
+ }
+ return result;
}
@Override
- public synchronized boolean hasSpeculations() {
- return speculations != null && !speculations.isEmpty();
}
@Override
- public synchronized Speculation lookupSpeculation(JavaConstant constant) {
if (constant.isDefaultForKind()) {
return NO_SPECULATION;
}
! SpeculationReason reason = lookupSpeculation(constant.asLong());
! assert reason != null : "Speculation should have been registered";
return new HotSpotSpeculation(reason, constant);
! int flattenedIndex = decodeIndex(constant.asLong());
! int index = 0;
+ for (byte[] s : speculations) {
+ if (flattenedIndex == 0) {
+ SpeculationReason reason = speculationReasons.get(index);
+ return new HotSpotSpeculation(reason, constant, s);
+ }
+ index++;
+ flattenedIndex -= s.length;
+ }
+ throw new IllegalArgumentException("Unknown encoded speculation: " + constant);
+ }
+
+ @Override
+ public String toString() {
+ Formatter buf = new Formatter();
+ buf.format("{managed:%s, failedSpeculationsAddress:0x%x, failedSpeculations:[", managesFailedSpeculations, failedSpeculationsAddress);
+
+ String sep = "";
+ if (failedSpeculations != null) {
+ for (int i = 0; i < failedSpeculations.length; i++) {
+ buf.format("%s{len:%d, hash:0x%x}", sep, failedSpeculations[i].length, Arrays.hashCode(failedSpeculations[i]));
+ sep = ", ";
+ }
+ }
+
+ buf.format("], speculations:[");
+
+ int size = 0;
+ if (speculations != null) {
+ sep = "";
+ for (int i = 0; i < speculations.size(); i++) {
+ byte[] s = speculations.get(i);
+ size += s.length;
+ buf.format("%s{len:%d, hash:0x%x, reason:{%s}}", sep, s.length, Arrays.hashCode(s), speculationReasons.get(i));
+ sep = ", ";
+ }
+ }
+ buf.format("], len:%d, hash:0x%x}", size, Arrays.hashCode(getFlattenedSpeculations(false)));
+ return buf.toString();
+ }
+
+ /**
+ * Frees the native memory resources associated with {@link HotSpotSpeculationLog}s once they
+ * become reclaimable.
+ */
+ private static final class LogCleaner extends Cleaner {
+
+ LogCleaner(HotSpotSpeculationLog referent, long address) {
+ super(referent);
+ this.address = address;
+ }
+
+ @Override
+ void doCleanup() {
+ long pointer = UnsafeAccess.UNSAFE.getAddress(address);
+ if (pointer != 0) {
+ compilerToVM().releaseFailedSpeculations(address);
+ }
+ UnsafeAccess.UNSAFE.freeMemory(address);
+ }
+
+ final long address;
}
}
+
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotSpeculationLog.java
Index
Unified diffs
Context diffs
Sdiffs
Frames
Patch
New
Old
Previous File
Next File