Programiranje

Kaj je LLVM? Moč za Swiftom, Rustom, Clangom in še več

Novi jeziki in izboljšave obstoječih se širijo po vsej razvojni krajini. Mozilla's Rust, Apple Swift, Jetbrains's Kotlin in številni drugi jeziki razvijalcem ponujajo novo izbiro hitrosti, varnosti, udobja, prenosljivosti in moči.

Zakaj zdaj? Velik razlog so nova orodja za gradnjo jezikov - zlasti prevajalniki. In glavni med njimi je LLVM, odprtokodni projekt, ki ga je prvotno razvil ustvarjalec jezika Swift Chris Lattner kot raziskovalni projekt na Univerzi v Illinoisu.

LLVM olajša ustvarjanje novih jezikov, temveč tudi razvoj obstoječih. Ponuja orodja za avtomatizacijo mnogih najbolj nehvaležnih delov naloge ustvarjanja jezika: ustvarjanje prevajalnika, prenos izhodne kode na več platform in arhitektur, generiranje za arhitekturo specifičnih optimizacij, kot je vektorizacija, in pisanje kode za obdelavo skupnih jezikovnih metafor, kot so izjeme. Njegova liberalna licenca pomeni, da jo je mogoče prosto ponovno uporabiti kot programsko komponento ali uporabiti kot storitev.

Seznam jezikov, ki uporabljajo LLVM, ima številna znana imena. Appleov jezik Swift uporablja LLVM kot ogrodje prevajalnika, Rust pa LLVM kot osrednji del svoje verige orodij. Številni prevajalniki imajo tudi izdajo LLVM, na primer Clang, prevajalnik C / C ++ (to je ime, "C-lang"), ki je tudi sam projekt, ki je tesno povezan z LLVM. Mono, izvedba .NET, ima možnost prevajanja v izvorno kodo z uporabo LLVM back end. Kotlin, nominalno jezik JVM, razvija različico jezika, imenovano Kotlin Native, ki uporablja LLVM za prevajanje v domačo kodo.

LLVM definiran

V svojem bistvu je LLVM knjižnica za programsko ustvarjanje lastne strojne kode. Razvijalec uporablja API za ustvarjanje navodil v obliki, imenovani vmesna zastopanostali IR. LLVM lahko nato IR prevede v samostojno binarno datoteko ali izvede kodo JIT (just-in-time) za izvajanje v kontekstu drugega programa, na primer tolmača ali izvajalnega okolja za jezik.

API-ji LLVM ponujajo primitive za razvoj številnih skupnih struktur in vzorcev, ki jih najdemo v programskih jezikih. Na primer, skoraj vsak jezik ima koncept funkcije in globalne spremenljivke, mnogi pa imajo podprograme in vmesnike tuje funkcije C. LLVM ima funkcije in globalne spremenljivke kot standardne elemente v svojem IR-ju in ima metafore za ustvarjanje podprogramov in povezovanje s knjižnicami C.

Namesto da bi porabili čas in energijo za ponovno izumljanje teh koles, lahko preprosto uporabite implementacije LLVM in se osredotočite na dele svojega jezika, ki potrebujejo pozornost.

Preberite več o Go, Kotlin, Python in Rust

Pojdi:

  • Dotaknite se moči Googlovega jezika Go
  • Najboljši IDE in urejevalniki jezika Go

Kotlin:

  • Kaj je Kotlin? Razložena alternativa Java
  • Kotlinovi okviri: Pregled orodij za razvoj JVM

Python:

  • Kaj je Python? Vse, kar morate vedeti
  • Vadnica: Kako začeti z Pythonom
  • 6 bistvenih knjižnic za vsakega razvijalca Pythona

Rja:

  • Kaj je Rust? Način za varen, hiter in enostaven razvoj programske opreme
  • Naučite se, kako začeti z Rustom

LLVM: zasnovan za prenosljivost

Za razumevanje LLVM bi lahko pomagalo razmisliti o analogiji s programskim jezikom C: C včasih opisujejo kot prenosni montažni jezik na visoki ravni, ker ima konstrukcije, ki se lahko natančno preslikajo na sistemsko strojno opremo, in je bil prenesen na skoraj vsaka sistemska arhitektura. Toda C je uporaben kot prenosni montažni jezik le do določene točke; ni bil zasnovan za ta poseben namen.

Nasprotno pa je bil IR LLVM že od začetka zasnovan kot prenosni sklop. To prenosljivost lahko dosežemo tako, da ponudimo primitive, neodvisne od katere koli arhitekture stroja. Na primer, celoštevilske vrste niso omejene na največjo bitno širino osnovne strojne opreme (na primer 32 ali 64 bitov). Ustvarite lahko primitivne celoštevilne vrste z uporabo toliko bitov, kot je potrebno, na primer 128-bitno celo število. Prav tako vam ni treba skrbeti za izdelavo izhodov, ki ustrezajo določenemu naboru navodil procesorja; LLVM skrbi tudi za vas.

