LLVM bho shealladh Go

Is e obair gu math duilich a th’ ann a bhith a’ leasachadh compiler. Ach, gu fortanach, le leasachadh phròiseactan mar LLVM, tha am fuasgladh don duilgheadas seo air a dhèanamh nas sìmplidhe gu mòr, a leigeas le eadhon aon phrògramadair cànan ùr a chruthachadh a tha faisg air coileanadh C. Tha e toinnte a bhith ag obair le LLVM leis gu bheil seo. siostam air a riochdachadh le àireamh mhòr de chòd, uidheamaichte le glè bheag de sgrìobhainnean. Gus feuchainn ris an easbhaidh seo a cheartachadh, tha ùghdar an stuth, air a bheil sinn a’ foillseachadh an-diugh, a’ sealltainn eisimpleirean de chòd sgrìobhte ann an Go agus a’ sealltainn mar a tha iad air an eadar-theangachadh an toiseach gu Rach SSA, agus an uairsin ann an LLVM IR a’ cleachdadh an compiler beag GO. Chaidh an còd Go SSA agus LLVM IR a dheasachadh beagan gus rudan a thoirt air falbh nach eil buntainneach do na mìneachaidhean a tha air an toirt seachad an seo, gus na mìneachaidhean a dhèanamh nas so-thuigsinn.

LLVM bho shealladh Go

A’ chiad eisimpleir

Is e a’ chiad ghnìomh a tha mi a’ dol a choimhead an seo inneal sìmplidh airson àireamhan a chur ris:

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

Tha an gnìomh seo gu math sìmplidh, agus, is dòcha, chan urrainn dad a bhith nas sìmplidh. Bidh e ag eadar-theangachadh gu còd Go SSA a leanas:

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

Leis an t-sealladh seo, tha na sanasan seòrsa dàta air an cur air an taobh cheart agus faodar an leigeil seachad sa mhòr-chuid de chùisean.

Tha an eisimpleir bheag seo mu thràth a’ leigeil leat brìgh aon taobh de SSA fhaicinn. Is e sin, nuair a thèid còd a thionndadh gu cruth SSA, tha gach abairt air a bhriseadh sìos gu na pàirtean as bunaitiche dheth a tha e air a dhèanamh. Anns a 'chùis againn, an àithne return a + b, gu dearbh, a 'riochdachadh dà obrachadh: a' cur dà àireamh agus a 'tilleadh an toradh.

A bharrachd air an sin, an seo chì thu na blocaichean bunaiteach den phrògram; anns a ’chòd seo chan eil ann ach aon bhloc - am bloc inntrigidh. Bruidhnidh sinn barrachd mu bhlocaichean gu h-ìosal.

Bidh an còd Go SSA gu furasta ag atharrachadh gu LLVM IR:

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

Is e an rud a chì thu, ged a thathas a’ cleachdadh diofar structaran syntactic an seo, gu bheil structar na gnìomh gu bunaiteach gun atharrachadh. Tha an còd LLVM IR beagan nas làidire na còd Go SSA, coltach ri C. An seo, anns an dearbhadh gnìomh, an toiseach tha tuairisgeul air an t-seòrsa dàta a thilleas e, tha an seòrsa argamaid air a chomharrachadh ron ainm argamaid. A bharrachd air an sin, gus parsadh IR a dhèanamh nas sìmplidhe, tha an samhla air thoiseach air ainmean bhuidhnean cruinneil @, agus ro ainmean ionadail tha samhla ann % (tha gnìomh cuideachd air a mheas mar eintiteas cruinneil).

Is e aon rud ri thoirt fa-near mun chòd seo gu bheil co-dhùnadh riochdachaidh seòrsa Go int, a dh’ fhaodar a riochdachadh mar luach 32-bit no 64-bit, a rèir an cruinneachaidh agus targaid a’ cho-chruinneachaidh, nuair a ghineas LLVM an còd IR. Is e seo aon de na h-adhbharan airson nach eil còd LLVM IR, mar a tha mòran a’ smaoineachadh, àrd-ùrlar neo-eisimeileach. Chan urrainnear a leithid de chòd, a chaidh a chruthachadh airson aon àrd-ùrlar, a thogail agus a chuir ri chèile airson àrd-ùrlar eile (mura h-eil thu freagarrach airson an duilgheadas seo fhuasgladh le fìor chùram).

