Programovanie

Čo je LLVM? Sila za Swift, Rust, Clang a ďalšími

Nové jazyky a vylepšenia tých súčasných sa rozmnožujú v celej rozvojovej krajine. Mozilla’s Rust, Apple's Swift, Jetbrains's Kotlin a mnoho ďalších jazykov poskytujú vývojárom novú škálu možností pre rýchlosť, bezpečnosť, pohodlie, prenosnosť a výkon.

Prečo teraz? Jedným z veľkých dôvodov sú nové nástroje na vytváranie jazykov - konkrétne kompilátory. A hlavným z nich je LLVM, open source projekt, ktorý pôvodne vytvoril tvorca jazyka Swift Chris Lattner ako výskumný projekt na University of Illinois.

LLVM uľahčuje nielen vytváranie nových jazykov, ale aj rozvoj tých existujúcich. Poskytuje nástroje na automatizáciu mnohých najnevďačnejších častí úlohy vytvárania jazykov: vytvorenie kompilátora, prenos výstupného kódu na viac platforiem a architektúr, generovanie optimalizácií špecifických pre architektúru, ako je vektorizácia, a písanie kódu na spracovanie metafor bežných jazykov, ako je napríklad výnimky. Jeho liberálne licencovanie znamená, že ho možno voľne znovu použiť ako softvérový komponent alebo nasadiť ako službu.

Zoznam jazykov využívajúcich LLVM má veľa známych mien. Jazyk Swift od spoločnosti Apple používa ako rámec kompilátora LLVM a Rust používa LLVM ako základnú súčasť reťazca nástrojov. Mnoho prekladačov má tiež vydanie LLVM, napríklad Clang, kompilátor C / C ++ (tento názov „C-lang“), ktorý je projektom úzko spojeným s LLVM. Mono, implementácia .NET, má možnosť kompilovať do natívneho kódu pomocou back-endu LLVM. A Kotlin, nominálne jazyk JVM, vyvíja verziu jazyka s názvom Kotlin Native, ktorý používa LLVM na kompiláciu do strojovo natívneho kódu.

Definované LLVM

LLVM je vo svojom jadre knižnica na programové vytváranie strojovo natívneho kódu. Vývojár používa API na generovanie pokynov vo formáte nazvanom stredné zastúpeniealebo IR. LLVM potom môže kompilovať IR do samostatného binárneho súboru alebo vykonať kompiláciu JIT (just-in-time) v kóde, ktorá sa má spustiť v kontexte iného programu, napríklad tlmočníka alebo modulu runtime pre daný jazyk.

Rozhrania API LLVM poskytujú primitívne nástroje na vývoj mnohých bežných štruktúr a vzorov nájdených v programovacích jazykoch. Napríklad takmer každý jazyk má koncept funkcie a globálnej premennej a veľa z nich má coroutiny a rozhranie C s cudzou funkciou. LLVM má funkcie a globálne premenné ako štandardné prvky vo svojom IR a má metafory na vytváranie korutín a prepojenie s C knižnicami.

Namiesto toho, aby ste trávili čas a energiu objavovaním týchto konkrétnych kolies, stačí použiť implementácie LLVM a zamerať sa na tie časti vášho jazyka, ktoré si vyžadujú pozornosť.

Prečítajte si viac o Go, Kotlin, Python a Rust

Choď:

  • Klepnite na silu jazyka Google Go
  • Najlepšie IDE a editory v jazyku Go

Kotlin:

  • Čo je Kotlin? Vysvetlenie alternatívy Java
  • Rámce Kotlin: Prieskum vývojových nástrojov JVM

Python:

  • Čo je to Python? Všetko, čo potrebujete vedieť
  • Výukový program: Ako začať s programom Python
  • Šesť základných knižníc pre každého vývojára Pythonu

Hrdza:

  • Čo je to Rust? Spôsob bezpečného, ​​rýchleho a ľahkého vývoja softvéru
  • Naučte sa, ako začať s Rustom