Arhitekturno nevtralna zasnova LLVM olajša podporo strojne opreme vseh vrst, sedanjih in prihodnjih. Na primer, IBM je nedavno prispeval kodo za podporo svojih z / OS, Linux on Power (vključno s podporo za IBM-ovo knjižnico vektorizacije MASS) in arhitekturo AIX za LLVM-ove projekte C, C ++ in Fortran.

Če si želite ogledati primere LLVM IR v živo, pojdite na spletno mesto projekta ELLCC in preizkusite predstavitev v živo, ki pretvori kodo C v LLVM IR neposredno v brskalniku.

Kako programski jeziki uporabljajo LLVM

Najpogostejši primer uporabe LLVM je vnaprej pripravljen (AOT) prevajalnik za jezik. Na primer, projekt Clang vnaprej prevede C in C ++ v domače binarne datoteke. Toda LLVM omogoča tudi druge stvari.

Pravočasno sestavljanje z LLVM

Nekatere situacije zahtevajo, da se koda generira sproti med izvajanjem, namesto da se zbere predčasno. Jezik Julia, na primer, JIT sestavi svojo kodo, ker mora hitro teči in interakcijo z uporabnikom izvajati prek REPL (zanka za branje-eval-tiskanje) ali interaktivnega poziva.

Numba, paket matematičnega pospeševanja za Python, JIT prevede izbrane funkcije Pythona v strojno kodo. Prav tako lahko predčasno sestavi kodo, okrašeno z Numbo, vendar (tako kot Julia) Python ponuja hiter razvoj, saj je tolmačen jezik. Uporaba JIT prevajanja za izdelavo takšne kode bolje dopolnjuje Pythonov interaktivni potek dela bolj kot predčasno prevajanje.

Drugi eksperimentirajo z novimi načini uporabe LLVM kot JIT, kot je sestavljanje poizvedb PostgreSQL, kar prinese do petkratno povečanje zmogljivosti.

Samodejna optimizacija kode z LLVM

LLVM IR ne prevede samo v domačo strojno kodo. Lahko ga tudi programsko usmerite tako, da optimizira kodo z visoko mero natančnosti, vse do postopka povezovanja. Optimizacije so lahko precej agresivne, vključno s stvarmi, kot so vstavljanje funkcij, odstranjevanje mrtve kode (vključno z neuporabljenimi izjavami o tipih in argumenti funkcij) in odvijanje zank.

Spet je moč v tem, da vam vsega tega ni treba izvajati sami. LLVM jih lahko naredi za vas ali pa ga usmerite, da jih po potrebi izklopi. Če na primer želite manjše binarne datoteke za ceno določene zmogljivosti, lahko prednji del prevajalnika pove LLVM, naj onemogoči odvijanje zanke.

Domain-specific languages ​​with LLVM

LLVM je bil uporabljen za izdelavo prevajalnikov za številne jezike za splošno uporabo, uporaben pa je tudi za izdelavo jezikov, ki so zelo vertikalni ali izključni za problematično domeno. Na nek način tu LLVM najsvetleje zasije, saj odstrani veliko trdega dela pri ustvarjanju takega jezika in mu omogoči dobro delovanje.

Projekt Emscripten na primer vzame IR kodo LLVM in jo pretvori v JavaScript, kar v teoriji omogoča, da kateri koli jezik z zaledjem LLVM izvozi kodo, ki se lahko zažene v brskalniku. Dolgoročni načrt je, da imamo LLVM-osnova, ki lahko ustvari WebAssembly, toda Emscripten je dober primer, kako prilagodljiv je lahko LLVM.

Drug način uporabe LLVM je dodajanje razširitev, specifičnih za domeno, obstoječemu jeziku. Nvidia je uporabila LLVM za ustvarjanje prevajalnika Nvidia CUDA, ki jezikom omogoča, da dodajo izvorno podporo za CUDA, ki se prevede kot del izvorne kode, ki jo generirate (hitreje), namesto da bi jo klicali prek knjižnice, ki je priložena z njim (počasneje).

Uspeh LLVM z jeziki, specifičnimi za domeno, je spodbudil nove projekte znotraj LLVM za reševanje težav, ki jih ustvarjajo. Največja težava je v tem, kako je nekatere DSL-je težko prevesti v IR LLVM brez veliko trdega dela na sprednjem delu. Ena od rešitev v delih je večstopenjsko vmesno predstavništvo ali projekt MLIR.

MLIR ponuja priročne načine za predstavitev zapletenih podatkovnih struktur in operacij, ki jih je nato mogoče samodejno prevesti v LLVM IR. Na primer, ogrodje strojnega učenja TensorFlow bi lahko imelo številne svoje zapletene operacije grafikonov pretoka podatkov učinkovito prevedene v izvorno kodo z MLIR.

Delo z LLVM v različnih jezikih

