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