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 ```