LLVM ó pheirspictíocht Go

Is tasc an-deacair é tiomsaitheoir a fhorbairt. Ach, ar an dea-uair, le forbairt tionscadal ar nós LLVM, tá an réiteach ar an bhfadhb seo simplithe go mór, rud a ligeann do ríomhchláraitheoir amháin fiú teanga nua a chruthú atá gar do C. Tá sé casta oibriú le LLVM toisc go bhfuil sé seo casta. córas Tá ionadaíocht ag méid ollmhór de chód , atá feistithe le doiciméadú beag . Chun iarracht a dhéanamh an t-easnamh seo a chur ina cheart, tá údar an ábhair, a bhfuil an t-aistriúchán á fhoilsiú againn inniu, chun samplaí de chód scríofa in Go a léiriú agus conas a aistrítear iad ar dtús. Téigh SSA, agus ansin in LLVM IR ag baint úsáide as an tiomsaitheoir bídeach. Tá an cód Go SSA agus LLVM IR curtha in eagar beagán chun rudaí nach mbaineann le hábhar na mínithe a thugtar anseo a bhaint, ionas go mbeidh na mínithe níos intuigthe.

LLVM ó pheirspictíocht Go

An chéad sampla

Is é an chéad fheidhm a bhfuilim chun breathnú uirthi anseo ná meicníocht shimplí chun uimhreacha a shuimiú:

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

Tá an fheidhm seo an-simplí, agus, b'fhéidir, ní fhéadfadh aon rud a bheith níos simplí. Aistrítear é go dtí an cód Go SSA seo a leanas:

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

Leis an dearcadh seo, cuirtear na leideanna cineál sonraí ar dheis agus is féidir neamhaird a dhéanamh orthu i bhformhór na gcásanna.

Ligeann an sampla beag seo duit croílár gné amháin de SSA a fheiceáil cheana féin. Is é sin, nuair a dhéantar cód a thiontú go foirm SSA, déantar gach slonn a mhiondealú sna codanna is bunúsaí dá bhfuil sé comhdhéanta. Is é ár gcás, an t-ordú return a + bIs ionann , i ndáiríre, agus dhá oibríocht: dhá uimhir a shuimiú agus an toradh a thabhairt ar ais.

Ina theannta sin, anseo is féidir leat bloic bhunúsacha an chláir a fheiceáil; sa chód seo níl ach bloc amháin - an bloc iontrála. Déanfaimid labhairt níos mó faoi bloic thíos.

Athraíonn an cód Go SSA go LLVM IR go héasca:

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

Is féidir leat a thabhairt faoi deara, cé go n-úsáidtear struchtúir chomhréire éagsúla anseo, níl aon athrú ar struchtúr na feidhme go bunúsach. Tá an cód LLVM IR beagán níos láidre ná an cód Go SSA, cosúil le C. Anseo, sa dearbhú feidhme, ar dtús tá cur síos ar an gcineál sonraí a chuireann sé ar ais, cuirtear an cineál argóint in iúl roimh an ainm argóint. Ina theannta sin, chun parsáil IR a shimpliú, cuirtear an tsiombail roimh ainmneacha na n-eintiteas domhanda @, agus roimh ainmneacha áitiúla tá siombail % (meastar feidhm freisin mar aonán domhanda).

Rud amháin le tabhairt faoi deara faoin gcód seo ná cinneadh ionadaíochta cineál Go int, is féidir a léiriú mar luach 32-giotán nó 64-giotán, ag brath ar an tiomsaitheoir agus sprioc an tiomsaithe, glactar leis nuair a ghineann LLVM an cód IR. Is é seo ceann de na cúiseanna iomadúla nach bhfuil cód LLVM IR neamhspleách ar an ardán, mar a cheapann go leor daoine. Ní féidir a leithéid de chód, a cruthaíodh le haghaidh ardán amháin, a thógáil agus a thiomsú d'ardán eile (ach amháin má tá tú oiriúnach chun an fhadhb seo a réiteach le cúram an-mhór).

Is pointe suimiúil eile fiú a thabhairt faoi deara go bhfuil an cineál i64 nach slánuimhir sínithe é: tá sé neodrach ó thaobh comhartha na huimhreach a léiriú. Ag brath ar an treoir, féadfaidh sé uimhreacha sínithe agus neamhshínithe a léiriú. I gcás ionadaíocht na hoibríochta breisithe, ní bhaineann sé seo le hábhar, mar sin níl aon difríocht ag obair le huimhreacha sínithe nó gan síniú. Anseo ba mhaith liom a thabhairt faoi deara go n-eascraíonn iompar neamhshainithe sa teanga C má chuirtear athróg slánuimhir shínithe thar maoil, agus mar sin cuireann an t-éadanas Clang bratach leis an oibríocht nsw (gan clúdach sínithe), a insíonn do LLVM gur féidir leis glacadh leis nach sáraíonn an suimiú riamh.

