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