Tipičen način dela z LLVM je prek kode v jeziku, ki vam ustreza (in ki ima seveda podporo za knjižnice LLVM).

Dve pogosti izbiri jezika sta C in C ++. Mnogi razvijalci LLVM privzamejo enega od teh dveh iz več dobrih razlogov:

  • LLVM je sam napisan v jeziku C ++.
  • API-ji LLVM so na voljo v inkarnacijah C in C ++.
  • Veliko jezikovnega razvoja se običajno dogaja s C / C ++ kot osnovo

Kljub temu ta dva jezika nista edina izbira. Številni jeziki lahko izvorno pokličejo v knjižnice C, zato je teoretično mogoče izvesti razvoj LLVM s katerim koli takim jezikom. Pomaga pa imeti dejansko knjižnico v jeziku, ki elegantno zavije API-je LLVM. Na srečo imajo številni jeziki in jezikovni programi takšne knjižnice, vključno s C # /. NET / Mono, Rust, Haskell, OCAML, Node.js, Go in Python.

Eno opozorilo je, da so nekatere jezikovne vezave na LLVM morda manj popolne kot druge. Na primer s Pythonom obstaja veliko možnosti, vendar se vsaka razlikuje glede svoje popolnosti in uporabnosti:

  • llvmlite, ki ga je razvila ekipa, ki ustvarja Numbo, je postal trenutni kandidat za delo z LLVM v Pythonu. Izvaja le podnabor funkcionalnosti LLVM, kot to narekujejo potrebe projekta Numba. Toda ta podskupina zagotavlja veliko večino tega, kar potrebujejo uporabniki LLVM. (llvmlite je na splošno najboljša izbira za delo z LLVM v Pythonu.)
  • Projekt LLVM ohranja svoj nabor vezav na API API LLVM, vendar trenutno niso vzdrževani.
  • llvmpy, prva priljubljena vezava Pythona za LLVM, je leta 2015 izpadla iz vzdrževanja. Slabo za kateri koli projekt programske opreme, še slabše pri delu z LLVM, glede na število sprememb, ki se pojavijo v vsaki izdaji LLVM.
  • Cilj llvmcpy je posodobiti povezave Pythona za knjižnico C, jih samodejno posodabljati in omogočiti dostop z uporabo Pythonovih domačih idiomov. llvmcpy je še vedno v zgodnji fazi, vendar že lahko opravi nekaj osnovnega dela z API-ji LLVM.

Če vas zanima, kako uporabiti knjižnice LLVM za izdelavo jezika, imajo lastni ustvarjalci LLVM vadnico s pomočjo C ++ ali OCAML, ki vas vodi skozi ustvarjanje preprostega jezika, imenovanega Kaleidoscope. Od takrat je prenesen v druge jezike:

  • Haskell:Neposredna vrata prvotne vadnice.
  • Python: Eno takih vrat natančno sledi vadnici, drugo pa ambicioznejše prepisovanje z interaktivno ukazno vrstico. Oba uporabljata llvmlite kot vez na LLVM.
  • RjainHitro: Neizogibno se je zdelo, da bi dobili vadnice v dveh jezikih, ki sta jim pomagala LLVM.

Končno je vadnica na voljo tudi včlovek jezikov. Preveden je bil v kitajščino z uporabo izvirnika C ++ in Python.

Česar LLVM ne počne

Z vsem, kar ponuja LLVM, je koristno vedeti tudi, česa ne počne.

Na primer, LLVM ne razčlenjuje slovnice jezika. To delo že opravlja veliko orodij, kot so lex / yacc, flex / bison, Lark in ANTLR. Razčlenjevanje naj bi bilo tako ali tako ločeno od prevajanja, zato ni presenetljivo, da LLVM ne poskuša obravnavati ničesar od tega.

LLVM prav tako ne naslavlja večje kulture programske opreme v določenem jeziku. Namestitev binarnih datotek prevajalnika, upravljanje paketov v namestitvi in ​​nadgradnja verige orodij - to morate storiti sami.

Nazadnje in kar je najpomembneje, še vedno obstajajo skupni deli jezikov, za katere LLVM ne zagotavlja primitivov. Številni jeziki imajo na nek način upravljanje zbranega pomnilnika, bodisi kot glavni način za upravljanje pomnilnika bodisi kot dodatek k strategijam, kot je RAII (ki ga uporabljata C ++ in Rust). LLVM vam ne daje mehanizma za zbiranje smeti, ponuja pa orodja za izvajanje zbiranja smeti, tako da omogoča označevanje kode z metapodatki, kar olajša pisanje zbiralcev smeti.

Nič od tega pa ne izključuje možnosti, da bi LLVM sčasoma dodal izvorne mehanizme za izvajanje zbiranja smeti. LLVM se hitro razvija, z večjo izdajo vsakih šest mesecev. Hitrost razvoja se bo verjetno okrepila le zahvaljujoč načinu, kako so številni sedanji jeziki postavili LLVM v središče svojega razvojnega procesa.

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