Is e puing inntinneach eile as fhiach a thoirt fa-near gu bheil an seòrsa i64 chan e àireamh-shluaigh a th’ ann: tha e neodrach a thaobh soidhne na h-àireimh a riochdachadh. A rèir an stiùiridh, faodaidh e àireamhan soidhnichte agus gun ainm a riochdachadh. A thaobh riochdachadh an obrachaidh cur-ris, chan eil seo gu diofar, agus mar sin chan eil eadar-dhealachadh ann a bhith ag obair le àireamhan soidhnichte no gun ainm. An seo bu mhath leam a thoirt fa-near, anns a’ chànan C, gu bheil a bhith a’ cur thairis air caochladair sàmhchair soidhnichte a’ leantainn gu giùlan neo-mhìnichte, agus mar sin tha aghaidh Clang a’ cur bratach ris an obair nsw (gun chòmhdach soidhnichte), a tha ag innse do LLVM gum faod e gabhail ris nach bi cur-ris a’ dol thairis.

Faodaidh seo a bhith cudromach airson cuid de optimizations. Mar eisimpleir, cuir dà luach ris i16 air àrd-ùrlar 32-bit (le clàran 32-bit) feumach, às deidh sin, gnìomhachd leudachaidh soidhne gus fuireach ann an raon i16. Air sgàth seo, tha e gu tric nas èifeachdaiche a bhith a’ dèanamh obrachaidhean slàn-ìre stèidhichte air meud clàr innealan.

Chan eil na thachras a-nis leis a’ chòd IR seo gu sònraichte inntinneach dhuinn a-nis. Tha an còd air a mheudachadh (ach a thaobh eisimpleir shìmplidh mar an tè againne, chan eil dad air a mheudachadh) agus an uairsin air a thionndadh gu còd inneal.

An dàrna eisimpleir

Bidh an ath eisimpleir air am bi sinn a’ coimhead beagan nas iom-fhillte. Is e sin, tha sinn a’ bruidhinn mu dheidhinn gnìomh a tha a’ toirt cunntas air sliseag iomlan:

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

Bidh an còd seo ag atharrachadh gu còd Go SSA 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

An seo chì thu mar-thà barrachd thogalaichean a tha àbhaisteach airson còd a riochdachadh ann am foirm SSA. Is dòcha gur e am feart as fhollaisiche den chòd seo nach eil òrdughan smachd sruth structarail ann. Gus smachd a chumail air an t-sruth àireamhachaidh, chan eil ann ach leuman gun chumha agus gun chumhachan, agus, ma tha sinn a’ beachdachadh air an àithne seo mar àithne airson smachd a chumail air an t-sruth, àithne tilleadh.

Gu dearbh, an seo faodaidh tu aire a thoirt don fhìrinn nach eil am prògram air a roinn ann am blocaichean le bhith a ’cleachdadh braces lùbach (mar anns an teaghlach chànanan C). Tha e air a roinn le bileagan, mar chuimhneachan air cànanan cruinneachaidh, agus air a thaisbeanadh ann an cruth blocaichean bunaiteach. Ann an SSA, tha blocaichean bunaiteach air am mìneachadh mar shreathan còd ri thaobh a’ tòiseachadh le leubail agus a’ crìochnachadh le stiùireadh crìochnachaidh bloca bunaiteach, leithid - return и jump.

Tha mion-fhiosrachadh inntinneach eile mun chòd seo air a riochdachadh leis an stiùireadh phi. Tha an stiùireadh gu math neo-àbhaisteach agus is dòcha gun toir e beagan ùine airson a thuigsinn. cuimhnich, sin SSA tha e goirid airson Sònrachadh Singilte Statach. Is e seo riochdachadh eadar-mheadhanach den chòd a chleachdas luchd-cruinneachaidh, anns a bheil luach air a thoirt do gach caochladair dìreach aon turas. Tha seo sgoinneil airson gnìomhan sìmplidh leithid ar gnìomh a chuir an cèill myAdda chithear gu h-àrd, ach chan eil e freagarrach airson gnìomhan nas iom-fhillte leithid an gnìomh air a bheilear a’ beachdachadh san earrann seo sum. Gu sònraichte, bidh caochladairean ag atharrachadh nuair a thèid an lùb a chuir gu bàs i и n.

