Obsah
2. Specifika překladu pro různé mikroprocesorové architektury
6. Demonstrační příklad: jednoduchá programová smyčka typu while
7. Překlad demonstračního příkladu do nativního kódu
7.1 Překlad pro architekturu x86 (32 bit)
7.2 Překlad pro architekturu x86_64 (64 bit)
7.3 Překlad pro architekturu ARM (32 bit)
1. LuaJIT – Just in Time překladač pro programovací jazyk Lua (11 – JIT překlad do nativního kódu procesorů s architekturami x86 a ARM)
V dnešní části seriálu o trasovacím just-in-time překladači LuaJIT se budeme zabývat převážně způsobem transformace pseudokódu do nativního (strojového) kódu zvoleného typu mikroprocesoru. Připomeňme si, že se jedná o třetí překlad (či možná lépe řečeno transformaci) prováděný LuaJITem. První překlad je aplikován na veškerý spouštěný kód: zdrojový text napsaný v Lue je překládán do bajtkódu ([1], [2], [3], [4], [5] a [6]).
Tento bajtkód je následně interpretován a teprve ve chvíli, kdy se zjistí, že nějakou část bajtkódu lze považovat za hot loop či hot call, je spuštěn vlastní just-in-time překlad, který detekované „stopy“ (trace) transformuje do mezikódu a následně pak do nativního kódu kompatibilního s aktuálně používaným mikroprocesorem.
2. Specifika překladu pro různé mikroprocesorové architektury
LuaJIT při překladu do nativního kódu využívá některé optimalizační techniky, ovšem velmi důležitý je i fakt, že se snaží o zjištění, zda se v proměnných (a výrazech, programových smyčkách apod.) při práci s numerickými hodnotami používají celá čísla (integer) či čísla reálná (float/double). V programovacím jazyku Lua, přesněji řečeno v jeho doposud používaných verzích, se totiž předpokládá, že numerické hodnoty jsou typu double, což je z hlediska vysokoúrovňového skriptovacího jazyka pochopitelné (omezí se tím poměrně velký počet běhových chyb typu přetečení apod.).
Na druhou stranu ovšem naprostá většina v současnosti používaných mikroprocesorových architektur je postavena na bázi celočíselné aritmeticko-logické jednotky (ALU), která je sice (většinou!) doplněna matematickým koprocesorem, ovšem pro některé operace, typicky pro implementace programových smyček atd., je výhodnější využívat celočíselnou ALU a taktéž celočíselné pracovní registry. Detekce těch proměnných, v nichž se pracuje jen s celými čísly, je tedy pro LuaJIT důležitá, především na architektuře ARM se soft-float. LuaJIT ovšem musí počítat s tím, že může dojít k přetečení, kterému se musí zabránit a tedy tiše a zejména bez ovlivnění běhu aplikace přejít z celočíselného typu int na double.
3. x86 (32 bit)
Na 32bitové platformě x86 se využívají jak celočíselné registry EAX, EBX, ECX, EDX, ESI, EDI a částečně i EBP, tak i registry přidané v rámci rozšiřující instrukční sady SSE. Nová sada registrů je pojmenovaná XMM0 až XMM7. Všechny nové registry mají šířku 128 bitů, tj. jsou dvakrát širší, než registry používané v MMX i 3DNow! a čtyřikrát širší, než běžné pracovní registry na platformě x86 (bavíme se nyní o 32bitovém režimu). Do každého registru je možné uložit čtveřici reálných numerických hodnot reprezentovaných v systému plovoucí řádové tečky podle normy IEEE 754 (single), přičemž tato norma je dodržována přesněji, než v případě 3DNow! (různé zaokrouhlovací režimy či práce s denormalizovanými čísly sice mohou vypadat trošku jako černá magie, ovšem například v knihovnách pro numerické výpočty, které musí vždy za specifikovaných okolností dát stejný výsledek, se jedná o velmi důležitou vlastnost). K osmi novým registrům XMM* byl ještě přidán jeden 32bitový registr nazvaný MXCSR, jenž byl určený pro nastavení (řízení) režimů výpočtu.
Ve skutečnosti je však většina instrukcí SSE pro jazyk Lua nepoužitelná :-), protože tyto instrukce pracují pouze s numerickými hodnotami typu single. Podpora pro hodnoty typu double se objevuje až v rozšiřující instrukční sadě SSE2. V rámci SSE2 totiž byly zavedeny dvouprvkové vektory obsahující hodnoty reprezentované ve formátu plovoucí řádové čárky, ovšem tentokrát se jedná o čísla uložená v 64 bitech (2×64=128) odpovídající dvojité přesnosti (double) z normy IEEE 754. LuaJIT SSE2 dokáže velmi efektivně využít, což ostatně uvidíme i na způsobu překladu demonstračního příkladu. Všechny nové operace implementované v SSE2 jsou vypsány v následující tabulce. Ve třetím sloupci je naznačeno, jaké vektory jsou danou operací zpracovávány, přičemž první číslo znamená počet prvků vektoru, za nímž následuje bitová šířka jednotlivých prvků:
# | Instrukce | Operace/funkce | Struktura vektoru | Datový typ | Saturace? | Poznámka |
---|---|---|---|---|---|---|
1 | addpd | součet | 2×64bit | double | × | |
2 | addsd | součet | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
3 | subpd | rozdíl | 2×64bit | double | × | |
4 | subsd | rozdíl | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
5 | mulpd | součin | 2×64bit | double | × | |
6 | mulsd | součin | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
7 | divpd | podíl | 2×64bit | double | × | |
8 | divsd | podíl | 1×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
9 | paddb | součet | 16×8bit | integer | ne | |
10 | paddw | součet | 8×16bit | integer | ne | |
11 | paddd | součet | 4×32bit | integer | ne | |
12 | paddq | součet | 2×64bit | integer | ne | |
13 | paddsb | součet | 16×8bit | integer | ano | |
14 | paddsw | součet | 8×16bit | integer | ano | |
15 | paddusb | součet | 16×8bit | unsigned | ano | |
16 | paddusw | součet | 8×16bit | unsigned | ano | |
17 | psubb | rozdíl | 16×8bit | integer | ne | |
18 | psubw | rozdíl | 8×16bit | integer | ne | |
19 | psubd | rozdíl | 4×32bit | integer | ne | |
20 | psubq | rozdíl | 2×64bit | integer | ne | |
21 | psubsb | rozdíl | 16×8bit | integer | ano | |
22 | psubsw | rozdíl | 8×16bit | integer | ano | |
23 | psubusb | rozdíl | 16×8bit | unsigned | ano | |
24 | psubusw | rozdíl | 8×16bit | unsigned | ano | |
25 | maxpd | maximum | 2×64bit | double | × | |
26 | maxsd | maximum | 2×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
27 | minpd | minimum | 2×64bit | double | × | |
28 | minsd | minimum | 2×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
29 | pmaddwd | součin/add | 8×16bit | integer | × | |
30 | pmulhw | součin | 8×16bit | integer | × | vrací vektor horních 16 bitů výsledků |
31 | pmullw | součin | 8×16bit | integer | × | vrací vektor dolních 16 bitů výsledků |
32 | pmuludq | součin | 4×32bit | integer | × | 64 bitový výsledek pro každý součin |
33 | rcpps | převrácená hodnota | 4×32bit | single | × | aproximace |
34 | rcpss | převrácená hodnota | 4×32bit | single | × | operace provedena jen s pravým prvkem vektorů |
35 | sqrtpd | druhá odmocnina | 2×64bit | double | × | |
36 | sqrtsd | druhá odmocnina | 2×64bit | double | × | operace provedena jen s pravým prvkem vektorů |
4. x86_64 (64 bit)
Pokud LuaJIT zjistí, že je provozován na mikroprocesoru, který podporuje 64bitovou instrukční sadu x86_64 (x64) a současně běžícího v 64bitovém režimu, dokáže při just-in-time překladu využít všech šestnáct univerzálních pracovních registrů (namísto původních osmi registrů), v nichž lze navíc pracovat s 64bitovými hodnotami a nejenom s hodnotami 32bitovými. Možná důležitější je však fakt, že i počet XMM* registrů používaných v SSE/SSE2 se zdvojnásobil z osmi (XMM0-XMM7) na šestnáct, takže lineární alokátor registrů zde má poněkud snazší práci. V mnoha dalších ohledech se však nativní kód určený pro 32bitový režim a 64bitový režim lišit nebude, alespoň u těch demonstračních příkladů, které si budeme ukazovat v navazujících kapitolách popř. i v následujícím pokračování tohoto seriálu.
5. ARM (32 bit)
V případě procesorů z rodiny ARM může LuaJIT, na základě konkrétní konfigurace a možností daného procesoru, využívat soft-float ABI a soft-float operace s numerickými hodnotami typu double (což je nejpomalejší varianta), dále pak soft-float ABI a operace VFPv2 popř. hard-float ABI a taktéž operace VFPv2. Kdykoli je to možné – například u mnoha typů programových smyček – provádí se operace s celočíselnými registry a tím pádem se tyto výpočty odehrávají v ALU procesorů ARM. Význam některých „celočíselných“ instrukcí použitých v nativním kódu dále popsaného demonstračního příkladu je uveden v následující tabulce:
# | Instrukce | Význam |
---|---|---|
1 | adds | r0:=r1+r2 a nastav příznaky N, V, Z, C |
2 | cmp | operand1-operand2 (compare a nastav příznaky N, V, Z, C) |
3 | cmn | operand1+operand2 (compare negative a nastav příznaky N, V, Z, C) |
4 | blne | podmíněný skok provedený při nerovnosti (Z=0) |
5 | blvs | podmíněný skok provedený při přetečení (V=1) |
6 | blge | podmíněný skok provedený při N=V |
7 | blt | podmíněný skok provedený při N!=V |
8 | bl | skok do subrutiny |
9 | ldrd | načtení hodnoty do registru |
Vraťme se nyní na chvíli k numerickým hodnotám s plovoucí řádovou čárkou. Technologie VFP byla navržena takovým způsobem, aby ji bylo možné použít v mnoha aplikačních oblastech, například v řídicích jednotkách automobilů, pro zpracování obrazu (konvoluční filtry, rychlá Fourierova transformace, rasterizace a další operace prováděné v tiskových procesorech), při zpracování řeči (kodeky) a taktéž pro provádění různých 3D operací (transformace) – právě v těchto oblastech lze totiž využít práci nikoli pouze se skalárními hodnotami, ale taktéž s vektory o dvou až osmi prvcích. Zajímavé je, že později došlo ke sloučení VFP s architekturou NEON.
Původní architektura VFPv1 je již dnes považována za překonanou a v žádných současných čipech se s níž již nesetkáme. Druhá verze této architektury VFPv2 začala být používána na některých čipech ARMv5E, ARMv5TEJ a taktéž na ARMv6 – instrukce VFP v tomto případě rozšiřovaly původní instrukční sady ARM. Zajímavější je dnes třetí verze architektury VFP značená VFPv3 používaná od ARMv7 (samozřejmě jen u vybraných čipů – zdaleka ne všechny aplikační oblasti totiž nutně vyžadují matematický koprocesor). V tomto případě lze nové „vektorové“ instrukce používat v instrukční sadě ARM, Thumb i ThumbEE.
Z hlediska programovacího jazyka Lua je důležité, že se u technologie VFP používají především formáty single/float a double, přičemž existují rozšíření i pro formáty s poloviční přesností (half-float), které lze v některých oblastech s výhodou používat, například pro ukládání barvových složek pixelů (zvýší se tím mj. i dynamický rozsah při filtraci obrazu). Vzhledem k tomu, že technologie VFP je určena i pro aplikaci v systémech, v nichž je mnohdy důležité dosáhnout co největšího výpočetního výkonu popř. co nejkratší doby odezvy (RT aplikace), může matematický koprocesor VFP pracovat buď v režimu full compliance, který je přímo kompatibilní s normou IEEE 754, popř. je možné provést přepnutí do režimu RunFast, v němž se negenerují některé výjimky a taktéž může dojít ke ztrátě přesnosti v nejnižších bitech mantisy (většinou pouze v bitu nejnižším). Vlastnosti obou režimů jsou samozřejmě velmi přesně popsány, takže záleží jen na vývojáři, který režim v daný okamžik použije – v případě jazyka Lua to bude typ double a pravděpodobně i režim full compliance.
Matematické koprocesory VFP obecně obsahují šestnáct pracovních registrů, každý o šířce 64 bitů. Tyto registry lze použít buď pro práci s hodnotami s dvojitou přesností (double) – potom se tyto registry v assembleru označují jmény d0 až d15 (podobně jsou označeny ve zdrojových kódech LuaJITu, akorát s velkými písmeny). Ovšem taktéž je možné libovolný registr rozdělit na dva registry o šířce 32 bitů, z nichž každý dokáže pojmout číselnou hodnotu s jednoduchou přesností (single/float). Díky tomuto rozdělení se počet registrů pro formát single zvětšil na dvojnásobek – tyto registry jsou v assembleru pojmenovány s0 až s31. Podle konvence dodržované jak překladači, tak i v programových knihovnách se při volání subrutin používají registry d0 až d7 pro předávání parametrů subrutině, popř. pro získání návratových hodnot ze subrutiny. Samozřejmě se tyto registry taktéž používají při výpočtech v subrutině. Ostatní registry lze taktéž použít, ovšem jejich hodnota by měla být při návratu ze subrutiny obnovena.
Při vzniku technologie VFP se její tvůrci zaměřili na to, aby instrukční sada VFP umožňovala jak práci se skalárními hodnotami, tak i práci s vektory. Na první pohled by se tedy mohlo zdát, že se jedná o jednu z mnoha aplikací architektury SIMD, ve skutečnosti se ovšem v případě VFP vektory zpracovávají sekvenčně. To například znamená, že součet dvou osmiprvkových vektorů realizovaný instrukcí VADD používá tu samou FP-sčítačku a prvky vektorů jsou tedy sečítány postupně. Stále se jedná o rychlejší operaci, než osm krát opakovaná instrukce ADF (mimo jiné se ušetří cykly strávené při načítání a dekódování instrukce – instruction fetch a instruction decode) – ovšem reálný SIMD systém to není, na rozdíl od na první pohled obdobných technologií: 3DNow!, SSE atd. Změna přišla až při sloučení VFP s technologií NEON, která na procesory ARM přinesla skutečné operace SIMD.
6. Demonstrační příklad: jednoduchá programová smyčka typu while
Pro ukázku překladu z mezikódu do nativního kódu bude použit demonstrační příklad, s nímž jsme se již ve stručnosti seznámili minule. V tomto příkladu je implementována programová smyčka typu while, v níž se pracuje s dvojicí proměnných – počitadla i a taktéž proměnné sum. Po provedení 100 iterací je vypsána aktuální hodnota proměnné sum:
-- -- LuaJIT: demonstrační příklad číslo 43. -- -- Test JITu. -- local i = 0 local sum = 0 while i < 100 do sum = sum + 1 i = i + 1 end print(sum) -- -- Finito. --
Tento typ programové smyčky je díky provedení 100 iterací detekován jako stopa vhodná pro JITování (hot loop). Struktura bajtkódu detekované stopy používá instrukci LOOP, která je doplněna o ISGE:
---- TRACE 1 start test43.lua:12 0007 ADDVN 1 1 0 ; 1 0008 ADDVN 0 0 0 ; 1 0009 JMP 2 => 0003 0003 KSHORT 2 100 0004 ISGE 0 2 0005 JMP 2 => 0010 0006 LOOP 2 => 0010
Transformace bajtkódu do sekvence pseudoinstrukcí:
---- TRACE 1 IR 0001 > int SLOAD #2 T 0002 >+ int ADDOV 0001 +1 0003 > int SLOAD #1 T 0004 >+ int ADDOV 0003 +1 0005 > int LT 0004 +100 0006 ------ LOOP ------------ 0007 >+ int ADDOV 0002 +1 0008 >+ int ADDOV 0004 +1 0009 > int LT 0008 +100 0010 int PHI 0002 0007 0011 int PHI 0004 0008 ---- TRACE 1 stop -> loop
Povšimněte si především použití datového typu int a taktéž využití pseudoinstrukcí ADDOV. LuaJIT předpokládá, že jak proměnná i, tak i proměnná sum bude obsahovat celá čísla typu int, což je sice pravda, ovšem při zvyšování hodnoty těchto proměnných může dojít k přetečení, kterému je zapotřebí zamezit. V následujících podkapitolách uvidíme, že se tyto pseudoinstrukce v některých případech překládají poměrně složitým způsobem, a to kvůli tomu, aby se detekovalo již zmíněné přetečení výsledku a přechod na použití jiného datového typu.
7. Překlad demonstračního příkladu do nativního kódu
Pojďme se nyní podívat na překlad demonstračního příkladu just-in-time překladačem do nativního kódu procesorů s architekturou x86 (32bit), x86_64 (64bit) a ARM (32bit).
7.1 Překlad pro architekturu x86 (32 bit)
Pro 32bitovou platformu x86 se detekovaný hot loop přeloží následujícím způsobem:
---- TRACE 1 mcode 95 0xb7f68f97 mov dword [0xb7d9f2bc], 0x1 0xb7f68fa1 movsd xmm1, [0xb7db1bb0] 0xb7f68fa9 movsd xmm0, [0xb7db1bb8] 0xb7f68fb1 cmp dword [edx+0xc], -0x0f 0xb7f68fb5 jnb 0xb7f61008 ->0 0xb7f68fbb movsd xmm6, [edx+0x8] 0xb7f68fc0 addsd xmm6, xmm1 0xb7f68fc4 cmp dword [edx+0x4], -0x0f 0xb7f68fc8 jnb 0xb7f61008 ->0 0xb7f68fce movsd xmm7, [edx] 0xb7f68fd2 addsd xmm7, xmm1 0xb7f68fd6 ucomisd xmm0, xmm7 0xb7f68fda jbe 0xb7f6100c ->1 ->LOOP: 0xb7f68fe0 addsd xmm6, xmm1 ; registr xmm1 obsahuje jedničku, která se přičte k obsahu xmm6 0xb7f68fe4 movaps xmm5, xmm7 0xb7f68fe7 addsd xmm7, xmm1 ; další přičtení jedničky 0xb7f68feb ucomisd xmm0, xmm7 ; test, zda nemá dojít k ukončení smyčky 0xb7f68fef ja 0xb7f68fe0 ->LOOP ; opakování smyčky 0xb7f68ff1 jmp 0xb7f61014 ->3 ---- TRACE 1 stop -> loop
Můžeme zde vidět několik podmíněných skoků (jnb – jump if not below, jbe – jump if below or equal) a ja – jump if above s podobným významem jako v předchozím nativním kódu.
Z instrukcí, které nebyly vypsány ve třetí kapitole stojí za zmínku movaps (přenos celého 128bitového slova mezi dvěma XMM registry) a ucomisd (porovnání obsahu spodní poloviny dvou registrů a nastavení příznaků v EFLAGS podobným způsobem, jako u instrukce cmp – zde se však porovnávají dvě hodnoty typu double). Vidíme, že všechny výpočty probíhají v registrech XMM a využívá se zde instrukční sada SSE2, což se pozná podle toho, že instrukce končí na písmeno d – double.
7.2 Překlad pro architekturu x86_64 (64 bit)
Pro platformu x86_64 dostaneme v detailech nepatrně odlišný „stroják“:
---- TRACE 1 mcode 104 0x0bceff8e mov dword [0x409604a0], 0x1 0x0bceff99 movsd xmm1, [0x404b0d38] 0x0bceffa2 movsd xmm0, [0x404b0d40] 0x0bceffab cmp dword [rdx+0xc], 0xfffeffff 0x0bceffb2 jnb 0x0bce0010 ->0 0x0bceffb8 movsd xmm6, [rdx+0x8] 0x0bceffbd addsd xmm6, xmm1 0x0bceffc1 cmp dword [rdx+0x4], 0xfffeffff 0x0bceffc8 jnb 0x0bce0010 ->0 0x0bceffce movsd xmm7, [rdx] 0x0bceffd2 addsd xmm7, xmm1 0x0bceffd6 ucomisd xmm0, xmm7 0x0bceffda jbe 0x0bce0014 ->1 ->LOOP: 0x0bceffe0 addsd xmm6, xmm1 ; registr xmm1 obsahuje jedničku, která se přičte k obsahu xmm6 0x0bceffe4 movaps xmm5, xmm7 0x0bceffe7 addsd xmm7, xmm1 ; další přičtení jedničky 0x0bceffeb ucomisd xmm0, xmm7 ; test, zda nemá dojít k ukončení smyčky 0x0bceffef ja 0x0bceffe0 ->LOOP ; opakování smyčky 0x0bcefff1 jmp 0x0bce001c ->3 ---- TRACE 1 stop -> loop
V tomto jednoduchém demonstračním příkladu se od sebe oba vygenerované kódy na úrovni zdrojových kódů prakticky neliší, až na použití odlišných registrů: EDX vs. RDX. Programová smyčka typu while dokonce byla přeložena totožným způsobem:
x86 (32bit) x86_64 (64bit) ----------------------------------------------------------- addsd xmm6, xmm1 addsd xmm6, xmm1 movaps xmm5, xmm7 movaps xmm5, xmm7 addsd xmm7, xmm1 addsd xmm7, xmm1 ucomisd xmm0, xmm7 ucomisd xmm0, xmm7 ja 0xb7f68fe0 ->LOOP ja 0x0bceffe0 ->LOOP
Ve skutečnosti se dá toto chování očekávat, protože část LuaJITu, která se stará o překlad na 32bitovou a 64bitovou architekturu, je prakticky shodná.
7.3 Překlad pro architekturu ARM (32 bit)
Překlad do nativního strojového kódu pro 32bitové mikroprocesory ARM vypadá již na první pohled velmi čitelně (což jen ukazuje, jak může být RISCová instrukční sada elegantní):
---- TRACE 1 start test43.lua:12 ---- TRACE 1 mcode 84 0x00397fac ldrd r4, [r9, #8] 0x00397fb0 cmn r5, #14 0x00397fb4 blne 0x00390018 ->0 0x00397fb8 adds r4, r4, #1 0x00397fbc blvs 0x00390018 ->0 0x00397fc0 ldrd r6, [r9] 0x00397fc4 cmn r7, #14 0x00397fc8 blne 0x00390018 ->0 0x00397fcc adds r6, r6, #1 0x00397fd0 blvs 0x00390018 ->0 0x00397fd4 cmp r6, #100 0x00397fd8 blge 0x0039001c ->1 ->LOOP: 0x00397fdc mov r11, r6 ; přenos hodnot proměnných do pracovních registrů 0x00397fe0 mov r10, r4 0x00397fe4 adds r4, r10, #1 ; přičtení jedničky s uložením výsledku do nového registru a nastavením příznaků 0x00397fe8 blvs 0x00390020 ->2 ; test na přetečení 0x00397fec adds r6, r11, #1 ; přičtení jedničky s uložením výsledku do nového registru a nastavením příznaků 0x00397ff0 blvs 0x00390020 ->2 ; test na přetečení 0x00397ff4 cmp r6, #100 ; test na ukončení smyčky 0x00397ff8 blt 0x00397fdc ->LOOP ; skok na začátek smyčky - další iterace 0x00397ffc bl 0x00390024 ->3 ---- TRACE 1 stop -> loop
Povšimněte si především množství podmíněných skoků (blne, blvs), kterými jsou realizovány asserce zmíněné v předchozím textu. Dále stojí za zmínku použití instrukcí pracujících pouze s celočíselnými registry – v tomto případě LuaJIT zvolil použití operací s celými čísly typu integer o šířce 32 bitů. To si vyžádalo úpravu programové smyčky takovým způsobem, aby se kontrolovalo přetečení (instrukce adds nastaví všechny potřebné příznaky testované ihned poté podmíněným skokem blvs). Pokud by měl LuaJIT jistotu, že k přetečení nedojde, mohly by se instrukce adds nahradit za add a skoky blvs zcela odstranit (snad se dočkáme v další verzi).
8. Literatura
- Bolz, Cuni, Fijalkowski, Rigo:
„Tracing the Meta-Level: PyPy's Tracing JIT Compiler“ - Vasanth Bala, Evelyn Duesterwald, Sanjeev Banerjia:
„Dynamo: A Transparent Dynamic Optimization System“ - Bolz, Cuni, Fijalkowski, Leuschel, Pedroni, Rigo: „Allocation removal by partial evaluation in a tracing JIT“
- Bolz:
„Automatic JIT Compiler Generation with Runtime Partial Evaluation“ - Bolz, Kuhn, Lienhard, Matsakis, Nierstrasz, Renggli, Rigo and T. Verwaest:
„Back to the Future in One Week – Implementing a Smalltalk VM in PyPy“
pages 123–139. 2008. - Bolz and Rigo:
„How to not write a virtual machine“
In Proceedings of the 3rd Workshop on Dynamic Languages and Applications (DYLA), 2007 - Bruni, Verwaest:
„PyGirl: generating Whole-System VMs from High-Level prototypes using PyPy“
In Tools, accepted for publication, 2009. - Sullivan, Bruening, Baron, Garnett and Amarasinghe:
„Dynamic native optimization of interpreters“
In Proceedings of the 2003 Workshop on Interpreters,
Virtual Machines and Emulators pages 50–57, San Diego, California, 2003. ACM.
9. Odkazy na Internetu
- Static single assignment form (SSA)
http://en.wikipedia.org/wiki/Static_single_assignment_form - LuaJIT 2.0 SSA IR
http://wiki.luajit.org/SSA-IR-2.0 - Dynamic Assembler
http://luajit.org/dynasm.html - The Unofficial DynASM Documentation: Introduction
http://corsix.github.io/dynasm-doc/index.html - Have tracing JIT compilers won?
http://lambda-the-ultimate.org/node/3851 - Tracing just-in-time compilation
http://en.wikipedia.org/wiki/Tracing_just-in-time_compilation - How does LuaJIT's trace compiler work?
http://www.freelists.org/post/luajit/How-does-LuaJITs-trace-compiler-work,1 - How does LuaJIT's trace compiler work?
http://stackoverflow.com/questions/20266523/how-does-luajits-trace-compiler-work - TraceMonkey
https://wiki.mozilla.org/JavaScript:TraceMonkey - TraceMonkey
http://brendaneich.com/2008/08/tracemonkey-javascript-lightspeed/ - Improving JavaScript performance with JägerMonkey
http://hacks.mozilla.org/2010/03/improving-javascript-performance-with-jagermonkey/ - Wikipedia: Mezijazyk
http://cs.wikipedia.org/wiki/Mezijazyk - The LuaJIT Project
http://luajit.org/index.html - LuaJIT FAQ
http://luajit.org/faq.html - LuaJIT Performance Comparison
http://luajit.org/performance.html - LuaJIT 2.0 intellectual property disclosure and research opportunities
http://article.gmane.org/gmane.comp.lang.lua.general/58908 - LuaJIT Wiki
http://wiki.luajit.org/Home - LuaJIT 2.0 Bytecode Instructions
http://wiki.luajit.org/Bytecode-2.0 - Programming in Lua 9.1 – Coroutine Basics,
http://www.lua.org/pil/9.1.html - Programming in Lua (first edition)
http://www.lua.org/pil/contents.html - Programming in Lua: 6 – More about Functions
http://www.lua.org/pil/6.html - Lua Lanes
http://kotisivu.dnainternet.net/askok/bin/lanes/ - Programming in Lua: 6.1 – Closures
http://www.lua.org/pil/6.1.html - Programming in Lua: 9.1 – Coroutine Basics
http://www.lua.org/pil/9.1.html - Programming in Lua: Numeric for
http://www.lua.org/pil/4.3.4.html - Programming in Lua: break and return
http://www.lua.org/pil/4.4.html - Programming in Lua: Tables
http://www.lua.org/pil/2.5.html - Programming in Lua: Table Constructors
http://www.lua.org/pil/3.6.html - Programovací jazyk Lua
http://palmknihy.cz/web/kniha/programovaci-jazyk-lua-12651.htm - Lua: Tables Tutorial
http://lua-users.org/wiki/TablesTutorial - Lua: Control Structure Tutorial
http://lua-users.org/wiki/ControlStructureTutorial - Lua Types Tutorial
http://lua-users.org/wiki/LuaTypesTutorial - Goto Statement in Lua
http://lua-users.org/wiki/GotoStatement - Lua 5.2 sources
http://www.lua.org/source/5.2/ - Lua 5.2 sources – lopcodes.h
http://www.lua.org/source/5.2/lopcodes.h.html - Lua 5.2 sources – lopcodes.c
http://www.lua.org/source/5.2/lopcodes.c.html - Lambda the Ultimate: Coroutines in Lua,
http://lambda-the-ultimate.org/node/438 - Coroutines Tutorial,
http://lua-users.org/wiki/CoroutinesTutorial - Lua Coroutines Versus Python Generators,
http://lua-users.org/wiki/LuaCoroutinesVersusPythonGenerators - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the World's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - Sixth Generation Processors
http://www.pcguide.com/ref/cpu/fam/g6.htm - Great Microprocessors of the Past and Present
http://www.cpushack.com/CPU/cpu1.html - The VFP architecture
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0056d/Bcfibfha.html - ARM Floating Point Accelerator (ARM FPA)
http://vswww.kaist.ac.kr/ver4.0/index.php/research/past-research/arm-fpa.html - The ARM Processor Architecture
http://www.arm.com/products/processors/technologies/instruction-set-architectures.php