001/* 002 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.nodes; 024 025import java.util.*; 026import java.util.concurrent.atomic.*; 027import java.util.function.*; 028 029import jdk.internal.jvmci.compiler.Compiler; 030import com.oracle.graal.debug.*; 031import jdk.internal.jvmci.meta.*; 032import jdk.internal.jvmci.meta.Assumptions.Assumption; 033 034import com.oracle.graal.compiler.common.type.*; 035import com.oracle.graal.graph.*; 036import com.oracle.graal.graph.spi.*; 037import com.oracle.graal.nodes.calc.*; 038import com.oracle.graal.nodes.java.*; 039import com.oracle.graal.nodes.util.*; 040 041/** 042 * A graph that contains at least one distinguished node : the {@link #start() start} node. This 043 * node is the start of the control flow of the graph. 044 */ 045public class StructuredGraph extends Graph implements JavaMethodContext { 046 047 /** 048 * The different stages of the compilation of a {@link Graph} regarding the status of 049 * {@link GuardNode guards}, {@link DeoptimizingNode deoptimizations} and {@link FrameState 050 * framestates}. The stage of a graph progresses monotonously. 051 * 052 */ 053 public static enum GuardsStage { 054 /** 055 * During this stage, there can be {@link FloatingNode floating} {@link DeoptimizingNode} 056 * such as {@link GuardNode GuardNodes}. New {@link DeoptimizingNode DeoptimizingNodes} can 057 * be introduced without constraints. {@link FrameState} nodes are associated with 058 * {@link StateSplit} nodes. 059 */ 060 FLOATING_GUARDS, 061 /** 062 * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be 063 * {@link FixedNode fixed} but new {@link DeoptimizingNode DeoptimizingNodes} can still be 064 * introduced. {@link FrameState} nodes are still associated with {@link StateSplit} nodes. 065 */ 066 FIXED_DEOPTS, 067 /** 068 * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be 069 * {@link FixedNode fixed}. New {@link DeoptimizingNode DeoptimizingNodes} can not be 070 * introduced any more. {@link FrameState} nodes are now associated with 071 * {@link DeoptimizingNode} nodes. 072 */ 073 AFTER_FSA; 074 075 public boolean allowsFloatingGuards() { 076 return this == FLOATING_GUARDS; 077 } 078 079 public boolean areFrameStatesAtDeopts() { 080 return this == AFTER_FSA; 081 } 082 083 public boolean areFrameStatesAtSideEffects() { 084 return !this.areFrameStatesAtDeopts(); 085 } 086 087 public boolean areDeoptsFixed() { 088 return this.ordinal() >= FIXED_DEOPTS.ordinal(); 089 } 090 } 091 092 /** 093 * Constants denoting whether or not {@link Assumption}s can be made while processing a graph. 094 */ 095 public enum AllowAssumptions { 096 YES, 097 NO; 098 public static AllowAssumptions from(boolean flag) { 099 return flag ? YES : NO; 100 } 101 } 102 103 public static final long INVALID_GRAPH_ID = -1; 104 105 private static final AtomicLong uniqueGraphIds = new AtomicLong(); 106 107 private StartNode start; 108 private final ResolvedJavaMethod method; 109 private final long graphId; 110 private final int entryBCI; 111 private GuardsStage guardsStage = GuardsStage.FLOATING_GUARDS; 112 private boolean isAfterFloatingReadPhase = false; 113 private boolean hasValueProxies = true; 114 115 /** 116 * The assumptions made while constructing and transforming this graph. 117 */ 118 private final Assumptions assumptions; 119 120 private final SpeculationLog speculationLog; 121 122 /** 123 * Records the methods that were inlined while constructing this graph along with how many times 124 * each method was inlined. 125 */ 126 private Map<ResolvedJavaMethod, Integer> inlinedMethods = new HashMap<>(); 127 128 private static enum UnsafeAccessState { 129 NO_ACCESS, 130 HAS_ACCESS, 131 DISABLED 132 } 133 134 private UnsafeAccessState hasUnsafeAccess = UnsafeAccessState.NO_ACCESS; 135 136 /** 137 * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start() 138 * start} node. 139 */ 140 public StructuredGraph(AllowAssumptions allowAssumptions) { 141 this(null, null, allowAssumptions); 142 } 143 144 /** 145 * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start() 146 * start} node. 147 */ 148 public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions) { 149 this(name, method, uniqueGraphIds.incrementAndGet(), Compiler.INVOCATION_ENTRY_BCI, allowAssumptions, null); 150 } 151 152 public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog) { 153 this(name, method, uniqueGraphIds.incrementAndGet(), Compiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog); 154 } 155 156 public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions) { 157 this(null, method, uniqueGraphIds.incrementAndGet(), Compiler.INVOCATION_ENTRY_BCI, allowAssumptions, null); 158 } 159 160 public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog) { 161 this(null, method, uniqueGraphIds.incrementAndGet(), Compiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog); 162 } 163 164 public StructuredGraph(ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog) { 165 this(null, method, uniqueGraphIds.incrementAndGet(), entryBCI, allowAssumptions, speculationLog); 166 } 167 168 private StructuredGraph(String name, ResolvedJavaMethod method, long graphId, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog) { 169 super(name); 170 this.setStart(add(new StartNode())); 171 this.method = method; 172 this.graphId = graphId; 173 this.entryBCI = entryBCI; 174 this.assumptions = allowAssumptions == AllowAssumptions.YES ? new Assumptions() : null; 175 this.speculationLog = speculationLog; 176 } 177 178 public Stamp getReturnStamp() { 179 Stamp returnStamp = null; 180 for (ReturnNode returnNode : getNodes(ReturnNode.TYPE)) { 181 ValueNode result = returnNode.result(); 182 if (result != null) { 183 if (returnStamp == null) { 184 returnStamp = result.stamp(); 185 } else { 186 returnStamp = returnStamp.meet(result.stamp()); 187 } 188 } 189 } 190 return returnStamp; 191 } 192 193 @Override 194 public String toString() { 195 StringBuilder buf = new StringBuilder(getClass().getSimpleName() + ":" + graphId); 196 String sep = "{"; 197 if (name != null) { 198 buf.append(sep); 199 buf.append(name); 200 sep = ", "; 201 } 202 if (method != null) { 203 buf.append(sep); 204 buf.append(method); 205 sep = ", "; 206 } 207 208 if (!sep.equals("{")) { 209 buf.append("}"); 210 } 211 return buf.toString(); 212 } 213 214 public StartNode start() { 215 return start; 216 } 217 218 /** 219 * Gets the method from which this graph was built. 220 * 221 * @return null if this method was not built from a method or the method is not available 222 */ 223 public ResolvedJavaMethod method() { 224 return method; 225 } 226 227 public int getEntryBCI() { 228 return entryBCI; 229 } 230 231 public boolean isOSR() { 232 return entryBCI != Compiler.INVOCATION_ENTRY_BCI; 233 } 234 235 public long graphId() { 236 return graphId; 237 } 238 239 public void setStart(StartNode start) { 240 this.start = start; 241 } 242 243 /** 244 * Creates a copy of this graph. 245 * 246 * @param newName the name of the copy, used for debugging purposes (can be null) 247 * @param duplicationMapCallback consumer of the duplication map created during the copying 248 */ 249 @Override 250 protected Graph copy(String newName, Consumer<Map<Node, Node>> duplicationMapCallback) { 251 AllowAssumptions allowAssumptions = AllowAssumptions.from(assumptions != null); 252 boolean enableInlinedMethodRecording = isInlinedMethodRecordingEnabled(); 253 StructuredGraph copy = new StructuredGraph(newName, method, graphId, entryBCI, allowAssumptions, speculationLog); 254 if (allowAssumptions == AllowAssumptions.YES && assumptions != null) { 255 copy.assumptions.record(assumptions); 256 } 257 if (!enableInlinedMethodRecording) { 258 copy.disableInlinedMethodRecording(); 259 } 260 copy.hasUnsafeAccess = hasUnsafeAccess; 261 copy.setGuardsStage(getGuardsStage()); 262 copy.isAfterFloatingReadPhase = isAfterFloatingReadPhase; 263 copy.hasValueProxies = hasValueProxies; 264 Map<Node, Node> replacements = Node.newMap(); 265 replacements.put(start, copy.start); 266 Map<Node, Node> duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), replacements); 267 if (duplicationMapCallback != null) { 268 duplicationMapCallback.accept(duplicates); 269 } 270 return copy; 271 } 272 273 public ParameterNode getParameter(int index) { 274 for (ParameterNode param : getNodes(ParameterNode.TYPE)) { 275 if (param.index() == index) { 276 return param; 277 } 278 } 279 return null; 280 } 281 282 public Iterable<Invoke> getInvokes() { 283 final Iterator<MethodCallTargetNode> callTargets = getNodes(MethodCallTargetNode.TYPE).iterator(); 284 return new Iterable<Invoke>() { 285 286 private Invoke next; 287 288 @Override 289 public Iterator<Invoke> iterator() { 290 return new Iterator<Invoke>() { 291 292 @Override 293 public boolean hasNext() { 294 if (next == null) { 295 while (callTargets.hasNext()) { 296 Invoke i = callTargets.next().invoke(); 297 if (i != null) { 298 next = i; 299 return true; 300 } 301 } 302 return false; 303 } else { 304 return true; 305 } 306 } 307 308 @Override 309 public Invoke next() { 310 try { 311 return next; 312 } finally { 313 next = null; 314 } 315 } 316 317 @Override 318 public void remove() { 319 throw new UnsupportedOperationException(); 320 } 321 }; 322 } 323 }; 324 } 325 326 public boolean hasLoops() { 327 return hasNode(LoopBeginNode.TYPE); 328 } 329 330 public void removeFloating(FloatingNode node) { 331 assert node != null && node.isAlive() : "cannot remove " + node; 332 node.safeDelete(); 333 } 334 335 public void replaceFloating(FloatingNode node, Node replacement) { 336 assert node != null && node.isAlive() && (replacement == null || replacement.isAlive()) : "cannot replace " + node + " with " + replacement; 337 node.replaceAtUsages(replacement); 338 node.safeDelete(); 339 } 340 341 /** 342 * Unlinks a node from all its control flow neighbors and then removes it from its graph. The 343 * node must have no {@linkplain Node#usages() usages}. 344 * 345 * @param node the node to be unlinked and removed 346 */ 347 public void removeFixed(FixedWithNextNode node) { 348 assert node != null; 349 if (node instanceof AbstractBeginNode) { 350 ((AbstractBeginNode) node).prepareDelete(); 351 } 352 assert node.hasNoUsages() : node + " " + node.usages().count() + ", " + node.usages().first(); 353 GraphUtil.unlinkFixedNode(node); 354 node.safeDelete(); 355 } 356 357 public void replaceFixed(FixedWithNextNode node, Node replacement) { 358 if (replacement instanceof FixedWithNextNode) { 359 replaceFixedWithFixed(node, (FixedWithNextNode) replacement); 360 } else { 361 assert replacement != null : "cannot replace " + node + " with null"; 362 assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement; 363 replaceFixedWithFloating(node, (FloatingNode) replacement); 364 } 365 } 366 367 public void replaceFixedWithFixed(FixedWithNextNode node, FixedWithNextNode replacement) { 368 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 369 FixedNode next = node.next(); 370 node.setNext(null); 371 replacement.setNext(next); 372 node.replaceAndDelete(replacement); 373 if (node == start) { 374 setStart((StartNode) replacement); 375 } 376 } 377 378 public void replaceFixedWithFloating(FixedWithNextNode node, FloatingNode replacement) { 379 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 380 GraphUtil.unlinkFixedNode(node); 381 node.replaceAtUsages(replacement); 382 node.safeDelete(); 383 } 384 385 public void removeSplit(ControlSplitNode node, AbstractBeginNode survivingSuccessor) { 386 assert node != null; 387 assert node.hasNoUsages(); 388 assert survivingSuccessor != null; 389 node.clearSuccessors(); 390 node.replaceAtPredecessor(survivingSuccessor); 391 node.safeDelete(); 392 } 393 394 public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor) { 395 removeSplitPropagate(node, survivingSuccessor, null); 396 } 397 398 public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor, SimplifierTool tool) { 399 assert node != null; 400 assert node.hasNoUsages(); 401 assert survivingSuccessor != null; 402 List<Node> snapshot = node.successors().snapshot(); 403 node.clearSuccessors(); 404 node.replaceAtPredecessor(survivingSuccessor); 405 node.safeDelete(); 406 for (Node successor : snapshot) { 407 if (successor != null && successor.isAlive()) { 408 if (successor != survivingSuccessor) { 409 GraphUtil.killCFG(successor, tool); 410 } 411 } 412 } 413 } 414 415 public void replaceSplit(ControlSplitNode node, Node replacement, AbstractBeginNode survivingSuccessor) { 416 if (replacement instanceof FixedWithNextNode) { 417 replaceSplitWithFixed(node, (FixedWithNextNode) replacement, survivingSuccessor); 418 } else { 419 assert replacement != null : "cannot replace " + node + " with null"; 420 assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement; 421 replaceSplitWithFloating(node, (FloatingNode) replacement, survivingSuccessor); 422 } 423 } 424 425 public void replaceSplitWithFixed(ControlSplitNode node, FixedWithNextNode replacement, AbstractBeginNode survivingSuccessor) { 426 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 427 assert survivingSuccessor != null; 428 node.clearSuccessors(); 429 replacement.setNext(survivingSuccessor); 430 node.replaceAndDelete(replacement); 431 } 432 433 public void replaceSplitWithFloating(ControlSplitNode node, FloatingNode replacement, AbstractBeginNode survivingSuccessor) { 434 assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; 435 assert survivingSuccessor != null; 436 node.clearSuccessors(); 437 node.replaceAtPredecessor(survivingSuccessor); 438 node.replaceAtUsages(replacement); 439 node.safeDelete(); 440 } 441 442 public void addAfterFixed(FixedWithNextNode node, FixedNode newNode) { 443 assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " after " + node; 444 FixedNode next = node.next(); 445 node.setNext(newNode); 446 if (next != null) { 447 assert newNode instanceof FixedWithNextNode; 448 FixedWithNextNode newFixedWithNext = (FixedWithNextNode) newNode; 449 assert newFixedWithNext.next() == null; 450 newFixedWithNext.setNext(next); 451 } 452 } 453 454 public void addBeforeFixed(FixedNode node, FixedWithNextNode newNode) { 455 assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " before " + node; 456 assert node.predecessor() != null && node.predecessor() instanceof FixedWithNextNode : "cannot add " + newNode + " before " + node; 457 assert newNode.next() == null : newNode; 458 assert !(node instanceof AbstractMergeNode); 459 FixedWithNextNode pred = (FixedWithNextNode) node.predecessor(); 460 pred.setNext(newNode); 461 newNode.setNext(node); 462 } 463 464 public void reduceDegenerateLoopBegin(LoopBeginNode begin) { 465 assert begin.loopEnds().isEmpty() : "Loop begin still has backedges"; 466 if (begin.forwardEndCount() == 1) { // bypass merge and remove 467 reduceTrivialMerge(begin); 468 } else { // convert to merge 469 AbstractMergeNode merge = this.add(new MergeNode()); 470 this.replaceFixedWithFixed(begin, merge); 471 } 472 } 473 474 public void reduceTrivialMerge(AbstractMergeNode merge) { 475 assert merge.forwardEndCount() == 1; 476 assert !(merge instanceof LoopBeginNode) || ((LoopBeginNode) merge).loopEnds().isEmpty(); 477 for (PhiNode phi : merge.phis().snapshot()) { 478 assert phi.valueCount() == 1; 479 ValueNode singleValue = phi.valueAt(0); 480 phi.replaceAtUsages(singleValue); 481 phi.safeDelete(); 482 } 483 // remove loop exits 484 if (merge instanceof LoopBeginNode) { 485 ((LoopBeginNode) merge).removeExits(); 486 } 487 AbstractEndNode singleEnd = merge.forwardEndAt(0); 488 FixedNode sux = merge.next(); 489 FrameState stateAfter = merge.stateAfter(); 490 // evacuateGuards 491 merge.prepareDelete((FixedNode) singleEnd.predecessor()); 492 merge.safeDelete(); 493 if (stateAfter != null && stateAfter.isAlive() && stateAfter.hasNoUsages()) { 494 GraphUtil.killWithUnusedFloatingInputs(stateAfter); 495 } 496 if (sux == null) { 497 singleEnd.replaceAtPredecessor(null); 498 singleEnd.safeDelete(); 499 } else { 500 singleEnd.replaceAndDelete(sux); 501 } 502 } 503 504 public GuardsStage getGuardsStage() { 505 return guardsStage; 506 } 507 508 public void setGuardsStage(GuardsStage guardsStage) { 509 assert guardsStage.ordinal() >= this.guardsStage.ordinal(); 510 this.guardsStage = guardsStage; 511 } 512 513 public boolean isAfterFloatingReadPhase() { 514 return isAfterFloatingReadPhase; 515 } 516 517 public void setAfterFloatingReadPhase(boolean state) { 518 assert state : "cannot 'unapply' floating read phase on graph"; 519 isAfterFloatingReadPhase = state; 520 } 521 522 public boolean hasValueProxies() { 523 return hasValueProxies; 524 } 525 526 public void setHasValueProxies(boolean state) { 527 assert !state : "cannot 'unapply' value proxy removal on graph"; 528 hasValueProxies = state; 529 } 530 531 /** 532 * Gets the object for recording assumptions while constructing of this graph. 533 * 534 * @return {@code null} if assumptions cannot be made for this graph 535 */ 536 public Assumptions getAssumptions() { 537 return assumptions; 538 } 539 540 /** 541 * Disables method inlining recording while constructing this graph. This can be done at most 542 * once and must be done before any inlined methods are recorded. 543 */ 544 public void disableInlinedMethodRecording() { 545 assert inlinedMethods != null : "cannot disable method inlining recording more than once"; 546 assert inlinedMethods.isEmpty() : "cannot disable method inlining recording once methods have been recorded"; 547 inlinedMethods = null; 548 } 549 550 public boolean isInlinedMethodRecordingEnabled() { 551 return inlinedMethods != null; 552 } 553 554 /** 555 * Gets the methods that were inlined while constructing this graph. 556 * 557 * @return {@code null} if method inlining recording has been 558 * {@linkplain #disableInlinedMethodRecording() disabled} 559 */ 560 public Set<ResolvedJavaMethod> getInlinedMethods() { 561 return inlinedMethods == null ? null : inlinedMethods.keySet(); 562 } 563 564 /** 565 * If method inlining recording has not been {@linkplain #disableInlinedMethodRecording() 566 * disabled}, records that {@code inlinedMethod} was inlined to this graph. Otherwise, this 567 * method does nothing. 568 */ 569 public void recordInlinedMethod(ResolvedJavaMethod inlinedMethod) { 570 if (inlinedMethods != null) { 571 Integer count = inlinedMethods.get(inlinedMethod); 572 if (count != null) { 573 inlinedMethods.put(inlinedMethod, count + 1); 574 } else { 575 inlinedMethods.put(inlinedMethod, 1); 576 } 577 } 578 } 579 580 /** 581 * If method inlining recording has not been {@linkplain #disableInlinedMethodRecording() 582 * disabled}, updates the {@linkplain #getInlinedMethods() inlined methods} of this graph with 583 * the inlined methods of another graph. Otherwise, this method does nothing. 584 */ 585 public void updateInlinedMethods(StructuredGraph other) { 586 if (inlinedMethods != null) { 587 assert this != other; 588 Map<ResolvedJavaMethod, Integer> otherInlinedMethods = other.inlinedMethods; 589 if (otherInlinedMethods != null) { 590 for (Map.Entry<ResolvedJavaMethod, Integer> e : otherInlinedMethods.entrySet()) { 591 ResolvedJavaMethod key = e.getKey(); 592 Integer count = inlinedMethods.get(key); 593 if (count != null) { 594 inlinedMethods.put(key, count + e.getValue()); 595 } else { 596 inlinedMethods.put(key, e.getValue()); 597 } 598 } 599 } 600 } 601 } 602 603 /** 604 * Gets the input bytecode {@linkplain ResolvedJavaMethod#getCodeSize() size} from which this 605 * graph is constructed. This ignores how many bytecodes in each constituent method are actually 606 * parsed (which may be none for methods whose IR is retrieved from a cache or less than the 607 * full amount for any given method due to profile guided branch pruning). If method inlining 608 * recording has been {@linkplain #disableInlinedMethodRecording() disabled} for this graph, 609 * bytecode counts for inlined methods are not included in the returned value. 610 */ 611 public int getBytecodeSize() { 612 int res = method.getCodeSize(); 613 if (inlinedMethods != null) { 614 for (Map.Entry<ResolvedJavaMethod, Integer> e : inlinedMethods.entrySet()) { 615 int inlinedBytes = e.getValue() * e.getKey().getCodeSize(); 616 res += inlinedBytes; 617 } 618 } 619 return res; 620 } 621 622 /** 623 * 624 * @return true if the graph contains only a {@link StartNode} and {@link ReturnNode} 625 */ 626 public boolean isTrivial() { 627 return !(start.next() instanceof ReturnNode); 628 } 629 630 public JavaMethod asJavaMethod() { 631 return method(); 632 } 633 634 public boolean hasUnsafeAccess() { 635 return hasUnsafeAccess == UnsafeAccessState.HAS_ACCESS; 636 } 637 638 public void markUnsafeAccess() { 639 if (hasUnsafeAccess == UnsafeAccessState.DISABLED) { 640 return; 641 } 642 hasUnsafeAccess = UnsafeAccessState.HAS_ACCESS; 643 } 644 645 public void disableUnsafeAccessTracking() { 646 hasUnsafeAccess = UnsafeAccessState.DISABLED; 647 } 648 649 public boolean isUnsafeAccessTrackingEnabled() { 650 return hasUnsafeAccess != UnsafeAccessState.DISABLED; 651 } 652 653 public SpeculationLog getSpeculationLog() { 654 return speculationLog; 655 } 656}