Stáhnout prezentaci
Prezentace se nahrává, počkejte prosím
1
Konstrukce překladačů David Bednárek www.ksi.mff.cuni.cz
2
Pravidla studia SWI109 2/1 Z,Zk
3
3 Pravidla studia 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á
4
Literatura
5
5 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)
6
Historie překladačů
7
Vývoj hardware a programovacích jazyků 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ý 7
8
Source: James Laurus @ HiPEAC 2015 8
9
Source: William J. Dally @ HiPEAC 2015 9
10
10
11
Source: William J. Dally @ HiPEAC 2015 11
12
Architektura překladače
13
13 Architektura překladače Amatérský pohled Lexikální analyzátor Parser Sémantický analyzátor Generátor kódu Posloupnost tokenů Derivační strom Cílový kód
14
14 Architektura překladače Z velké dálky Generátor mezikódu Lexikální analyzátor Parser Sémantický analyzátor Generátor kódu Posloupnost tokenů Derivační strom Mezikód Derivační strom Cílový kód front-end závislý na vstupním jazyku back-end závislý na cílovém stroji
15
15 Architektura překladače S optimalizacemi Optimalizace Generátor mezikódu Strojově závislé optimalizace Lexikální analyzátor Parser Sémantický analyzátor Optimalizace Generátor kódu Strojově závislé optimalizace Posloupnost tokenů Derivační strom Mezikód (střední úrovně) Derivační strom Mezikód (střední úrovně) Mezikód nízké úrovně Cílový kód front-end závislý na vstupním jazyku back-end závislý na cílovém stroji
16
16 Architektura překladače Detailní pohled akademika (pouze optimalizace) Muchnick: Advanced Compiler Design and Implementation Scalar replacement of array references Data-cache optimizations 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 Constant folding Algebraic simplification and reassociation
17
17 Architektura překladače 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 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 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
18
Názvosloví
19
19 Základní bloky Procedura Procedura nebo funkce Call graph Graf (možného) volání mezi procedurami 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
20
20 Dag 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í
21
21 Typy 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ší
22
22 Proměnné 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
23
23 Alias 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
24
24 Live range 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
25
25 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 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ů
26
26 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 Pokročilá interprocedurální optimalizace: Automatická úprava volací konvence podle místních podmínek volaného a všech volajících
27
27 Činnost překladače, optimalizace Intraprocedurální Uvnitř jedné procedury Lokální Uvnitř jednoho základního bloku Příklad: Jednodušší verze schedulingu nebo CSE Globální 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
28
28 Typy 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í
29
29 Mezikódy 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)
30
Mezikódy
31
31 Mezikódy 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
32
32 Mezikódy 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ů
33
33 Mezikódy 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.)
34
34 Mezikódy 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ů
35
35 Plně sekvenční čtveřicový mezikód 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
36
36 Částečně sekvenční čtveřicový mezikód 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 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
37
37 Nesekvenční mezikód (před analýzou aliasů) 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”) 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) GTC_I32(C1) JC LD_I32(Px) LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 LD_I32(Py) RET_I32 Control-Flow vždy if true if false Dag Data-flow Závislosti
38
38 Mezikódy střední úrovně 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
39
39 Mezikódy střední úrovně 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
40
40 Detekce základních bloků 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
41
41 Detekce základních bloků 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 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 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
42
42 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
43
43 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; } 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 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
44
44 Nesekvenční mezikód s hranicemi příkazů 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”) 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) GTC_I32(C1) JC LD_I32(Px) LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 LD_I32(Py) RET_I32 Control-Flow vždy if true if false Dag operand
45
45 Nesekvenční mezikód před analýzou aliasů 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”) 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) GTC_I32(C1) JC LD_I32(Px) LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 LD_I32(Py) RET_I32 Control-Flow vždy if true if false Dag operand pořadí
46
46 Odstranění závislostí po analýze aliasů LD_I32(Py) ST_I32(Vz) ST_I32(Py) LD_I32(Vz) ST_I32(Px) MOD_I32 LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 LD_I32(Px)
47
47 Odstranění závislostí po analýze aliasů 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) GTC_I32(C1) JC LD_I32(Px) LD_I32(Py) ST_I32(Vz) LD_I32(Px) ST_I32(Py) LD_I32(Vz) ST_I32(Px) LD_I32(Px) MOD_I32 LD_I32(Py) RET_I32
48
48 Nesekvenční mezikód po analýze aliasů 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”) 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) Control-Flow vždy if true if false Dag dependence: operand w-r w-? antidependence: r-w ?-w
49
49 Nesekvenční mezikód s rozsahy platnosti 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”) Dag operand antidependence: r-w 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) Oblasti platnosti Px Py Vz/1 Vz/2
50
50 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
51
51 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 for each b in BB L(b,v) = R(b,v); do { for each 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
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
53
53 Nesekvenční mezikód s rozhraními BB 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”) Rozhraní 1 Px Py Rozhraní 2 Px Py 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 Dag operand antidependence: r-w
54
54 Náhrada proměnných rozhraním BB GT_I32 JC ENTER GTC_I32(C1) JC RET_I32 MOD_I32 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
55
55 Nesekvenční mezikód bez proměnných 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”) Dag data Rozhraní 1 Px Py Rozhraní 2 Px Py GT_I32 JC ENTER GTC_I32(C1) JC RET_I32 MOD_I32
56
56 Nesekvenční mezikód po duplikaci BB 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”) Dag data Rozhraní 1 Px Py Rozhraní 2 Px Py RET_I32 GTC_I32(C1) JC GT_I32 JC ENTER MOD_I32 GTC_I32(C1) JC RET_I32
57
57 Nesekvenční mezikód po přeznačení 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”) RET_I32 GTC_I32(C1) JC GT_I32 JC ENTER MOD_I32 GTC_I32(C1) JC RET_I32
58
58 Nesekvenční mezikód po přeznačení 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; } RET_I32 GTC_I32(C1) JC GT_I32 JC ENTER MOD_I32 GTC_I32(C1) JC RET_I32
59
59 Nesekvenční mezikód po optimalizaci skoků 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; } GTC_I32(C1) JC MOD_I32 GTC_I32(C1) JC GTC_I32(C1) JC GTC_I32(C1) JC MOD_I32 RET_I32 GT_I32 JC ENTER RET_I32 L4 L3 L1 L2L5
60
60 Architektura back-endu 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í Strojově závislé optimalizace Optimalizace Generátor kódu Strojově závislé optimalizace Mezikód (střední úrovně) Mezikód nízké úrovně Cílový kód Mezikód (střední úrovně)
61
61 Architektura back-endu Sekvenční mezikód Strojově závislé optimalizace Alokace registrů Sekvenční mezikód nízké úrovně s fyzickými registry Strojově závislé optimalizace Optimalizace Instruction selection = Výběr instrukcí Strojově závislé optimalizace Sekvenční mezikód (střední úrovně) Sekvenční mezikód (střední úrovně) 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 (střední úrovně) Finalizer Sekvenční mezikód Strojový kód
62
62 Architektura back-endu Sekvenční mezikód Alokace registrů Optimalizace Instruction selection = Výběr instrukcí Finalizer Zjednodušená analýza rozsahů platnosti Sekvenční mezikód (střední úrovně) Sekvenční mezikód nízké úrovně s virtuálními registry Sekvenční mezikód nízké úrovně s fyzickými registry Strojový kód Analýza aliasů Optimalizace
63
63 Architektura back-endu Částečně sekvenční mezikód bez schedulingu Optimalizace Alokace registrů Optimalizace Instruction selection = Výběr instrukcí Finalizer Detekce základních bloků Live-range analysis = Analýza rozsahů platnosti Basic-block (re)ordering = Serializace control-flow 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 Analýza aliasů Optimalizace
64
64 Architektura back-endu Částečně sekvenční mezikód se schedulingem Optimalizace Alokace registrů Optimalizace Instruction selection = Výběr instrukcí Finalizer Detekce základních bloků Live-range analysis = Analýza rozsahů platnosti Basic-block (re)ordering = Serializace control-flow 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 Analýza aliasů Optimalizace Instruction scheduling = Řazení instrukcí Instruction scheduling = Řazení instrukcí
65
65 Architektura back-endu Nesekvenční mezikód Optimalizace Alokace registrů Optimalizace Instruction selection = Výběr instrukcí Finalizer Detekce základních bloků Live-range analysis = Analýza rozsahů platnosti Basic-block (re)ordering = Serializace control-flow Sekvenční mezikód (střední úrovně) Nesekvenč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 Analýza aliasů Optimalizace Instruction scheduling = Řazení instrukcí Instruction scheduling = Řazení instrukcí Nesekvenční mezikód nízké úrovně s virtuálními registry
66
66 Architektura back-endu 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
67
67 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
68
68 Alokace registrů 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
69
69 Alokace registrů 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
70
70 Alokace registrů 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
71
71 Alokace registrů 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; 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
72
72 Alokace registrů 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
73
73 Alokace registrů 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ů
74
74 Alokace registrů 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
75
75 Alokace registrů 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
76
76 Scheduling 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
77
77 Scheduling 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í
78
78 Scheduling 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
79
79 Scheduling 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í
80
80 Scheduling 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í
81
81 Scheduling 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 super- skalární Mají Limit(k)=1, ale různé a tudíž konfliktní rezervační tabulky
82
82 Příklad – mezikód střední úrovně char chksum( char * p, int i) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; } GTC_I32(C1) JC XLD_I8 RET_I32 GTC_I32(C1) JC ENTER C_I8(C2) ADDC_P(C3) SUBC_I32(C4) XOR_I8
83
83 Příklad – mezikód nízké úrovně char chksum( char * p, int i) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; } cmp ri,0 jgt mov r1,[rp] ret cmp i,0 jgt mov rs,0 inc rp dec rixor rs,r1
84
84 Příklad – latence Tabulka latencí Instrukce-instrukce Instrukce-konec BB Pesimistická varianta (instrukce musí být dokončena v tomto BB) 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é Instrukce jgt musí být poslední Latence vůči konci BB se normalizují odečtením latence jgt cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 1 z instrukcedo instrukcečas cmp ri,0jgt1 mov r1,[rp]xor rs,r14 dec ricmp ri,c2 mov r1,[rp]inc rp0 z instrukcečas inc rp2 jgt1 dec ri2 xor rs,r12 1 1 1 0
85
85 Rezervační tabulky Příklad – rezervační tabulky Kapacita procesoru inc r1 dec r1 xor r1,r2 cmp r1,r2 RMEMALUW 01 11 21 mov r1,[r2]RMEMALUW 01 11 21 31 41 jgtRMEMALUW 01 RMEMALUW 1121 cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 1 1 1 1 0
86
86 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 Příklad – scheduling časRMEMALUW 0mov 1 2 3 4 cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 1 1 1 1 0
87
87 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í Příklad – scheduling časRMEMALUW 0mov 1 2 3 4xormov 5xor 6 cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 1 1 1 1 0 0
88
88 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 Příklad – scheduling časRMEMALUW 0mov 1decmov 2 dec 3movdec 4xormov 5xor 6 cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 1 1 1 1 0 0
89
89 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í Příklad – scheduling časRMEMALUW 0mov 1decmov 2 dec 3cmpmovdec 4xorcmpmov 5xorcmp 6xor cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 1 1 1 1 0 1 0
90
90 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 Příklad – scheduling časRMEMALUW 0mov 1decmov 2 dec 3cmpmovdec 4xorcmpmov 5incxorcmp 6incxor 7inc cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 3 1 1 1 1 0 1 0
91
91 Krok 6 Připravené instrukce jgt Latenci vyhovuje čas 4 Instrukce však musí být poslední Vybrán čas 5 Příklad – scheduling časRMEMALUW 0mov 1decmov 2 dec 3cmpmovdec 4xorcmpmov 5incxor, jgtcmp 6incxor 7inc cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 3 1 1 1 1 0 1 5
92
92 char chksum( char * p, int i) { char s = 0; while ( i > 0 ) { s ^= *p++; --i; } return s; } Výsledný kód mov r1,[rp] dec ri cmp ri,0 xor rs,r1 inc rp jgt Příklad – scheduling časRMEMALUW 0mov 1decmov 2 dec 3cmpmovdec 4xorcmpmov 5incxor, jgtcmp 6incxor 7inc cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 3 5 1 1 1 0 1 5
93
93 Scheduling 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
94
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 Příklad – scheduling časRMEMALUW 0mov 1decmov 2 dec 3cmpmovdec 4inccmpmov 5xorinc, jgtcmp 6movxorinc 7decmovxor 8movdec 9cmpmovdec 10inccmpmov 11xorinc, jgtcmp 12movxorinc 13decmovxor 14movdec 15cmpmovdec
95
95 Efekt kapacity procesoru Stejné latence i res. tabulky mov r1,[rp] dec ri inc rp cmp ri,0 xor rs,r1 jgt Výkon: 1/5 iterace/cyklus Využití jednotek: R: 5/10 MEM: 3/5 ALU: 5/10 W: 5/10 Příklad – scheduling časRMEMALUW 0mov, dec 1incmovdec 2cmpmovincdec 3movcmpinc 4xorjgtmov, cmp 5mov, decxor 6incmovdecxor 7cmpmovincdec 8movcmpinc 9xorjgtmov, cmp 10mov, decxor 11incmovdecxor 1212cmpmovincdec 1313movcmpinc 1414xorjgtmov, cmp RMEMALUW 2122
96
96 Závislosti uvnitř BB Závislosti přes hranice BB (loop-carried dependences) Příklad – software pipelining z instrukcedo instrukcečas cmp ri,0jgt1 mov r1,[rp]xor rs,r14 dec ricmp ri,c2 mov r1,[rp]inc rp0 cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 10 2 2 2 10 z instrukcedo instrukcečas inc rp 2 mov r1,[rp]2 dec ri 2 cmp ri,0dec ri0 xor rs,r1 2 mov r1,[rp]0 jgtcmp ri,01 jgtmov r1,[rp]1 1 0 2
97
97 Příklad – software pipelining časRMEMALUW 0mov1, dec1 1inc1mov1dec1 2cmp1mov1inc1dec1 3dec2mov1cmp1, jgt1inc1 4xor1, mov2dec2mov1, cmp1 5inc2, cmp2mov2xor1dec2 6dec3mov2inc2, cmp2xor1 7mov2dec3, jgt2inc2, cmp2 8xor2, mov3mov2, dec3 9inc3, cmp3mov3xor2 10dec4mov3inc3, cmp3xor2 11mov3dec4, jgt3inc3, cmp3 1212xor3, mov4mov3, dec4 1313inc4, cmp4mov4xor3 14dec5mov4inc4, cmp4xor3 15mov4dec5, jgt4inc4, cmp4 16xor4, mov5mov4, dec5 17inc5, cmp5mov5xor4 18dec6mov5inc5, cmp5xor4 19mov5dec6, jgt5inc5, cmp5 20xor5mov5, dec6 21xor5 22xor5 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
98
98 Příklad – software pipelining Jiná abstrakce latence/iterace cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/1 1/1 cmp ri,0 jgt mov r1,[rp] inc rp dec rixor rs,r1 4 2 10 2 2 2 10 1 0 2 0/0 0/1
99
99 Příklad – software pipelining časRMEMALUW 0mov1, dec1 1inc1mov1dec1 2cmp1mov1inc1dec1 3dec2mov1cmp1, jgt1inc1 4xor1, mov2dec2mov1, cmp1 5inc2, cmp2mov2xor1dec2 6dec3mov2inc2, cmp2xor1 7mov2dec3, jgt2inc2, cmp2 8xor2, mov3mov2, dec3 9inc3, cmp3mov3xor2 10dec4mov3inc3, cmp3xor2 11mov3dec4, jgt3inc3, cmp3 1212xor3, mov4mov3, dec4 1313inc4, cmp4mov4xor3 14dec5mov4inc4, cmp4xor3 15mov4dec5, jgt4inc4, cmp4 16xor4, mov5mov4, dec5 17inc5, cmp5mov5xor4 18dec6mov5inc5, cmp5xor4 19mov5dec6, jgt5inc5, cmp5 20xor5mov5, dec6 21xor5 22xor5 cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/1 1/1 0/0 0/1
100
100 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 cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/1 1/1 0/0 0/1
101
101 Modulo scheduling 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 pokud (T B -T A )-M*(I B -I A ) ≥ L-M*D cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/1 1/1 0/0 0/1 časRMEMALUW T+0xor[N], mov[N+1] mov[N], dec[N+1] T+1inc[N+1], cmp[N+1] mov[N+1]xor[N] T+2dec[N+2]mov[N+1]inc[N+1], cmp[N+1] xor[N] T+3mov[N+1]dec[N+2], jgt[N+1] inc[N+1], cmp[N+1] 1/1 2/2 3/1 0/1 0/0 B A L/DL/D TB/IBTB/IB TA/IATA/IA
102
102 Modulo scheduling cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/1 1/1 0/0 0/1 časRMEMALUW 0xor2, mov3mov2, dec3 1inc3, cmp3mov3xor2 2dec4mov3inc3, cmp3xor2 3mov3dec4, jgt3inc3, cmp3 4xor3, mov4mov3, dec4 5inc4, cmp4mov4xor3 6dec5mov4inc4, cmp4xor3 7mov4dec5, jgt4inc4, cmp4 1/1 2/2 3/1 0/1 0/0
103
103 Příklad – software pipelining časRMEMALUW 0mov1, dec1 1inc1mov1dec1 2cmp1mov1inc1dec1 3dec2mov1cmp1, jgt1inc1 4xor1, mov2dec2mov1, cmp1 5inc2, cmp2mov2xor1dec2 6dec3mov2inc2, cmp2xor1 7mov2dec3, jgt2inc2, cmp2 8xor2, mov3mov2, dec3 9inc3, cmp3mov3xor2 10dec4mov3inc3, cmp3xor2 11mov3dec4, jgt3inc3, cmp3 1212xor3, mov4mov3, dec4 1313inc4, cmp4mov4xor3 14dec5mov4inc4, cmp4xor3 15mov4dec5, jgt4inc4, cmp4 16xor4, mov5mov4, dec5 17inc5, cmp5mov5xor4 18dec6mov5inc5, cmp5xor4 19mov5dec6, jgt5inc5, cmp5 20xor5mov5, dec6 21xor5 22xor5 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
104
104 Příklad – software pipelining časRMEMALUW 0mov1, dec1 1inc1mov1dec1 2cmp1mov1inc1dec1 3dec2mov1cmp1, jgt1inc1 4xor1, mov2dec2mov1, cmp1 5inc2, cmp2mov2xor1dec2 6dec3mov2inc2, cmp2xor1 7mov2dec3, jgt2inc2, cmp2 8xor2, mov3mov2, dec3 9inc3, cmp3mov3xor2 10dec4mov3inc3, cmp3xor2 11mov3dec4, jgt3inc3, cmp3 1212xor3, mov4mov3, dec4 1313inc4, cmp4mov4xor3 14dec5mov4inc4, cmp4xor3 15mov4dec5, jgt4inc4, cmp4 16xor4, mov5mov4, dec5 17inc5, cmp5mov5xor4 18dec6mov5inc5, cmp5xor4 19mov5dec6, jgt5inc5, cmp5 20xor5mov5, dec6 21xor5 22xor5 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
105
105 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 cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/1 1/1 0/0 0/1
106
106 Variable expansion 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 Příklad: Zdvojením proměnné r1 se kritický cyklus zkrátí z poměru 4/1 na 4/2 cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 0/2 1/1 0/0 0/1 cmp ri,0 jgt mov r2,[rp] inc rp dec ri xor rs,r2 2/1 1/1 1/0 2/0 4/0 0/2 1/1 0/0 0/1
107
107 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í cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 1/1
108
108 Příklad – modulo scheduling s duplikací časRMEMALUW 0xor2, inc3mov3dec4, jgt3mov2, cmp3 1cmp4mov3xor2, inc3dec4 2mov4, dec5mov3cmp4xor2, inc3 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: cmp ri,0 jgt mov r1,[rp] inc rp dec ri xor rs,r1 2/1 1/1 1/0 2/0 4/0 1/1 0/0 0/1 1/2 2/3 0/1 2/2 0/0 0/2
109
109 Příklad – Intel compiler – x64 /*...*/ k = i >> 1; j = 0; do { r8 = *p; r9 = *(p+1); s ^= r8; s ^= r9; p += 2; j += 1; } while ( j < k ); /*... */ 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
110
Cray 1 110
111
Cray 1 111
112
Cray 1 112
113
Cray 1 113
114
114 Paralelizace 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) 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 jinde Vektorové instrukce (1974: Cray) – částečná duplikace
115
115 Paralelizace Z pohledu software Jemnozrnný paralelismus ILP Pipelining Superskalarita SIMD Mírně vektorové instrukce (2-8) Vektorové instrukce (64) Hrubozrnný paralelismus SMP Hyperthreading Multi-core Multi-socket NUMA Multi-core a multi-socket (cc-NUMA) NUMA architektury Cluster
116
116 Paralelizace Z pohledu překladače Jemnozrnný paralelismus ILP Scheduling, software pipelining – změna pořadí instrukcí Vektorizace - automatické použití SIMD instrukcí Překladač vybírá speciální instrukce Překladač určuje pořadí provádění Hrubozrnný paralelismus SMP – Multithreading Strip-Mining - Překladač dělí kód na nezávislé bloky Pořadí provádění je náhodné
117
117 Paralelizace Vektorizace Nízký stupeň (2-8) Náhodné seskupování a = b + c; d = e + f; Vektorové seskupování a[i] = b[i] + c[i]; a[i+1] = b[i+1] + c[i+1]; Rozvíjení cyklů podle dostupného stupně vektorizace Vysoký stupeň (64) Aplikovatelné na cykly s afinním chováním Numerické aplikace (FORTRAN 90) Nepoužitelné při nebezpečí aliasingu (objektové programování) Řada afinních transformací cyklů Loop Reversal Loop Skewing
118
Loop reversal Výměna vzájemného zanoření cyklů Příklad: Učebnicový zápis násobení matic for I := 1 to M do for J := 1 to N do for K := 1 to P do C[I,J] := C[I,J]+A[I,K]*B[K,J] Nelze paralelizovat - následující iterace závisí na předchozí Po výměně zanoření cyklů K/J for I := 1 to M do for K := 1 to P do for J := 1 to N do C[I,J] := C[I,J]+A[I,K]*B[K,J] Překladač si musí být jistý zachováním semantiky Je nutno vyloučit aliasing C=A resp. C=B Tato úprava může zhoršit chování vzhledem k cache Závisí na rozměrech P, N V nenumerických aplikacích je užitečnost nejistá 118
119
Loop reversal Původní průchod Většina sousedů v průchodu je závislá 119
120
Loop reversal Upravený průchod Většina sousedů v průchodu je nezávislá 120
121
Loop skewing Obecnější příklad for J:=1 to N do for K:=N-J to P do A[J,K]:=A[J-1,K]+A[J,K-1] 121 K J
122
Loop skewing Obecnější příklad for J:=1 to N do for K:=N-J to P do A[J,K]:=A[J-1,K]+A[J,K-1] 122 K J
123
Loop skewing Loop skewing for J:=1 to N do for K:=N-J to P do A[J,K]:=A[J-1,K]+A[J,K-1] Prostor iterací = prostor k-tic řídících proměnných Hranice dány lineárními nerovnicemi (s konstantními koeficienty) Detekce závislostí mezi iteracemi Přístupy do paměti (indexy) určeny lineárními výrazy (s k. k.) Směry (vektory) závislostí popisují zakázané směry iterací Hledá se vektor ležící mimo konvexní obal vektorů závislostí Výsledná smyčka pro jemnozrnnou paralelizaci Vnější iterace: Vektor (vektory) v konvexním obalu závislostí Vnitřní iterace: Vektor (vektory) mimo konvexní obal závislostí Složitější případy: Prostor iterací se dělí na části řešené zvlášť Zjednodušení mezí cyklů: dělení na části jednoduchých tvarů 123
124
Hrubozrnná paralelizace Hrubozrnná paralelizace Velká běhová režie Vytváření vláken/úloh, plánování Vyžaduje relativně velké na sobě nezávislé bloky Předvídatelně velké bloky: Počet bloků odpovídá počtu CPU Nepředvídatelně velké bloky: Počet bloků větší než počet CPU Bloky se přidělují na CPU dynamicky Různé schopnosti běhového prostředí Vlákna bez vzájemné synchronizace Bloky na sobě nesmějí vůbec záviset Pipeline parallelism, task parallelism Plánovač respektuje vzájemné závislosti bloků Závislosti nesmějí být cyklické 124
125
Strip mining Hledání zcela nezávislých bloků 125
126
Hrubozrnná paralelizace Hrubozrnná paralelizace Bloky k paralelizaci je třeba vybírat v horních patrech iterací Zajištění dostatečné velikosti bloků Smyčku je vhodné upravit, aby vnější iterace nebyly závislé Stejné metody jako u jemnozrnné paralelizace, ale opačný cíl Loop skewing pro hrubo- i jemnozrnnou paralelizaci Vnější iterace nezávislé (pro hrubozrnnou paralelizaci) Střední iterace závislé Vnitřní iterace nezávislé (pro jemnozrnnou paralelizaci) Problém: Vrstev iterací je obvykle málo Středních iterací musí být dost na pokrytí konvexního obalu závislostí 126
127
Hrubozrnná paralelizace Problém: Vrstev iterací je obvykle málo Řešení: Blocking Prostor iterací se rozdělí na menší díly (bloky) Tím vznikají další úrovně iterace Iterace nad bloky se uspořádají pro hrubozrnný paralelismus Vnější iterace řeší nezávislé skupiny bloků Strip mining: Vlákna Vnitřní iterace procházejí závislé bloky uvnitř skupin Task parallelism: Tasky Iterace uvnitř bloky se upraví pro jemnozrnný paralelismus Vnější iterace jsou závislé Vnitřní iterace jsou bez závislostí SIMD, ILP 127
128
Blocking Vytvoření bloků 128
129
Blocking Strip mining nad bloky 129
130
Blocking Loop skewing uvnitř bloků 130
131
Blocking Blocking Původní iterace rozděleny na 2 patra Vždy ekvivalentní Problémy se zbytky Nové iterace promíchány Pouze některé výměny jsou možné Cílem je nezávislost v okrajových patrech parallel for J1 := 0 to N-1 step SJ do for K1 := 0 to P-1 step SK do for K2 := 1 to SK do parallel for J2 := 1 to SJ do begin J := J1 * SJ + J2; K := K1 * SK + K2; C[I,J] := C[I,J]+A[I,K]*B[K,J] end 131 K J
132
Blocking Blocking Implementace vlákny for J1 := 0 to N-1 step SJ do create_thread( f, J1); wait(); function f( J1) begin for K1 := 0 to P-1 step SK do for K2 := 1 to SK do parallel for J2 := 1 to SJ do begin J := J1 * SJ + J2; K := K1 * SK + K2; C[I,J] := C[I,J]+A[I,K]*B[K,J] end 132 K J
133
Blocking Blocking Task parallelism for J1 := 0 to N-1 step SJ do for K1 := 0 to P-1 step SK do begin TN := new_task( f, J1, K1); add_dep( TP, TN); TP := TN; end; run(); function f( J1, K1) begin for K2 := 1 to SK do parallel for J2 := 1 to SJ do begin J := J1 * SJ + J2; K := K1 * SK + K2; C[I,J] := C[I,J]+A[I,K]*B[K,J] end 133 K J
134
Optimalizace pro cache Podmínky dobrého využití cache Prostorová lokalita Jednotkou přístupu je cache line (typ. 64 B) Přístupy do sousedních míst mají být blízko, nejlépe najednou Zápisy také způsobují výpadky - antidependence se řeší také Časová lokalita Čím delší je doba mezi přístupy, tím větší je pravděpodobnost výpadku Přístupy k témuž paměťovému místu je výhodné v čase rozmístit nepravidelně Blocking většinou vyhovuje i z pohledu cache Obvykle řeší časovou lokalitu Prostorová lokalita závisí na datových strukturách Překladače procedurálních jazyků datové struktury neupravují Mohou však upravit časový průběh přístupů 134
135
Points-to analysis Alias analysis
136
Points-to analysis Points-to analysis Paměťová místa se rozdělí do skupin Zahrnuje proměnné i dynamicky alokovanou paměť U každého přístupu se určí, které skupiny může zasáhnout Přístupem je výraz (lvalue) v překládaném kódu Cíl: Přesnější analýza závislostí Mají-li dva přístupy disjunktní množiny points-to skupin, nemůže mezi nimi být alias, a tudíž jsou nezávislé Problém: Paměťových míst je neomezeně mnoho Překladač má k dispozici omezené prostředky Překladač řeší všechny možnosti control-flow najednou 136
137
Points-to analysis Varianty Kontextová analýza (flow-sensitive) Points-to informace je relativní vůči pozici v programu Semantika: Na pozici P v programu výraz E může označovat paměťové místo ze skupiny M Pozice může a nemusí zahrnovat kontext, odkud byla procedura volána (call stack) Bezkontextová analýza (flow-insensitive) Points-to informace je společná pro celý program Semantika: Existuje pozice v programu, kde výraz E může označovat paměťové místo ze skupiny M Překladač počítá horní odhad Sémantika: "může" = "překladač nebyl schopen vyloučit" 137
138
Points-to analysis Překladač počítá horní odhad Sémantika: "může" = "překladač nebyl schopen vyloučit" Všechny metody analýzy jsou iterativní (rekurzivní) Zvolená přesnost reprezentace dramaticky ovlivňuje výsledek Analýza s kontextem: p = & x;// p -> x * p = & y; // x -> y p = & u;// p -> u * p = & v;// u -> v Bez kontextu: p -> {x, u} x -> {y, v} u -> {y, v} 138
139
Points-to analysis 139
140
Points-to analysis 140
141
Další optimalizace 141
142
142 Architektura překladače Detailní pohled akademika (pouze optimalizace) Muchnick: Advanced Compiler Design and Implementation Scalar replacement of array references Data-cache optimizations 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 Constant folding Algebraic simplification and reassociation
143
143 Další optimalizace Částečné vyhodnocení Část požadovaného výpočtu je vyhodnocována již překladačem Výpočet konstantních výrazů Constant-expression evaluation (constant folding) Výpočet podmíněně konstantních výrazů Sparse conditional constant propagation Algebraické úpravy Využití algebraických identit ke zjednodušení kódu Algebraické úpravy výrazů Redukce síly v cyklech Strength reduction Odstranění zbytečných kontrol mezí
144
144 Další optimalizace Odstranění redundance Nahrazení opakovaných výpočtů uložením výsledku Copy propagation Lokální/globální eliminace společných podvýrazů Common-subexpression elimination Přesun invariantního kódu z cyklu Loop-invariant code motion Partial-redundancy elimination Lazy code motion Odstranění neužitečného kódu Odstranění mrtvého kódu Dead-code elimination Odstranění nedosažitelného kódu Unreachable-code elimination Optimalizace skoků Jump optimization
145
145 Částečné vyhodnocení Výpočet (pod)výrazů obsahujících pouze konstanty Constant-expression evaluation Obvykle prováděn již front- endem Určení proměnných s konstantním obsahem Constant folding Určení proměnných s podmíněně konstantním obsahem Sparse conditional constant propagation Upravuje control-flow! a = b + (4 * 10); c = 4 * 10; d = c + 5; e = f + (d * 2); if ( g > h ) i = 1; else i = 0; j = i + 1; if ( j > 1 ) k = k + 1;
146
146 Částečné vyhodnocení Výpočet (pod)výrazů obsahujících pouze konstanty Constant-expression evaluation Obvykle prováděn již front- endem Určení proměnných s konstantním obsahem Constant folding Určení proměnných s podmíněně konstantním obsahem Sparse conditional constant propagation Upravuje control-flow! Integrace procedur generuje nové příležitosti pro částečné vyhodnocení void f( int i, bool f) { int j = i + 1; if ( f ) g( j); else h( j); } f( k + 1, false);
147
147 Částečné vyhodnocení a = b + 40; c = 40; d = 45; e = f + 90; if ( g > h ) { i = 1; j = 2; k = k + 1; } else { i = 0; j = 1; } a = b + (4 * 10); c = 4 * 10; d = c + 5; e = f + (d * 2); if ( g > h ) i = 1; else i = 0; j = i + 1; if ( j > 1 ) k = k + 1;
148
148 Algebraické úpravy Algebraické úpravy výrazů Většinou v souvislosti s přítomností konstant Důležité pro ukazatelovou aritmetiku (přístup k polím) Úprava do kanonického tvaru Redukce síly v cyklech Důležité pro přístup k polím Odstranění zbytečných kontrol mezí Machine idioms Instrukce provádějící speciální kombinace nebo varianty operací a = ((b * 3) + 7) * 5 a = b * 15 + 35 for ( i = 1; i < 10; ++i ) a[ 4 * i] = 0; for ( j = 4; j < 40; j += 40 ) a[ j] = 0; int b[ 10]; for ( i = 0; i < 10; ++i ) b[ i] = 0;
149
149 Algebraické úpravy Převedení control-flow na algebraické operace Conditional move Ušetří podmíněné skoky Může přidat zbytečné operace Užitečnost obtížně odhadnutelná Cena podmíněných skoků závisí na úspěšnosti predikce Překladač úspěšnost nedokáže odhadnout Profilem řízené optimalizace if ( a > b ) c = d + e; CMP t,a,b ADD u,d,e CMOV t,c,u
150
150 Odstranění redundance Copy propagation Lokální/globální eliminace společných podvýrazů Přesun invariantního kódu z cyklu Častý výskyt u přístupu k polím Partial-redundancy elimination Lazy code motion Integrace procedur generuje nové redundance b = a; c = b; // c = a; c = a + b; d = a + b; // d = c; for ( i = 0; i < 10; ++i) a[ i] = k + l; if ( a < b ) c = d + e; f = d + e;
151
151 Odstranění neužitečného kódu Odstranění mrtvého kódu Kód, jehož efekt nebude využit Přiřazení do proměnných, které již nebudou čteny Odstranění nedosažitelného kódu Kód, ke kterému nevede cesta Optimalizace skoků Skoky na skoky apod. Řeší především chyby vyprodukované předchozími fázemi Aplikuje se opakovaně a = b + 40; c = 40; d = 45; e = f + 90; if ( g > h ) { i = 1; j = 2; k = k + 1; } else { i = 0; j = 1; }
152
152 Přesouvání kódu Code hoisting Provedení operace dříve, než předepisoval původní program Omezeno závislostmi „very busy“ expression = operace, která bude provedena v každém pokračování Algoritmus: Lazy code motion Užitečnost úpravy je nejistá Paralelismus Omezený počet registrů if ( g > h ) { for ( i = 1; i < k; ++ i) { a[ i] = x + y; } u = x + y; } else { z = x + y; }
153
153 Optimalizace režie volání Integrace procedur a.k.a inline-expansion Listové procedury Nevolají žádné další Není třeba kompletní prolog a epilog procedury Užitečné zejména v přítomnosti výjimek Shrink wrapping Přesouvání prologu a epilogu Ve větvích, které nevolají další procedury, se prolog a epilog anihilují Tail merging Ztotožnění identických konců procedur Poslední BB často obsahuje pouze epilog a je tudíž shodný Šetří velikost kódu Zlepšuje využití I-cache
154
154 Interprocedurální úpravy Specializace procedur Procedury se naklonují Jednotlivé klony se přizpůsobují okolnostem, které panují při jejich volání Speciální tvary argumentů Konstantní argumenty Aliasing Interprocedurální alokace registrů Volací konvence se upravuje podle místních podmínek Efekt je obdobný jako u integrace procedur Specializace vede k menší expanzi kódu Specializace se obtížněji řídí Integrace odstraňuje režii volání Integrace umožňuje další optimalizace
155
155 Nápověda pro procesor Intraprocedural I-cache optimization Využití atomicity cache-line Serializace BB tak, aby chování CPU vedlo k minimálnímu počtu výpadků I-cache Instruction prefetching Data prefetching Využití speciálních instrukcí pro nedestruktivní čtení Branch prediction Generování nápovědy pro branch prediction
156
156 Architektura překladače Detailní pohled akademika (pouze optimalizace) Muchnick: Advanced Compiler Design and Implementation Scalar replacement of array references Data-cache optimizations 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 Constant folding Algebraic simplification and reassociation
157
157 Architektura překladače 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 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 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
Podobné prezentace
© 2024 SlidePlayer.cz Inc.
All rights reserved.