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
