1 /* 2 * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.vm.ci.hotspot; 24 25 import java.util.HashMap; 26 import java.util.HashSet; 27 import java.util.Map; 28 import java.util.Set; 29 30 import jdk.vm.ci.meta.JavaConstant; 31 import jdk.vm.ci.meta.SpeculationLog; 32 33 public class HotSpotSpeculationLog implements SpeculationLog { 34 public static final class HotSpotSpeculation extends Speculation { 35 private JavaConstant encoding; 36 37 HotSpotSpeculation(SpeculationReason reason, JavaConstant encoding) { 38 super(reason); 39 this.encoding = encoding; 40 } 41 42 public JavaConstant getEncoding() { 43 return encoding; 44 } 45 } 46 47 /** Written by the C++ code that performs deoptimization. */ 48 private volatile long lastFailed; 49 50 /** All speculations that have caused a deoptimization. */ 51 private Set<SpeculationReason> failedSpeculations; 52 53 /** Strong references to all reasons embedded in the current nmethod. */ 54 private HashMap<SpeculationReason, JavaConstant> speculations; 55 56 private long currentSpeculationID; 57 58 @Override 59 public synchronized void collectFailedSpeculations() { 60 if (lastFailed != 0) { 61 if (failedSpeculations == null) { 62 failedSpeculations = new HashSet<>(2); 63 } 64 if (speculations != null) { 65 SpeculationReason lastFailedSpeculation = lookupSpeculation(this.lastFailed); 66 if (lastFailedSpeculation != null) { 67 failedSpeculations.add(lastFailedSpeculation); 68 } 69 lastFailed = 0; 70 speculations = null; 71 } 72 } 73 } 74 75 private SpeculationReason lookupSpeculation(long value) { 76 for (Map.Entry<SpeculationReason, JavaConstant> entry : speculations.entrySet()) { 77 if (value == entry.getValue().asLong()) { 78 return entry.getKey(); 79 } 80 } 81 return null; 82 } 83 84 @Override 85 public synchronized boolean maySpeculate(SpeculationReason reason) { 86 if (failedSpeculations != null && failedSpeculations.contains(reason)) { 87 return false; 88 } 89 return true; 90 } 91 92 @Override 93 public synchronized Speculation speculate(SpeculationReason reason) { 94 if (speculations == null) { 95 speculations = new HashMap<>(); 96 } 97 JavaConstant id = speculations.get(reason); 98 if (id == null) { 99 id = JavaConstant.forLong(++currentSpeculationID); 100 speculations.put(reason, id); 101 } 102 return new HotSpotSpeculation(reason, id); 103 } 104 105 @Override 106 public synchronized boolean hasSpeculations() { 107 return speculations != null && !speculations.isEmpty(); 108 } 109 110 @Override 111 public synchronized Speculation lookupSpeculation(JavaConstant constant) { 112 if (constant.isDefaultForKind()) { 113 return NO_SPECULATION; 114 } 115 SpeculationReason reason = lookupSpeculation(constant.asLong()); 116 assert reason != null : "Speculation should have been registered"; 117 return new HotSpotSpeculation(reason, constant); 118 } 119 } | 1 /* 2 * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.vm.ci.hotspot; 24 25 import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM; 26 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Formatter; 30 import java.util.List; 31 32 import jdk.vm.ci.code.BailoutException; 33 import jdk.vm.ci.meta.JavaConstant; 34 import jdk.vm.ci.meta.SpeculationLog; 35 36 /** 37 * Implements a {@link SpeculationLog} that can be used to: 38 * <ul> 39 * <li>Query failed speculations recorded in a native linked list of {@code FailedSpeculation}s (see 40 * methodData.hpp).</li> 41 * <li>Make speculations during compilation and record them in compiled code. This must only be done 42 * on compilation-local {@link HotSpotSpeculationLog} objects.</li> 43 * </ul> 44 * 45 * The choice of constructor determines whether the native failed speculations list is 46 * {@linkplain #managesFailedSpeculations() managed} by a {@link HotSpotSpeculationLog} object. 47 */ 48 public class HotSpotSpeculationLog implements SpeculationLog { 49 50 private static final byte[] NO_FLATTENED_SPECULATIONS = {}; 51 52 /** 53 * Creates a speculation log that manages a failed speculation list. That is, when this object 54 * dies, the native resources of the list are freed. 55 * 56 * @see #managesFailedSpeculations() 57 * @see #getFailedSpeculationsAddress() 58 */ 59 public HotSpotSpeculationLog() { 60 managesFailedSpeculations = true; 61 } 62 63 /** 64 * Creates a speculation log that reads from an externally managed failed speculation list. That 65 * is, the lifetime of the list is independent of this object. 66 * 67 * @param failedSpeculationsAddress an address in native memory at which the pointer to the 68 * externally managed sailed speculation list resides 69 */ 70 public HotSpotSpeculationLog(long failedSpeculationsAddress) { 71 if (failedSpeculationsAddress == 0) { 72 throw new IllegalArgumentException("failedSpeculationsAddress cannot be 0"); 73 } 74 this.failedSpeculationsAddress = failedSpeculationsAddress; 75 managesFailedSpeculations = false; 76 } 77 78 /** 79 * Gets the address of the pointer to the native failed speculations list. 80 * 81 * @see #managesFailedSpeculations() 82 */ 83 public long getFailedSpeculationsAddress() { 84 if (managesFailedSpeculations) { 85 synchronized (this) { 86 if (failedSpeculationsAddress == 0L) { 87 failedSpeculationsAddress = UnsafeAccess.UNSAFE.allocateMemory(HotSpotJVMCIRuntime.getHostWordKind().getByteCount()); 88 UnsafeAccess.UNSAFE.putAddress(failedSpeculationsAddress, 0L); 89 LogCleaner c = new LogCleaner(this, failedSpeculationsAddress); 90 assert c.address == failedSpeculationsAddress; 91 } 92 } 93 } 94 return failedSpeculationsAddress; 95 } 96 97 /** 98 * Adds {@code speculation} to the native list of failed speculations. To update this object's 99 * view of the failed speculations, {@link #collectFailedSpeculations()} must be called after 100 * this method returns. 101 * 102 * This method exists primarily for testing purposes. Speculations are normally only added to 103 * the list by HotSpot during deoptimization. 104 * 105 * @return {@code false} if the speculation could not be appended to the list 106 */ 107 public boolean addFailedSpeculation(Speculation speculation) { 108 return compilerToVM().addFailedSpeculation(getFailedSpeculationsAddress(), ((HotSpotSpeculation) speculation).encoding); 109 } 110 111 /** 112 * Returns {@code true} if the value returned by {@link #getFailedSpeculationsAddress()} is only 113 * valid only as long as this object is alive, {@code false} otherwise. 114 */ 115 public boolean managesFailedSpeculations() { 116 return managesFailedSpeculations; 117 } 118 119 public static final class HotSpotSpeculation extends Speculation { 120 121 /** 122 * A speculation id is a long encoding an offset (high 32 bits) and a length (low 32 bts). 123 * Combined, the index and length denote where the {@linkplain #encoding encoded 124 * speculation} is in a {@linkplain HotSpotSpeculationLog#getFlattenedSpeculations 125 * flattened} speculations array. 126 */ 127 private final JavaConstant id; 128 129 private final byte[] encoding; 130 131 HotSpotSpeculation(SpeculationReason reason, JavaConstant id, byte[] encoding) { 132 super(reason); 133 this.id = id; 134 this.encoding = encoding; 135 } 136 137 public JavaConstant getEncoding() { 138 return id; 139 } 140 141 @Override 142 public String toString() { 143 long indexAndLength = id.asLong(); 144 int index = decodeIndex(indexAndLength); 145 int length = decodeLength(indexAndLength); 146 return String.format("{0x%016x[index: %d, len: %d, hash: 0x%x]: %s}", indexAndLength, index, length, Arrays.hashCode(encoding), getReason()); 147 } 148 } 149 150 /** 151 * Address of a pointer to a set of failed speculations. The address is recorded in the nmethod 152 * compiled with this speculation log such that when it fails a speculation, the speculation is 153 * added to the list. 154 */ 155 private long failedSpeculationsAddress; 156 157 private final boolean managesFailedSpeculations; 158 159 /** 160 * The list of failed speculations read from native memory via 161 * {@link CompilerToVM#getFailedSpeculations}. 162 */ 163 private byte[][] failedSpeculations; 164 165 /** 166 * Speculations made during the compilation associated with this log. 167 */ 168 private List<byte[]> speculations; 169 private List<SpeculationReason> speculationReasons; 170 171 @Override 172 public void collectFailedSpeculations() { 173 if (failedSpeculationsAddress != 0 && UnsafeAccess.UNSAFE.getLong(failedSpeculationsAddress) != 0) { 174 failedSpeculations = compilerToVM().getFailedSpeculations(failedSpeculationsAddress, failedSpeculations); 175 assert failedSpeculations.getClass() == byte[][].class; 176 } 177 } 178 179 byte[] getFlattenedSpeculations(boolean validate) { 180 if (speculations == null) { 181 return NO_FLATTENED_SPECULATIONS; 182 } 183 if (validate) { 184 int newFailuresStart = failedSpeculations == null ? 0 : failedSpeculations.length; 185 collectFailedSpeculations(); 186 if (failedSpeculations != null && failedSpeculations.length != newFailuresStart) { 187 for (SpeculationReason reason : speculationReasons) { 188 byte[] encoding = encode(reason); 189 // Only check against new failures 190 if (contains(failedSpeculations, newFailuresStart, encoding)) { 191 throw new BailoutException(false, "Speculation failed: " + reason); 192 } 193 } 194 } 195 } 196 int size = 0; 197 for (byte[] s : speculations) { 198 size += s.length; 199 } 200 byte[] result = new byte[size]; 201 size = 0; 202 for (byte[] s : speculations) { 203 System.arraycopy(s, 0, result, size, s.length); 204 size += s.length; 205 } 206 return result; 207 } 208 209 @Override 210 public boolean maySpeculate(SpeculationReason reason) { 211 if (failedSpeculations == null) { 212 collectFailedSpeculations(); 213 } 214 if (failedSpeculations != null && failedSpeculations.length != 0) { 215 byte[] encoding = encode(reason); 216 return !contains(failedSpeculations, 0, encoding); 217 } 218 return true; 219 } 220 221 /** 222 * @return {@code true} if {@code needle} is in {@code haystack[fromIndex..haystack.length-1]} 223 */ 224 private static boolean contains(byte[][] haystack, int fromIndex, byte[] needle) { 225 for (int i = fromIndex; i < haystack.length; i++) { 226 byte[] fs = haystack[i]; 227 228 if (Arrays.equals(fs, needle)) { 229 return true; 230 } 231 } 232 return false; 233 } 234 235 private static long encodeIndexAndLength(int index, int length) { 236 return ((long) index) << 32 | length; 237 } 238 239 private static int decodeIndex(long indexAndLength) { 240 return (int) (indexAndLength >>> 32); 241 } 242 243 private static int decodeLength(long indexAndLength) { 244 return (int) indexAndLength & 0xFFFFFFFF; 245 } 246 247 @Override 248 public Speculation speculate(SpeculationReason reason) { 249 byte[] encoding = encode(reason); 250 JavaConstant id; 251 if (speculations == null) { 252 speculations = new ArrayList<>(); 253 speculationReasons = new ArrayList<>(); 254 id = JavaConstant.forLong(encodeIndexAndLength(0, encoding.length)); 255 speculations.add(encoding); 256 speculationReasons.add(reason); 257 } else { 258 id = null; 259 int flattenedIndex = 0; 260 for (byte[] fs : speculations) { 261 if (Arrays.equals(fs, encoding)) { 262 id = JavaConstant.forLong(encodeIndexAndLength(flattenedIndex, fs.length)); 263 break; 264 } 265 flattenedIndex += fs.length; 266 } 267 if (id == null) { 268 id = JavaConstant.forLong(encodeIndexAndLength(flattenedIndex, encoding.length)); 269 speculations.add(encoding); 270 speculationReasons.add(reason); 271 } 272 } 273 274 return new HotSpotSpeculation(reason, id, encoding); 275 } 276 277 private static byte[] encode(SpeculationReason reason) { 278 HotSpotSpeculationEncoding encoding = (HotSpotSpeculationEncoding) reason.encode(HotSpotSpeculationEncoding::new); 279 byte[] result = encoding == null ? null : encoding.getByteArray(); 280 if (result == null) { 281 throw new IllegalArgumentException(HotSpotSpeculationLog.class.getName() + " expects " + reason.getClass().getName() + ".encode() to return a non-empty encoding"); 282 } 283 return result; 284 } 285 286 @Override 287 public boolean hasSpeculations() { 288 return speculations != null; 289 } 290 291 @Override 292 public Speculation lookupSpeculation(JavaConstant constant) { 293 if (constant.isDefaultForKind()) { 294 return NO_SPECULATION; 295 } 296 int flattenedIndex = decodeIndex(constant.asLong()); 297 int index = 0; 298 for (byte[] s : speculations) { 299 if (flattenedIndex == 0) { 300 SpeculationReason reason = speculationReasons.get(index); 301 return new HotSpotSpeculation(reason, constant, s); 302 } 303 index++; 304 flattenedIndex -= s.length; 305 } 306 throw new IllegalArgumentException("Unknown encoded speculation: " + constant); 307 } 308 309 @Override 310 public String toString() { 311 Formatter buf = new Formatter(); 312 buf.format("{managed:%s, failedSpeculationsAddress:0x%x, failedSpeculations:[", managesFailedSpeculations, failedSpeculationsAddress); 313 314 String sep = ""; 315 if (failedSpeculations != null) { 316 for (int i = 0; i < failedSpeculations.length; i++) { 317 buf.format("%s{len:%d, hash:0x%x}", sep, failedSpeculations[i].length, Arrays.hashCode(failedSpeculations[i])); 318 sep = ", "; 319 } 320 } 321 322 buf.format("], speculations:["); 323 324 int size = 0; 325 if (speculations != null) { 326 sep = ""; 327 for (int i = 0; i < speculations.size(); i++) { 328 byte[] s = speculations.get(i); 329 size += s.length; 330 buf.format("%s{len:%d, hash:0x%x, reason:{%s}}", sep, s.length, Arrays.hashCode(s), speculationReasons.get(i)); 331 sep = ", "; 332 } 333 } 334 buf.format("], len:%d, hash:0x%x}", size, Arrays.hashCode(getFlattenedSpeculations(false))); 335 return buf.toString(); 336 } 337 338 /** 339 * Frees the native memory resources associated with {@link HotSpotSpeculationLog}s once they 340 * become reclaimable. 341 */ 342 private static final class LogCleaner extends Cleaner { 343 344 LogCleaner(HotSpotSpeculationLog referent, long address) { 345 super(referent); 346 this.address = address; 347 } 348 349 @Override 350 void doCleanup() { 351 long pointer = UnsafeAccess.UNSAFE.getAddress(address); 352 if (pointer != 0) { 353 compilerToVM().releaseFailedSpeculations(address); 354 } 355 UnsafeAccess.UNSAFE.freeMemory(address); 356 } 357 358 final long address; 359 } 360 } 361 |