1 # Notes on InvokeDynamic and AOT
2
3
4 ## Overview
5
6 This document is current as of the GIT revision date. It describes the behavior of the branch that contains this document.
7
8 CDS Indy optimization is enabled by the `-XX:+AOTInvokeDynamicLinking` flag. This flag
9 is enabled by default if you specify `-XX:AOTClassLinking`. It affect only the assembly of the
10 static CDS archive. I.e., with `-Xshare:dump` or `-XX:CacheDataStore=<file>.aot`
11
12 If you suspect there's a bug, you can explicitly disable this optimization
13 with `-XX:+UnlockDiagnosticVMOptions -XX:-AOTInvokeDynamicLinking`, and rerun your tests.
14
15
16 Notes:
17
18 - Indy call sites are pre-resolved only for the static CDS archive. Therefore,
19 if the AOT wants to generate code for pre-resolved indy call sites in a class X,
20 then X must be in the static CDS archive.
21
22 - CDS does not pre-resolve all indys. Instead, it only pre-resolve indys that were actually
23 executed when generating the classlist. Therefore, you should make sure that your classlist generation
24 covers all indy call sites that you want to generate good AOT code for.
25
26 - Only two BSMs are supported so far. See `ClassPrelinker::should_preresolve_invokedynamic()` in
27 [classPrelinker.cpp](../../../../src/hotspot/share/cds/classPrelinker.cpp)
28
29 - `StringConcatFactory::makeConcatWithConstants`
30 - `LambdaMetafactory::metafactory`
31
32
33 ## Simple Example
34
35
36 ```
37 class ConcatA {
38 static {
39 bar("000", "222");
40 foo("000", "222");
41 }
42
43 public static void main(String args[]) throws Exception {
44 foo("000", "222");
45 System.out.println(x);
46 System.out.println(ConcatB.foo("123" , "abc"));
47
48 doit(() -> {
49 System.out.println("Hello Lambda");
50 Thread.dumpStack();
51 });
52 }
53
54 static void doit(Runnable r) {
55 r.run();
56 }
57
58 static String x;
59 static void foo(String a, String b) {
60 x = a + b;
61 }
62 static void bar(String a, String b) {
63 x = a + b;
64 }
65 }
66
67 class ConcatB {
68 static String foo(String a, String b) {
69 return "a" + b;
70 }
71 }
72 ```
73
74 You can compile it using:
75
76 ```
77 (
78 cd ~/tmp
79 rm -rf tmpclasses
80 mkdir -p tmpclasses
81 javac -d tmpclasses ConcatA.java && jar cvf ConcatA.jar -C tmpclasses .
82 java -cp ConcatA.jar ConcatA
83 )
84 ```
85
86 Then, create classlist, dump static archive with `-XX:+AOTClassLinking` and run it. The `-Xshare:dump` step prints some warnings,
87 which can be ignored for now.
88
89
90 ```
91 java -Xshare:off -cp ~/tmp/ConcatA.jar -XX:DumpLoadedClassList=concata.lst ConcatA
92 java -Xshare:dump -XX:+AOTClassLinking -XX:SharedClassListFile=concata.lst -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa
93 java -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa ConcatA
94 ```
95
96 ## Verify that the call sites are archived
97
98 To verify that the indy call sites are indeed archived, compare the following output:
99
100 ```
101 java -Xlog:methodhandles -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa ConcatA
102 vs
103 java -Xlog:methodhandles -cp ~/tmp/ConcatA.jar ConcatA
104 ```
105
106 The second one shows a lot more output than the first one.
107
108 You can also use `-XX:+TraceBytecodes`. With `-Xshare:dump -XX:+AOTClassLinking`, you can see that the first
109 indy for string concat took only a handful of bytecodes before reaching the `StringConcatHelper.simpleConcat()`
110 method. The BSM is completely skipped.
111
112
113 ```
114 [3289356] static void ConcatA.bar(jobject, jobject)
115 [3289356] 192633 0 nofast_aload_0
116 [3289356] 192634 1 aload_1
117 [3289356] 192635 2 invokedynamic bsm=90 54 <makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
118
119 [3289356] static jobject java.lang.invoke.Invokers$Holder.linkToTargetMethod(jobject, jobject, jobject)
120 [3289356] 192636 0 aload_2
121 [3289356] 192637 1 checkcast 12 <java/lang/invoke/MethodHandle>
122 [3289356] 192638 4 nofast_aload_0
123 [3289356] 192639 5 aload_1
124 [3289356] 192640 6 invokehandle 45 <java/lang/invoke/MethodHandle.invokeBasic(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;>
125
126 [3289356] static jobject java.lang.invoke.DelegatingMethodHandle$Holder.reinvoke_L(jobject, jobject, jobject)
127 [3289356] 192641 0 nofast_aload_0
128 [3289356] 192642 1 checkcast 12 <java/lang/invoke/BoundMethodHandle$Species_L>
129 [3289356] 192643 4 nofast_getfield 16 <java/lang/invoke/BoundMethodHandle$Species_L.argL0/Ljava/lang/Object;>
130 [3289356] 192644 7 astore_3
131 [3289356] 192645 8 aload_3
132 [3289356] 192646 9 checkcast 18 <java/lang/invoke/MethodHandle>
133 [3289356] 192647 12 aload_1
134 [3289356] 192648 13 aload_2
135 [3289356] 192649 14 invokehandle 67 <java/lang/invoke/MethodHandle.invokeBasic(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;>
136
137 [3289356] static jobject java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(jobject, jobject, jobject)
138 [3289356] 192650 0 nofast_aload_0
139 [3289356] 192651 1 invokestatic 16 <java/lang/invoke/DirectMethodHandle.internalMemberName(Ljava/lang/Object;)Ljava/lang/Object;>
140
141 [3289356] static jobject java.lang.invoke.DirectMethodHandle.internalMemberName(jobject)
142 [3289356] 192652 0 nofast_aload_0
143 [3289356] 192653 1 checkcast 3 <java/lang/invoke/DirectMethodHandle>
144 [3289356] 192654 4 nofast_getfield 80 <java/lang/invoke/DirectMethodHandle.member/Ljava/lang/invoke/MemberName;>
145 [3289356] 192655 7 areturn
146
147 [3289356] static jobject java.lang.invoke.DirectMethodHandle$Holder.invokeStatic(jobject, jobject, jobject)
148 [3289356] 192656 4 astore_3
149 [3289356] 192657 5 aload_1
150 [3289356] 192658 6 aload_2
151 [3289356] 192659 7 aload_3
152 [3289356] 192660 8 checkcast 21 <java/lang/invoke/MemberName>
153 [3289356] 192661 11 invokestatic 257 <java/lang/invoke/MethodHandle.linkToStatic(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/invoke/MemberName;)Ljava/lang/Object;>
154
155 [3289356] static jobject java.lang.StringConcatHelper.simpleConcat(jobject, jobject)
156 ```
157
158 The same is true for Lambda proxies:
159
160 ```
161 [3289356] static void ConcatA.main(jobject)
162 [3289356] 199257 29 invokedynamic bsm=79 42 <run()Ljava/lang/Runnable;>
163
164 [3289356] static jobject java.lang.invoke.Invokers$Holder.linkToTargetMethod(jobject)
165 [3289356] 199258 0 nofast_aload_0
166 [3289356] 199259 1 checkcast 12 <java/lang/invoke/MethodHandle>
167 [3289356] 199260 4 invokehandle 56 <java/lang/invoke/MethodHandle.invokeBasic()Ljava/lang/Object;>
168
169 [3289356] static jobject java.lang.invoke.LambdaForm$MH/0x800000003.invoke(jobject)
170 [3289356] 199261 0 nofast_aload_0
171 [3289356] 199262 1 checkcast 12 <java/lang/invoke/BoundMethodHandle$Species_L>
172 [3289356] 199263 4 nofast_getfield 16 <java/lang/invoke/BoundMethodHandle$Species_L.argL0/Ljava/lang/Object;>
173 [3289356] 199264 7 areturn
174
175 [3289356] static jobject java.lang.invoke.Invokers$Holder.linkToTargetMethod(jobject)
176 [3289356] 199265 7 areturn
177
178 [3289356] static void ConcatA.main(jobject)
179 [3289356] 199266 34 invokestatic 46 <ConcatA.doit(Ljava/lang/Runnable;)V>
180
181 [3289356] static void ConcatA.doit(jobject)
182 [3289356] 199267 0 nofast_aload_0
183 [3289356] 199268 1 invokeinterface 50 <java/lang/Runnable.run()V>
184
185 [3289356] virtual void ConcatA$$Lambda/0x800000005.run()
186 [3289356] 199269 0 invokestatic 16 <ConcatA.lambda$main$0()V>
187
188 [3289356] static void ConcatA.lambda$main$0()
189 [3289356] 199270 0 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;>
190 [3289356] 199271 3 fast_aldc Hello Lambda
191 [3289356] 199272 5 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V>
192 ```
193
194 ## Inspect the archived call sites
195
196 To inspect the archived call sites, the easiest is to call the `findclass` function inside gdb:
197
198
199 - `gdb --args java -cp ~/tmp/ConcatA.jar -XX:SharedArchiveFile=concata.jsa ConcatA`
200 - set a breakpoint at `exit_globals`
201 - When this breakpoint is hit, do the following. You can see that the appendix objects of the call sites have a "CDS perm index", which can be used by the compiler to inline such objects into AOT code.
202
203
204 ```
205 Thread 2 "java" hit Breakpoint 2, exit_globals () at /jdk3/le1/open/src/hotspot/share/runtime/init.cpp:205
206 205 if (!destructorsCalled) {
207 (gdb) call findclass("ConcatA", 0xff)
208
209 "Executing findclass"
210 flags (bitmask):
211 0x01 - print names of methods
212 0x02 - print bytecodes
213 0x04 - print the address of bytecodes
214 0x08 - print info for invokedynamic
215 0x10 - print info for invokehandle
216
217 [ 0] 0x0000000800256120 class ConcatA loader data: 0x00007ffff032fe70 for instance a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x00000007ffce0f68}
218 0x0000000800256848 method <init> : ()V
219 0x0000000800731ae0 0 nofast_aload_0
220 0x0000000800731ae1 1 invokespecial 1 <java/lang/Object.<init>()V>
221 0x0000000800731ae4 4 return
222
223 0x00000008002568c8 static method <clinit> : ()V
224 0x0000000800731b20 0 fast_aldc 000
225 0x0000000800731b22 2 fast_aldc 222
226 0x0000000800731b24 4 invokestatic 64 <ConcatA.bar(Ljava/lang/String;Ljava/lang/String;)V>
227 0x0000000800731b27 7 fast_aldc 000
228 0x0000000800731b29 9 fast_aldc 222
229 0x0000000800731b2b 11 invokestatic 11 <ConcatA.foo(Ljava/lang/String;Ljava/lang/String;)V>
230 0x0000000800731b2e 14 return
231
232 0x0000000800256948 static method main : ([Ljava/lang/String;)V
233 0x0000000800731b70 0 fast_aldc 000
234 0x0000000800731b72 2 fast_aldc 222
235 0x0000000800731b74 4 invokestatic 11 <ConcatA.foo(Ljava/lang/String;Ljava/lang/String;)V>
236 0x0000000800731b77 7 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;>
237 0x0000000800731b7a 10 getstatic 23 <ConcatA.x/Ljava/lang/String;>
238 0x0000000800731b7d 13 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V>
239 0x0000000800731b80 16 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;>
240 0x0000000800731b83 19 fast_aldc 123
241 0x0000000800731b85 21 fast_aldc abc
242 0x0000000800731b87 23 invokestatic 37 <ConcatB.foo(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
243 0x0000000800731b8a 26 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V>
244 0x0000000800731b8d 29 invokedynamic bsm=79 42 <run()Ljava/lang/Runnable;>
245 BSM: REF_invokeStatic 80 <java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;>
246 arguments[3] = {
247 <MethodType> 6 Symbol: '()V' count 65535
248 <MethodHandle of kind 6 index at 88> 88 <ConcatA.lambda$main$0()V>
249 <MethodType> 6 Symbol: '()V' count 65535
250 }
251 ResolvedIndyEntry: Resolved InvokeDynamic Info:
252 - Method: 0x00000008000f5e40 java.lang.Object java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object)
253 - Resolved References Index: 12
254 - CP Index: 42
255 - Num Parameters: 1
256 - Return type: object
257 - Has Appendix: 1
258 - Resolution Failed 0
259 - appendix = 0x00000007ffc93648, CDS perm index = 14038
260 0x0000000800731b92 34 invokestatic 46 <ConcatA.doit(Ljava/lang/Runnable;)V>
261 0x0000000800731b95 37 return
262
263 0x00000008002569c8 static method doit : (Ljava/lang/Runnable;)V
264 0x0000000800731be0 0 nofast_aload_0
265 0x0000000800731be1 1 invokeinterface 50 <java/lang/Runnable.run()V>
266 0x0000000800731be6 6 return
267
268 0x0000000800256a48 static method foo : (Ljava/lang/String;Ljava/lang/String;)V
269 0x0000000800731c28 0 nofast_aload_0
270 0x0000000800731c29 1 aload_1
271 0x0000000800731c2a 2 invokedynamic bsm=90 54 <makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
272 BSM: REF_invokeStatic 91 <java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;>
273 arguments[1] = {
274
275 }
276 ResolvedIndyEntry: Resolved InvokeDynamic Info:
277 - Method: 0x00000008000f6540 java.lang.Object java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object, java.lang.Object, java.lang.Object)
278 - Resolved References Index: 11
279 - CP Index: 54
280 - Num Parameters: 3
281 - Return type: object
282 - Has Appendix: 1
283 - Resolution Failed 0
284 - appendix = 0x00000007ffc93620, CDS perm index = 14037
285 0x0000000800731c2f 7 putstatic 23 <ConcatA.x/Ljava/lang/String;>
286 0x0000000800731c32 10 return
287
288 0x0000000800256ac8 static method bar : (Ljava/lang/String;Ljava/lang/String;)V
289 0x0000000800731c70 0 nofast_aload_0
290 0x0000000800731c71 1 aload_1
291 0x0000000800731c72 2 invokedynamic bsm=90 54 <makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;>
292 BSM: REF_invokeStatic 91 <java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;>
293 arguments[1] = {
294
295 }
296 ResolvedIndyEntry: Resolved InvokeDynamic Info:
297 - Method: 0x00000008000f6540 java.lang.Object java.lang.invoke.Invokers$Holder.linkToTargetMethod(java.lang.Object, java.lang.Object, java.lang.Object)
298 - Resolved References Index: 10
299 - CP Index: 54
300 - Num Parameters: 3
301 - Return type: object
302 - Has Appendix: 1
303 - Resolution Failed 0
304 - appendix = 0x00000007ffc93508, CDS perm index = 14029
305 0x0000000800731c77 7 putstatic 23 <ConcatA.x/Ljava/lang/String;>
306 0x0000000800731c7a 10 return
307
308 0x0000000800256b48 static method lambda$main$0 : ()V
309 0x0000000800731cb8 0 getstatic 17 <java/lang/System.out/Ljava/io/PrintStream;>
310 0x0000000800731cbb 3 fast_aldc Hello Lambda
311 0x0000000800731cbd 5 invokevirtual 27 <java/io/PrintStream.println(Ljava/lang/String;)V>
312 0x0000000800731cc0 8 invokestatic 59 <java/lang/Thread.dumpStack()V>
313 0x0000000800731cc3 11 return
314 (gdb)
315 ```
316
317 ## Benchmarking with Javac
318
319 The benchmark [javac_helloworld](javac_helloworld) measures the total elapsed time of `javac HelloWorld.java` using
320 the premain branch vs the JDK mainline. About 180 indy call sites are archived.
321
322 As of 2023/08/28, we can see the following improvement. Please see [javac_helloworld/run.sh](javac_helloworld/run.sh)
323 for details.
324
325 ```
326 $ bash run.sh $MAINLINE_JAVA $PREMAIN_JAVA
327 [...]
328 Wall clock time - geomean over 10 runs of 'perf stat -r 16 javac HelloWorld.java'
329 Mainline JDK (CDS disabled) 302.86 ms
330 Mainline JDK (CDS enabled) 161.34 ms
331 Premain Prototype (CDS only) 131.71 ms
332 Premain Prototype (CDS + AOT) 92.84 ms
333
334 $ grep entries.*archived Javac-static.dump.log
335 [0.966s][info ][cds ] Class CP entries = 35021, archived = 11400 ( 32.6%)
336 [0.966s][info ][cds ] Field CP entries = 19668, archived = 5643 ( 28.7%)
337 [0.966s][info ][cds ] Method CP entries = 47213, archived = 3289 ( 7.0%)
338 [0.966s][info ][cds ] Indy CP entries = 1192, archived = 184 ( 15.4%)
339
340 ```