LLVM nan yon pèspektiv Go

Devlope yon du se yon travay trè difisil. Men, erezman, ak devlopman pwojè tankou LLVM, solisyon pwoblèm sa a senplifye anpil, sa ki pèmèt menm yon sèl pwogramè kreye yon nouvo lang ki pwòch nan pèfòmans C. Travay ak LLVM konplike pa lefèt ke sa a. sistèm reprezante pa yon gwo kantite kòd, ekipe ak ti dokimantasyon. Pou eseye korije enpèfeksyon sa a, otè a nan materyèl la, tradiksyon an ke nou pibliye jodi a, pral montre egzanp nan kòd ki ekri nan Go epi montre ki jan yo premye tradui nan. Ale SSA, ak Lè sa a, nan LLVM IR lè l sèvi avèk du a tinyGO. Go SSA ak LLVM IR kòd la te yon ti kras modifye pou retire bagay ki pa gen rapò ak eksplikasyon yo bay isit la, nan lòd yo fè eksplikasyon yo pi konprann.

LLVM nan yon pèspektiv Go

Premye egzanp

Premye fonksyon mwen pral gade isit la se yon mekanis senp pou ajoute nimewo:

func myAdd(a, b int) int{
    return a + b
}

Fonksyon sa a trè senp, e petèt pa gen anyen ki pi senp. Li tradwi nan kòd Go SSA sa a:

func myAdd(a int, b int) int:
entry:
    t0 = a + b                                                    int
    return t0

Avèk wè sa a, sijesyon kalite done yo mete sou bò dwat la epi yo ka inyore nan pifò ka yo.

Ti egzanp sa a deja pèmèt ou wè sans nan yon aspè nan SSA. Savwa, lè konvèti kòd nan fòm SSA, chak ekspresyon yo divize an pati ki pi elemantè nan ki li konpoze. Nan ka nou an, lòd la return a + b, an reyalite, reprezante de operasyon: ajoute de nimewo epi retounen rezilta a.

Anplis de sa, isit la ou ka wè blòk debaz yo nan pwogram nan; nan kòd sa a gen yon sèl blòk - blòk la antre. Nou pral pale plis sou blòk anba a.

Kòd Go SSA fasil konvèti an LLVM IR:

define i64 @myAdd(i64 %a, i64 %b) {
entry:
  %0 = add i64 %a, %b
  ret i64 %0
}

Ki sa ou ka remake se ke byenke diferan estrikti sentaktik yo itilize isit la, estrikti a nan fonksyon an se fondamantalman chanje. Kòd LLVM IR a yon ti kras pi fò pase kòd Go SSA a, menm jan ak C. Isit la, nan deklarasyon fonksyon an, premye gen yon deskripsyon kalite done li retounen, kalite agiman an endike anvan non agiman an. Anplis de sa, pou senplifye analiz IR, non antite mondyal yo presye pa senbòl la @, epi anvan non lokal yo gen yon senbòl % (yon fonksyon konsidere tou kòm yon antite mondyal).

Youn nan bagay yo sonje sou kòd sa a se ke Go a kalite reprezantasyon desizyon int, ki ka reprezante kòm yon valè 32-bit oswa 64-bit, depann sou du a ak sib la nan konpilasyon an, yo aksepte lè LLVM jenere kòd IR la. Sa a se youn nan anpil rezon ki fè kòd LLVM IR pa, jan anpil moun panse, platfòm endepandan. Kòd sa a, ki te kreye pou yon platfòm, pa ka senpleman pran ak konpile pou yon lòt platfòm (sòf si ou apwopriye pou rezoud pwoblèm sa a. avèk swen ekstrèm).

Yon lòt pwen enteresan vo anyen se ke kalite a i64 se pa yon nonb antye siy: li net an tèm de reprezante siy nimewo a. Tou depan de enstriksyon an, li ka reprezante tou de nimewo siyen ak nimewo ki pa siyen. Nan ka a nan reprezantasyon an nan operasyon an adisyon, sa a pa gen pwoblèm, kidonk pa gen okenn diferans nan travay ak nimewo siyen oswa ki pa siyen. Isit la mwen ta renmen sonje ke nan lang C a, debòde yon varyab nonb antye relatif siyen mennen nan konpòtman endefini, kidonk frontend Clang la ajoute yon drapo nan operasyon an. nsw (pa gen okenn vlope siyen), ki di LLVM ke li ka asime ke adisyon pa janm debòde.

Sa a ka enpòtan pou kèk optimize. Pou egzanp, ajoute de valè i16 sou yon platfòm 32-bit (ak rejis 32-bit) mande, apre adisyon, yon operasyon ekspansyon siy yo nan lòd yo rete nan ranje. i16. Poutèt sa, li souvan pi efikas pou fè operasyon nonb antye ki baze sou gwosè rejis machin yo.