Bidh SSA a’ dol seachad air a’ bhacadh air luachan caochlaideach a shònrachadh aon uair a’ cleachdadh stiùireadh ris an canar phi (tha an t-ainm air a thoirt bhon aibidil Ghreugach). Is e an fhìrinn, gus an tèid riochdachadh còd SSA a chruthachadh airson cànanan mar C, feumaidh tu a dhol gu cuid de chleasan. Is e toradh gairm an stiùiridh seo luach làithreach a’ chaochladair (i no n), agus tha liosta de bhlocaichean bunaiteach air a chleachdadh mar na crìochan aige. Mar eisimpleir, beachdaich air an stiùireadh seo:

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

Tha a bhrìgh mar a leanas: nam biodh am bloc bunaiteach roimhe na bhloc entry (cuir a-steach), an uairsin t0 tha seasmhach 0, agus ma bha am bloc bunaiteach roimhe for.body, an uairsin feumaidh tu an luach a ghabhail t6 bhon bhloc seo. Is dòcha gu bheil seo uile a’ coimhead gu math dìomhair, ach is e an dòigh seo a bheir air an SSA obrachadh. Bho shealladh daonna, tha seo uile a ’dèanamh a’ chòd duilich a thuigsinn, ach leis gu bheil gach luach air a shònrachadh dìreach aon uair a ’dèanamh mòran optimizations gu math nas fhasa.

Thoir an aire ma sgrìobhas tu an neach-cruinneachaidh agad fhèin, mar as trice cha bhith agad ri dèiligeadh ris an t-seòrsa stuth seo. Cha bhith eadhon Clang a’ gineadh an stiùireadh seo gu lèir phi, bidh e a 'cleachdadh uidheamachd alloca (tha e coltach ri bhith ag obair le caochladairean ionadail àbhaisteach). An uairsin, nuair a bhios tu a’ ruith pas optimization LLVM ris an canar mem2reg, stiùireadh alloca air atharrachadh gu foirm SSA. Tha TinyGo, ge-tà, a 'faighinn a-steach bho Go SSA, a tha, gu h-iomchaidh, air a thionndadh gu foirm SSA.

Is e ùr-ghnàthachadh eile den chriomag de chòd eadar-mheadhanach air a bheilear a’ beachdachadh gu bheil ruigsinneachd air eileamaidean sliseag le clàr-amais air a riochdachadh ann an cruth gnìomhachd àireamhachadh an t-seòlaidh agus obrachadh a bhith a’ toirt iomradh air a’ phuing a thig às. An seo chì thu cuir-a-steach dìreach ris a’ chòd IR (mar eisimpleir - 1:int). Anns an eisimpleir leis a 'ghnìomh myAdd cha deach seo a chleachdadh. A-nis gu bheil na feartan sin againn a-mach às an t-slighe, leig dhuinn sùil a thoirt air na thig an còd seo nuair a thèid a thionndadh gu 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
}

An seo, mar roimhe, chì sinn an aon structar, a tha a 'gabhail a-steach structaran syntactic eile. Mar eisimpleir, ann an gairmean phi luachan agus bileagan air an iomlaid. Ach, tha rudeigin an seo as fhiach aire shònraichte a thoirt dha.

An toiseach, an seo chì thu ainm-sgrìobhte gnìomh gu tur eadar-dhealaichte. Chan eil LLVM a’ toirt taic do sliseagan, agus mar thoradh air an sin, mar optimization, roinn an neach-cruinneachaidh TinyGo a chruthaich an còd eadar-mheadhanach seo an tuairisgeul air an structar dàta seo gu pàirtean. Dh’ fhaodadh e trì pìosan eileamaidean a riochdachadh (ptr, len и cap) mar structar (structar), ach le bhith gan riochdachadh mar trì buidhnean fa leth leigidh sin cuid de optimizations. Faodaidh luchd-cruinneachaidh eile an sliseag a riochdachadh ann an dòighean eile, a rèir gnàthasan gairm gnìomhan an àrd-ùrlar targaid.

