Obsah
1. Výpis symbolického kódu generovaného JIT překladačem (disassembler)
2. Ukázka kódu generovaného na platformě i386 (C1 překladač)
3. Ukázka kódu generovaného na platformě i386 (C2 překladač)
4. Žhavá technologická novinka – kód generovaný na platformě ARM 64 (AArch64)
5. Složitější příklad – výpočty ve formátu pohyblivé (plovoucí) řádové čárky
6. Generovaný kód v symbolickém formátu (C1 překladač)
7. Generovaný kód v symbolickém formátu (C2 překladač)
8. Generovaný kód v symbolickém formátu (AArch64)
9. Obsah následující části seriálu
10. Repositář se zdrojovými kódy všech demonstračních i testovacích příkladů
1. Výpis symbolického kódu generovaného JIT překladačem (disassembler)
V předchozí části tohoto seriálu jsme si ukázali, jakým způsobem je možné do jisté míry určit, ve kterém okamžiku se spustí JIT (Just-In-Time) překladač. Taktéž jsme si řekli, jaké základní optimalizace provádí JIT překladač typu client a o jaké optimalizace se pokouší sice pomaleji pracující, ale zato výkonnější překladač typu server. V některých případech je však dobré zjistit i sekvenci strojových instrukcí generovanou JIT překladačem. K tomuto účelu slouží volba -XX:+PrintAssembly, kterou je nutné zkombinovat s již minule popsanou volbou -XX:+UnlockDiagnosticVMOptions. Jakmile je volba -XX:+PrintAssembly použita, bude se na standardní výstup vypisovat disasemblovaný kód generovaný JIT překladačem. Termín „disasemblovaný“ zde značí to, že se virtuální stroj Javy pokusí převést strojový kód (sekvenci bajtů) vytvořenou JIT překladačem na čitelný zdrojový kód v assembleru dané architektury (i386, x86_64, ARM32, ARM64, SPARC…).
Samotný disassembler však v současnosti není součástí JVM ani JRE, ale vyžaduje se spolupráce s externí knihovnou nazvanou hsdis-${architektura}.so či hsdis-${architektura}.dll, kde se za slovo architektura doplňuje aktuálně používaná architektura mikroprocesoru, což může být například již zmíněný i386 atd. Tuto knihovnu je nutné buď přeložit (což není na Linuxu tak těžké, využívá se zde binutils) nebo získat již v přeložené podobě na Internetu. Informace o překladu získáte na stránkách http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README (README k OpenJDK7, odzkoušeno) a http://dropzone.nfshost.com/hsdis.htm (tento návod jsem nezkoušel), přeloženou knihovnu pro 32bitová Windows pak lze stáhnout ze stránky http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html.
Pro vyzkoušení možností JIT překladačů využijeme demonstrační příklad nazvaný ArrayTest3, který jsme ostatně využili již v předchozí části tohoto seriálu:
/** * Velmi jednoduchy benchmark - pruchod polem */ public class ArrayTest3 { // velikost pole pouziteho v testu public static final int ARRAY_SIZE = 5000; public static void main(String[] args) throws InterruptedException, java.io.IOException { // nacist pocet opakovani testu final int iter = Integer.parseInt(args[0]); // priblizny celkovy cas behu testu // (bez GC a cekani na dokonceni GC) long total_time = 0; // provest zadany pocet testu for (int i = 0; i < iter; i++) { // pro jistotu nejdrive provedeme GC // a pockame na jeho dokonceni (nepresne!) System.gc(); Thread.sleep(1000); // provest test a zmerit cas behu testu long t1 = System.nanoTime(); test(); long t2 = System.nanoTime(); long delta_t = t2 - t1; // vypis casu pro jeden test System.out.format("Round #%2d time: %,12d ns\n", i, delta_t); // vypocet celkoveho casu behu vsech testu total_time += delta_t; } System.out.format("Total time: %,d ns\n", total_time); // cekat na stisk klavesy System.in.read(); } /** * Vlastni test, kde se prochazi polem a postupne se * naplnuji jednotlive prvky tohoto pole. */ private static void test() { int[] array = new int[ARRAY_SIZE]; final int length = array.length; for (int i = 0; i < length; i++) { array[i] = i; } } }
Důležitý je i bajtkód metody ArrayTest3.test(), protože právě tato metoda bude překládána:
private static void test(); Code: 0: sipush 5000 3: newarray int 5: astore_0 6: aload_0 7: arraylength 8: istore_1 9: iconst_0 10: istore_2 11: iload_2 <--------+ 12: iload_1 | +---- 13: if_icmpge 26 | | 16: aload_0 | | 17: iload_2 | | 18: iload_2 | | 19: iastore | | 20: iinc 2, 1 | | 23: goto 11 ----+ +---> 26: return }
2. Ukázka kódu generovaného na platformě i386 (C1 překladač)
Předpokládejme nyní, že máme k dispozici výše zmíněnou dynamickou knihovnu hsdis-${architektura}.so či hsdis-${architektura}.dll, která je umístěna do aktuálního adresáře, popř. do adresáře, na nějž se odkazuje proměnná LD_LIBRARY_PATH. Nyní můžeme demonstrační příklad ArrayTest3 spustit takovým způsobem, aby se vynutil překlad metody ArrayTest3.test(). Připomeňme si, že metoda či smyčka uvnitř metody je přeložena ve chvíli, kdy proběhne takový počet iterací, který je určen parametrem -XX:CompileThreshold=10000, popř. dosáhne výchozí hodnoty 15000 iterací. Volání JVM může vypadat následovně:
java -client -XX:CompileThreshold=10000 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly ArrayTest3 3 > output1.asm
Do souboru nazvaného output1.asm se nejdříve vypíše typ JIT překladače (zde HotSpot Client), jméno knihovny s disassemblerem (zde hsdis-i386.so) a taktéž architektura (mach=‚i386‘, což zde není zcela přesná informace, jak ostatně uvidíme dále):
Java HotSpot(TM) Client VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output Loaded disassembler from hsdis-i386.so Decoding compiled method 0x00a00908: Code: [Disassembling for mach='i386']
Dále se v průběhu překladu vypisuje i disasemblovaný kód i s dalšími informacemi. Nás zajímá především způsob překladu metody ArrayTest3.test(), který vypadá následovně:
[Entry Point] [Verified Entry Point] [Constants] # {method} 'test' '()V' in 'ArrayTest3' # [sp+0x20] (sp of caller) 0x00a00a00: mov %eax,0xffffc000(%esp) 0x00a00a07: push %ebp 0x00a00a08: sub $0x18,%esp ;*sipush ; - ArrayTest3::test@0 (line 47) 0x00a00a0b: mov $0x1388,%ebx 0x00a00a10: mov $0x2fb1c6a8,%edx ; {oop({type array int})} 0x00a00a15: mov %ebx,%edi 0x00a00a17: cmp $0xffffff,%ebx 0x00a00a1d: ja 0x00a00ab7 0x00a00a23: mov $0x13,%esi 0x00a00a28: lea (%esi,%ebx,4),%esi 0x00a00a2b: and $0xfffffff8,%esi 0x00a00a2e: mov %fs:0x0,%ecx 0x00a00a36: mov 0xfffffff4(%ecx),%ecx 0x00a00a39: mov 0x34(%ecx),%eax 0x00a00a3c: lea (%eax,%esi,1),%esi 0x00a00a3f: cmp 0x3c(%ecx),%esi 0x00a00a42: ja 0x00a00ab7 0x00a00a48: mov %esi,0x34(%ecx) 0x00a00a4b: sub %eax,%esi 0x00a00a4d: movl $0x1,(%eax) 0x00a00a53: mov %edx,0x4(%eax) 0x00a00a56: mov %ebx,0x8(%eax) 0x00a00a59: sub $0xc,%esi 0x00a00a5c: je 0x00a00a82 0x00a00a62: xor %ebx,%ebx 0x00a00a64: shr $0x3,%esi 0x00a00a67: jae 0x00a00a77 0x00a00a6d: mov %ebx,0xc(%eax,%esi,8) 0x00a00a71: je 0x00a00a82 0x00a00a77: mov %ebx,0x8(%eax,%esi,8) 0x00a00a7b: mov %ebx,0x4(%eax,%esi,8) 0x00a00a7f: dec %esi 0x00a00a80: jne 0x00a00a77 ;*newarray ; - ArrayTest3::test@3 (line 47) 0x00a00a82: mov $0x0,%esi 0x00a00a87: jmp 0x00a00aa4 ;*iload_2 ; - ArrayTest3::test@11 (line 49) 0x00a00a8c: mov $0x1388,%edi 0x00a00a91: cmp %esi,%edi 0x00a00a93: jbe 0x00a00abe 0x00a00a99: mov %esi,0xc(%eax,%esi,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x00a00a9d: inc %esi ; OopMap{eax=Oop off=158} ;*goto ; - ArrayTest3::test@23 (line 49) 0x00a00a9e: test %eax,0x940100 ;*goto ; - ArrayTest3::test@23 (line 49) ; {poll} 0x00a00aa4: cmp $0x1388,%esi 0x00a00aaa: jl 0x00a00a8c ;*if_icmpge ; - ArrayTest3::test@13 (line 49) 0x00a00aac: add $0x18,%esp 0x00a00aaf: pop %ebp 0x00a00ab0: test %eax,0x940100 ; {poll_return} 0x00a00ab6: ret 0x00a00ab7: call 0x009fef00 ; OopMap{off=188} ;*newarray ; - ArrayTest3::test@3 (line 47) ; {runtime_call} 0x00a00abc: jmp 0x00a00a82 0x00a00abe: mov %esi,(%esp) 0x00a00ac1: call 0x009fe250 ; OopMap{eax=Oop off=198} ;*iastore ; - ArrayTest3::test@19 (line 50) ; {runtime_call} 0x00a00ac6: nop 0x00a00ac7: nop 0x00a00ac8: mov %fs:0x0,%esi 0x00a00ad0: mov 0xfffffff4(%esi),%esi 0x00a00ad3: mov 0x188(%esi),%eax 0x00a00ad9: movl $0x0,0x188(%esi) 0x00a00ae3: movl $0x0,0x18c(%esi) 0x00a00aed: add $0x18,%esp 0x00a00af0: pop %ebp 0x00a00af1: jmp 0x009abb80 ; {runtime_call} 0x00a00af6: hlt 0x00a00af7: hlt 0x00a00af8: hlt 0x00a00af9: hlt 0x00a00afa: hlt 0x00a00afb: hlt 0x00a00afc: hlt 0x00a00afd: hlt 0x00a00afe: hlt 0x00a00aff: hlt [Exception Handler] [Stub Code] 0x00a00b00: call 0x009ff880 ; {no_reloc} 0x00a00b05: push $0x6eb63690 ; {external_word} 0x00a00b0a: call 0x00a00b0f 0x00a00b0f: pusha 0x00a00b10: call 0x6ea45f60 ; {runtime_call} 0x00a00b15: hlt [Deopt Handler Code] 0x00a00b16: push $0xa00b16 ; {section_word} 0x00a00b1b: jmp 0x0099ca70 ; {runtime_call}
uff :-)
Vypadá to sice velmi složitě, ovšem vzhledem k tomu, že JIT překladač typu client (označovaný C1) je poměrně primitivní, nedá nám velký problém najít v celém kódu vlastní smyčku, v níž se naplňují prvky pole. Zde je zmíněná část kódu. Poznámka: 0×1388 je dekadicky přesně 5000, tedy délka pole i maximální počet průchodů smyčkou:
0x00a00a82: mov $0x0,%esi ; počitadlo +------ 0x00a00a87: jmp 0x00a00aa4 ; test na konci smyčky (trošku divné...) | ; - ArrayTest3::test@11 (line 49) | +--> 0x00a00a8c: mov $0x1388,%edi ; délka pole | | 0x00a00a91: cmp %esi,%edi ; konec? | | 0x00a00a93: jbe 0x00a00abe | | 0x00a00a99: mov %esi,0xc(%eax,%esi,4) ; zápis do pole | | ; - ArrayTest3::test@19 (line 50) | | 0x00a00a9d: inc %esi ; zvýšení hodnoty počitadla | | ;*goto | | ; - ArrayTest3::test@23 (line 49) | | 0x00a00a9e: test %eax,0x940100 ;*goto | | ; - ArrayTest3::test@23 (line 49) | | ; {poll} +-----> 0x00a00aa4: cmp $0x1388,%esi ; dosáhli jsme konce smyčky? +--- 0x00a00aaa: jl 0x00a00a8c ; jestli ne, hup na začátek ; - ArrayTest3::test@13 (line 49) 0x00a00aac: add $0x18,%esp ; správa zásobníku při návratu 0x00a00aaf: pop %ebp 0x00a00ab0: test %eax,0x940100 ; {poll_return} 0x00a00ab6: ret 0x00a00ab7: call 0x009fef00 ; OopMap{off=188} ;*newarray ; - ArrayTest3::test@3 (line 47) ; {runtime_call} 0x00a00abc: jmp 0x00a00a82 0x00a00abe: mov %esi,(%esp)
Můžeme vidět, že kód není příliš optimální vzhledem k množství prováděných skoků – ručně napsaný assembler by mohl být mnohem výkonnější. Význam „prapodivné“ instrukce test %eax,0×940100, která v kódu nemá žádný viditelný vliv, si vysvětlíme příště.
3. Ukázka kódu generovaného na platformě i386 (C2 překladač)
Podívejme se nyní na to, jak se situace změní při použití JIT překladače typu server. Spuštění JVM bude v tomto případě nepatrně odlišné:
java -server -XX:CompileThreshold=10000 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly ArrayTest3 3 > output2.asm
Změní se samozřejmě hlavička vypisovaná do souboru output2.asm:
Java HotSpot(TM) Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output Loaded disassembler from hsdis-i386.so Decoding compiled method 0x009b8e88: Code: [Disassembling for mach='i386']
Mnohem zajímavější je však sekvence strojových instrukcí generovaná pro námi testovanou metodu ArrayTest3.java. Metoda se přeloží hned dvakrát; zde je první verze disasemblovaného strojového kódu:
[Entry Point] [Verified Entry Point] [Constants] # {method} 'test' '()V' in 'ArrayTest3' # [sp+0x40] (sp of caller) 0x009b8f80: mov %eax,0xffffc000(%esp) 0x009b8f87: push %ebp 0x009b8f88: sub $0x38,%esp ;*synchronization entry ; - ArrayTest3::test@-1 (line 47) 0x009b8f8b: mov %fs:0x0,%ebp 0x009b8f93: mov 0xfffffff4(%ebp),%ebp 0x009b8f96: mov 0x34(%ebp),%ebx 0x009b8f99: lea 0x4e30(%ebx),%edi 0x009b8f9f: cmp 0x3c(%ebp),%edi 0x009b8fa2: jae 0x009b90fc 0x009b8fa8: mov %edi,0x34(%ebp) 0x009b8fab: prefetchnta 0x100(%edi) 0x009b8fb2: movl $0x1,(%ebx) 0x009b8fb8: prefetchnta 0x120(%edi) 0x009b8fbf: movl $0xbb30a30,0x4(%ebx) ; {oop({type array int})} 0x009b8fc6: prefetchnta 0x140(%edi) 0x009b8fcd: movl $0x1388,0x8(%ebx) 0x009b8fd4: lea 0x10(%ebx),%edi 0x009b8fd7: movl $0x0,0xc(%ebx) 0x009b8fde: mov $0x9c4,%ecx 0x009b8fe3: shl %ecx 0x009b8fe5: xor %eax,%eax 0x009b8fe7: rep stos %eax,%es:(%edi) ;*newarray ; - ArrayTest3::test@3 (line 47) 0x009b8fe9: mov $0x1,%ecx 0x009b8fee: jmp 0x009b8ff2 0x009b8ff0: mov %ebp,%ecx ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b8ff2: mov %ecx,0xc(%ebx,%ecx,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b8ff6: mov %ecx,%edi 0x009b8ff8: add $0x10,%edi ;*iinc ; - ArrayTest3::test@20 (line 49) 0x009b8ffb: mov %edi,(%esp) 0x009b8ffe: mov %ecx,%ebp 0x009b9000: add $0xf,%ebp 0x009b9003: mov %ebp,0x4(%esp) 0x009b9007: mov %ecx,%edi 0x009b9009: inc %edi 0x009b900a: mov %edi,0x10(%ebx,%ecx,4) 0x009b900e: mov %ecx,%edi 0x009b9010: add $0xe,%edi 0x009b9013: mov %edi,0x8(%esp) 0x009b9017: mov %ecx,%ebp 0x009b9019: add $0xd,%ebp 0x009b901c: mov %ebp,0xc(%esp) 0x009b9020: mov %ecx,%edi 0x009b9022: add $0xc,%edi 0x009b9025: mov %edi,0x10(%esp) 0x009b9029: mov %ecx,%ebp 0x009b902b: add $0xb,%ebp 0x009b902e: mov %ebp,0x14(%esp) 0x009b9032: mov %ecx,%ebp 0x009b9034: add $0xa,%ebp 0x009b9037: mov %ebp,0x18(%esp) 0x009b903b: mov %ecx,%ebp 0x009b903d: add $0x9,%ebp 0x009b9040: mov %ebp,0x1c(%esp) 0x009b9044: mov %ecx,%ebp 0x009b9046: add $0x8,%ebp 0x009b9049: mov %ebp,0x20(%esp) 0x009b904d: mov %ecx,%ebp 0x009b904f: add $0x7,%ebp 0x009b9052: mov %ebp,0x24(%esp) 0x009b9056: mov %ecx,%edi 0x009b9058: add $0x6,%edi 0x009b905b: mov %ecx,%ebp 0x009b905d: add $0x5,%ebp 0x009b9060: mov %ecx,%edx 0x009b9062: add $0x4,%edx 0x009b9065: mov %ecx,%eax 0x009b9067: add $0x3,%eax 0x009b906a: mov %ecx,%esi 0x009b906c: add $0x2,%esi 0x009b906f: mov %esi,0x14(%ebx,%ecx,4) 0x009b9073: mov %eax,0x18(%ebx,%ecx,4) 0x009b9077: mov %edx,0x1c(%ebx,%ecx,4) 0x009b907b: mov %ebp,0x20(%ebx,%ecx,4) 0x009b907f: mov %edi,0x24(%ebx,%ecx,4) 0x009b9083: mov 0x24(%esp),%ebp 0x009b9087: mov %ebp,0x28(%ebx,%ecx,4) 0x009b908b: mov 0x20(%esp),%ebp 0x009b908f: mov %ebp,0x2c(%ebx,%ecx,4) 0x009b9093: mov 0x1c(%esp),%ebp 0x009b9097: mov %ebp,0x30(%ebx,%ecx,4) 0x009b909b: mov 0x18(%esp),%ebp 0x009b909f: mov %ebp,0x34(%ebx,%ecx,4) 0x009b90a3: mov 0x14(%esp),%ebp 0x009b90a7: mov %ebp,0x38(%ebx,%ecx,4) 0x009b90ab: mov 0x10(%esp),%ebp 0x009b90af: mov %ebp,0x3c(%ebx,%ecx,4) 0x009b90b3: mov 0xc(%esp),%ebp 0x009b90b7: mov %ebp,0x40(%ebx,%ecx,4) 0x009b90bb: mov 0x8(%esp),%edi 0x009b90bf: mov %edi,0x44(%ebx,%ecx,4) 0x009b90c3: mov 0x4(%esp),%ebp 0x009b90c7: mov %ebp,0x48(%ebx,%ecx,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b90cb: mov (%esp),%ebp 0x009b90ce: cmp $0x1379,%ebp 0x009b90d4: jl 0x009b8ff0 ;*if_icmpge ; - ArrayTest3::test@13 (line 49) 0x009b90da: mov %ebp,%ecx 0x009b90dc: cmp $0x1388,%ecx 0x009b90e2: jge 0x009b90f1 ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b90e4: mov %ecx,0xc(%ebx,%ecx,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b90e8: inc %ecx ;*iinc ; - ArrayTest3::test@20 (line 49) 0x009b90e9: cmp $0x1388,%ecx 0x009b90ef: jl 0x009b90e4 0x009b90f1: add $0x38,%esp 0x009b90f4: pop %ebp 0x009b90f5: test %eax,0x940000 ; {poll_return} 0x009b90fb: ret 0x009b90fc: mov $0x1388,%edx 0x009b9101: mov $0xbb30a30,%ecx ; {oop({type array int})} 0x009b9106: nop 0x009b9107: call 0x009b78c0 ; OopMap{off=396} ;*newarray ; - ArrayTest3::test@3 (line 47) ; {runtime_call} 0x009b910c: mov %eax,%ebx 0x009b910e: jmp 0x009b8fe9 ;*newarray ; - ArrayTest3::test@3 (line 47) 0x009b9113: mov %eax,%ecx 0x009b9115: add $0x38,%esp 0x009b9118: pop %ebp 0x009b9119: jmp 0x009bae80 ; {runtime_call} 0x009b911e: hlt 0x009b911f: hlt [Exception Handler] [Stub Code] 0x009b9120: jmp 0x009b7a40 ; {no_reloc} [Deopt Handler Code] 0x009b9125: push $0x9b9125 ; {section_word} 0x009b912a: jmp 0x0099e280 ; {runtime_call} 0x009b912f: hlt Decoding compiled method 0x009b89c8: Code:
Co zde vlastně vidíme? Jedná se o takzvané rozbalení smyčky (loop unrolling), které má zajistit co nejdelší sekvenci instrukcí bez podmíněných či nepodmíněných skoků. Tím je na většině současných mikroprocesorů v co největší míře využita instrukční pipeline, což se samozřejmě projeví při běhu smyčky (výhody a nevýhody jsme si vysvětlili minule).
Druhá varianta přeložené metody ArrayTest3.test() je poněkud odlišná, některé instrukce byly prohozeny a poněkud se změnila i logika vyhodnocování:
[Entry Point] [Verified Entry Point] [Constants] # {method} 'test' '()V' in 'ArrayTest3' 0x009b8ac0: int3 0x009b8ac1: xchg %ax,%ax 0x009b8ac4: mov %eax,0xffffc000(%esp) 0x009b8acb: push %ebp 0x009b8acc: sub $0x48,%esp 0x009b8acf: mov (%ecx),%edi 0x009b8ad1: mov 0x8(%ecx),%ebp 0x009b8ad4: mov 0x4(%ecx),%ebx 0x009b8ad7: mov %ecx,(%esp) 0x009b8ada: call 0x6ee57140 ; {runtime_call} 0x009b8adf: test %ebp,%ebp 0x009b8ae1: je 0x009b8c5c 0x009b8ae7: mov 0x4(%ebp),%eax 0x009b8aea: cmp $0xbb30a30,%eax ; {oop({type array int})} 0x009b8af0: jne 0x009b8c7f ;*iload_2 ; - ArrayTest3::test@11 (line 49) 0x009b8af6: cmp %ebx,%edi 0x009b8af8: jge 0x009b8c51 ;*if_icmpge ; - ArrayTest3::test@13 (line 49) 0x009b8afe: mov 0x8(%ebp),%eax ;*iastore ; - ArrayTest3::test@19 (line 50) ; implicit exception: dispatches to 0x009b8c66 0x009b8b01: cmp %eax,%edi 0x009b8b03: jae 0x009b8c66 0x009b8b09: mov %ebx,%edx 0x009b8b0b: dec %edx 0x009b8b0c: cmp %eax,%edx 0x009b8b0e: jae 0x009b8c66 0x009b8b14: mov %edi,%ecx 0x009b8b16: inc %ecx ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b8b17: mov %edi,0xc(%ebp,%edi,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b8b1b: inc %edi ;*iinc ; - ArrayTest3::test@20 (line 49) 0x009b8b1c: cmp %ecx,%edi 0x009b8b1e: jl 0x009b8b17 ;*if_icmpge ; - ArrayTest3::test@13 (line 49) 0x009b8b20: mov %ebx,%edx 0x009b8b22: add $0xfffffff1,%edx 0x009b8b25: mov $0x80000000,%eax 0x009b8b2a: cmp %edx,%ebx 0x009b8b2c: cmovl %eax,%edx 0x009b8b2f: cmp %edx,%edi 0x009b8b31: jge 0x009b8c79 0x009b8b37: mov %ebx,0x2c(%esp) 0x009b8b3b: mov %edx,0x30(%esp) 0x009b8b3f: jmp 0x009b8b52 0x009b8b41: nopw 0x0(%eax,%eax,1) 0x009b8b4c: xchg %ax,%ax 0x009b8b50: mov %ebx,%edi ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b8b52: mov %edi,0xc(%ebp,%edi,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b8b56: mov %edi,%ecx 0x009b8b58: add $0x10,%ecx ;*iinc ; - ArrayTest3::test@20 (line 49) 0x009b8b5b: mov %ecx,0x4(%esp) 0x009b8b5f: mov %edi,%ebx 0x009b8b61: add $0xf,%ebx 0x009b8b64: mov %ebx,0x8(%esp) 0x009b8b68: mov %edi,%ecx 0x009b8b6a: inc %ecx 0x009b8b6b: mov %ecx,0x10(%ebp,%edi,4) 0x009b8b6f: mov %edi,%ecx 0x009b8b71: add $0xe,%ecx 0x009b8b74: mov %ecx,0xc(%esp) 0x009b8b78: mov %edi,%ecx 0x009b8b7a: add $0xd,%ecx 0x009b8b7d: mov %ecx,0x10(%esp) 0x009b8b81: mov %edi,%ebx 0x009b8b83: add $0xc,%ebx 0x009b8b86: mov %ebx,0x14(%esp) 0x009b8b8a: mov %edi,%ecx 0x009b8b8c: add $0xb,%ecx 0x009b8b8f: mov %ecx,0x18(%esp) 0x009b8b93: mov %edi,%ecx 0x009b8b95: add $0xa,%ecx 0x009b8b98: mov %ecx,0x1c(%esp) 0x009b8b9c: mov %edi,%ecx 0x009b8b9e: add $0x9,%ecx 0x009b8ba1: mov %ecx,0x20(%esp) 0x009b8ba5: mov %edi,%ecx 0x009b8ba7: add $0x8,%ecx 0x009b8baa: mov %ecx,0x24(%esp) 0x009b8bae: mov %edi,%ecx 0x009b8bb0: add $0x7,%ecx 0x009b8bb3: mov %ecx,0x28(%esp) 0x009b8bb7: mov %edi,%ebx 0x009b8bb9: add $0x6,%ebx 0x009b8bbc: mov %edi,%ecx 0x009b8bbe: add $0x5,%ecx 0x009b8bc1: mov %edi,%edx 0x009b8bc3: add $0x4,%edx 0x009b8bc6: mov %edi,%eax 0x009b8bc8: add $0x3,%eax 0x009b8bcb: mov %edi,%esi 0x009b8bcd: add $0x2,%esi 0x009b8bd0: mov %esi,0x14(%ebp,%edi,4) 0x009b8bd4: mov %eax,0x18(%ebp,%edi,4) 0x009b8bd8: mov %edx,0x1c(%ebp,%edi,4) 0x009b8bdc: mov %ecx,0x20(%ebp,%edi,4) 0x009b8be0: mov %ebx,0x24(%ebp,%edi,4) 0x009b8be4: mov 0x28(%esp),%ecx 0x009b8be8: mov %ecx,0x28(%ebp,%edi,4) 0x009b8bec: mov 0x24(%esp),%ecx 0x009b8bf0: mov %ecx,0x2c(%ebp,%edi,4) 0x009b8bf4: mov 0x20(%esp),%ecx 0x009b8bf8: mov %ecx,0x30(%ebp,%edi,4) 0x009b8bfc: mov 0x1c(%esp),%ecx 0x009b8c00: mov %ecx,0x34(%ebp,%edi,4) 0x009b8c04: mov 0x18(%esp),%ecx 0x009b8c08: mov %ecx,0x38(%ebp,%edi,4) 0x009b8c0c: mov 0x14(%esp),%ecx 0x009b8c10: mov %ecx,0x3c(%ebp,%edi,4) 0x009b8c14: mov 0x10(%esp),%ebx 0x009b8c18: mov %ebx,0x40(%ebp,%edi,4) 0x009b8c1c: mov 0xc(%esp),%ebx 0x009b8c20: mov %ebx,0x44(%ebp,%edi,4) 0x009b8c24: mov 0x8(%esp),%ecx 0x009b8c28: mov %ecx,0x48(%ebp,%edi,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b8c2c: mov 0x4(%esp),%ebx 0x009b8c30: cmp 0x30(%esp),%ebx 0x009b8c34: jl 0x009b8b50 ;*if_icmpge ; - ArrayTest3::test@13 (line 49) 0x009b8c3a: mov 0x2c(%esp),%ebx 0x009b8c3e: mov 0x4(%esp),%ecx 0x009b8c42: cmp %ebx,%ecx 0x009b8c44: jge 0x009b8c51 0x009b8c46: xchg %ax,%ax ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b8c48: mov %ecx,0xc(%ebp,%ecx,4) ;*iastore ; - ArrayTest3::test@19 (line 50) 0x009b8c4c: inc %ecx ;*iinc ; - ArrayTest3::test@20 (line 49) 0x009b8c4d: cmp %ebx,%ecx 0x009b8c4f: jl 0x009b8c48 ;*iload_2 ; - ArrayTest3::test@11 (line 49) 0x009b8c51: add $0x48,%esp 0x009b8c54: pop %ebp 0x009b8c55: test %eax,0x940000 ; {poll_return} 0x009b8c5b: ret 0x009b8c5c: mov $0x0,%ebp 0x009b8c61: jmp 0x009b8af6 0x009b8c66: mov $0xffffff86,%ecx 0x009b8c6b: mov %edi,0x4(%esp) 0x009b8c6f: mov %ebx,0x8(%esp) 0x009b8c73: call 0x0099dd00 ; OopMap{ebp=Oop off=440} ;*aload_0 ; - ArrayTest3::test@16 (line 50) ; {runtime_call} 0x009b8c78: int3 ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b8c79: mov %edi,0x4(%esp) 0x009b8c7d: jmp 0x009b8c3e 0x009b8c7f: mov $0xffffffad,%ecx 0x009b8c84: mov %edi,0x4(%esp) 0x009b8c88: mov %ebx,0x8(%esp) 0x009b8c8c: xchg %ax,%ax 0x009b8c8f: call 0x0099dd00 ; OopMap{ebp=Oop off=468} ;*iload_2 ; - ArrayTest3::test@11 (line 49) ; {runtime_call} 0x009b8c94: int3 ;*aload_0 ; - ArrayTest3::test@16 (line 50) 0x009b8c95: hlt 0x009b8c96: hlt 0x009b8c97: hlt 0x009b8c98: hlt 0x009b8c99: hlt 0x009b8c9a: hlt 0x009b8c9b: hlt 0x009b8c9c: hlt 0x009b8c9d: hlt 0x009b8c9e: hlt 0x009b8c9f: hlt [Exception Handler] [Stub Code] 0x009b8ca0: jmp 0x009b7a40 ; {no_reloc} [Deopt Handler Code] 0x009b8ca5: push $0x9b8ca5 ; {section_word} 0x009b8caa: jmp 0x0099e280 ; {runtime_call} 0x009b8caf: hlt
4. Žhavá technologická novinka – kód generovaný na platformě ARM 64 (AArch64)
Pro zajímavost se podívejme na to, jak pracuje JIT překladač na zcela nové platformě ARM 64 (korektněji AArch64). I zde je v případě OpenJDK k dispozici JIT překladač. JVM se spustí následujícím způsobem:
java -XX:CompileThreshold=10000 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly ArrayTest3 3 > output3.asm
Loaded disassembler from /home/pavel/jdk8/build/linux-aarch64-normal-client-slowdebug/images/j2sdk-image/jre/lib/aarch64/hsdis-aarch64.so Decoding compiled method 0x00007fd632201090: Code: [Disassembling for mach='aarch64'] [Entry Point] [Verified Entry Point] # {method} {0x00007fd639ca96a0} 'test' '()V' in 'ArrayTest3' # [sp+0x40] (sp of caller) ;; block B4 [0, 0] ;; 0xFFFFFFFFFFFF7000 0x00007fd632204eb0: movn xscratch2, #0x8fff ; {no_reloc} 0x00007fd632204eb4: ldr xzr, [sp,x9] 0x00007fd632204eb8: stp xfp, xlr, [sp,#-16]! 0x00007fd632204ebc: mov xfp, sp 0x00007fd632204ec0: sub sp, sp, #0x30 0x00007fd632204ec4: notify entry ;*sipush ; - ArrayTest3::test@0 (line 47) ;; block B0 [0, 11] ;; 0x1388 0x00007fd632204ec8: movz w3, #0x1388 0x00007fd632204ecc: mov x19, x3 0x00007fd632204ed0: ldr x3, 0x00007fd632204ea0 ; {section_word} 0x00007fd632204ed4: ubfx x19, x19, #0, #32 0x00007fd632204ed8: mov x5, x19 ;; 0xFFFFFF 0x00007fd632204edc: orr xscratch1, xzr, #0xffffff 0x00007fd632204ee0: cmp x19, xscratch1 0x00007fd632204ee4: b.cs 0x00007fd6322050b8 ;; 0x1F 0x00007fd632204ee8: orr x4, xzr, #0x1f 0x00007fd632204eec: add x4, x4, w19, uxtw #2 0x00007fd632204ef0: and x4, x4, #0xfffffffffffffff8 0x00007fd632204ef4: ldr x0, [xthread,#112] 0x00007fd632204ef8: add x4, x0, x4, uxtx 0x00007fd632204efc: ldr xscratch1, [xthread,#128] 0x00007fd632204f00: cmp x4, xscratch1 0x00007fd632204f04: b.hi 0x00007fd6322050b8 0x00007fd632204f08: str x4, [xthread,#112] 0x00007fd632204f0c: sub x4, x4, x0 ;; 0x1 0x00007fd632204f10: orr x2, xzr, #0x1 0x00007fd632204f14: str x2, [x0] 0x00007fd632204f18: str x3, [x0,#8] 0x00007fd632204f1c: str w19, [x0,#16] 0x00007fd632204f20: subs x4, x4, #0x18 0x00007fd632204f24: b.eq 0x00007fd63220506c 0x00007fd632204f28: tst x4, #0x7 0x00007fd632204f2c: b.eq 0x00007fd632204fa0 0x00007fd632204f30: stp xlr, xzr, [sp,#-16]! 0x00007fd632204f34: stp xthread, xfp, [sp,#-16]! 0x00007fd632204f38: stp xcpool, xheapbase, [sp,#-16]! 0x00007fd632204f3c: stp xlocals, xmonitors, [sp,#-16]! 0x00007fd632204f40: stp xbcp, x23, [sp,#-16]! 0x00007fd632204f44: stp xesp, xdispatch, [sp,#-16]! 0x00007fd632204f48: stp x18, x19, [sp,#-16]! 0x00007fd632204f4c: stp x16, x17, [sp,#-16]! 0x00007fd632204f50: stp x14, x15, [sp,#-16]! 0x00007fd632204f54: stp xmethod, x13, [sp,#-16]! 0x00007fd632204f58: stp x10, x11, [sp,#-16]! 0x00007fd632204f5c: stp xscratch1, xscratch2, [sp,#-16]! 0x00007fd632204f60: stp x6, x7, [sp,#-16]! 0x00007fd632204f64: stp x4, x5, [sp,#-16]! 0x00007fd632204f68: stp x2, x3, [sp,#-16]! 0x00007fd632204f6c: stp x0, x1, [sp,#-16]! ;; 0x7FD63B4D2E28 0x00007fd632204f70: movz x0, #0x2e28 0x00007fd632204f74: movk x0, #0x3b4d, lsl #16 0x00007fd632204f78: movk x0, #0x7fd6, lsl #32 ;; 0x7FD63216CE50 0x00007fd632204f7c: movz x1, #0xce50 0x00007fd632204f80: movk x1, #0x3216, lsl #16 0x00007fd632204f84: movk x1, #0x7fd6, lsl #32 0x00007fd632204f88: mov x2, sp ;; 0x7FD63B2AE8E8 0x00007fd632204f8c: movz x3, #0xe8e8 0x00007fd632204f90: movk x3, #0x3b2a, lsl #16 0x00007fd632204f94: movk x3, #0x7fd6, lsl #32 0x00007fd632204f98: brx86 x3, 3, 0, 1 0x00007fd632204f9c: hlt #0x0 0x00007fd632204fa0: add x0, x0, #0x18 0x00007fd632204fa4: tst x4, #0x7 0x00007fd632204fa8: b.eq 0x00007fd63220501c 0x00007fd632204fac: stp xlr, xzr, [sp,#-16]! 0x00007fd632204fb0: stp xthread, xfp, [sp,#-16]! 0x00007fd632204fb4: stp xcpool, xheapbase, [sp,#-16]! 0x00007fd632204fb8: stp xlocals, xmonitors, [sp,#-16]! 0x00007fd632204fbc: stp xbcp, x23, [sp,#-16]! 0x00007fd632204fc0: stp xesp, xdispatch, [sp,#-16]! 0x00007fd632204fc4: stp x18, x19, [sp,#-16]! 0x00007fd632204fc8: stp x16, x17, [sp,#-16]! 0x00007fd632204fcc: stp x14, x15, [sp,#-16]! 0x00007fd632204fd0: stp xmethod, x13, [sp,#-16]! 0x00007fd632204fd4: stp x10, x11, [sp,#-16]! 0x00007fd632204fd8: stp xscratch1, xscratch2, [sp,#-16]! 0x00007fd632204fdc: stp x6, x7, [sp,#-16]! 0x00007fd632204fe0: stp x4, x5, [sp,#-16]! 0x00007fd632204fe4: stp x2, x3, [sp,#-16]! 0x00007fd632204fe8: stp x0, x1, [sp,#-16]! ;; 0x7FD63B4D2D38 0x00007fd632204fec: movz x0, #0x2d38 0x00007fd632204ff0: movk x0, #0x3b4d, lsl #16 0x00007fd632204ff4: movk x0, #0x7fd6, lsl #32 ;; 0x7FD63216CECC 0x00007fd632204ff8: movz x1, #0xcecc 0x00007fd632204ffc: movk x1, #0x3216, lsl #16 0x00007fd632205000: movk x1, #0x7fd6, lsl #32 0x00007fd632205004: mov x2, sp ;; 0x7FD63B2AE8E8 0x00007fd632205008: movz x3, #0xe8e8 0x00007fd63220500c: movk x3, #0x3b2a, lsl #16 0x00007fd632205010: movk x3, #0x7fd6, lsl #32 0x00007fd632205014: brx86 x3, 3, 0, 1 0x00007fd632205018: hlt #0x0 ;; zero memory 0x00007fd63220501c: lsr x4, x4, #3 0x00007fd632205020: mov xscratch1, x0 0x00007fd632205024: tst x4, #0x1 0x00007fd632205028: b.eq 0x00007fd632205034 0x00007fd63220502c: str xzr, [xscratch1],#8 0x00007fd632205030: sub x4, x4, #0x1 0x00007fd632205034: mov x19, xzr 0x00007fd632205038: b 0x00007fd632205044 0x00007fd63220503c: stp xzr, x19, [xscratch1],#16 0x00007fd632205040: sub x4, x4, #0x2 0x00007fd632205044: tst x4, #0x7 0x00007fd632205048: b.ne 0x00007fd63220503c 0x00007fd63220504c: cbz x4, 0x00007fd632205068 0x00007fd632205050: stp xzr, x19, [xscratch1],#16 0x00007fd632205054: stp xzr, x19, [xscratch1],#16 0x00007fd632205058: stp xzr, x19, [xscratch1],#16 0x00007fd63220505c: stp xzr, x19, [xscratch1],#16 0x00007fd632205060: sub x4, x4, #0x8 0x00007fd632205064: cbnz x4, 0x00007fd632205050 0x00007fd632205068: sub x0, x0, #0x18 ;*newarray ; - ArrayTest3::test@3 (line 47) ;; 0x0 0x00007fd63220506c: movz w1, #0x0, lsl #16 ;; 20 branch [AL] [B1] 0x00007fd632205070: b 0x00007fd632205094 ;*iload_2 ; - ArrayTest3::test@11 (line 49) ;; block B2 [16, 23] ;; 0x1388 0x00007fd632205074: movz w2, #0x1388 0x00007fd632205078: add x3, x0, #0x18 0x00007fd63220507c: cmp w2, w1 ;; 40 branch [BE] [RangeCheckStub: 0x1b458] [bci:19] 0x00007fd632205080: b.ls 0x00007fd6322050c0 0x00007fd632205084: str w1, [x3,w1,sxtw #2] ;*iastore ; - ArrayTest3::test@19 (line 50) 0x00007fd632205088: add w1, w1, #0x1 0x00007fd63220508c: adrp xscratch1, 0x00007fd63bd32000 ; OopMap{c_rarg0=Oop off=480} ;*goto ; - ArrayTest3::test@23 (line 49) ; {poll} 0x00007fd632205090: ldr wzr, [xscratch1,#256] ;*goto ; - ArrayTest3::test@23 (line 49) ; {poll} ;; block B1 [11, 13] ;; 0x1388 0x00007fd632205094: movz w2, #0x1388 0x00007fd632205098: cmp w1, w2 ;; 28 branch [LT] [B2] 0x00007fd63220509c: b.lt 0x00007fd632205074 ;*if_icmpge ; - ArrayTest3::test@13 (line 49) ;; block B3 [26, 26] 0x00007fd6322050a0: add sp, sp, #0x30 0x00007fd6322050a4: ldp xfp, xlr, [sp],#16 0x00007fd6322050a8: notify reentry 0x00007fd6322050ac: adrp xscratch1, 0x00007fd63bd32000 ; {poll_return} 0x00007fd6322050b0: ldr wzr, [xscratch1,#256] ; {poll_return} 0x00007fd6322050b4: ret ;; NewTypeArrayStub slow case 0x00007fd6322050b8: bl 0x00007fd6321fc550 ; OopMap{off=524} ;*newarray ; - ArrayTest3::test@3 (line 47) ; {runtime_call} 0x00007fd6322050bc: b 0x00007fd63220506c ;; RangeCheckStub slow case 0x00007fd6322050c0: mov xscratch1, x1 0x00007fd6322050c4: bl 0x00007fd6321fa2a0 ; OopMap{c_rarg0=Oop off=536} ;*iastore ; - ArrayTest3::test@19 (line 50) ; {runtime_call} 0x00007fd6322050c8: stp xlr, xzr, [sp,#-16]! 0x00007fd6322050cc: stp xthread, xfp, [sp,#-16]! 0x00007fd6322050d0: stp xcpool, xheapbase, [sp,#-16]! 0x00007fd6322050d4: stp xlocals, xmonitors, [sp,#-16]! 0x00007fd6322050d8: stp xbcp, x23, [sp,#-16]! 0x00007fd6322050dc: stp xesp, xdispatch, [sp,#-16]! 0x00007fd6322050e0: stp x18, x19, [sp,#-16]! 0x00007fd6322050e4: stp x16, x17, [sp,#-16]! 0x00007fd6322050e8: stp x14, x15, [sp,#-16]! 0x00007fd6322050ec: stp xmethod, x13, [sp,#-16]! 0x00007fd6322050f0: stp x10, x11, [sp,#-16]! 0x00007fd6322050f4: stp xscratch1, xscratch2, [sp,#-16]! 0x00007fd6322050f8: stp x6, x7, [sp,#-16]! 0x00007fd6322050fc: stp x4, x5, [sp,#-16]! 0x00007fd632205100: stp x2, x3, [sp,#-16]! 0x00007fd632205104: stp x0, x1, [sp,#-16]! ;; 0x7FD63B4AB0AA 0x00007fd632205108: movz x0, #0xb0aa 0x00007fd63220510c: movk x0, #0x3b4a, lsl #16 0x00007fd632205110: movk x0, #0x7fd6, lsl #32 ;; 0x7FD63216CFE8 0x00007fd632205114: movz x1, #0xcfe8 0x00007fd632205118: movk x1, #0x3216, lsl #16 0x00007fd63220511c: movk x1, #0x7fd6, lsl #32 0x00007fd632205120: mov x2, sp ;; 0x7FD63B2AE8E8 0x00007fd632205124: movz x3, #0xe8e8 0x00007fd632205128: movk x3, #0x3b2a, lsl #16 0x00007fd63220512c: movk x3, #0x7fd6, lsl #32 0x00007fd632205130: brx86 x3, 3, 0, 1 0x00007fd632205134: hlt #0x0 0x00007fd632205138: nop 0x00007fd63220513c: nop ;; Unwind handler 0x00007fd632205140: ldr x0, [xthread,#704] 0x00007fd632205144: str xzr, [xthread,#704] 0x00007fd632205148: str xzr, [xthread,#712] ;; remove_frame and dispatch to the unwind handler 0x00007fd63220514c: add sp, sp, #0x30 0x00007fd632205150: ldp xfp, xlr, [sp],#16 0x00007fd632205154: notify reentry 0x00007fd632205158: b 0x00007fd6321f9d60 ; {runtime_call} 0x00007fd63220515c: .inst 0x00000000 ; undefined [Exception Handler] [Stub Code] 0x00007fd632205160: movz x19, #0xdead ; {no_reloc} 0x00007fd632205164: movz x2, #0xd 0x00007fd632205168: movz x4, #0xdead 0x00007fd63220516c: movz x5, #0xdead 0x00007fd632205170: bl 0x00007fd6321fe1a0 ; {runtime_call} 0x00007fd632205174: stp xlr, xzr, [sp,#-16]! 0x00007fd632205178: stp xthread, xfp, [sp,#-16]! 0x00007fd63220517c: stp xcpool, xheapbase, [sp,#-16]! 0x00007fd632205180: stp xlocals, xmonitors, [sp,#-16]! 0x00007fd632205184: stp xbcp, x23, [sp,#-16]! 0x00007fd632205188: stp xesp, xdispatch, [sp,#-16]! 0x00007fd63220518c: stp x18, x19, [sp,#-16]! 0x00007fd632205190: stp x16, x17, [sp,#-16]! 0x00007fd632205194: stp x14, x15, [sp,#-16]! 0x00007fd632205198: stp xmethod, x13, [sp,#-16]! 0x00007fd63220519c: stp x10, x11, [sp,#-16]! 0x00007fd6322051a0: stp xscratch1, xscratch2, [sp,#-16]! 0x00007fd6322051a4: stp x6, x7, [sp,#-16]! 0x00007fd6322051a8: stp x4, x5, [sp,#-16]! 0x00007fd6322051ac: stp x2, x3, [sp,#-16]! 0x00007fd6322051b0: stp x0, x1, [sp,#-16]! 0x00007fd6322051b4: movz x0, #0xb0aa 0x00007fd6322051b8: movk x0, #0x3b4a, lsl #16 0x00007fd6322051bc: movk x0, #0x7fd6, lsl #32 0x00007fd6322051c0: movz x1, #0xc9a4 0x00007fd6322051c4: movk x1, #0x321e, lsl #16 0x00007fd6322051c8: movk x1, #0x7fd6, lsl #32 0x00007fd6322051cc: mov x2, sp 0x00007fd6322051d0: movz x3, #0xe8e8 0x00007fd6322051d4: movk x3, #0x3b2a, lsl #16 0x00007fd6322051d8: movk x3, #0x7fd6, lsl #32 0x00007fd6322051dc: brx86 x3, 3, 0, 1 0x00007fd6322051e0: hlt #0x0 [Deopt Handler Code] 0x00007fd6322051e4: adr xlr, 0x00007fd6322051e4 0x00007fd6322051e8: b 0x00007fd632148cb0 ; {runtime_call} 0x00007fd6322051ec: .inst 0x00000000 ; undefined
Generované instrukce budou pravděpodobně pro většinu čtenářů poněkud méně srozumitelné, takže si některé z nich popíšeme. Většina aritmetických a logických instrukcí používá takzvaný tříadresový kód, tj. pracuje se třemi registry – dvěma registry zdrojovými a jedním registrem cílovým (celkem je k dispozici 31 registrů R0 až R30). U některých instrukcí lze namísto druhého zdrojového registru použít i celočíselnou konstantu. Dalšími instrukcemi jsou skoky, tyto instrukce vždy začínají písmenem b. Zvláštní skupinu pak tvoří instrukce pro přenosy dat. Pokud některé registry začínají písmenem w, značí to použití dolních 32 bitů z 64 bitů:
# | Instrukce | Stručný popis |
---|---|---|
1 | add | x,y,z součet x=y+z |
2 | add | x,y,#const součet x=y+konstanta |
3 | sub | rozdíl |
4 | and | bitový součin |
5 | orr | bitový součet |
6 | lsl | bitový posun |
7 | neg | negace (jen dva operandy) |
8 | b | skok |
9 | bl | skok do podprogramu (branch and link) |
10 | ret | návrat z podprogramu |
11 | mov | klasický přesun dat |
12 | movz | načtení 16bitové konstanty (zbylých 16 či 48 bitů se vynuluje) |
13 | movn | jako movz, ale konstanta je negována (vhodné pro malá záporná čísla) |
14 | movk | načtení 16bitové konstanty do bitů 16–32, zbylé bity jsou ponechány na původní hodnotě |
15 | ldr | načtení dat z paměti |
16 | str | uložení registru do paměti |
17 | ldp | načtení registrového páru z paměti |
18 | stp | uložení registrového páru do paměti |
5. Složitější příklad – výpočty ve formátu pohyblivé (plovoucí) řádové čárky
V předchozích kapitolách jsme si ukázali, jakým způsobem se podařilo JIT překladačům typu client i server přeložit velmi jednoduchou počítanou programovou smyčku, v níž se přistupovalo do pole. Zajímavé bude se podívat na to, jak vypadá překlad metod, v nichž se provádí výpočty ve formátu pohyblivé (plovoucí) řádové čárky; konkrétně výpočty s hodnotami typu double. Následující demonstrační příklad vypočítá a vykreslí výřez ze známé Mandelbrotovy množiny (http://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xii/, http://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-xiii/). Při výpočtech se iterativně (ve smyčce) provádí výpočty s několika proměnnými typu double, takže by se zde měly projevit výhody optimalizujících JIT překladačů typu server:
import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; /** * Priklad, na kterem si ukazeme pouziti JIT prekladace * pri prekladu vypoctu s cisly typu double. * * @autor Pavel Tisnovsky */ public class MandelbrotRenderer { /** * Prefix jmena souboru s vygenerovanou bitmapou. */ private static final String OUTPUT_FILE_NAME_PREFIX = "mandelbrot"; /** * Horizontalni rozmer bitmapy. */ private static final int IMAGE_HEIGHT = 1024; /** * Vertikalni rozmer bitmapy. */ private static final int IMAGE_WIDTH = 1024; /** * Vypocet jedne iterace * @param x realna cast komplexniho cisla C * @param y imaginarni cast komplexniho cisla C * @param maxiter * @return */ static int iteration(double x, double y, int maxiter) { double zx = 0, zy = 0, cx = x, cy = y, zx2 = 0, zy2 = 0; int iter=0; // iteracni smycka Z=Z^2+C for (iter = 0; iter < maxiter; iter++) { zx2 = zx * zx; zy2 = zy * zy; // kdyz |Z|>2 ukonci smycku if (zx2 + zy2 > 4.0) { break; } zy = 2.0 * zx * zy + cy; zx = zx2 - zy2 + cx; } return iter; } /** * Vypocet a zobrazeni vyrezu Mandelbrotovy mnoziny. */ static void calculateMandelbrot(BufferedImage image) { final int width = image.getWidth(); final int height = image.getHeight(); // umozneni pristupu k jednotlivym pixelum DataBuffer dataBuffer = image.getRaster().getDataBuffer(); // vlastni naplneni bitmapy int index = 0; double cy = -0.18; for (int y = 0; y < height; y++) { double cx = -.8; for (int x = 0; x < width; x++) { // barva pixelu final int iter = 0xff - iteration(cx, cy, 255); // zapis barvy pixelu dataBuffer.setElem(index, iter); cx += 0.07 / width; index++; } cy += 0.07 / height; } } /** * Vytvoreni nove bitmapy se stupni sedi. * * @return nove vytvorena bitmapa */ private static BufferedImage createEmptyImage() { return new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_BYTE_GRAY); } /** * Zapis bitmapy na disk ve formatu PNG. * * @param image * testovaci bitmapa * @throws IOException */ private static void writeImageIntoFile(BufferedImage image, String fileName) throws IOException { ImageIO.write(image, "png", new File(fileName)); } /** * Vypocet a zobrazeni vyrezu Mandelbrotovy mnoziny. */ public static void main(String[] args) throws IOException { // vytvoreni bitmapy BufferedImage image = createEmptyImage(); long t1 = System.nanoTime(); calculateMandelbrot(image); long t2 = System.nanoTime(); System.out.println("Time: " + (long) (t2 - t1)); // zapis bitmapy na disk writeImageIntoFile(image, OUTPUT_FILE_NAME_PREFIX + ".png"); } }
Vykreslený obrázek vypadá následovně:
Bajtkód metody MandelbrotRenderer.iteration():
static int iteration(double, double, int); Code: 0: dconst_0 1: dstore 5 3: dconst_0 4: dstore 7 6: dload_0 7: dstore 9 9: dload_2 10: dstore 11 12: dconst_0 13: dstore 13 15: dconst_0 16: dstore 15 18: iconst_0 19: istore 17 21: iconst_0 22: istore 17 24: iload 17 26: iload 4 28: if_icmpge 90 31: dload 5 33: dload 5 35: dmul 36: dstore 13 38: dload 7 40: dload 7 42: dmul 43: dstore 15 45: dload 13 47: dload 15 49: dadd 50: ldc2_w #2 // double 4.0d 53: dcmpl 54: ifle 60 57: goto 90 60: ldc2_w #4 // double 2.0d 63: dload 5 65: dmul 66: dload 7 68: dmul 69: dload 11 71: dadd 72: dstore 7 74: dload 13 76: dload 15 78: dsub 79: dload 9 81: dadd 82: dstore 5 84: iinc 17, 1 87: goto 24 90: iload 17 92: ireturn
6. Generovaný kód v symbolickém formátu (C1 překladač)
V této kapitole nás bude zajímat především to, jakým způsobem dokázal JIT překladač typu client přeložit metodu MandelbrotRenderer.iteration. Povšimněte si, že na začátku výpisu je uvedeno, které registry jsou použity pro předávání parametrů do přeložené metody (tuto informaci jsme v předchozích výpisech neviděli, protože metoda ArrayTest3.test() žádné parametry neakceptovala):
Java HotSpot(TM) Client VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output Loaded disassembler from hsdis-i386.so Decoding compiled method 0x00a00bc8: Code: [Constants] 0x00a00cc0 (offset: 0): 0x00000000 0x4010000000000000 0x00a00cc4 (offset: 4): 0x40100000 0x00a00cc8 (offset: 8): 0x00000000 0x4000000000000000 0x00a00ccc (offset: 12): 0x40000000 [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] # {method} 'iteration' '(DDI)I' in 'MandelbrotRenderer' # parm0: xmm0:xmm0 = double # parm1: xmm1:xmm1 = double # parm2: ecx = int # [sp+0x20] (sp of caller) 0x00a00cd0: mov %eax,0xffffc000(%esp) ; {no_reloc} 0x00a00cd7: push %ebp 0x00a00cd8: sub $0x18,%esp ;*dconst_0 ; - MandelbrotRenderer::iteration@0 (line 40) 0x00a00cdb: mov $0x0,%eax 0x00a00ce0: xorpd %xmm2,%xmm2 0x00a00ce4: xorpd %xmm3,%xmm3 0x00a00ce8: jmp 0x00a00d47 ;*iload ; - MandelbrotRenderer::iteration@24 (line 44) 0x00a00ced: xchg %ax,%ax 0x00a00cf0: movsd %xmm3,%xmm4 0x00a00cf4: mulsd %xmm3,%xmm4 0x00a00cf8: movsd %xmm2,%xmm5 0x00a00cfc: mulsd %xmm2,%xmm5 0x00a00d00: movsd %xmm4,%xmm6 0x00a00d04: addsd %xmm5,%xmm6 0x00a00d08: movsd 0xa00cc0,%xmm7 ; {section_word} 0x00a00d10: ucomisd %xmm7,%xmm6 0x00a00d14: jp 0x00a00d20 0x00a00d1a: ja 0x00a00d4b ;*dcmpl ; - MandelbrotRenderer::iteration@53 (line 48) 0x00a00d20: mulsd 0xa00cc8,%xmm3 ; {section_word} 0x00a00d28: mulsd %xmm2,%xmm3 0x00a00d2c: addsd %xmm1,%xmm3 0x00a00d30: subsd %xmm5,%xmm4 0x00a00d34: addsd %xmm0,%xmm4 0x00a00d38: inc %eax ; OopMap{off=105} ;*goto ; - MandelbrotRenderer::iteration@87 (line 44) 0x00a00d39: test %eax,0x940100 ; {poll} 0x00a00d3f: movsd %xmm3,%xmm2 0x00a00d43: movsd %xmm4,%xmm3 ;*goto ; - MandelbrotRenderer::iteration@87 (line 44) 0x00a00d47: cmp %ecx,%eax 0x00a00d49: jl 0x00a00cf0 ;*if_icmpge ; - MandelbrotRenderer::iteration@28 (line 44) 0x00a00d4b: add $0x18,%esp 0x00a00d4e: pop %ebp 0x00a00d4f: test %eax,0x940100 ; {poll_return} 0x00a00d55: ret 0x00a00d56: nop 0x00a00d57: nop 0x00a00d58: mov %fs:0x0,%esi 0x00a00d60: mov 0xfffffff4(%esi),%esi 0x00a00d63: mov 0x188(%esi),%eax 0x00a00d69: movl $0x0,0x188(%esi) 0x00a00d73: movl $0x0,0x18c(%esi) 0x00a00d7d: add $0x18,%esp 0x00a00d80: pop %ebp 0x00a00d81: jmp 0x009abb80 ; {runtime_call} 0x00a00d86: hlt 0x00a00d87: hlt 0x00a00d88: hlt 0x00a00d89: hlt 0x00a00d8a: hlt 0x00a00d8b: hlt 0x00a00d8c: hlt 0x00a00d8d: hlt 0x00a00d8e: hlt 0x00a00d8f: hlt [Exception Handler] [Stub Code] 0x00a00d90: call 0x009ffb40 ; {no_reloc} 0x00a00d95: push $0x6eb63690 ; {external_word} 0x00a00d9a: call 0x00a00d9f 0x00a00d9f: pusha 0x00a00da0: call 0x6ea45f60 ; {runtime_call} 0x00a00da5: hlt [Deopt Handler Code] 0x00a00da6: push $0xa00da6 ; {section_word} 0x00a00dab: jmp 0x0099ca70 ; {runtime_call}
(opět zde můžeme vidět použití zdánlivě nelogické instrukce test %eax,0×940100). Registr EAX totiž obsahuje počitadlo iterací, které je %porovnáváno s registrem ECX obsahujícím předávaný maximální počet %iterací.
Výsledek překladu je možná poněkud překvapivý, protože jsou zde využity registry XMM0 až XMM7, které jsou dostupné v rozšíření instrukční sady SSE2. Fakt, že JIT překladač tyto instrukce generuje, byl zajištěn testem možností mikroprocesoru na začátku spuštění JVM. Poznámka: instrukce končící písmenem „D“ jsou prováděny s hodnotami typu double, pokud instrukce navíc končí dvojicí znaků „SD“, jedná se o skalární operace (SSE2 podporuje i vektorové operace).
V následující tabulce jsou vypsány některé instrukce použité v přeložené metodě:
# | Instrukce | Operace/funkce | Struktura vektoru | Datový typ | Saturace? | Poznámka |
---|---|---|---|---|---|---|
1 | movsd | přesun | 1×64bit | double | × | |
2 | addsd | součet | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
3 | subsd | rozdíl | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
4 | mulsd | součin | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
7. Generovaný kód v symbolickém formátu (C2 překladač)
Nikoho asi nepřekvapí, že JIT překladači typu server (C2) se podařilo poměrně elegantním způsobem celou programovou smyčku rozbalit a kód tak (zde dosti výrazným způsobem) urychlit:
Java HotSpot(TM) Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output Loaded disassembler from hsdis-i386.so Decoding compiled method 0x009b95c8: Code: [Constants] 0x009b96e0 (offset: 0): 0x00000000 0x4000000000000000 0x009b96e4 (offset: 4): 0x40000000 0x009b96e8 (offset: 8): 0x00000000 0x0000000000000000 0x009b96ec (offset: 12): 0x00000000 0x009b96f0 (offset: 16): 0x00000000 0x4010000000000000 0x009b96f4 (offset: 20): 0x40100000 0x009b96f8 (offset: 24): 0xf4f4f4f4 0xf4f4f4f4f4f4f4f4 0x009b96fc (offset: 28): 0xf4f4f4f4 [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] # {method} 'iteration' '(DDI)I' in 'MandelbrotRenderer' # parm0: xmm0:xmm0 = double # parm1: xmm1:xmm1 = double # parm2: ecx = int # [sp+0x10] (sp of caller) 0x009b9700: sub $0xc,%esp ; {no_reloc} 0x009b9706: mov %ebp,0x8(%esp) ;*synchronization entry ; - MandelbrotRenderer::iteration@-1 (line 40) 0x009b970a: test %ecx,%ecx 0x009b970c: jle 0x009b98da ;*if_icmpge ; - MandelbrotRenderer::iteration@28 (line 44) 0x009b9712: mov %ecx,%ebx 0x009b9714: add $0xfffffffd,%ebx 0x009b9717: mov $0x1,%eax 0x009b971c: movsd %xmm1,%xmm3 0x009b9720: addsd 0x9b96e8,%xmm3 ;*dadd ; - MandelbrotRenderer::iteration@71 (line 51) ; {section_word} 0x009b9728: movsd %xmm0,%xmm4 0x009b972c: addsd 0x9b96e8,%xmm4 ;*dadd ; - MandelbrotRenderer::iteration@81 (line 52) ; {section_word} 0x009b9734: movsd %xmm3,%xmm7 0x009b9738: mulsd %xmm3,%xmm7 ;*dmul ; - MandelbrotRenderer::iteration@42 (line 46) 0x009b973c: movsd %xmm4,%xmm5 0x009b9740: mulsd %xmm4,%xmm5 ;*dmul ; - MandelbrotRenderer::iteration@35 (line 45) 0x009b9744: movsd 0x9b96f0,%xmm6 ; {section_word} 0x009b974c: mov $0x80000000,%ebp 0x009b9751: cmp %ebx,%ecx 0x009b9753: cmovl %ebp,%ebx 0x009b9756: cmp $0x1,%ebx 0x009b9759: jg 0x009b9772 0x009b975b: mov $0x1,%edi 0x009b9760: xor %eax,%eax 0x009b9762: jmp 0x009b986a 0x009b9767: nopw 0x0(%eax,%eax,1) 0x009b9770: mov %edi,%eax ;*dload ; - MandelbrotRenderer::iteration@31 (line 45) 0x009b9772: movsd %xmm7,%xmm2 0x009b9776: addsd %xmm5,%xmm2 0x009b977a: ucomisd %xmm6,%xmm2 0x009b977e: ja 0x009b98cf ;*ifle ; - MandelbrotRenderer::iteration@54 (line 48) 0x009b9784: subsd %xmm7,%xmm5 0x009b9788: mulsd 0x9b96e0,%xmm4 ; {section_word} 0x009b9790: addsd %xmm0,%xmm5 ;*dadd ; - MandelbrotRenderer::iteration@81 (line 52) 0x009b9794: mulsd %xmm3,%xmm4 0x009b9798: movsd %xmm5,%xmm7 0x009b979c: mulsd %xmm5,%xmm7 ;*dmul ; - MandelbrotRenderer::iteration@35 (line 45) 0x009b97a0: addsd %xmm1,%xmm4 ;*dadd ; - MandelbrotRenderer::iteration@71 (line 51) 0x009b97a4: movsd %xmm4,%xmm2 0x009b97a8: mulsd %xmm4,%xmm2 ;*dmul ; - MandelbrotRenderer::iteration@42 (line 46) 0x009b97ac: movsd %xmm2,%xmm3 0x009b97b0: addsd %xmm7,%xmm3 0x009b97b4: ucomisd %xmm6,%xmm3 0x009b97b8: ja 0x009b98c4 ;*ifle ; - MandelbrotRenderer::iteration@54 (line 48) 0x009b97be: mulsd 0x9b96e0,%xmm5 ; {section_word} 0x009b97c6: subsd %xmm2,%xmm7 0x009b97ca: mulsd %xmm4,%xmm5 0x009b97ce: addsd %xmm0,%xmm7 ;*dadd ; - MandelbrotRenderer::iteration@81 (line 52) 0x009b97d2: addsd %xmm1,%xmm5 ;*dadd ; - MandelbrotRenderer::iteration@71 (line 51) 0x009b97d6: movsd %xmm7,%xmm3 0x009b97da: mulsd %xmm7,%xmm3 ;*dmul ; - MandelbrotRenderer::iteration@35 (line 45) 0x009b97de: movsd %xmm5,%xmm2 0x009b97e2: mulsd %xmm5,%xmm2 ;*dmul ; - MandelbrotRenderer::iteration@42 (line 46) 0x009b97e6: movsd %xmm2,%xmm4 0x009b97ea: addsd %xmm3,%xmm4 0x009b97ee: ucomisd %xmm6,%xmm4 0x009b97f2: ja 0x009b98c7 ;*ifle ; - MandelbrotRenderer::iteration@54 (line 48) 0x009b97f8: subsd %xmm2,%xmm3 0x009b97fc: mulsd 0x9b96e0,%xmm7 ; {section_word} 0x009b9804: addsd %xmm0,%xmm3 ;*dadd ; - MandelbrotRenderer::iteration@81 (line 52) 0x009b9808: mulsd %xmm5,%xmm7 0x009b980c: movsd %xmm3,%xmm4 0x009b9810: mulsd %xmm3,%xmm4 ;*dmul ; - MandelbrotRenderer::iteration@35 (line 45) 0x009b9814: addsd %xmm1,%xmm7 ;*dadd ; - MandelbrotRenderer::iteration@71 (line 51) 0x009b9818: movsd %xmm7,%xmm2 0x009b981c: mulsd %xmm7,%xmm2 ;*dmul ; - MandelbrotRenderer::iteration@42 (line 46) 0x009b9820: movsd %xmm4,%xmm5 0x009b9824: addsd %xmm2,%xmm5 0x009b9828: ucomisd %xmm6,%xmm5 0x009b982c: ja 0x009b98cc ;*ifle ; - MandelbrotRenderer::iteration@54 (line 48) 0x009b9832: subsd %xmm2,%xmm4 0x009b9836: mulsd 0x9b96e0,%xmm3 ; {section_word} 0x009b983e: addsd %xmm0,%xmm4 ;*dadd ; - MandelbrotRenderer::iteration@81 (line 52) 0x009b9842: mulsd %xmm7,%xmm3 0x009b9846: movsd %xmm4,%xmm5 0x009b984a: mulsd %xmm4,%xmm5 ;*dmul ; - MandelbrotRenderer::iteration@35 (line 45) 0x009b984e: addsd %xmm1,%xmm3 ;*dadd ; - MandelbrotRenderer::iteration@71 (line 51) 0x009b9852: movsd %xmm3,%xmm7 0x009b9856: mulsd %xmm3,%xmm7 ;*dmul ; - MandelbrotRenderer::iteration@42 (line 46) 0x009b985a: mov %eax,%edi 0x009b985c: add $0x4,%edi ;*iinc ; - MandelbrotRenderer::iteration@84 (line 44) 0x009b985f: cmp %ebx,%edi 0x009b9861: jl 0x009b9770 ;*if_icmpge ; - MandelbrotRenderer::iteration@28 (line 44) 0x009b9867: add $0x3,%eax ;*iinc ; - MandelbrotRenderer::iteration@84 (line 44) 0x009b986a: cmp %ecx,%edi 0x009b986c: jl 0x009b98aa 0x009b986e: mov %eax,%edi 0x009b9870: jmp 0x009b98bf 0x009b9872: xchg %ax,%ax 0x009b9874: subsd %xmm7,%xmm5 0x009b9878: movsd %xmm4,%xmm2 0x009b987c: mulsd 0x9b96e0,%xmm2 ; {section_word} 0x009b9884: movsd %xmm5,%xmm4 0x009b9888: addsd %xmm0,%xmm4 ;*dadd ; - MandelbrotRenderer::iteration@81 (line 52) 0x009b988c: mulsd %xmm3,%xmm2 0x009b9890: movsd %xmm4,%xmm5 0x009b9894: mulsd %xmm4,%xmm5 ;*dmul ; - MandelbrotRenderer::iteration@35 (line 45) 0x009b9898: movsd %xmm2,%xmm3 0x009b989c: addsd %xmm1,%xmm3 ;*dadd ; - MandelbrotRenderer::iteration@71 (line 51) 0x009b98a0: movsd %xmm3,%xmm7 0x009b98a4: mulsd %xmm3,%xmm7 ;*dmul ; - MandelbrotRenderer::iteration@42 (line 46) 0x009b98a8: mov %ebx,%edi ;*dload ; - MandelbrotRenderer::iteration@31 (line 45) 0x009b98aa: movsd %xmm5,%xmm2 0x009b98ae: addsd %xmm7,%xmm2 0x009b98b2: ucomisd %xmm6,%xmm2 0x009b98b6: ja 0x009b98de ;*ifle ; - MandelbrotRenderer::iteration@54 (line 48) 0x009b98b8: mov %edi,%ebx 0x009b98ba: inc %ebx ;*iinc ; - MandelbrotRenderer::iteration@84 (line 44) 0x009b98bb: cmp %ecx,%ebx 0x009b98bd: jl 0x009b9874 ;*if_icmpge ; - MandelbrotRenderer::iteration@28 (line 44) 0x009b98bf: mov %edi,%eax 0x009b98c1: inc %eax 0x009b98c2: jmp 0x009b98cf 0x009b98c4: inc %eax 0x009b98c5: jmp 0x009b98cf 0x009b98c7: add $0x2,%eax 0x009b98ca: jmp 0x009b98cf 0x009b98cc: add $0x3,%eax ;*goto ; - MandelbrotRenderer::iteration@57 (line 49) 0x009b98cf: add $0x8,%esp 0x009b98d2: pop %ebp 0x009b98d3: test %eax,0x940000 ; {poll_return} 0x009b98d9: ret 0x009b98da: xor %eax,%eax 0x009b98dc: jmp 0x009b98cf 0x009b98de: mov %edi,%eax 0x009b98e0: jmp 0x009b98cf 0x009b98e2: hlt 0x009b98e3: hlt 0x009b98e4: hlt 0x009b98e5: hlt 0x009b98e6: hlt 0x009b98e7: hlt 0x009b98e8: hlt 0x009b98e9: hlt 0x009b98ea: hlt 0x009b98eb: hlt 0x009b98ec: hlt 0x009b98ed: hlt 0x009b98ee: hlt 0x009b98ef: hlt 0x009b98f0: hlt 0x009b98f1: hlt 0x009b98f2: hlt 0x009b98f3: hlt 0x009b98f4: hlt 0x009b98f5: hlt 0x009b98f6: hlt 0x009b98f7: hlt 0x009b98f8: hlt 0x009b98f9: hlt 0x009b98fa: hlt 0x009b98fb: hlt 0x009b98fc: hlt 0x009b98fd: hlt 0x009b98fe: hlt 0x009b98ff: hlt [Exception Handler] [Stub Code] 0x009b9900: jmp 0x009b7d00 ; {no_reloc} [Deopt Handler Code] 0x009b9905: push $0x9b9905 ; {section_word} 0x009b990a: jmp 0x0099e280 ; {runtime_call} 0x009b990f: hlt Decoding compiled method 0x009b7dc8: Code: [Entry Point]
Instrukce htl (halt) na konci metody slouží především pro zarovnání strojového kódu.
8. Generovaný kód v symbolickém formátu (AArch64)
Na platformě ARM 64/AArch64 bude vygenerovaný strojový kód vypadat po svém disasemblování následovně:
Loaded disassembler from /home/pavel/jdk8/build/linux-aarch64-normal-client-slowdebug/images/j2sdk-image/jre/lib/aarch64/hsdis-aarch64.so Decoding compiled method 0x00007fcdf8cd8a10: Code: [Constants] 0x00007fcdf8cd8b60 (offset: 0): 0x00000000 0x4010000000000000 0x00007fcdf8cd8b64 (offset: 4): 0x40100000 0x00007fcdf8cd8b68 (offset: 8): 0x00000000 0x4000000000000000 0x00007fcdf8cd8b6c (offset: 12): 0x40000000 [Disassembling for mach='aarch64'] [Entry Point] [Verified Entry Point] # {method} {0x00007fcdfaed3770} 'iteration' '(DDI)I' in 'MandelbrotRenderer' # parm0: v0:v0 = double # parm1: v1:v1 = double # parm2: c_rarg1 = int # [sp+0x50] (sp of caller) ;; block B6 [0, 0] ;; 0xFFFFFFFFFFFF7000 0x00007fcdf8cd8b70: movn xscratch2, #0x8fff ; {no_reloc} 0x00007fcdf8cd8b74: ldr xzr, [sp,x9] 0x00007fcdf8cd8b78: stp xfp, xlr, [sp,#-16]! 0x00007fcdf8cd8b7c: mov xfp, sp 0x00007fcdf8cd8b80: sub sp, sp, #0x40 0x00007fcdf8cd8b84: notify entry ;*dconst_0 ; - MandelbrotRenderer::iteration@0 (line 40) ;; block B0 [0, 24] ;; 0x0 0x00007fcdf8cd8b88: movz w0, #0x0, lsl #16 0x00007fcdf8cd8b8c: fmov d2, xzr 0x00007fcdf8cd8b90: fmov d3, xzr ;; 20 branch [AL] [B1] 0x00007fcdf8cd8b94: b 0x00007fcdf8cd8bdc ;*iload ; - MandelbrotRenderer::iteration@24 (line 44) ;; block B2 [31, 53] 0x00007fcdf8cd8b98: fmul d4, d3, d3 0x00007fcdf8cd8b9c: fmul d5, d2, d2 0x00007fcdf8cd8ba0: fadd d6, d4, d5 0x00007fcdf8cd8ba4: adr xscratch1, 0x00007fcdf8cd8b60 ; {section_word} 0x00007fcdf8cd8ba8: ldr d7, [xscratch1] 0x00007fcdf8cd8bac: fcmp d6, d7 0x00007fcdf8cd8bb0: b.gt 0x00007fcdf8cd8be4 ;*dcmpl ; - MandelbrotRenderer::iteration@53 (line 48) ;; block B5 [60, 87] 0x00007fcdf8cd8bb4: adr xscratch1, 0x00007fcdf8cd8b68 ; {section_word} 0x00007fcdf8cd8bb8: ldr d6, [xscratch1] 0x00007fcdf8cd8bbc: fmul d3, d3, d6 0x00007fcdf8cd8bc0: fmul d2, d3, d2 0x00007fcdf8cd8bc4: fadd d2, d2, d1 0x00007fcdf8cd8bc8: fsub d3, d4, d5 0x00007fcdf8cd8bcc: fadd d3, d3, d0 0x00007fcdf8cd8bd0: add w0, w0, #0x1 0x00007fcdf8cd8bd4: adrp xscratch1, 0x00007fcdfcf5c000 ; OopMap{off=104} ;*goto ; - MandelbrotRenderer::iteration@87 (line 44) ; {poll} 0x00007fcdf8cd8bd8: ldr wzr, [xscratch1,#256] ;*goto ; - MandelbrotRenderer::iteration@87 (line 44) ; {poll} ;; block B1 [24, 28] 0x00007fcdf8cd8bdc: cmp w0, w1 ;; 26 branch [LT] [B2] 0x00007fcdf8cd8be0: b.lt 0x00007fcdf8cd8b98 ;*if_icmpge ; - MandelbrotRenderer::iteration@28 (line 44) ;; block B3 [90, 92] 0x00007fcdf8cd8be4: add sp, sp, #0x40 0x00007fcdf8cd8be8: ldp xfp, xlr, [sp],#16 0x00007fcdf8cd8bec: notify reentry 0x00007fcdf8cd8bf0: adrp xscratch1, 0x00007fcdfcf5c000 ; {poll_return} 0x00007fcdf8cd8bf4: ldr wzr, [xscratch1,#256] ; {poll_return} 0x00007fcdf8cd8bf8: ret 0x00007fcdf8cd8bfc: nop 0x00007fcdf8cd8c00: nop ;; Unwind handler 0x00007fcdf8cd8c04: ldr x0, [xthread,#704] 0x00007fcdf8cd8c08: str xzr, [xthread,#704] 0x00007fcdf8cd8c0c: str xzr, [xthread,#712] ;; remove_frame and dispatch to the unwind handler 0x00007fcdf8cd8c10: add sp, sp, #0x40 0x00007fcdf8cd8c14: ldp xfp, xlr, [sp],#16 0x00007fcdf8cd8c18: notify reentry 0x00007fcdf8cd8c1c: b 0x00007fcdf8cd16e0 ; {runtime_call} [Exception Handler] [Stub Code] 0x00007fcdf8cd8c20: movz x19, #0xdead ; {no_reloc} 0x00007fcdf8cd8c24: movz x2, #0xa 0x00007fcdf8cd8c28: movz x4, #0xdead 0x00007fcdf8cd8c2c: movz x5, #0xdead 0x00007fcdf8cd8c30: bl 0x00007fcdf8cd5b20 ; {runtime_call} 0x00007fcdf8cd8c34: stp xlr, xzr, [sp,#-16]! 0x00007fcdf8cd8c38: stp xthread, xfp, [sp,#-16]! 0x00007fcdf8cd8c3c: stp xcpool, xheapbase, [sp,#-16]! 0x00007fcdf8cd8c40: stp xlocals, xmonitors, [sp,#-16]! 0x00007fcdf8cd8c44: stp xbcp, x23, [sp,#-16]! 0x00007fcdf8cd8c48: stp xesp, xdispatch, [sp,#-16]! 0x00007fcdf8cd8c4c: stp x18, x19, [sp,#-16]! 0x00007fcdf8cd8c50: stp x16, x17, [sp,#-16]! 0x00007fcdf8cd8c54: stp x14, x15, [sp,#-16]! 0x00007fcdf8cd8c58: stp xmethod, x13, [sp,#-16]! 0x00007fcdf8cd8c5c: stp x10, x11, [sp,#-16]! 0x00007fcdf8cd8c60: stp xscratch1, xscratch2, [sp,#-16]! 0x00007fcdf8cd8c64: stp x6, x7, [sp,#-16]! 0x00007fcdf8cd8c68: stp x4, x5, [sp,#-16]! 0x00007fcdf8cd8c6c: stp x2, x3, [sp,#-16]! 0x00007fcdf8cd8c70: stp x0, x1, [sp,#-16]! 0x00007fcdf8cd8c74: movz x0, #0x50aa 0x00007fcdf8cd8c78: movk x0, #0xfc6d, lsl #16 0x00007fcdf8cd8c7c: movk x0, #0x7fcd, lsl #32 0x00007fcdf8cd8c80: movz x1, #0x4324 0x00007fcdf8cd8c84: movk x1, #0xf8cc, lsl #16 0x00007fcdf8cd8c88: movk x1, #0x7fcd, lsl #32 0x00007fcdf8cd8c8c: mov x2, sp 0x00007fcdf8cd8c90: movz x3, #0x88e8 0x00007fcdf8cd8c94: movk x3, #0xfc4d, lsl #16 0x00007fcdf8cd8c98: movk x3, #0x7fcd, lsl #32 0x00007fcdf8cd8c9c: brx86 x3, 3, 0, 1 0x00007fcdf8cd8ca0: hlt #0x0 [Deopt Handler Code] 0x00007fcdf8cd8ca4: adr xlr, 0x00007fcdf8cd8ca4 0x00007fcdf8cd8ca8: b 0x00007fcdf8c1bcb0 ; {runtime_call} 0x00007fcdf8cd8cac: .inst 0x00000000 ; undefined
V tomto případě se masivně využívají instrukce pro práci s hodnotami typu double. Tyto instrukce mají na začátku svého jména písmeno „f“ (fadd, fmul, fcmp) a pracují s registry d0 až d31 (kam se hrabe platforma x86 :-). Podrobnosti si v případě zájmu můžeme říci příště.
9. Obsah následující části seriálu
V následující části tohoto seriálu odbočíme od problematiky assembleru a disassembleru, protože se budeme zabývat problematikou synchronizace v javovských aplikacích. Mimo jiné si řekneme, jaké vážné problémy může způsobit použití atributů s modifikátorem volatile, především v těch případech, pokud je javovská aplikace provozována na 32bitovém systému, popř. na počítači s větším počtem jader (obě zmíněné varianty pravděpodobně zahrnují více než 90% všech případů použití Javy :-).
10. Repositář se zdrojovými kódy všech demonstračních i testovacích příkladů
Následuje – v tomto seriálu již tradiční – kapitola s odkazy na zdrojové kódy. V následující tabulce najdete odkazy na prozatím nejnovější verze obou dnes použitých benchmarků:
http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/05d05b5fd08d/jit/MandelbrotRenderer.javahttp://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/05d05b5fd08d/jit/mandel.sh# | Zdrojový soubor/skript | Umístění souboru v repositáři |
---|---|---|
1 | ArrayTest3.java | http://icedtea.classpath.org/people/ptisnovs/jvm-tools/file/83f13c381bc0/jit/ArrayTest3.java |
2 | MandelbrotRenderer.java | |
3 | mandel.sh |
11. Odkazy na Internetu
- Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html