LLVM: Navrhnuté pre prenosnosť

Aby sme pochopili LLVM, mohlo by pomôcť zvážiť analogiu s programovacím jazykom C: C sa niekedy označuje ako prenosný montážny jazyk na vysokej úrovni, pretože má konštrukcie, ktoré môžu úzko mapovať systémový hardvér, a bol prenesený na takmer každá architektúra systému. Ale C je užitočný ako prenosný montážny jazyk iba do istej miery; nebol navrhnutý na tento konkrétny účel.

Naopak, LLVM’s IR bol od začiatku navrhnutý ako prenosná zostava. Jedným zo spôsobov, ako dosahuje túto prenosnosť, je ponúkanie primitívov nezávislých od akejkoľvek konkrétnej architektúry strojov. Napríklad typy celých čísel nie sú obmedzené na maximálnu bitovú šírku základného hardvéru (napríklad 32 alebo 64 bitov). Primitívne typy celých čísel môžete vytvárať pomocou toľkých bitov, koľko je potrebné, napríklad 128-bitové celé číslo. Tiež sa nemusíte obávať remeselného výstupu, ktorý by zodpovedal inštrukčnej sade konkrétneho procesora; LLVM sa o to stará aj za vás.

Architektonicky neutrálny dizajn LLVM uľahčuje podporu hardvéru všetkého druhu, súčasného aj budúceho. Napríklad IBM nedávno prispela kódom na podporu svojich systémov z / OS, Linux on Power (vrátane podpory vektorizačnej knižnice MASS spoločnosti IBM) a architektúry AIX pre projekty C, C ++ a Fortran od LLVM.

Ak chcete vidieť živé príklady LLVM IR, choďte na webovú stránku projektu ELLCC a vyskúšajte živé ukážkové video, ktoré prevádza C kód na LLVM IR priamo v prehliadači.

Ako programovacie jazyky používajú LLVM

Najbežnejším prípadom použitia pre LLVM je kompilátor vopred nastaveného jazyka (AOT) pre daný jazyk. Napríklad projekt Clang v predstihu kompiluje C a C ++ na natívne binárne súbory. Ale LLVM umožňuje aj iné veci.

Just-in-time kompilácia s LLVM

Niektoré situácie vyžadujú, aby sa kód generoval za behu, skôr ako sa kompiluje vopred. Napríklad jazyk Julia, JIT, zostavuje svoj kód, pretože musí bežať rýchlo a komunikovať s používateľom prostredníctvom REPL (cyklus čítania, hodnotenia a tlače) alebo interaktívneho príkazu.

Numba, balíček matematickej akcelerácie pre Python, JIT kompiluje vybrané funkcie Pythonu do strojového kódu. Môže tiež vopred zostaviť kód zdobený Numbou, ale (podobne ako Julia) Python ponúka rýchly vývoj tým, že je interpretovaným jazykom. Používanie kompilácie JIT na výrobu takéhoto kódu dopĺňa interaktívny pracovný tok Pythonu lepšie ako kompilácia vopred.

Iní experimentujú s novými spôsobmi, ako používať LLVM ako JIT, napríklad kompiláciou dotazov PostgreSQL, čo vedie až k päťnásobnému zvýšeniu výkonu.

Automatická optimalizácia kódu pomocou LLVM

LLVM nielenže kompiluje IR do natívneho strojového kódu. Môžete ho tiež programovo nasmerovať tak, aby optimalizoval kód s vysokou mierou podrobnosti, až do konca procesu prepojenia. Optimalizácie môžu byť dosť agresívne, vrátane vecí ako vloženie funkcií, odstránenie mŕtveho kódu (vrátane nepoužitých vyhlásení o type a argumentov funkcií) a odvíjanie slučiek.