D'fhéadfadh sé seo a bheith tábhachtach le haghaidh roinnt optimizations. Mar shampla, ag cur dhá luach i16 ar ardán 32-giotán (le cláir 32-giotán) éilíonn, tar éis é a chur leis, oibríocht leathnaithe comhartha chun fanacht sa raon i16. Mar gheall air seo, is minic a bhíonn sé níos éifeachtaí oibríochtaí slánuimhir a dhéanamh bunaithe ar mhéideanna clár na meaisíní.

Ní ábhar spéise ar leith dúinn anois cad a tharlóidh ina dhiaidh sin leis an gcód IR seo. Tá an cód optamaithe (ach i gcás sampla simplí mar atá againne, níl aon rud optamaithe) agus ansin a thiontú go cód meaisín.

An dara sampla

Beidh an chéad sampla eile a bhreathnóimid beagán níos casta air. Eadhon, táimid ag caint faoi fheidhm a thugann slisne slánuimhreacha:

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

Athraíonn an cód seo go dtí an cód Go SSA seo a leanas:

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

Anseo is féidir leat a thuilleadh tógálacha a fheiceáil atá tipiciúil chun cód a léiriú san fhoirm SSA. B'fhéidir gurb é an ghné is soiléire den chód seo an bhfíric nach bhfuil aon orduithe rialaithe sreabhadh struchtúrtha ann. Chun sreabhadh na ríomhanna a rialú, níl ach jumps coinníollach agus neamhchoinníollach ann, agus, má mheasann muid an t-ordú seo mar ordú chun an sreabhadh a rialú, ordú fillte.

Go deimhin, anseo is féidir leat aird a thabhairt ar an bhfíric nach bhfuil an clár roinnte i mbloic ag baint úsáide as braces curly (mar atá sa teaghlach C teangacha). Tá sé roinnte ag lipéid, i gcuimhne ar theangacha tionóil, agus curtha i láthair i bhfoirm bloic bhunúsacha. I SSA, sainmhínítear bloic bhunúsacha mar sheichimh chóid chomhtheagmhálacha a thosaíonn le lipéad agus a chríochnaíonn le buntreoracha comhlánaithe na mbloc, mar shampla − return и jump.

Tá sonraí suimiúil eile den chód seo léirithe ag an treoir phi. Tá na treoracha sách neamhghnách agus b’fhéidir go dtógfadh sé roinnt ama iad a thuiscint. cuimhnigh, go S.S.A. is gearr don Tasc Aonair Statach. Léiriú idirmheánach é seo ar an gcód a úsáideann tiomsaitheoirí, nach sanntar luach ach uair amháin do gach athróg. Tá sé seo iontach chun feidhmeanna simplí cosúil lenár bhfeidhm a chur in iúl myAddléirithe thuas, ach níl sé oiriúnach le haghaidh feidhmeanna níos casta mar an fheidhm a phléitear sa chuid seo sum. Go háirithe, athraíonn athróga le linn an lúb a fhorghníomhú i и n.

Seachnaíonn SSA an srian ar luachanna athraitheacha a shannadh uair amháin agus úsáid á baint as treoir mar a thugtar air phi (Tógtar a ainm ón aibítir Ghréagach). Is é fírinne an scéil go gcaithfidh tú dul i muinín roinnt cleasanna le gur féidir cód ionadaíochta an SSA a ghiniúint do theangacha mar C. Is é an toradh a thugtar ar an treoir seo ná luach reatha na hathróige (in), agus úsáidtear liosta de na bloic bhunúsacha mar pharaiméadair. Mar shampla, smaoinigh ar an treoir seo:

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

Seo a leanas an bhrí atá leis: dá mba bhloc é an bunbhloc roimhe seo entry (ionchur), ansin t0 is tairiseach 0, agus má bhí an bloc bunúsach roimhe seo for.body, ansin ní mór duit an luach a ghlacadh t6 ón mbloc seo. D'fhéadfadh sé seo go léir a bheith cosúil go mistéireach, ach is é an mheicníocht seo a fhágann go n-oibríonn an SSA. Ó thaobh an duine de, déanann sé seo go léir an cód deacair a thuiscint, ach toisc go sanntar gach luach ach aon uair amháin déantar go leor optimizations i bhfad níos éasca.

Tabhair faoi deara má scríobhann tú do tiomsaitheoir féin, de ghnáth ní bheidh ort déileáil leis an gcineál seo rudaí. Ní ghineann Fiú Clang na treoracha seo go léir phi, úsáideann sé meicníocht alloca (cosúil le bheith ag obair le gnáth-athróga áitiúla). Ansin, nuair a rith pas leas iomlán a bhaint LLVM ar a dtugtar mem2reg, treoracha alloca thiontú go foirm SSA. Faigheann TinyGo, áfach, ionchur ó Go SSA, atá, go caothúil, aistrithe go foirm SSA cheana féin.

