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