Opäť je tu sila v tom, že to všetko nemusíte realizovať sami. Program LLVM ich môže spracovať za vás alebo ho môžete nasmerovať tak, aby ich podľa potreby vypínal. Napríklad, ak chcete menšie binárne súbory za cenu nejakého výkonu, môžete nechať svojmu front-endu kompilátora povedať LLVM, aby zakázal odvíjanie slučiek.

Jazyky špecifické pre doménu s LLVM

LLVM sa používa na výrobu kompilátorov pre mnoho univerzálnych jazykov, ale je tiež užitočné na produkciu jazykov, ktoré sú vysoko vertikálne alebo exkluzívne pre problémovú doménu. V niektorých ohľadoch práve tu svieti LLVM najjasnejšie, pretože odstraňuje veľa driny pri vytváraní takého jazyka a zaisťuje dobrý výkon.

Napríklad projekt Emscripten využíva LLVM IR kód a prevádza ho do JavaScriptu, čo teoreticky umožňuje exportovať kód, ktorý je možné spustiť v prehliadači, ľubovoľnému jazyku so zadným koncom LLVM. Dlhodobým plánom je mať back-endy založené na LLVM, ktoré môžu produkovať WebAssembly, ale Emscripten je dobrým príkladom toho, aká flexibilná môže byť LLVM.

Ďalším spôsobom, ako sa dá použiť LLVM, je pridať rozšírenia špecifické pre doménu do existujúceho jazyka. Nvidia použila program LLVM na vytvorenie kompilátora Nvidia CUDA, ktorý umožňuje jazykom pridať natívnu podporu pre CUDA, ktorá sa kompiluje ako súčasť natívneho kódu, ktorý generujete (rýchlejšie), namiesto aby bola vyvolaná prostredníctvom knižnice, ktorá je k nej dodávaná (pomalšie).

Úspech LLVM v jazykoch špecifických pre doménu podnietil nové projekty v rámci LLVM zamerané na riešenie problémov, ktoré vytvárajú. Najväčším problémom je, ako je ťažké niektoré DSL preložiť do LLVM IR bez toho, aby ste na prednom paneli pracovali. Jedným z riešení v prácach je projekt Multi-Level Intermediate Representation alebo MLIR.

MLIR poskytuje pohodlné spôsoby reprezentácie zložitých dátových štruktúr a operácií, ktoré je potom možné automaticky preložiť do LLVM IR. Napríklad rámec strojového učenia TensorFlow môže mať veľa svojich zložitých operácií grafu toku údajov efektívne zostavených do natívneho kódu pomocou MLIR.

Práca s LLVM v rôznych jazykoch

Typický spôsob práce s LLVM je prostredníctvom kódu v jazyku, ktorý vám vyhovuje (a ktorý samozrejme podporuje knižnice LLVM).

Dve spoločné možnosti jazyka sú C a C ++. Mnoho vývojárov LLVM predvolí jeden z týchto dvoch z niekoľkých dobrých dôvodov:

  • Samotný LLVM je napísaný v jazyku C ++.
  • Rozhrania API LLVM sú k dispozícii v inkarnáciách C a C ++.
  • Veľa jazykov sa zvykne diať s C / C ++ ako základom

Tieto dva jazyky však nie sú jedinými možnosťami. Mnoho jazykov môže natívne volať do knižníc jazyka C, takže je teoreticky možné vykonať vývoj LLVM s ktorýmkoľvek takýmto jazykom. Pomáha však mať skutočnú knižnicu v jazyku, ktorý elegantne obsahuje API LLVM. Našťastie veľa jazykov a jazykových runtime má takéto knižnice, vrátane C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go a Python.

