Konstrukce překladačů David Bednárek www.ksi.mff.cuni.cz
Pravidla studia SWI109 2/1 Z,Zk
Pravidla studia 3 Cvičení Každých 14 dní Zápis v SIS Zápočtové testy Hromadný termín na cvičení koncem semestru Opravné termíny individuálně ve zkušebních termínech Alternativa: Implementace části překladače Podle zadání ze šk.r. 2010/11 Přednáška Zkouška - písemná
Literatura
Literatura A.V. Aho, R. Sethi, J.D. Ullman Compiler: Principles, Techniques and Tools (1986, 2007) Grune, Bal, Jacobs, Langendoen Modern Compiler Design (2000) Přehled včetně front-endů a překladu neprocedurálních jazyků Steven S. Muchnick Advanced Compiler Design and Implementation (1997) Přehled optimalizací v back-endech Randy Allen, Ken Kennedy Optimizing Compilers for Modern Architectures (2001) Hardwarově závislé optimalizace R. Morgan Building an Optimized Compiler (1998) Srikant, Shankar (eds.) The Compiler Design Handbook (2003) Optimizations and Machine Code Generation Sbírka 22 článků J. R. Levine Linkers and Loaders (1999) 5
Historie překladačů
Vývoj hardware a programovacích jazyků 7 1957: FORTRAN (IBM) První překladače 1960: IBM 360 - general-purpose registers Alokace registrů 1970: Cray - Pipeline Scheduling 1993: PowerPC (IBM) - Out-of-order execution Scheduling v HW 2008: Intel Atom, ARMv7, GPGPU - In-order execution Scheduling překladačem opět důležitý
Source: James Laurus @ HiPEAC 2015 8
Source: William J. Dally @ HiPEAC 2015 9
Source: William J. Dally @ HiPEAC 2015 10
Source: William J. Dally @ HiPEAC 2015 11
Architektura překladače
Architektura překladače 13 Amatérský pohled Lexikální analyzátor Posloupnost tokenů Parser Derivační strom Sémantický analyzátor Derivační strom Generátor kódu Cílový kód
Architektura překladače 14 Z velké dálky front-end závislý na vstupním jazyku back-end závislý na cílovém stroji Lexikální analyzátor Posloupnost tokenů Parser Derivační strom Generátor kódu Sémantický analyzátor Mezikód Cílový kód Derivační strom Generátor mezikódu
Architektura překladače 15 S optimalizacemi front-end závislý na vstupním jazyku back-end závislý na cílovém stroji Lexikální analyzátor Posloupnost tokenů Optimalizace Parser Sémantický analyzátor Optimalizace Derivační strom Derivační strom Derivační strom Mezikód (střední úrovně) Strojově závislé optimalizace Generátor kódu Mezikód (střední úrovně) Mezikód (střední úrovně) Mezikód nízké úrovně Strojově závislé optimalizace Generátor mezikódu Cílový kód
Architektura překladače 16 Detailní pohled akademika (pouze optimalizace) Muchnick: Advanced Compiler Design and Implementation Scalar replacement of array references Data-cache optimizations Constant folding Algebraic simplification and reassociation Procedure integration Tail-call optimization Scalar replacement of aggregates Sparse conditional constant propagation Interprocedural constant propagation Procedure specialization and cloning Sparse conditional constant propagation Global value numbering Local and global copy propagation Sparse conditional constant propagation Dead-code elimination Local and global common-subexpression elimination Loop-invariant code motion Dead-code elimination Code hoisting Induction-variable strength reduction Linear-function test replacement Induction-variable removal Unnecessary bounds-checking elimination Control-flow optimizations In-line expansion Leaf-routine optimization Shrink wrapping Machine idioms Tail merging Branch optimizations and conditional moves Dead-code elimination Software pipelining, loop unrolling Basic-block and branch scheduling Register allocation Basic-block and branch scheduling Intraprocedural I-cache optimization Instruction prefetching Data prefetching Branch prediction Interprocedural register allocation Aggregation of global references Interprocedural I-cache optimization
Architektura překladače 17 Realita GNU Compiler Collection Internals Remove useless statements Mudflap declaration registration Lower control flow Lower exception handling control flow Build the control flow graph Find all referenced variables Enter static single assignment form Warn for uninitialized variables Dead code elimination Dominator optimizations Redundant phi elimination Forward propagation of single-use variables Copy renaming PHI node optimizations May-alias optimization Profiling Lower complex arithmetic Scalar replacement of aggregates Dead store elimination Tail recursion elimination Forward store motion Partial redundancy elimination Loop invariant motion Canonical induction variable creation Induction variable optimizations Loop unswitching Vectorization Tree level if-conversion for vectorizer Conditional constant propagation Folding builtin functions Split critical edges Partial redundancy elimination Control dependence dead code elimination Tail call elimination Warn for function return without value Mudflap statement annotation Leave static single assignment form RTL generation Generate exception handling landing pads Cleanup control flow graph Common subexpression elimination Global common subexpression elimination. Loop optimization Jump bypassing If conversion Web construction Life analysis Instruction combination Register movement Optimize mode switching Modulo scheduling Instruction scheduling Register class preferencing Local register allocation Global register allocation Reloading Basic block reordering Variable tracking Delayed branch scheduling Branch shortening Register-to-stack conversion Final Debugging information output
Názvosloví
Základní bloky Procedura Procedura nebo funkce Call graph Graf (možného) volání mezi procedurami 19 Základní blok (BB basic block) Část procedury bez větvení a smyček, se vstupem pouze na začátku a výstupem pouze na konci Volání procedury může a nemusí být považováno za předěl BB Tok řízení - control-flow (graph) Možnosti předávání řízení mezi základními bloky v proceduře Reprezentováno orientovaným (cyklickým) grafem Tok dat - data-flow Předávání dat, obvykle uvnitř jednoho základního bloku Pro jeden BB může být reprezentováno dagem
Dag 20 Závislost (dependence) Povinnost provést jednu operaci/instrukci po jiné Částečné uspořádání operací/instrukcí v jednom BB Datová závislost (dependence) Závislost producent-konzument v toku dat Antidependence Read-Write: Čtení se musí stihnout před zápisem Write-Write: Pořadí zápisů se nesmí změnit Jiné důvody, obvykle nízkoúrovňového původu Dag (directed acyclic graph) Orientovaný acyklický graf použitý pro zaznamenání data-flow závislostí
Typy 21 Skalární/jednoduchý/atomický typ (scalar) Typ, s nímž dokáže přímo pracovat cílový stroj Složený typ (aggregate) Pole, struktura, třída, řetězec apod. Zarovnání (alignment) Požadavek na umístění proměnné/paměťového místa na adrese dělitelné 2, 4, 8, nebo 16 Striktní: Při nedodržení procesor vyvolá výjimku Optimalizační: Při nedodržení bude kód pomalejší
Proměnné 22 Proměnná (variable) Proměnná deklarovaná ve vstupním jazyce, včetně parametrů Pomocná proměnná (temporary) vytvořená překladačem Statická/globální proměnná Proměnná s jedinou instancí přístupná všem procedurám (Lokální) proměnná Parametr, deklarovaná či pomocná proměnná přístupná pouze jedné proceduře Jazyky s vnořenými procedurami vyžadují další kategorii proměnných přístupných z vnořených procedur Paměťové místo Část proměnné nebo dynamicky alokované paměti
Alias 23 Alias Situace (nebo možnost výskytu situace), kdy k jedné proměnné, její části, či paměťovému místu, vedou dvě různé přistupové cesty int x; int a[ 20]; int * p = & x; a[ i] =...; a[ j] =...; Rozhodnutí, zda může jít o alias, je obecně algoritmicky neřešitelná úloha Pokud si překladač není jist, že o alias nejde, musí se chovat, jako by to alias byl Nejistý alias je ještě horší, než jistý Proměnná bez aliasu Lokální proměnná, která prokazatelně nemá alias Všechny přístupy k ní lze jednoznačně určit
Live range 24 Doba života/rozsah platnosti proměnné (live range) Množina míst v proceduře, kdy je proměnná zapotřebí Tedy existuje možnost, že by ještě byla čtena (před zápisem) Zkoumá se obvykle pouze pro skalární lokální proměnné bez aliasu Variable splitting/renaming Proměnnou s nesouvislým rozsahem platnosti lze nahradit několika jinými Odpadne omezení na shodnou alokaci v jednotlivých souvislých oblastích rozsahu platnosti
Alokace přidělování místa Statická alokace (static allocation) Pro statické proměnné Vyhrazení místa na pevné adrese Podléhá relokaci při spojování linkerem a zavádění loaderem 25 Registrová alokace (register allocation) Pro skalární lokální proměnné bez aliasu Umístění do fyzického registru cílového stroje Pouze po dobu života proměnné Omezeno počtem fyzických registrů Zásobníková alokace (stack allocation) Umístění na zásobníku Zásobník může být definován procesorem nebo emulován Složené nebo aliasované lokální proměnné Proměnné, které se nevešly do registrů Spill-code Kód navíc, který musel být přidán pro manipulaci s proměnnými, které se nevešly do registrů
Volací konvence Volací konvence Úmluva o způsobu spolupráce volající a volané procedury Definována cílovým prostředím nebo autorem překladače Umístění parametrů Zásobník nebo registry Pořadí Přesné umístění je komplikováno zarovnáním Umístění návratové hodnoty Obvykle registr Složené typy se obvykle řeší jako parametry předávané odkazem Odpovědnost za úklid zásobníku Volající/volaný Povinnost zachovat obsah registrů Všechny, některé, nebo žádné Další technické definice Úprava jména procedury jako symbolu pro linker 26 Pokročilá interprocedurální optimalizace: Automatická úprava volací konvence podle místních podmínek volaného a všech volajících
Činnost překladače, optimalizace 27 Intraprocedurální Uvnitř jedné procedury Lokální Uvnitř jednoho základního bloku Globální Příklad: Jednodušší verze schedulingu nebo CSE Pro celou proceduru najednou Příklad: Přidělování registrů, složitější CSE Interprocedurální Pro celý program najednou Při separátním překladu modulů je součástí linkeru Obvykle exponenciální nebo algoritmicky neřešitelné úlohy Příklad: Interprocedurální analýza aliasů Srovnání: In-line expanze procedury patří formálně mezi intraprocedurální optimalizace
Typy 28 Logický typ Datový typ definovaný vstupním jazykem Základní typy + typové konstrukce Neomezená množina typů Fyzický typ Typ rozeznávaný cílovým strojem Konečná množina vestavěných typů Celá čísla několika velikostí (znaménková a bezznaménková) Ukazatel (pokud nesplývá s celým číslem) Reálná čísla několika velikostí/přesností Na ostatní typy se pohlíží jako na posloupnost bajtů Rozhoduje délka, případně požadavek na zarovnání
Mezikódy 29 Vysokoúrovňový mezikód Reprezentace vstupního programu Během fází, řešících konstrukce a pravidla vstupního jazyka Užívá logické typy a operace vstupního jazyka Nejčastěji ve formě anotovaného AST (abstract syntax tree) Derivační strom podle abstraktní gramatiky Mezikód střední úrovně Nejčastější hranice mezi front- a back-endem Na vstupním jazyce nezávislá reprezentace Užívá fyzické typy a operace na nich Nejčastěji ve formě čtveřic Tříadresové pseudoinstrukce, pomocné proměnné Někdy ve speciálních formách (SSA static single assignment) Control-flow může být ve formě grafu BB (základních bloků) Nízkoúrovňový mezikód Ekvivalent strojových instrukcí Nekompaktní forma, symbolické a relokované operandy Před alokací registrů forma s neomezeným počtem virtuálních registrů Někdy v univerzální strojově nezávislé formě (GCC RTL)
Mezikódy
Mezikódy 31 Informace uložené v mezikódu střední úrovně Seznam globálních proměnných Další globální informace pro generovaný kód Seznam procedur Další informace pro debugger
Mezikódy 32 Informace uložené v mezikódu střední úrovně Seznam globálních proměnných Velikost Inicializace Příznak konstantnosti Jméno (pro linker) Logický typ (pro debugger) Další globální informace pro generovaný kód Konstanty (reálné, řetězcové, strukturované) Tabulky virtuálních funkcí, RTTI Často splývají s globálními proměnnými Seznam procedur Další informace pro debugger Jména a konstrukce typů
Mezikódy 33 Popis procedury Jméno (pro linker a chybová hlášení) Seznam parametrů Fyzický typ (+ velikost) Umístění podle volací konvence Jméno (pro debugger a chybová hlášení) Logický typ (pro debugger a určení aliasů) Seznam lokálních proměnných Fyzický typ (+ velikost) Jméno (pro debugger a chybová hlášení) Logický typ (pro debugger a určení aliasů) Proměnné ve vnořených blocích se obvykle povyšují na úroveň procedury Kód procedury Další informace (popisy výjimek apod.)
Mezikódy 34 Kód procedury Plně sekvenční forma Posloupnost (pseudo-)instrukcí virtuálního stroje Tok řízení popsán skokovými instrukcemi Částečně sekvenční forma Tok řízení popsán grafem, jehož uzly jsou základní bloky Každý základní blok obsahuje posloupnost (pseudo-)instrukcí Skokové instrukce pouze na konci BB nebo zaznamenány jinak Nesekvenční forma Tok řízení popsán grafem, jehož uzly jsou základní bloky Tok dat uvnitř základního bloku popsán dagem Různé formy podle stupně analýzy aliasů a rozsahů platnosti Pokročilejší formy nahrazují lokální proměnné rozhraními bloků
Plně sekvenční čtveřicový mezikód 35 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) TMP:(T1,B),(T2,B),(T3,I32) ENTER GT_I32 T1,Px,Py JF T1,L1 MOV_I32 Vz,Py MOV_I32 Py,Px MOV_I32 Px,Vz L1: GT_I32 T2,Px,C1 JF T2,L2 MOD_I32 T3,Py,Px MOV_I32 Vz,T3 MOV_I32 Py,Px MOV_I32 Px,Vz JMP L1 L2: RET_I32 Py
Částečně sekvenční čtveřicový mezikód 36 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) TMP:(T1,B),(T2,B),(T3,I32) ENTER GT_I32 T1,Px,Py JC T1 GT_I32 T2,Px,C1 JC T2 MOV_I32 Vz,Py MOV_I32 Py,Px MOV_I32 Px,Vz MOD_I32 T3,Py,Px MOV_I32 Vz,T3 MOV_I32 Py,Px MOV_I32 Px,Vz RET_I32 Py
Nesekvenční mezikód (před analýzou aliasů) 37 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) Control-Flow vždy if true if false Dag Data-flow Závislosti ENTER LD_I32(Px) LD_I32(Py) LD_I32(Py) GT_I32 ST_I32(Vz) JC LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) MOD_I32 LD_I32(Px) ST_I32(Vz) GTC_I32(C1) LD_I32(Px) JC ST_I32(Py) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) LD_I32(Py) RET_I32 LD_I32(Vz) ST_I32(Px)
Mezikódy střední úrovně 38 Plně sekvenční forma Částečně sekvenční forma Nesekvenční forma Všechny tyto formy lze generovat přímo z abstraktního syntaktického stromu Jedním průchodem zdola nahoru goto je nutné ošetřit dodatečnými zásahy (backpatching) Strom nemusí fyzicky existovat, postačí průchod myšleným stromem LR analýza: pravá derivace pozpátku LL analýza rekurzivním sestupem Většina front-endů přesto strom konstruuje Složité konstrukce jazyka (šablony, předkompilované části) Rozhraní mezi syntaktickým a sémantickým analyzátorem Optimalizace
Mezikódy střední úrovně 39 Plně sekvenční forma Částečně sekvenční forma Nesekvenční forma Táž forma se obvykle v průběhu překladu upravuje Připojují se odvozené informace a optimalizační rozhodnutí Provádějí se ekvivalentní úpravy (optimalizace) Jedna forma může mít různé variace A to i uvnitř jednoho překladače Odráží různé způsoby a/nebo různé stupně analýzy Řada překladačů užívá dvě z těchto forem Z historických důvodů (stabilita rozhraní front-end/back-end) Pro vytvoření druhé formy je nutná analýza první formy
Detekce základních bloků 40 CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) TMP:(T1,B),(T2,B),(T3,I32) ENTER GT_I32 T1,Px,Py JF T1,L1 MOV_I32 Vz,Py MOV_I32 Py,Px MOV_I32 Px,Vz L1: GT_I32 T2,Px,C1 JF T2,L2 MOD_I32 T3,Py,Px MOV_I32 Vz,T3 MOV_I32 Py,Px MOV_I32 Px,Vz JMP L1 L2: RET_I32 Py V sekvenčním mezikódu Základní blok Začíná Na začátku procedury V cíli skoku Za podmíněným skokem Končí Podmíněným skokem Nepodmíněným skokem Návratem z procedury Před cílem skoku
Detekce základních bloků 41 CONST:(C1,I32,0) CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) TMP:(T1,B),(T2,B),(T3,I32) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) TMP:(T1,B),(T2,B),(T3,I32) ENTER GT_I32 T1,Px,Py JF T1,L1 MOV_I32 Vz,Py MOV_I32 Py,Px MOV_I32 Px,Vz L1: GT_I32 T2,Px,C1 JF T2,L2 MOD_I32 T3,Py,Px MOV_I32 Vz,T3 MOV_I32 Py,Px MOV_I32 Px,Vz JMP L1 L2: RET_I32 Py ENTER GT_I32 T1,Px,Py JC T1 GT_I32 T2,Px,C1 JC T2 RET_I32 Py MOV_I32 Vz,Py MOV_I32 Py,Px MOV_I32 Px,Vz MOD_I32 T3,Py,Px MOV_I32 Vz,T3 MOV_I32 Py,Px MOV_I32 Px,Vz
Detekce základních bloků int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } Ve zdrojovém kódu Základní blok Začíná Na začátku procedury Na začátku then a else bloku Na začátku těla cyklu Za if příkazem Za while cyklem Končí Na konci procedury Na konci then a else bloku Na konci těla cyklu Na konci podmínky v if Na konci podmínky ve while Příkazem return/break apod. Komplikace Zkrácené vyhodnocování booleovských výrazů Podmíněný výraz Příkaz goto 42
Detekce základních bloků 43 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) TMP:(T1,B),(T2,B),(T3,I32) ENTER GT_I32 T1,Px,Py JC T1 GT_I32 T2,Px,C1 JC T2 MOV_I32 Vz,Py MOV_I32 Py,Px MOV_I32 Px,Vz MOD_I32 T3,Py,Px MOV_I32 Vz,T3 MOV_I32 Py,Px MOV_I32 Px,Vz RET_I32 Py
Nesekvenční mezikód s hranicemi příkazů 44 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) Control-Flow vždy if true if false Dag operand ENTER LD_I32(Px) LD_I32(Py) LD_I32(Py) GT_I32 ST_I32(Vz) JC LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) MOD_I32 LD_I32(Px) ST_I32(Vz) GTC_I32(C1) LD_I32(Px) JC ST_I32(Py) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) LD_I32(Py) RET_I32 LD_I32(Vz) ST_I32(Px)
Nesekvenční mezikód před analýzou aliasů 45 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) Control-Flow vždy if true if false Dag operand pořadí ENTER LD_I32(Px) LD_I32(Py) LD_I32(Py) GT_I32 ST_I32(Vz) JC LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) MOD_I32 LD_I32(Px) ST_I32(Vz) GTC_I32(C1) LD_I32(Px) JC ST_I32(Py) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) LD_I32(Py) RET_I32 LD_I32(Vz) ST_I32(Px)
Odstranění závislostí po analýze aliasů 46 LD_I32(Py) LD_I32(Px) MOD_I32 ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) MOD_I32 ST_I32(Vz) ST_I32(Py) LD_I32(Vz) ST_I32(Px)
Odstranění závislostí po analýze aliasů 47 ENTER ENTER LD_I32(Px) LD_I32(Py) LD_I32(Px) LD_I32(Py) GT_I32 LD_I32(Py) GT_I32 JC ST_I32(Vz) JC LD_I32(Px) LD_I32(Py) ST_I32(Py) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) ST_I32(Vz) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) LD_I32(Py) LD_I32(Px) LD_I32(Px) GTC_I32(C1) JC MOD_I32 ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Px) GTC_I32(C1) JC ST_I32(Py) MOD_I32 ST_I32(Vz) LD_I32(Vz) ST_I32(Px) LD_I32(Vz) LD_I32(Py) ST_I32(Px) LD_I32(Py) RET_I32 RET_I32
Nesekvenční mezikód po analýze aliasů 48 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) Control-Flow vždy if true if false Dag dependence: operand w-r w-? antidependence: r-w?-w ENTER LD_I32(Px) LD_I32(Py) GT_I32 JC LD_I32(Py) LD_I32(Px) ST_I32(Py) ST_I32(Vz) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) ST_I32(Py) MOD_I32 LD_I32(Px) ST_I32(Vz) GTC_I32(C1) LD_I32(Vz) JC ST_I32(Px) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) LD_I32(Py) RET_I32
Nesekvenční mezikód s rozsahy platnosti 49 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) Dag operand antidependence: r-w Oblasti platnosti Px Py Vz/1 Vz/2 ENTER LD_I32(Px) LD_I32(Py) GT_I32 JC LD_I32(Py) LD_I32(Px) ST_I32(Py) ST_I32(Vz) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) ST_I32(Py) MOD_I32 LD_I32(Px) ST_I32(Vz) GTC_I32(C1) LD_I32(Vz) JC ST_I32(Px) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) LD_I32(Py) RET_I32
Algoritmus: Určení rozsahů platnosti VAR množina proměnných Orientovaný graf control-flow v proceduře: (BB,CF) BB množina základních bloků CF BB BB přechody mezi základními bloky Lokální analýza Vnitřní chování každého základního bloku W : BB VAR Bool blok zapisuje do proměnné R : BB VAR Bool blok čte proměnnou před prvním zápisem Triviální algoritmus Globální analýza Platnost proměnných na začátku každého základního bloku L : BB VAR Bool proměnná je živá na začátku bloku Polynomiální algoritmus Dopočet Platnost proměnných na koncích bloků a uvnitř bloků Detekce čtení nezapsaných proměnných Triviální algoritmus Vylepšení Určení komponent souvislosti a přeznačení proměnných 50
Algoritmus: Určení rozsahů platnosti Vstup (BB,CF) graf control flow W(b,v) blok b zapisuje do proměnné v R(b,v) blok b čte proměnnou v před prvním zápisem do ní Výstup L(b,v) proměnná v je živá na začátku bloku b 51 for each b in BB L(b,v) = R(b,v); do { for each <b1,b2> in CF L(b1,v) = ~ W(b1,v) & L(b2,v); } while changed; Algoritmus se provádí vektorově Pro všechny proměnné (v) najednou Překladač využije SIMD instrukce O( BB * CF * VAR )
52 Určení rozhraní BB GT_I32 LD_I32(Px) LD_I32(Py) JC ENTER GTC_I32(C1) JC LD_I32(Px) LD_I32(Py) RET_I32 LD_I32(Py) ST_I32(Vz) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) GT_I32 LD_I32(Px) LD_I32(Py) JC ENTER LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Py) ST_I32(Vz) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 GTC_I32(C1) JC LD_I32(Px) LD_I32(Py) RET_I32
Nesekvenční mezikód s rozhraními BB 53 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } CONST:(C1,I32,0) Dag operand antidependence: r-w Rozhraní 1 Px Py Rozhraní 2 Px Py ENTER LD_I32(Px) LD_I32(Py) GT_I32 JC LD_I32(Py) LD_I32(Px) ST_I32(Py) ST_I32(Vz) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) ST_I32(Py) MOD_I32 LD_I32(Px) ST_I32(Vz) GTC_I32(C1) LD_I32(Vz) JC ST_I32(Px) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) VAR:(Vz,I32, z ) LD_I32(Py) RET_I32
Náhrada proměnných rozhraním BB 54 ENTER LD_I32(Px) LD_I32(Py) GT_I32 JC ENTER GT_I32 JC LD_I32(Py) LD_I32(Px) ST_I32(Py) ST_I32(Vz) LD_I32(Vz) ST_I32(Px) LD_I32(Py) LD_I32(Px) LD_I32(Px) GTC_I32(C1) JC ST_I32(Py) MOD_I32 ST_I32(Vz) LD_I32(Vz) ST_I32(Px) GTC_I32(C1) JC MOD_I32 LD_I32(Py) RET_I32 RET_I32
Nesekvenční mezikód bez proměnných 55 int gcd( int x, int y) { int z; if ( x > y ) Dag data ENTER { GT_I32 z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; Rozhraní 1 Px Py Rozhraní 2 Px Py JC x = z; } return y; MOD_I32 } GTC_I32(C1) CONST:(C1,I32,0) JC PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) RET_I32
Nesekvenční mezikód po duplikaci BB 56 int gcd( int x, int y) { int z; if ( x > y ) { z = y; Dag data ENTER GT_I32 JC y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; Rozhraní 1 Px Py Rozhraní 2 Px Py GTC_I32(C1) JC RET_I32 MOD_I32 } } return y; GTC_I32(C1) JC CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) MOD_I32 RET_I32
Nesekvenční mezikód po přeznačení 57 int gcd( int x, int y) { int z; if ( x > y ) { z = y; y = x; x = z; } while ( x > 0 ) { z = y % x; y = x; x = z; } return y; } ENTER GT_I32 JC GTC_I32(C1) JC RET_I32 MOD_I32 GTC_I32(C1) JC CONST:(C1,I32,0) PROC gcd PARAM:(Px,I32, x ),(Py,I32, y ) MOD_I32 RET_I32
Nesekvenční mezikód po přeznačení 58 int gcd( int x, int y) { if ( x > y ) goto L2; L1: if ( x <= 0 ) return y; y = y % x; L2: if ( y <= 0 ) return x; x = x % y; goto L1; } ENTER GT_I32 JC GTC_I32(C1) JC RET_I32 MOD_I32 GTC_I32(C1) JC MOD_I32 RET_I32
Nesekvenční mezikód po optimalizaci skoků 59 int gcd( int x, int y) { if ( x > y ) goto L4; if ( x <= 0 ) goto L3; L1: if ( (y = y % x) <= 0 ) goto L5; L2: if ( (x = x % y) > 0 ) goto L1; L3: return y; L4: if ( y > 0 ) goto L2; L5: return x; } L3 ENTER GT_I32 JC L4 GTC_I32(C1) GTC_I32(C1) JC JC L1 MOD_I32 GTC_I32(C1) JC L2 L5 MOD_I32 RET_I32 GTC_I32(C1) JC RET_I32
Architektura back-endu 60 Různé vnitřní reprezentace Mezikód střední úrovně Nezávislá sada operací ADD_I32 a,b,c Forma Nesekvenční Částečně sekvenční Mezikód nízké úrovně Ekvivalenty strojových instrukcí add r1,r2 Forma Nesekvenční Částečně sekvenční Sekvenční Optimalizace Strojově závislé optimalizace Generátor kódu Mezikód (střední úrovně) Mezikód (střední úrovně) Mezikód (střední úrovně) Mezikód nízké úrovně Strojově závislé optimalizace Cílový kód
Architektura back-endu 61 Sekvenční mezikód Sekvenční mezikód (střední úrovně) Optimalizace Sekvenční mezikód (střední úrovně) Strojově závislé optimalizace Alokace registrů Sekvenční mezikód (střední úrovně) Instruction selection = Výběr instrukcí Sekvenční mezikód nízké úrovně s virtuálními registry Sekvenční mezikód nízké úrovně s virtuálními registry Sekvenční mezikód nízké úrovně s fyzickými registry Strojově závislé optimalizace Strojově závislé optimalizace Sekvenční mezikód Strojový kód Finalizer
Architektura back-endu 62 Sekvenční mezikód Sekvenční mezikód (střední úrovně) Sekvenční mezikód nízké úrovně s virtuálními registry Analýza aliasů Zjednodušená analýza rozsahů platnosti Sekvenční mezikód nízké úrovně s fyzickými registry Strojový kód Optimalizace Instruction selection = Výběr instrukcí Alokace registrů Optimalizace Optimalizace Finalizer
Architektura back-endu 63 Částečně sekvenční mezikód bez schedulingu Detekce základních bloků Analýza aliasů Live-range analysis = Analýza rozsahů platnosti Sekvenční mezikód (střední úrovně) Částečně sekvenční mezikód (střední úrovně) Částečně sekvenční mezikód nízké úrovně s virtuálními registry Částečně sekvenční mezikód nízké úrovně s fyzickými registry Sekvenční mezikód nízké úrovně s fyzickými registry Strojový kód Optimalizace Instruction selection = Výběr instrukcí Alokace registrů Optimalizace Optimalizace Finalizer Optimalizace Basic-block (re)ordering = Serializace control-flow
Architektura back-endu 64 Částečně sekvenční mezikód se schedulingem Detekce základních bloků Analýza aliasů Live-range analysis = Analýza rozsahů platnosti Optimalizace Instruction selection = Výběr instrukcí Optimalizace Optimalizace Finalizer Sekvenční mezikód (střední úrovně) Částečně sekvenční mezikód (střední úrovně) Částečně sekvenční mezikód nízké úrovně s virtuálními registry Částečně sekvenční mezikód nízké úrovně s fyzickými registry Sekvenční mezikód nízké úrovně s fyzickými registry Strojový kód Instruction scheduling = Řazení instrukcí Alokace registrů Instruction scheduling = Řazení instrukcí Optimalizace Basic-block (re)ordering = Serializace control-flow
Architektura back-endu 65 Nesekvenční mezikód Detekce základních bloků Analýza aliasů Live-range analysis = Analýza rozsahů platnosti Optimalizace Instruction selection = Výběr instrukcí Optimalizace Optimalizace Finalizer Sekvenční mezikód (střední úrovně) Nesekvenční mezikód (střední úrovně) Nesekvenční mezikód nízké úrovně s virtuálními registry Částečně sekvenční mezikód nízké úrovně s virtuálními registry Částečně sekvenční mezikód nízké úrovně s fyzickými registry Sekvenční mezikód nízké úrovně s fyzickými registry Strojový kód Instruction scheduling = Řazení instrukcí Alokace registrů Instruction scheduling = Řazení instrukcí Optimalizace Basic-block (re)ordering = Serializace control-flow
Architektura back-endu 66 Instruction selection Výběr strojových instrukcí 1:n přímočaré řešení m:n stromové/grafové gramatiky apod. Vliv na kvalitu kódu poklesl RISC, load-store kód apod. Instruction scheduling Řazení instrukcí pro lepší využití ILP (instruction-level parallelism) NP-úplná úloha Lokální v BB Speciální řešení smyček (software pipelining) Částečně globální varianty (trace scheduling) Zrychluje kód o 30-150% Register allocation Přidělování fyzických registrů NP-úplná úloha Standardní řešení: Barvení grafu
Architektura back-endu ILP (instruction-level parallelism) Pipeline Instrukce se zpracovává v několika fázích (stages) fetch read execute write V jednom okamžiku se provádějí různé fáze různých instrukcí Závislosti po sobě jdoucích instrukcí způsobují: Pipeline stall zdržení (i486) Nekorektní provedení kódu (Sparc) Superskalární procesory (MIMD) Několik výkonných jednotek (Pentium: 2) V jednom okamžiku se provádějí stejné fáze několika instrukcí Vektorové procesory (SIMD) Mnoho výkonných jednotek (Cray: 64) Slabší varianty: Pentium MMX/SSE V jednom okamžiku se provádí tatáž instrukce mnohokrát Využití nepatří pod pojem scheduling Automatická vektorizace používá podobné algoritmy jako některé metody schedulingu 67
Alokace registrů 68 Vstup Částečně sekvenční kód Možnost přesně definovat rozsahy platnosti Ekvivalenty strojových instrukcí Speciální zpracování přesunových instrukcí Instrukce pracují s virtuálními registry Optimistický kód Všechny jednoduché proměnné bez aliasu v registrech Spočtené rozsahy platnosti proměnných Množina bodů v mezikódu, kde je proměnná živá Hranice základních bloků Hranice mezi instrukcemi Přesnější informace v místech, kde je proměnná čtena/zapisována Výstup Přiřazení virtuálních registrů fyzickým
Alokace registrů 69 Optimalizace: Minimalizace přesunových instrukcí Vytvoření matice kolizí C : VAR VAR Bool Matice je velká, ale potřebujeme rychlý přístup Kolize = neprázný průnik oblastí platnosti Zvláštní posouzení okrajových doteků oblastí Odhad ceny za spill-kód P : VAR Integer Odhad počtu použití proměnné v průměrném provedení procedury Vlastní algoritmus alokace Barvení grafu (VAR,C) n barvami n je počet fyzických registrů NP-úplná úloha, dobré heuristické řešení Doplnění spill-kódu
Alokace registrů 70 Barvení grafu (VAR,C) n barvami n je počet fyzických registrů NP-úplná úloha, dobré heuristické řešení Princip Má-li vrchol méně než n sousedů, lze jej vždy obarvit Dvě fáze Postupné odebírání vrcholů Ve vhodném pořadí tak, aby později měly šanci na obarvení Odebrané vrcholy se ukládají na zásobník Zpětná rekonstrukce grafu Vrcholy se odebírají ze zásobníku Pokud je lze obarvit, obarví se a přidají do grafu Neobarvitelné nebudou přiděleny do registru
Alokace registrů 71 Vstup: graf (VAR,C), ceny P, počet n (VAR2,C2) := (VAR,C) while not empty(var2) do if not exists v:var2 st deg(v,c2) < n then select v st P(v) is minimal; // or deg(v,c2) is maximal end if; (VAR2,C2) := (VAR2-{v},C2-edges(v)) stack.push(v); end while; while not empty(stack) do stack.pop(v) M = {u VAR2; <v,u> C} if exists i {1..n} st not i color(m) then color(v) := i (VAR2,C2) := (VAR2+{v},C2+) else spill(v) := true end if end while Výstup: přidělení registrů color, nepřidělené registry spill
Alokace registrů 72 Optimalizace: Minimalizace přesunových instrukcí Jednoduché řešení: Ztotožnit virtuální registry spojené přesunovou instrukcí Nelze vždy ztotožňované registry mohou spolu kolidovat Duplikace kódu může pomoci Složitější řešení: Úprava algoritmu alokace Určení skupin uzlů svázaných přesunovými instrukcemi Při obarvování vybírat zároveň všechny obarvitelné uzly skupiny
Alokace registrů 73 Doplnění spill-kódu Alokovat neobarvené uzly na zásobníku Podobný algoritmus barvení grafu s neomezeným n Opatřit každou instrukci spill-kódem Prefix: Přesun neobarvených vstupních operandů do registrů Suffix: Přesun neobarvených výstupních operandů do paměti Problém: kde vzít registry pro tyto operandy Jednoduché řešení: Rezervované nepřidělované registry Lepší řešení: Alokovat pomocné registry v rámci normální alokace Vstupy a výstupy každé instrukce označeny jako uzly grafu Nekolidují s virtuálními registry, na které se vážou Kolidují mezi sebou a s ostatními živými virtuálními registry Opatřeny nejvyšší cenou Problém: spill-kód je často zbytečný Dodatečná optimalizace eliminace redundantních přesunů
Alokace registrů 74 Množiny povolených registrů Pro každý operand každé instrukce je určena množina registrů, v nichž může být tento operand Ortogonální sada instrukcí Každé dvě množiny povolených registrů jsou buď identické nebo disjunktní Alokace registrů se spouští pro každou množinu zvlášť Neortogonální sada instrukcí Množiny povolených registrů se simulují v grafu kolizí Přidat úplný podgraf uzlů vysoké priority - reprezentují fyzické registry Spojit hranou všechny virtuální registry s těmi fyzickými, do kterých nesmí být přiděleny Problém: Nemusí existovat obarvení Odložení do paměti, přestože stačí přesun mezi registry
Alokace registrů 75 Další problémy Subregistry Intel IA-32: al-ax-eax-edx:eax Řešení: Povýšení na větší registr - neoptimální Různé triky při vytváření grafů kolizí Registrové volací konvence Volání procedury se chová jako instrukce používající několik registrů Neortogonální volací konvence předepisuje, ve kterém registru má být který operand Často vede k neřešitelným kolizím zbytečný spill-kód Řešení: Rozsah platnosti proměnné se rozdělí na oblasti Kolem každé instrukce, používající proměnnou, jedna oblast Oblasti se dotýkají v místě, kde by byla přesunová instrukce, pokud by byla zapotřebí Uzly reprezentující oblasti téže proměnné se přednostně obarvují toutéž barvou
Scheduling 76 Provádí se na jednom základním bloku Trace scheduling: Vybraná posloupnost BB slita do jednoho Software pipelining: Speciální řešení pro BB jako cyklus Vstup Dag Uzly = instrukce Hrany = závislosti Model procesoru Latence časování závislých dvojic instrukcí Rezervační tabulky schopnosti paralelního zpracování Výstup Přiřazení času každému uzlu dagu Čas měřen cykly procesoru Instrukce trvá několik cyklů zvolen referenční bod Obvykle začátek zpracování po načtení a dekódování instrukce
Scheduling 77 Dag Uzly = instrukce Hrany = závislosti Dependence vzniklé z předávání dat v registrech Dependence a antidependence z přístupů do paměti Opatrný přístup: Možná závislost => závislost Antidependence vzniklé ze soupeření o registry Při schedulingu po alokaci registrů Další závislosti z technických příčin Manipulace se zásobníkem, skoky apod. Instrukce volání procedury Nekonečně dlouhé časování Často považována za hranici mezi BB Omezená optimalizace přesunem přes volání
Scheduling 78 Výstup Přiřazení času každému uzlu dagu Aplikace výstupu schedulingu Běžné procesory (Intel IA-32, včetně x86_64) Seřazení instrukcí podle schedulovaného času Procesor nemusí dodržet předpokládaný čas Procesory se sekvenčními body (Intel IA-64) V kódu jsou mezi instrukcemi označeny sekvenční body (stops) Procesor má právo přeházet pořadí instrukcí mezi sekvenčními body Ignorují se antidependence i některé dependence Výstupem scheduleru jsou i sekvenční body VLIW procesory Very Large Instruction Word Instrukce řídí paralelní činnost jednotek procesoru Jeden schedulovaný čas = jedna instrukce
Scheduling 79 Scheduling pouze odhaduje skutečné časování Skutečné časování je ovlivněno nepředvídatelnými jevy Zbytky rozpracovaných instrukcí z předchozího BB Řešení: Trace-scheduling, řízení profilem Paměťová hierarchie Doba přístupu k paměti závisí na přítomnosti v cache Obvykle se předpokládá nejlepší možný průběh Speciální problém: Multithreaded aplikace na multiprocesorech Fetch bandwidth Instrukce nemusí být načteny a dekódovány včas Zdržují skoky a soupeření o přístup do paměti Přesné simulování fetch jednotky by neúměrně komplikovalo scheduler Scheduler nezná skutečné závislosti přístupů do paměti Musí postupovat opatrně a zohledňuje i nejisté závislosti Procesor zná skutečné adresy přístupů a detekuje pouze skutečné závislosti Agresivně optimalizující procesor může zvolit zcela jiné pořadí instrukcí
Scheduling 80 Model procesoru Latence časování závislých dvojic instrukcí Počet cyklů procesoru, který musí proběhnout mezi referenčními body závislých instrukcí U antidependencí a ve speciálních případech může být nulová U procesorů se sekvenčními body může být záporná Latence se obvykle zapisuje ke hranám dagu Přiřazena na základě klasifikace závislosti podle tabulek latencí
Scheduling 81 Model procesoru Rezervační tabulky schopnosti paralelního zpracování Procesor je rozdělen na funkční jednotky různých druhů Je určen počet jednotek každého druhu Limit: Kind -> N Pro každou instrukci definována rezervační tabulka Res(instr): Time Kind N Počet jednotek daného druhu, který instrukce potřebuje v daném čase (měřeno od referenčního bodu) Rezervační tabulky jsou nutné i pro procesory, které nejsou superskalární Mají Limit(k)=1, ale různé a tudíž konfliktní rezervační tabulky
Příklad mezikód střední úrovně 82 char chksum( char * p, int i) { char s = 0; while ( i > 0 ) ENTER GTC_I32(C1) JC C_I8(C2) { s ^= *p++; --i; } return s; } XLD_I8 ADDC_P(C3) SUBC_I32(C4) XOR_I8 GTC_I32(C1) JC RET_I32
Příklad mezikód nízké úrovně 83 char chksum( char * p, int i) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; } inc rp cmp i,0 jgt mov rs,0 mov r1,[rp] dec ri xor rs,r1 cmp ri,0 jgt ret
Příklad latence 84 Tabulka latencí Instrukce-instrukce z instrukce do instrukce čas cmp ri,0 jgt 1 Pesimistický přístup na konci BB umožňuje optimistiký přístup na začátku: Latence začátek BB-instrukce jsou považovány za nulové mov r1,[rp] xor rs,r1 4 dec ri cmp ri,c 2 mov r1,[rp] inc rp 0 z instrukce Instrukce-konec BB Pesimistická varianta (instrukce musí být dokončena v tomto BB) čas inc rp 2 jgt 1 dec ri 2 inc rp 1 2 cmp ri,0 1 0 jgt dec ri mov r1,[rp] xor rs,r1 Instrukce jgt musí být poslední Latence vůči konci BB se normalizují odečtením latence jgt 1 4 1 xor rs,r1 2
Příklad rezervační tabulky Rezervační tabulky inc r1 dec r1 xor r1,r2 cmp r1,r2 0 1 R MEM ALU W 1 1 2 1 Kapacita procesoru R MEM ALU W 1 1 2 1 85 mov r1,[r2] R MEM ALU W 0 1 1 1 2 1 3 1 4 1 inc rp 1 cmp ri,0 1 0 2 jgt mov r1,[rp] dec ri xor rs,r1 4 1 1 jgt R MEM ALU W 0 1
Příklad scheduling Krok 1 Připravené instrukce dec ri mov r1,[rp] Kritická cesta mov r1,[rp] 5 Vybrána instrukce mov r1,[rp] Umístěna do času 0 čas R MEM ALU W 0 mov 1 mov 2 mov 3 mov 4 mov 86 inc rp 1 0 cmp ri,0 1 2 jgt mov r1,[rp] dec ri xor rs,r1 4 1 1
Příklad scheduling Krok 2 Připravené instrukce inc rp dec ri xor rs,r1 Kritická cesta xor rs,r1 5 Vybrána instrukce xor rs,r1 Čas 4 určen latencí čas R MEM ALU W 0 mov 1 mov 2 mov 3 mov 4 xor mov 5 xor 6 xor inc rp 1 cmp ri,0 1 0 2 jgt mov r1,[rp] dec ri xor rs,r1 4 0 1 1 87
Příklad scheduling Krok 3 Připravené instrukce inc rp dec ri Kritická cesta dec ri - 4 Vybrána instrukce dec ri Čas 0 vyhovuje latenci Rezervační tabulky jsou obsazeny Zvolen čas 1 čas R MEM ALU W 0 mov 1 dec mov 2 mov dec 3 mov dec 4 xor mov 5 xor 6 xor inc rp 1 cmp ri,0 1 0 2 jgt dec ri mov r1,[rp] 1 0 4 xor rs,r1 1 88
Příklad scheduling Krok 4 Připravené instrukce inc rp cmp ri,0 Kritická cesta cmp ri,0 4 Vybrána instrukce cmp ri,0 4 Čas 3 určen latencí čas R MEM ALU W 0 mov 1 dec mov 2 mov dec 3 cmp mov dec 4 xor cmp mov 5 xor cmp 6 xor 89 inc rp 1 cmp ri,0 1 0 2 jgt dec ri mov r1,[rp] 1 0 4 xor rs,r1 1 1
Příklad scheduling Krok 5 Připravené instrukce inc rp (jgt) musí být poslední Vybrána instrukce inc rp Čas 0 vyhovuje latenci Rezervační tabulky pro časy 0-4 obsazeny Umístěno v čase 5 čas R MEM ALU W 0 mov 1 dec mov 2 mov dec 3 cmp mov dec 4 xor cmp mov 5 inc xor cmp 6 inc xor 7 inc inc rp 1 3 cmp ri,0 1 0 jgt dec ri mov r1,[rp] 1 4 0 xor rs,r1 1 1 90
Příklad scheduling Krok 6 Připravené instrukce jgt Latenci vyhovuje čas 4 Instrukce však musí být poslední Vybrán čas 5 čas R MEM ALU W 0 mov 1 dec mov 2 mov dec 3 cmp mov dec 4 xor cmp mov 5 inc xor, jgt cmp 6 inc xor 7 inc 91 5 inc rp 1 3 cmp ri,0 1 jgt dec ri mov r1,[rp] 1 4 0 xor rs,r1 1 1
Příklad scheduling 92 char chksum( char * p, int i) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; } Výsledný kód čas R MEM ALU W 0 mov 1 dec mov 2 mov dec 3 cmp mov dec 4 xor cmp mov 5 inc xor, jgt cmp 6 inc xor 7 inc mov r1,[rp] dec ri cmp ri,0 xor rs,r1 inc rp jgt 5 inc rp 1 3 cmp ri,0 5 jgt dec ri mov r1,[rp] 1 1 0 4 xor rs,r1 1
Scheduling 93 Základní algoritmus: List scheduling V grafu závislostí s latencemi se pro každý uzel spočte délka kritické, tj. nejdelší cesty ke konci V každém kroku se určí připravené instrukce a z nich se vybere nejvhodnější Přednost mají instrukce s nejdelší kritickou cestou Mezi nimi se vybírá podle dalších heuristik Při správné implementaci má tento postup složitost O(n 2 ) Vylepšené algoritmy Posun směrem k exhaustivnímu prohledávání všech možností Branch-and-bound přístup: Zkouší se všechny možnosti v pořadí od nejnadějnější, nalezené list schedulingem Beznadějné pokusy se včas zastaví porovnáváním odhadů úspěšnosti výsledku pomocí kritických cest s doposud známým nejlepším řešením
Příklad scheduling 94 Výsledný kód mov r1,[rp] dec ri cmp ri,0 inc rp xor rs,r1 jgt Chování ve smyčce Za předpokladu správné predikce skoku procesorem Výkon: 1/6 iterace/cyklus Využití jednotek: R: 5/6 MEM: 3/6 ALU: 5/12 W: 5/6 čas R MEM ALU W 0 mov 1 dec mov 2 mov dec 3 cmp mov dec 4 inc cmp mov 5 xor inc, jgt cmp 6 mov xor inc 7 dec mov xor 8 mov dec 9 cmp mov dec 10 inc cmp mov 11 xor inc, jgt cmp 12 mov xor inc 13 dec mov xor 14 mov dec 15 cmp mov dec
Příklad scheduling 95 Efekt kapacity procesoru R MEM ALU W 2 1 2 2 mov r1,[rp] dec ri inc rp cmp ri,0 xor rs,r1 jgt Stejné latence i res. tabulky Výkon: 1/5 iterace/cyklus Využití jednotek: R: 5/10 MEM: 3/5 ALU: 5/10 W: 5/10 čas R MEM ALU W 0 mov, dec 1 inc mov dec 2 cmp mov inc dec 3 mov cmp inc 4 xor jgt mov, cmp 5 mov, dec xor 6 inc mov dec xor 7 cmp mov inc dec 8 mov cmp inc 9 xor jgt mov, cmp 10 mov, dec xor 11 inc mov dec xor 12 cmp mov inc dec 13 mov cmp inc 14 xor jgt mov, cmp
Příklad software pipelining 96 Závislosti uvnitř BB z instrukce do instrukce čas cmp ri,0 jgt 1 mov r1,[rp] xor rs,r1 4 dec ri cmp ri,c 2 mov r1,[rp] inc rp 0 Závislosti přes hranice BB (loop-carried dependences) z instrukce do instrukce čas inc rp inc rp 2 inc rp mov r1,[rp] 2 dec ri dec ri 2 cmp ri,0 dec ri 0 xor rs,r1 xor rs,r1 2 xor rs,r1 mov r1,[rp] 0 jgt cmp ri,0 1 jgt mov r1,[rp] 1 2 inc rp 0 1 2 cmp ri,0 1 0 jgt 1 0 2 mov r1,[rp] 4 2 dec ri xor rs,r1 2
Příklad software pipelining 97 Software pipelining Unroll-and-compact Rozvrhuje se rozvinutý cyklus tak dlouho, dokud nevznikne opakující se vzorek Perioda opakování může odpovídat více iteracím Problém: Není jasné, co je to kritická cesta Hledá se kritická smyčka čas R MEM ALU W 0 mov1, dec1 1 inc1 mov1 dec1 2 cmp1 mov1 inc1 dec1 3 dec2 mov1 cmp1, jgt1 inc1 4 xor1, mov2 dec2 mov1, cmp1 5 inc2, cmp2 mov2 xor1 dec2 6 dec3 mov2 inc2, cmp2 xor1 7 mov2 dec3, jgt2 inc2, cmp2 8 xor2, mov3 mov2, dec3 9 inc3, cmp3 mov3 xor2 10 dec4 mov3 inc3, cmp3 xor2 11 mov3 dec4, jgt3 inc3, cmp3 12 xor3, mov4 mov3, dec4 13 inc4, cmp4 mov4 xor3 14 dec5 mov4 inc4, cmp4 xor3 15 mov4 dec5, jgt4 inc4, cmp4 16 xor4, mov5 mov4, dec5 17 inc5, cmp5 mov5 xor4 18 dec6 mov5 inc5, cmp5 xor4 19 mov5 dec6, jgt5 inc5, cmp5 20 xor5 mov5, dec6 21 xor5 22 xor5
Příklad software pipelining 98 Jiná abstrakce latence/iterace 2/1 2 inc rp 0 1 2 cmp ri,0 1 0 jgt mov r1,[rp] dec ri xor rs,r1 2 1 0 2 4 2 2/1 inc rp 0/0 2/1 1/1 mov r1,[rp] 0/1 2/1 4/0 xor rs,r1 dec ri 0/1 2/0 cmp ri,0 1/1 1/0 jgt
Příklad software pipelining 99 čas R MEM ALU W 0 mov1, dec1 1 inc1 mov1 dec1 2/1 inc rp 0/0 2/1 1/1 mov r1,[rp] xor rs,r1 2/1 0/1 2/1 4/0 dec ri 0/1 2/0 cmp ri,0 1/1 1/0 jgt 2 cmp1 mov1 inc1 dec1 3 dec2 mov1 cmp1, jgt1 inc1 4 xor1, mov2 dec2 mov1, cmp1 5 inc2, cmp2 mov2 xor1 dec2 6 dec3 mov2 inc2, cmp2 xor1 7 mov2 dec3, jgt2 inc2, cmp2 8 xor2, mov3 mov2, dec3 9 inc3, cmp3 mov3 xor2 10 dec4 mov3 inc3, cmp3 xor2 11 mov3 dec4, jgt3 inc3, cmp3 12 xor3, mov4 mov3, dec4 13 inc4, cmp4 mov4 xor3 14 dec5 mov4 inc4, cmp4 xor3 15 mov4 dec5, jgt4 inc4, cmp4 16 xor4, mov5 mov4, dec5 17 inc5, cmp5 mov5 xor4 18 dec6 mov5 inc5, cmp5 xor4 19 mov5 dec6, jgt5 inc5, cmp5 20 xor5 mov5, dec6 21 xor5 22 xor5
Software pipelining Modulo scheduling Analýzou grafu závislostí se odhadne počet cyklů na iteraci Sčítají se ohodnocení hran ve smyčkách dvojicemi latence/iterace Rozhoduje nejvyšší podíl součtů na smyčce, např. 4/1 Hledá se rozvrh s daným počtem cyklů na iteraci Nemusí existovat opakuje se pro větší počet Složitější verze zvládají necelé počty cyklů na iteraci Pokud je v grafu závislostí kritická smyčka se součtem ohodnocení např. 5/2 2/1 inc rp 0/0 2/1 1/1 mov r1,[rp] xor rs,r1 2/1 0/1 2/1 4/0 dec ri 0/1 2/0 cmp ri,0 1/1 1/0 jgt 100
Modulo scheduling 101 čas R MEM ALU W T+0 xor[n], mov[n+1] mov[n], dec[n+1] T+1 inc[n+1], cmp[n+1] mov[n+1] xor[n] T+2 dec[n+2] mov[n+1] inc[n+1], cmp[n+1] xor[n] 2/1 dec ri 2/2 T+3 mov[n+1] dec[n+2], jgt[n+1] Zvolená perioda M Rezervační tabulky jsou používány modulo M: časy 0..(M-1) Instrukce jsou rozmisťovány do dvojrozměrného prostoru čas/iterace Rozvrh vyhovuje závislosti T A /I A A L/D pokud (T B -T A )-M*(I B -I A ) L-M*D inc[n+1], cmp[n+1] T B /I B B 2/1 inc rp cmp ri,0 1/1 0/0 2/1 1/1 mov r1,[rp] 0/1 2/1 4/0 xor rs,r1 0/0 0/1 2/0 1/1 1/0 jgt 3/1 0/1 1/1
Modulo scheduling 102 čas R MEM ALU W 0 xor2, mov3 mov2, dec3 1 inc3, cmp3 mov3 xor2 2 dec4 mov3 inc3, cmp3 xor2 3 mov3 dec4, jgt3 inc3, cmp3 4 xor3, mov4 mov3, dec4 5 inc4, cmp4 mov4 xor3 6 dec5 mov4 inc4, cmp4 xor3 2/1 7 mov4 dec5, jgt4 inc4, cmp4 inc rp 1/1 0/0 2/1 dec ri 2/2 0/1 2/0 cmp ri,0 1/1 1/0 jgt 3/1 1/1 2/1 1/1 mov r1,[rp] 0/1 0/1 2/1 4/0 xor rs,r1 0/0
Příklad software pipelining 103 Výsledek pro příklad Výkon: 1/4 iterace/cyklus Zlepšení o 25% Využití jednotek: R: 5/8 MEM: 3/4 ALU: 5/8 W: 5/8 Poslední opakování vzorku provede zbytečně instrukci dec To není chyba Vytvoření kódu z rozvrhu Prolog-smyčka-epilog Dokončení pro odbočky čas R MEM ALU W 0 mov1, dec1 1 inc1 mov1 dec1 2 cmp1 mov1 inc1 dec1 3 dec2 mov1 cmp1, jgt1 inc1 4 xor1, mov2 dec2 mov1, cmp1 5 inc2, cmp2 mov2 xor1 dec2 6 dec3 mov2 inc2, cmp2 xor1 7 mov2 dec3, jgt2 inc2, cmp2 8 xor2, mov3 mov2, dec3 9 inc3, cmp3 mov3 xor2 10 dec4 mov3 inc3, cmp3 xor2 11 mov3 dec4, jgt3 inc3, cmp3 12 xor3, mov4 mov3, dec4 13 inc4, cmp4 mov4 xor3 14 dec5 mov4 inc4, cmp4 xor3 15 mov4 dec5, jgt4 inc4, cmp4 16 xor4, mov5 mov4, dec5 17 inc5, cmp5 mov5 xor4 18 dec6 mov5 inc5, cmp5 xor4 19 mov5 dec6, jgt5 inc5, cmp5 20 xor5 mov5, dec6 21 xor5 22 xor5
Příklad software pipelining 104 mov r1,[rp] dec ri inc rp cmp ri,0 jle l2 dec ri l1: xor rs,r1 mov r1,[rp] inc rp cmp ri,0 dec ri jgt l1 l2: xor rs,r1 čas R MEM ALU W 0 mov1, dec1 1 inc1 mov1 dec1 2 cmp1 mov1 inc1 dec1 3 dec2 mov1 cmp1, jgt1 inc1 4 xor1, mov2 dec2 mov1, cmp1 5 inc2, cmp2 mov2 xor1 dec2 6 dec3 mov2 inc2, cmp2 xor1 7 mov2 dec3, jgt2 inc2, cmp2 8 xor2, mov3 mov2, dec3 9 inc3, cmp3 mov3 xor2 10 dec4 mov3 inc3, cmp3 xor2 11 mov3 dec4, jgt3 inc3, cmp3 12 xor3, mov4 mov3, dec4 13 inc4, cmp4 mov4 xor3 14 dec5 mov4 inc4, cmp4 xor3 15 mov4 dec5, jgt4 inc4, cmp4 16 xor4, mov5 mov4, dec5 17 inc5, cmp5 mov5 xor4 18 dec6 mov5 inc5, cmp5 xor4 19 mov5 dec6, jgt5 inc5, cmp5 20 xor5 mov5, dec6 21 xor5 22 xor5
Software pipelining Duplikace proměnných (Variable expansion) Duplikací proměnných lze odstranit některé antidependence Teoreticky všechny registrové, roste však počet použitých registrů i velikost kódu Duplikace proměnných se provede duplikací kódu a vhodným přeznačením 2/1 inc rp 0/0 2/1 1/1 mov r1,[rp] xor rs,r1 2/1 0/1 2/1 4/0 dec ri 0/1 2/0 cmp ri,0 1/1 1/0 jgt 105
Variable expansion 106 Duplikace proměnných (Variable expansion) Duplikací proměnných lze odstranit některé antidependence Teoreticky všechny registrové, roste však počet použitých registrů i velikost kódu Duplikace proměnných se provede duplikací kódu a vhodným přeznačením dec ri 0/1 2/0 cmp ri,0 1/0 jgt inc rp 1/1 2/1 2/1 2/1 2/1 0/1 2/0 cmp ri,0 1/1 1/0 dec ri jgt inc rp Příklad: Zdvojením proměnné r1 se kritický cyklus zkrátí z poměru 4/1 na 4/2 0/0 1/1 mov r1,[rp] 0/2 4/0 xor rs,r1 2/1 2/1 0/0 2/1 1/1 mov r2,[rp] 0/2 4/0 2/1 xor rs,r2
Software pipelining Duplikace proměnných (Variable expansion) Duplikací proměnných lze odstranit některé antidependence Teoreticky všechny registrové, roste však počet použitých registrů i velikost kódu Duplikace proměnných se provede duplikací kódu a vhodným přeznačením Duplikaci je možné provést až po schedulingu, který odhalí, která duplikace je užitečná Před schedulingem se odstraní antidependence odstranitelné duplikací 2/1 inc rp 2/1 1/1 mov r1,[rp] 2/1 4/0 xor rs,r1 2/1 dec ri 2/0 cmp ri,0 1/1 1/0 jgt 107
Příklad modulo scheduling s duplikací 108 l1: xor rs,r1 ; čas 0, iterace 2 inc rp ; čas 0, iterace 3 cmp ri,0 ; čas 1, iterace 4 mov r1,[rp] ; čas 2, iterace 4 dec ri ; čas 2, iterace 5 jle l2 ; čas 3, iterace 4 xor rs,r2 ; čas 3, iterace 3 inc rp ; čas 3, iterace 4 cmp ri,0 ; čas 4, iterace 5 mov r2,[rp] ; čas 5, iterace 5 dec ri ; čas 5, iterace 6 jgt l1 ; čas 6, iterace 5 l2: 2/1 inc rp cmp ri,0 0/1 0/0 2/1 1/1 mov r1,[rp] 2/1 0/2 2/1 4/0 xor rs,r1 0/0 dec ri 2/3 0/1 2/0 1/1 1/0 jgt 0/1 2/2 1/2 čas R MEM ALU W 0 xor2, inc3 mov3 dec4, jgt3 mov2, cmp3 1 cmp4 mov3 xor2, inc3 dec4 2 mov4, dec5 mov3 cmp4 xor2, inc3
Příklad Intel compiler x64 109 char chksum( char * p, int i) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; }..B1.4: movsbq (%rdi), %r8 movsbq 1(%rdi), %r9 xorl %r8d, %eax xorl %r9d, %eax addq $2, %rdi addl $1, %ecx cmpl %edx, %ecx jb..b1.4 /*...*/ k = i >> 1; j = 0; do { r8 = *p; r9 = *(p+1); s ^= r8; s ^= r9; p += 2; j += 1; } while ( j < k ); /*... */
Cray 1 110
Cray 1 111
Cray 1 112
Cray 1 113
Paralelizace 114 Hardware Proč paralelizace? Zrychlení stojí příliš mnoho energie P = k f 2 Místa je dost (10 8 tranzistorů/chip) Jak paralelizovat? ve světě Intel (IA-32) jinde Pipelining (1989: i486) bez duplikace Superskalarita (1993: Pentium) duplikace ALU, původní instrukce SIMD (1996: Pentium MMX) duplikace ALU, nové instrukce Hyperthreading (1998: Xeon) duplikace registrů Multi-core (2005: Core 2 Duo) duplikace CPU Vektorové instrukce (1974: Cray) částečná duplikace