Kisa k ap pase apre kòd IR sa a pa yon enterè patikilye pou nou kounye a. Kòd la optimize (men nan ka a nan yon egzanp senp tankou nou an, pa gen anyen ki optimize) ak Lè sa a, konvèti nan kòd machin.

Dezyèm egzanp

Pwochen egzanp nou pral gade pral yon ti kras pi konplike. Savwa, nou ap pale de yon fonksyon ki sòm yon tranch nan nonm antye:

func sum(numbers []int) int {
    n := 0
    for i := 0; i < len(numbers); i++ {
        n += numbers[i]
    }
    return n
}

Kòd sa a konvèti nan kòd Go SSA sa a:

func sum(numbers []int) int:
entry:
    jump for.loop
for.loop:
    t0 = phi [entry: 0:int, for.body: t6] #n                       int
    t1 = phi [entry: 0:int, for.body: t7] #i                       int
    t2 = len(numbers)                                              int
    t3 = t1 < t2                                                  bool
    if t3 goto for.body else for.done
for.body:
    t4 = &numbers[t1]                                             *int
    t5 = *t4                                                       int
    t6 = t0 + t5                                                   int
    t7 = t1 + 1:int                                                int
    jump for.loop
for.done:
    return t0

Isit la ou ka deja wè plis konstriksyon tipik pou reprezante kòd nan fòm SSA. Petèt karakteristik ki pi evidan nan kòd sa a se lefèt ke pa gen okenn kòmandman kontwòl koule estriktire. Pou kontwole koule nan kalkil, gen sèlman kondisyonèl ak enkondisyonèl so, epi, si nou konsidere kòmandman sa a kòm yon kòmandman kontwole koule a, yon lòd retounen.

An reyalite, isit la ou ka peye atansyon sou lefèt ke pwogram nan pa divize an blòk lè l sèvi avèk aparèy òtopedik Curly (tankou nan fanmi C nan lang). Li divize pa etikèt, ki okoumansman de lang asanble, epi li prezante nan fòm blòk debaz yo. Nan SSA, blòk debaz yo defini kòm sekans vwazen nan kòd kòmanse ak yon etikèt epi fini ak enstriksyon debaz pou konplete blòk, tankou - return и jump.

Yon lòt detay enteresan nan kòd sa a reprezante pa enstriksyon an phi. Enstriksyon yo se byen etranj epi yo ka pran kèk tan yo konprann. sonje ke S.S.A. se kout pou Statik Single Assignment. Sa a se yon reprezantasyon entèmedyè nan kòd la itilize pa konpilateur, nan ki chak varyab yo asiyen yon valè yon sèl fwa. Sa a se gwo pou eksprime fonksyon senp tankou fonksyon nou an myAddmontre pi wo a, men li pa apwopriye pou fonksyon ki pi konplèks tankou fonksyon ki diskite nan seksyon sa a sum. An patikilye, varyab chanje pandan ekzekisyon bouk la i и n.

SSA iyore restriksyon sou bay valè varyab yon fwa lè l sèvi avèk yon enstriksyon sa yo rele phi (non li pran nan alfabè grèk la). Reyalite a se ke yo nan lòd pou reprezantasyon SSA nan kòd yo dwe pwodwi pou lang tankou C, ou dwe ale nan kèk ke trik nouvèl. Rezilta lè w rele enstriksyon sa a se valè aktyèl varyab la (i oswa n), epi yo itilize yon lis blòk debaz kòm paramèt li yo. Pou egzanp, konsidere enstriksyon sa a:

t0 = phi [entry: 0:int, for.body: t6] #n

Siyifikasyon li se jan sa a: si blòk debaz anvan an te yon blòk entry (antre), lè sa a t0 se yon konstan 0, epi si blòk debaz anvan an te for.body, Lè sa a, ou bezwen pran valè a t6 soti nan blòk sa a. Tout bagay sa yo ka sanble byen misterye, men mekanis sa a se sa ki fè SSA a travay. Soti nan yon pèspektiv imen, sa a tout fè kòd la difisil pou konprann, men lefèt ke chak valè yo asiyen sèlman yon fwa fè anpil optimize pi fasil.

Remake byen ke si ou ekri pwòp du ou, anjeneral ou pa pral gen fè fas ak kalite bagay sa yo. Menm Clang pa jenere tout enstriksyon sa yo phi, li itilize yon mekanis alloca (li sanble ak travay ak varyab lokal òdinè). Lè sa a, lè kouri yon pas optimisation LLVM rele mem2reg, enstriksyon alloca konvèti nan fòm SSA. Men, TinyGo resevwa opinyon nan men Go SSA, ki, fasilman, deja konvèti nan fòm SSA.

Yon lòt inovasyon nan fragman nan kòd entèmedyè anba konsiderasyon se ke aksè nan eleman tranch pa endèks reprezante nan fòm lan nan yon operasyon nan kalkile adrès la ak yon operasyon nan dereferencing pointeur a ki kapab lakòz. Isit la ou ka wè adisyon dirèk nan konstan nan kòd la IR (pa egzanp - 1:int). Nan egzanp lan ak fonksyon an myAdd sa a pa te itilize. Kounye a ke nou te gen karakteristik sa yo soti nan wout la, ann pran yon gade nan ki sa kòd sa a vin lè konvèti nan fòm LLVM IR:

