|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
24 | 24 | */
|
25 | 25 | package jdk.graal.compiler.core.common.spi;
|
26 | 26 |
|
27 |
| -import jdk.graal.compiler.options.OptionValues; |
| 27 | +import java.lang.foreign.MemorySegment; |
28 | 28 |
|
| 29 | +import jdk.graal.compiler.core.common.util.PhasePlan; |
| 30 | +import jdk.graal.compiler.nodes.Invoke; |
| 31 | +import jdk.graal.compiler.nodes.StartNode; |
| 32 | +import jdk.graal.compiler.nodes.memory.FloatingReadNode; |
| 33 | +import jdk.graal.compiler.nodes.spi.CanonicalizerTool; |
| 34 | +import jdk.graal.compiler.options.OptionValues; |
29 | 35 | import jdk.vm.ci.meta.JavaConstant;
|
30 | 36 | import jdk.vm.ci.meta.ResolvedJavaField;
|
31 | 37 |
|
@@ -68,4 +74,70 @@ interface ConstantFieldTool<T> {
|
68 | 74 | default boolean maybeFinal(@SuppressWarnings("unused") ResolvedJavaField field) {
|
69 | 75 | return false;
|
70 | 76 | }
|
| 77 | + |
| 78 | + /** |
| 79 | + * Returns {@code true} if {@code field} is final and considered "trusted". A trusted final |
| 80 | + * field is immutable after the initialization of the holder object. This allows us to schedule |
| 81 | + * a load from {@code field} freely up until the point at which we can guarantee that the object |
| 82 | + * is initialized. It also allows us to assume that there is no store to the field after it is |
| 83 | + * read, skipping the anti-dependency calculation while scheduling. |
| 84 | + * <p> |
| 85 | + * Example 1: |
| 86 | + * {@snippet : |
| 87 | + * class Holder { |
| 88 | + * final int x; |
| 89 | + * } |
| 90 | + * |
| 91 | + * void test(Holder h) { |
| 92 | + * for (int i = 0; i < 100; i++) { |
| 93 | + * nonInlinedCall(h.x); |
| 94 | + * } |
| 95 | + * } |
| 96 | + * } |
| 97 | + * Without further contexts, we cannot assume that {@code nonInlineCall} will not modify |
| 98 | + * {@code h.x}, even if it is a final field, because {@code Unsafe} and the core reflection API |
| 99 | + * can be used to modify a final field even after initialization. Additionally, there's a risk |
| 100 | + * that a constructor of {@code Holder} may use {@code Holder.x} as a flag for inter-thread |
| 101 | + * communication. As a result, the load from {@code h.x} needs to be executed in each iteration. |
| 102 | + * However, if we can trust that {@code h} should be initialized before being passed to |
| 103 | + * {@code test} and {@code x} should not be modified post-initialization, then we can hoist the |
| 104 | + * load outside the loop. |
| 105 | + * <p> |
| 106 | + * When a {@link FloatingReadNode} reads from a field for which this method returns |
| 107 | + * {@code true}, we rewire its memory input to the earliest location possible in the CFG. In the |
| 108 | + * example above, we rewire the memory input of the load {@code h.x} to the {@link StartNode} of |
| 109 | + * the method {@code test}. This allows the load to have the maximum freedom in scheduling, as |
| 110 | + * well as enables all the loads from {@code h.x} to be GVN-ed by making their memory inputs |
| 111 | + * identical. Note that the load is still not immutable, this is important because if the method |
| 112 | + * {@code test} is later inlined into another graph, it may turn out that {@code h} is created |
| 113 | + * and {@code h.x} is assigned in the caller. In those cases, marking the load node as immutable |
| 114 | + * would be incorrect. On the other hand, since the memory input of the node is the |
| 115 | + * {@link StartNode} of the method {@code test}, after inlining, this node is expanded into the |
| 116 | + * memory node corresponding to the input memory state of the invocation to {@code test} in the |
| 117 | + * caller graph, we can still ensure that the load is not scheduled too freely which can bypass |
| 118 | + * the initialization in the caller. This is generally not a concern because |
| 119 | + * {@link FloatingReadNode}s are only introduced after the high tier, while inlining is only |
| 120 | + * performed during high tier. However, there may be other mechanisms such as snippets which can |
| 121 | + * inline code in a limited manner after high tier, and it is more future-proof to not rely on |
| 122 | + * the {@link PhasePlan} for the correctness of this transformation. |
| 123 | + * <p> |
| 124 | + * To ensure that the load has the maximum freedom in scheduling, we mark that the load has no |
| 125 | + * anti-dependency. In other word, it aliases with no store that is dominated by its memory |
| 126 | + * input, and we do not have to take anti-dependency into consideration while scheduling the |
| 127 | + * load. This is necessary both in terms of correctness and efficiency. As moving the memory |
| 128 | + * input may introduce incorrect anti-dependency with nodes between the old and the new memory |
| 129 | + * input in the CFG, potentially leading to an unschedulable graph. |
| 130 | + * <p> |
| 131 | + * This transformation can be performed if the holder object is: |
| 132 | + * <ul> |
| 133 | + * <li>A parameter that is not the receiver of a constructor, the earliest memory input can be |
| 134 | + * the {@link StartNode} of the graph. |
| 135 | + * <li>A value returned from a method invocation, the earliest memory input can be the |
| 136 | + * {@link Invoke} corresponding to the invocation (to do). |
| 137 | + * </ul> |
| 138 | + * It can be trivially extended to other trusted final fields in the JDK (e.g. |
| 139 | + * {@link MemorySegment}). However, extending it to general uses would risk violating the JVM |
| 140 | + * semantics. |
| 141 | + */ |
| 142 | + boolean isTrustedFinal(CanonicalizerTool tool, ResolvedJavaField field); |
71 | 143 | }
|
0 commit comments