Jedna výhrada je, že niektoré jazykové väzby na LLVM môžu byť menej úplné ako iné. Napríklad v Pythone je veľa možností, ale každá sa líši svojou úplnosťou a užitočnosťou:

  • llvmlite, vyvinutý tímom, ktorý vytvára Numbu, sa stal súčasným uchádzačom o prácu s LLVM v Pythone. Implementuje iba podmnožinu funkčnosti LLVM, ktorá je diktovaná potrebami projektu Numba. Ale táto podmnožina poskytuje drvivú väčšinu toho, čo používatelia LLVM potrebujú. (llvmlite je všeobecne najlepšia voľba pre prácu s LLVM v Pythone.)
  • Projekt LLVM udržiava vlastnú skupinu väzieb na C API LLVM, ale v súčasnosti sa neudržiavajú.
  • llvmpy, prvá populárna väzba Pythonu pre LLVM, vypadla z údržby v roku 2015. Zlé pre akýkoľvek softvérový projekt, ale horšie pri práci s LLVM, vzhľadom na množstvo zmien, ktoré sa v jednotlivých vydaniach LLVM objavia.
  • Cieľom llvmcpy je aktualizovať väzby Pythonu pre knižnicu C, aktualizovať ich automatizovaným spôsobom a sprístupniť ich pomocou natívnych idiómov Pythonu. llvmcpy je stále v počiatočných fázach, ale už môže robiť základnú prácu s API LLVM.

Ak vás zaujíma, ako používať knižnice LLVM na zostavenie jazyka, vlastní tvorcovia LLVM majú výukový program, ktorý používa C ++ alebo OCAML a ktorý vás prevedie vytvorením jednoduchého jazyka s názvom Kaleidoscope. Odvtedy bol prenesený do iných jazykov:

  • Haskell:Priamy port pôvodného tutoriálu.
  • Python: Jeden taký port pozorne sleduje tutoriál, zatiaľ čo druhý je ambicióznejším prepisom pomocou interaktívneho príkazového riadku. Obaja používajú llvmlite ako väzby na LLVM.
  • HrdzaaSwift: Zdalo sa nevyhnutné, že porty tutoriálu dostaneme do dvoch jazykov, ktoré LLVM pomohol uskutočniť.

Nakoniec je tento tutoriál k dispozícii aj včlovek jazykoch. Bol preložený do čínštiny pomocou pôvodného jazyka C ++ a Python.

Čo LLVM nerobí

So všetkým, čo LLVM poskytuje, je užitočné vedieť aj to, čo nerobí.

Napríklad LLVM neanalyzuje gramatiku jazyka. Mnoho nástrojov už túto prácu vykonáva, napríklad lex / yacc, flex / bison, Lark a ANTLR. Analýza má byť aj tak oddelená od kompilácie, takže nie je prekvapujúce, že LLVM sa nič z toho nepokúša vyriešiť.

LLVM tiež nerieši priamo širšiu kultúru softvéru v danom jazyku. Inštalácia binárnych súborov kompilátora, správa balíkov v inštalácii a aktualizácia reťazca nástrojov - to musíte urobiť svojpomocne.

A nakoniec, a čo je najdôležitejšie, stále existujú bežné časti jazykov, pre ktoré LLVM neposkytuje primitívne prvky. Mnoho jazykov má nejaký spôsob správy pamäte zhromaždenej odpadkami, buď ako hlavný spôsob správy pamäte, alebo ako doplnok k stratégiám ako RAII (ktoré C ++ a Rust používajú). LLVM vám neposkytuje mechanizmus na zhromažďovanie odpadu, ale poskytuje nástroje na implementáciu zberu odpadu tým, že umožňuje označovanie kódu metadátami, čo uľahčuje písanie odpadu.

Nič z toho však nevylučuje možnosť, že LLVM môže nakoniec pridať natívne mechanizmy na implementáciu odvozu odpadu. LLVM sa vyvíja rýchlo, s hlavným vydaním zhruba každých šesť mesiacov. Tempo vývoja sa pravdepodobne zvýši iba vďaka tomu, ako mnoho súčasných jazykov postavilo LLVM do centra ich vývojového procesu.

$config[zx-auto] not found$config[zx-overlay] not found