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 open Cdiff src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotSpeculationLog.java

src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotSpeculationLog.java

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 2014, 2014, 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. --- 1,7 ---- /* ! * 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 **** * 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 jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.SpeculationLog; public class HotSpotSpeculationLog implements SpeculationLog { public static final class HotSpotSpeculation extends Speculation { - private JavaConstant encoding; ! HotSpotSpeculation(SpeculationReason reason, JavaConstant encoding) { super(reason); this.encoding = encoding; } public JavaConstant getEncoding() { ! return encoding; } } ! /** Written by the C++ code that performs deoptimization. */ ! private volatile long lastFailed; ! /** All speculations that have caused a deoptimization. */ ! private Set<SpeculationReason> failedSpeculations; ! /** Strong references to all reasons embedded in the current nmethod. */ ! private HashMap<SpeculationReason, JavaConstant> speculations; ! private long currentSpeculationID; @Override ! public synchronized void collectFailedSpeculations() { ! if (lastFailed != 0) { ! if (failedSpeculations == null) { ! failedSpeculations = new HashSet<>(2); } - if (speculations != null) { - SpeculationReason lastFailedSpeculation = lookupSpeculation(this.lastFailed); - if (lastFailedSpeculation != null) { - failedSpeculations.add(lastFailedSpeculation); } ! lastFailed = 0; ! speculations = null; } } } - - private SpeculationReason lookupSpeculation(long value) { - for (Map.Entry<SpeculationReason, JavaConstant> entry : speculations.entrySet()) { - if (value == entry.getValue().asLong()) { - return entry.getKey(); } } ! return null; } @Override ! public synchronized boolean maySpeculate(SpeculationReason reason) { ! if (failedSpeculations != null && failedSpeculations.contains(reason)) { ! return false; } return true; } @Override ! public synchronized Speculation speculate(SpeculationReason reason) { if (speculations == null) { ! speculations = new HashMap<>(); } - JavaConstant id = speculations.get(reason); if (id == null) { ! id = JavaConstant.forLong(++currentSpeculationID); ! speculations.put(reason, id); } ! return new HotSpotSpeculation(reason, id); } @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); } } --- 20,361 ---- * 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++) { ! 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