27
27
import static jdk .vm .ci .services .Services .IS_IN_NATIVE_IMAGE ;
28
28
29
29
import java .util .concurrent .ConcurrentHashMap ;
30
+ import java .util .function .Supplier ;
30
31
31
32
import org .graalvm .collections .EconomicMap ;
32
33
import org .graalvm .compiler .bytecode .BytecodeProvider ;
57
58
import org .graalvm .compiler .phases .util .Providers ;
58
59
59
60
import jdk .vm .ci .code .Architecture ;
61
+ import jdk .vm .ci .meta .Assumptions ;
60
62
import jdk .vm .ci .meta .ResolvedJavaMethod ;
63
+ import jdk .vm .ci .meta .ResolvedJavaType ;
61
64
62
65
/**
63
66
* A graph decoder that provides all necessary encoded graphs on-the-fly (by parsing the methods and
@@ -71,14 +74,17 @@ public class CachingPEGraphDecoder extends PEGraphDecoder {
71
74
protected final GraphBuilderConfiguration graphBuilderConfig ;
72
75
protected final OptimisticOptimizations optimisticOpts ;
73
76
private final AllowAssumptions allowAssumptions ;
74
- private final EconomicMap <ResolvedJavaMethod , EncodedGraph > graphCache ;
77
+ private final EconomicMap <ResolvedJavaMethod , EncodedGraph > persistentGraphCache ;
78
+ private final EconomicMap <ResolvedJavaMethod , EncodedGraph > localGraphCache ;
79
+ private final Supplier <AutoCloseable > createPersistentCachedGraphScope ;
75
80
private final BasePhase <? super CoreProviders > postParsingPhase ;
76
81
77
82
public CachingPEGraphDecoder (Architecture architecture , StructuredGraph graph , Providers providers , GraphBuilderConfiguration graphBuilderConfig , OptimisticOptimizations optimisticOpts ,
78
83
AllowAssumptions allowAssumptions , LoopExplosionPlugin loopExplosionPlugin , InvocationPlugins invocationPlugins , InlineInvokePlugin [] inlineInvokePlugins ,
79
84
ParameterPlugin parameterPlugin ,
80
85
NodePlugin [] nodePlugins , ResolvedJavaMethod peRootForInlining , SourceLanguagePositionProvider sourceLanguagePositionProvider ,
81
- BasePhase <? super CoreProviders > postParsingPhase , EconomicMap <ResolvedJavaMethod , EncodedGraph > graphCache , boolean needsExplicitException ) {
86
+ BasePhase <? super CoreProviders > postParsingPhase , EconomicMap <ResolvedJavaMethod , EncodedGraph > persistentGraphCache , Supplier <AutoCloseable > createPersistentCachedGraphScope ,
87
+ boolean needsExplicitException ) {
82
88
super (architecture , graph , providers , loopExplosionPlugin ,
83
89
invocationPlugins , inlineInvokePlugins , parameterPlugin , nodePlugins , peRootForInlining , sourceLanguagePositionProvider ,
84
90
new ConcurrentHashMap <>(), new ConcurrentHashMap <>(), needsExplicitException );
@@ -87,8 +93,10 @@ public CachingPEGraphDecoder(Architecture architecture, StructuredGraph graph, P
87
93
this .graphBuilderConfig = graphBuilderConfig ;
88
94
this .optimisticOpts = optimisticOpts ;
89
95
this .allowAssumptions = allowAssumptions ;
90
- this .graphCache = graphCache ;
91
96
this .postParsingPhase = postParsingPhase ;
97
+ this .persistentGraphCache = persistentGraphCache ;
98
+ this .createPersistentCachedGraphScope = createPersistentCachedGraphScope ;
99
+ this .localGraphCache = EconomicMap .create ();
92
100
}
93
101
94
102
protected GraphBuilderPhase .Instance createGraphBuilderPhaseInstance (IntrinsicContext initialIntrinsicContext ) {
@@ -120,7 +128,7 @@ private EncodedGraph createGraph(ResolvedJavaMethod method, BytecodeProvider int
120
128
}
121
129
122
130
EncodedGraph encodedGraph = GraphEncoder .encodeSingleGraph (graphToEncode , architecture );
123
- graphCache .put (method , encodedGraph );
131
+ persistentGraphCache .put (method , encodedGraph );
124
132
return encodedGraph ;
125
133
}
126
134
@@ -152,13 +160,114 @@ private StructuredGraph buildGraph(ResolvedJavaMethod method, BytecodeProvider i
152
160
return graphToEncode ;
153
161
}
154
162
163
+ private static boolean verifyAssumptions (EncodedGraph graph ) {
164
+ Assumptions assumptions = graph .getAssumptions ();
165
+ if (assumptions == null || assumptions .isEmpty ()) {
166
+ return true ; // verified
167
+ }
168
+ for (Assumptions .Assumption assumption : assumptions ) {
169
+ if (assumption instanceof Assumptions .LeafType ) {
170
+ Assumptions .LeafType leafType = (Assumptions .LeafType ) assumption ;
171
+ /*
172
+ * LeafType cannot be fully verified because the assumption doesn't imply that the
173
+ * type is (also) concrete. We check a common case (leaf + concrete type).
174
+ */
175
+ Assumptions .AssumptionResult <ResolvedJavaType > assumptionResult = leafType .context .findLeafConcreteSubtype ();
176
+ if (assumptionResult != null ) {
177
+ ResolvedJavaType candidate = assumptionResult .getResult ();
178
+ if (!leafType .context .equals (candidate )) {
179
+ return false ;
180
+ }
181
+ }
182
+ } else if (assumption instanceof Assumptions .ConcreteSubtype ) {
183
+ Assumptions .ConcreteSubtype concreteSubtype = (Assumptions .ConcreteSubtype ) assumption ;
184
+ /*
185
+ * ConcreteSubtype cannot be fully verified because the assumption doesn't imply
186
+ * that the concrete subtype is (also) a leaf. We check a common case (leaf +
187
+ * concrete type).
188
+ */
189
+ Assumptions .AssumptionResult <ResolvedJavaType > assumptionResult = concreteSubtype .context .findLeafConcreteSubtype ();
190
+ if (assumptionResult != null ) {
191
+ ResolvedJavaType candidate = assumptionResult .getResult ();
192
+ /*
193
+ * No equality check here because the ConcreteSubtype assumption allows
194
+ * non-concrete subtypes for interfaces.
195
+ */
196
+ if (!concreteSubtype .subtype .isAssignableFrom (candidate )) {
197
+ return false ;
198
+ }
199
+ }
200
+ } else if (assumption instanceof Assumptions .ConcreteMethod ) {
201
+ Assumptions .ConcreteMethod concreteMethod = (Assumptions .ConcreteMethod ) assumption ;
202
+ /*
203
+ * ConcreteMethod is the only assumption that can be verified since it matches
204
+ * findUniqueConcreteMethod semantics. If the assumption cannot be retrieved
205
+ * (findUniqueConcreteMethod returns null) then it was invalidated.
206
+ */
207
+ Assumptions .AssumptionResult <ResolvedJavaMethod > assumptionResult = concreteMethod .context .findUniqueConcreteMethod (concreteMethod .method );
208
+ if (assumptionResult == null || !concreteMethod .impl .equals (assumptionResult .getResult ())) {
209
+ return false ;
210
+ }
211
+ } else if (assumption instanceof Assumptions .NoFinalizableSubclass || assumption instanceof Assumptions .CallSiteTargetValue ||
212
+ "org.graalvm.compiler.truffle.compiler.nodes.TruffleAssumption" .equals (assumption .getClass ().getName ())) {
213
+ /*
214
+ * These assumptions cannot be (even partially) verified. The cached graph will be
215
+ * invalidated on code installation.
216
+ */
217
+ } else {
218
+ throw GraalError .shouldNotReachHere ("unexpected assumption " + assumption );
219
+ }
220
+ }
221
+ return true ;
222
+ }
223
+
224
+ @ SuppressWarnings ({"unused" , "try" })
225
+ private EncodedGraph lookupOrCreatePersistentEncodedGraph (ResolvedJavaMethod method , BytecodeProvider intrinsicBytecodeProvider , boolean isSubstitution ,
226
+ boolean trackNodeSourcePosition ) {
227
+ EncodedGraph result = persistentGraphCache .get (method );
228
+ if (result == null && method .hasBytecodes ()) {
229
+ try (AutoCloseable scope = createPersistentCachedGraphScope .get ()) {
230
+ // Encoded graphs that can be cached across compilations are wrapped by "scopes"
231
+ // provided by createCachedGraphScope.
232
+ result = createGraph (method , intrinsicBytecodeProvider , isSubstitution );
233
+ } catch (Throwable ex ) {
234
+ throw debug .handle (ex );
235
+ }
236
+ }
237
+ return result ;
238
+ }
239
+
155
240
@ Override
156
241
protected EncodedGraph lookupEncodedGraph (ResolvedJavaMethod method , BytecodeProvider intrinsicBytecodeProvider , boolean isSubstitution ,
157
242
boolean trackNodeSourcePosition ) {
158
- EncodedGraph result = graphCache .get (method );
159
- if (result == null && method .hasBytecodes ()) {
160
- result = createGraph (method , intrinsicBytecodeProvider , isSubstitution );
243
+ // Graph's assumptions are fresh or validated recently.
244
+ EncodedGraph result = localGraphCache .get (method );
245
+ if (result != null ) {
246
+ return result ;
161
247
}
248
+
249
+ result = persistentGraphCache .get (method );
250
+ if (result == null ) {
251
+ // Embedded assumptions in a fresh encoded graph should be up-to-date, so no need to
252
+ // validate them.
253
+ result = lookupOrCreatePersistentEncodedGraph (method , intrinsicBytecodeProvider , isSubstitution , trackNodeSourcePosition );
254
+ if (result != null ) {
255
+ localGraphCache .put (method , result );
256
+ }
257
+ } else if (!verifyAssumptions (result )) {
258
+ // Cached graph has invalid assumptions, drop from persistent cache and re-parse.
259
+ persistentGraphCache .removeKey (method );
260
+ // Embedded assumptions in a fresh encoded graph should be up-to-date, so no need to
261
+ // validate them.
262
+ result = lookupOrCreatePersistentEncodedGraph (method , intrinsicBytecodeProvider , isSubstitution , trackNodeSourcePosition );
263
+ if (result != null ) {
264
+ localGraphCache .put (method , result );
265
+ }
266
+ } else {
267
+ // Assumptions validated, avoid further checks.
268
+ localGraphCache .put (method , result );
269
+ }
270
+
162
271
return result ;
163
272
}
164
273
}
0 commit comments