Is e feart inntinneach eile den chòd seo cleachdadh an stiùiridh getelementptr (gu tric air a ghiorrachadh mar GEP).

Bidh an stiùireadh seo ag obair le comharran agus tha e air a chleachdadh gus puing fhaighinn gu eileamaid sliseag. Mar eisimpleir, dèanamaid coimeas eadar e agus an còd a leanas sgrìobhte ann an C:

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

No leis na leanas co-ionann ri seo:

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

Is e an rud as cudromaiche an seo gu bheil an stiùireadh getelementptr chan eil e a’ coileanadh gnìomhan dì-iomraidh. Bidh e dìreach a’ tomhas puing ùr stèidhichte air an fhear a th’ ann. Faodar a ghabhail mar stiùireadh mul и add aig ìre bathar-cruaidh. Faodaidh tu barrachd a leughadh mu stiùireadh GEP an seo.

Is e feart inntinneach eile den chòd eadar-mheadhanach seo cleachdadh an stiùiridh icmp. Is e stiùireadh adhbhar coitcheann a tha seo a thathar a’ cleachdadh gus coimeasan iomlan a thoirt gu buil. Is e toradh an stiùiridh seo an-còmhnaidh luach seòrsa i1 - luach loidsigeach. Anns a 'chùis seo, thathar a' dèanamh coimeas a 'cleachdadh a' phrìomh fhacal slt (air a shoidhnigeadh nas lugha na), leis gu bheil sinn a’ dèanamh coimeas eadar dà àireamh a bha air an riochdachadh roimhe leis an t-seòrsa int. Nam biodh sinn a’ dèanamh coimeas eadar dà shloinneadh gun ainm, bhiodh sinn a’ cleachdadh icmp, agus bhiodh am prìomh fhacal air a chleachdadh sa choimeas ult. Gus coimeas a dhèanamh eadar àireamhan puing-fleòdraidh, thathas a’ cleachdadh stiùireadh eile, fcmp, a tha ag obair san aon dòigh.

Builean

Tha mi a 'creidsinn gu bheil mi air na feartan as cudromaiche de LLVM IR a chòmhdach anns an stuth seo. Gu dearbh, tha tòrr a bharrachd an seo. Gu sònraichte, faodaidh mòran notaichean a bhith ann an riochdachadh eadar-mheadhanach a’ chòd a leigeas le pasan optimization aire a thoirt do fheartan sònraichte den chòd a tha aithnichte don neach-cruinneachaidh nach urrainnear a chuir an cèill ann an IR eile. Mar eisimpleir, is e bratach a tha seo inbounds Stiùireadh GEP, no brataichean nsw и nuw, a dh'fhaodar a chur ris an stiùireadh add. Tha an aon rud a 'dol airson a' phrìomh fhacal private, a’ nochdadh don optimizer nach tèid iomradh a thoirt air a’ ghnìomh a tha e a’ comharrachadh bho thaobh a-muigh an aonaid cruinneachaidh gnàthach. Leigidh seo le tòrr optimizations eadar-mhodhail inntinneach leithid cuir às do argamaidean nach deach a chleachdadh.

Faodaidh tu barrachd a leughadh mu LLVM ann an sgrìobhainnean, air am bi thu a’ toirt iomradh tric nuair a bhios tu a’ leasachadh an inneal-cruinneachaidh agad fhèin stèidhichte air LLVM. Seo stiùireadh, a tha a 'coimhead ri bhith a' leasachadh compiler airson cànan gu math sìmplidh. Bidh an dà thùs fiosrachaidh seo feumail dhut nuair a chruthaicheas tu an neach-cruinneachaidh agad fhèin.

Luchd leughaidh! A bheil thu a’ cleachdadh LLVM?

LLVM bho shealladh Go

Source: www.habr.com

Cuir beachd ann