Nuáil eile a bhaineann leis an mblúire den chód idirmheánach atá faoi bhreithniú ná go ndéantar rochtain ar shliseilimintí de réir innéacs a léiriú i bhfoirm oibríochta chun an seoladh a ríomh agus oibríocht chun an pointeoir a thagann as a dhíriúnú. Anseo is féidir leat tairisigh a chur leis an gcód IR go díreach (mar shampla - 1:int). Sa sampla leis an bhfeidhm myAdd níor úsáideadh é seo. Anois agus na gnéithe sin bainte amach againn, déanaimis féachaint ar cad a thiocfaidh chun bheith sa chód seo nuair a dhéantar é a thiontú go foirm 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
}

Anseo, mar a rinneadh cheana, is féidir linn an struchtúr céanna a fheiceáil, a chuimsíonn struchtúir chomhréire eile. Mar shampla, i glaonna phi mhalartaítear luachanna agus lipéid. Mar sin féin, tá rud éigin anseo ar fiú aird ar leith a thabhairt air.

Chun tús a chur leis, anseo is féidir leat síniú feidhm go hiomlán difriúil a fheiceáil. Ní thacaíonn LLVM le slisní, agus mar thoradh air sin, mar bharrfheabhsú, scoilt an tiomsaitheoir TinyGo a ghin an cód idirmheánach seo an cur síos ar an struchtúr sonraí seo ina chodanna. D'fhéadfadh sé trí shlisdhúl a léiriú (ptr, len и cap) mar struchtúr (structúr), ach trí iad a léiriú mar thrí eintiteas ar leith is féidir roinnt leas iomlán a bhaint as. Féadfaidh tiomsaitheoirí eile an tslis a léiriú ar bhealaí eile, ag brath ar na coinbhinsiúin glaonna a bhaineann le feidhmeanna an ardáin sprice.

Gné shuimiúil eile den chód seo ná úsáid an teagaisc getelementptr (giorraithe go minic mar GEP).

Oibríonn an treoir seo le leideanna agus úsáidtear é chun pointeoir a fháil ar dhúil slisne. Mar shampla, déanaimis é a chur i gcomparáid leis an gcód seo a leanas atá scríofa in C:

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

Nó leis an méid seo a leanas comhionann leis seo:

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

Is é an rud is tábhachtaí anseo go bhfuil na treoracha getelementptr nach ndéanann oibríochtaí díthagartha. Ríomhann sé pointeoir nua bunaithe ar an gceann atá ann cheana féin. Is féidir é a ghlacadh mar threoracha mul и add ag leibhéal na crua-earraí. Is féidir leat tuilleadh a léamh faoi na treoracha GEP anseo.

Gné shuimiúil eile den chód idirmheánach seo ná úsáid an teagaisc icmp. Is treoir ghinearálta é seo a úsáidtear chun comparáidí slánuimhreacha a chur i bhfeidhm. Is é an toradh a bhaineann leis an teagasc seo a fhorghníomhú ná luach cineáil i gcónaí i1 — luach loighciúil. Sa chás seo, déantar comparáid ag baint úsáide as an eochairfhocal slt (sínithe níos lú ná), ós rud é go bhfuil muid ag déanamh comparáide idir dhá uimhir a léirigh an cineál roimhe seo int. Dá mbeimis ag déanamh comparáide idir dhá shlánuimhir gan síniú, d’úsáidfinn icmp, agus bheadh ​​an eochairfhocal a úsáidtear sa chomparáid ult. Chun uimhreacha snámhphointe a chur i gcomparáid, úsáidtear treoir eile, fcmp, a oibríonn ar bhealach comhchosúil.

Torthaí

Creidim go bhfuil na gnéithe is tábhachtaí de LLVM IR clúdaithe agam san ábhar seo. Ar ndóigh, tá i bhfad níos mó anseo. Go háirithe, féadfaidh go leor nótaí a bheith i léiriú idirmheánach an chóid a cheadaíonn pasanna optamaithe chun gnéithe áirithe den chód atá ar eolas ag an tiomsaitheoir nach féidir a chur in iúl in IR ar shlí eile a chur san áireamh. Mar shampla, is bratach é seo inbounds Treoracha GEP, nó bratacha nsw и nuw, is féidir a chur leis na treoracha add. Téann an rud céanna leis an eochairfhocal private, rud a thugann le fios don optamóir nach ndéanfar tagairt don fheidhm a mharcálann sé ón taobh amuigh den aonad tiomsaithe reatha. Ligeann sé seo do go leor leas iomlán a bhaint idir nósanna imeachta suimiúla cosúil le deireadh a chur le hargóintí nár úsáideadh.

Is féidir leat tuilleadh a léamh faoi LLVM in doiciméadú, a ndéanfaidh tú tagairt dó go minic agus do thiomsaitheoir LLVM féin á fhorbairt agat. Anseo treoir, a fhéachann le tiomsaitheoir a fhorbairt do theanga an-simplí. Beidh an dá fhoinse faisnéise seo úsáideach duit agus do thiomsaitheoir féin á chruthú agat.

Léitheoirí a chara! An bhfuil LLVM in úsáid agat?

LLVM ó pheirspictíocht Go

Foinse: will.com

Add a comment