/*
* 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.
*
* 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 jdk.vm.ci.hotspot;
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 {
/**
* 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 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());
}
}
/**
* 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;
private final boolean managesFailedSpeculations;
/**
* The list of failed speculations read from native memory via
* {@link CompilerToVM#getFailedSpeculations}.
*/
private byte[][] failedSpeculations;
/**
* Speculations made during the compilation associated with this log.
*/
private List<byte[]> speculations;
private List<SpeculationReason> speculationReasons;
@Override
public void collectFailedSpeculations() {
if (failedSpeculationsAddress != 0 && UnsafeAccess.UNSAFE.getLong(failedSpeculationsAddress) != 0) {
failedSpeculations = compilerToVM().getFailedSpeculations(failedSpeculationsAddress, failedSpeculations);
assert failedSpeculations.getClass() == byte[][].class;
}
}
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);
}
}
}
}
int size = 0;
for (byte[] s : speculations) {
size += s.length;
}
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 boolean maySpeculate(SpeculationReason reason) {
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 Speculation speculate(SpeculationReason reason) {
byte[] encoding = encode(reason);
JavaConstant id;
if (speculations == null) {
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;
}
if (id == null) {
id = JavaConstant.forLong(encodeIndexAndLength(flattenedIndex, encoding.length));
speculations.add(encoding);
speculationReasons.add(reason);
}
}
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 boolean hasSpeculations() {
return speculations != null;
}
@Override
public Speculation lookupSpeculation(JavaConstant constant) {
if (constant.isDefaultForKind()) {
return NO_SPECULATION;
}
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++) {
/**代码未完, 请加载全部代码(NowJava.com).**/