define i64 @sum(i64* %ptr, i64 %len, i64 %cap) {
entry:
  br label %for.loop

for.loop:                                         ; preds = %for.body, %entry
  %0 = phi i64 [ 0, %entry ], [ %5, %deref.next ]
  %1 = phi i64 [ 0, %entry ], [ %6, %deref.next ]
  %2 = icmp slt i64 %1, %len
  br i1 %2, label %for.body, label %for.done

for.body:                                         ; preds = %for.loop
  %3 = getelementptr i64, i64* %ptr, i64 %1
  %4 = load i64, i64* %3
  %5 = add i64 %0, %4
  %6 = add i64 %1, 1
  br label %for.loop

for.done:                                         ; preds = %for.loop
  ret i64 %0
}

Isit la, tankou anvan, nou ka wè menm estrikti a, ki gen ladan lòt estrikti sentaktik. Pou egzanp, nan apèl yo phi valè ak etikèt chanje. Sepandan, gen yon bagay isit la ki vo peye atansyon espesyal.

Pou kòmanse, isit la ou ka wè yon siyati fonksyon konplètman diferan. LLVM pa sipòte tranch, epi kòm yon rezilta, kòm yon optimize, du TinyGo ki te pwodwi kòd entèmedyè sa a divize deskripsyon estrikti done sa a an pati. Li ta ka reprezante twa eleman tranch (ptr, len и cap) kòm yon estrikti (struct), men reprezante yo kòm twa antite separe pèmèt pou kèk optimize. Lòt konpilateur ka reprezante tranch la nan lòt fason, tou depann de konvansyon apèl yo nan fonksyon platfòm sib la.

Yon lòt karakteristik enteresan nan kòd sa a se itilizasyon ansèyman an getelementptr (souvan abreje kòm GEP).

Enstriksyon sa a travay ak endikasyon epi yo itilize pou jwenn yon konsèy sou yon eleman tranch. Pou egzanp, ann konpare li ak kòd sa a ki ekri nan C:

int* sliceptr(int *ptr, int index) {
    return &ptr[index];
}

Oswa ak ekivalan sa a:

int* sliceptr(int *ptr, int index) {
    return ptr + index;
}

Bagay ki pi enpòtan isit la se ke enstriksyon yo getelementptr pa fè operasyon dereferancing. Li jis kalkile yon nouvo konsèy ki baze sou yon sèl ki egziste deja. Li ka pran kòm enstriksyon mul и add nan nivo pyès ki nan konpitè. Ou ka li plis sou enstriksyon GEP yo isit la.

Yon lòt karakteristik enteresan nan kòd entèmedyè sa a se itilizasyon ansèyman an icmp. Sa a se yon enstriksyon jeneral itilize pou aplike konparezon nonb antye relatif. Rezilta enstriksyon sa a se toujou yon valè de kalite i1 - valè lojik. Nan ka sa a, yo fè yon konparezon lè l sèvi avèk mo kle a slt (siyen mwens pase), piske n ap konpare de nimewo ki te deja reprezante pa kalite a int. Si nou te konpare de nonm antye ki pa siyen, Lè sa a, nou ta itilize icmp, ak mo kle yo itilize nan konparezon an ta dwe ult. Pou konpare nimewo k ap flote, yo itilize yon lòt enstriksyon, fcmp, ki travay nan yon fason menm jan an.

Rezilta

Mwen kwè ke nan materyèl sa a mwen te kouvri karakteristik ki pi enpòtan nan LLVM IR. Natirèlman, gen anpil plis isit la. An patikilye, reprezantasyon entèmedyè kòd la ka genyen anpil annotasyon ki pèmèt pas optimize yo pran an kont sèten karakteristik kòd konpilatè a konnen ki pa ka eksprime nan IR. Pou egzanp, sa a se yon drapo inbounds GEP enstriksyon, oswa drapo nsw и nuw, ki ka ajoute nan enstriksyon yo add. Menm bagay la tou ale pou mo kle a private, ki endike optimize a ke fonksyon li make yo pa pral referans soti deyò inite konpilasyon aktyèl la. Sa a pèmèt pou yon anpil nan optimize entè-pwosedi enteresan tankou elimine agiman ki pa itilize yo.

Ou ka li plis sou LLVM nan dokiman, ki w ap refere a souvan lè w ap devlope pwòp konpilatè w ki baze sou LLVM. Isit la lidèchip, ki gade nan devlope yon du pou yon lang trè senp. Tou de sous enfòmasyon sa yo ap itil ou lè w ap kreye pwòp konpilateur ou.

Chè lektè! Èske w ap itilize LLVM?

LLVM nan yon pèspektiv Go

Sous: www.habr.com

Add nouvo kòmantè