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