LLVM a Go perspective

Compilator enucleare difficillimum negotium est. Sed feliciter, cum evolutione inceptorum, sicut LLVM, solutio huius problematis valde facilior est, quae vel unum programmatorem permittit creare novam linguam quae prope in actione C. Laboris cum LLVM perplexa est eo quod hoc systema repraesentatur per ingentem copiam codicis, parum documentis instructum. Ut huius defectus corrigere studeat, auctor materiae, translationem de qua hodie edimus, demonstrabit exempla codicis in Go scripti et monstra quomodo in primum translata sint. Ite SSAac deinde in LLVM IR utendo compilator tinyGO. Codex Go SSA et LLVM IR leviter editus est ad tollendas res quae ad explicationes hic positas non pertinent, ut explicationes magis intelligantur.

LLVM a Go perspective

Primum exemplum

Primum munus quod eo spectatum est ars simplex ad numeros additos;

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

Hoc munus est valde simplex, et fortasse nihil potest esse simplicius. Ite vertit in sequenti codice SATB:

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

Hac ratione, notitiae genus innuit in iure positae et in pluribus negligi possunt.

Hoc parvum exemplum iam tibi permittit videre essentiam unius aspectus SSA. Nempe cum codicem in formam SSA convertens, unaquaeque dictio in partes maxime elementarias, ex quibus componitur, dissipata est. In nobis, mandatum return a + bduas enim operationes repraesentat: duos numeros addens et exitum reducens.

Praeterea hic videre potes stipites fundamentales programmatis, in hoc codice unus tantum est stipes - clausus introitus. Plura de caudices infra loquemur.

Go SSA codice facile ad LLVM IR convertitur;

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

Quod animadvertere potes, licet variae structurae syntacticae hic adhibeantur, structuram functionis fundamentaliter immutatam esse. In codice LLVM IR paulo validior est quam Go SSA codice, similis C. Hic, in functione declarationis, primum est descriptio notitiae speciei quam redit, argumenti genus ante nomen argumenti indicatur. Praeterea, ut IR parsing simpliciorem reddat, nomina entitatum globalium praecedunt symbolo @, et ante nomina locorum symbolum est % functio etiam entitatis globalis censetur.

Unum de hoc codice notandum est quod Go's genus repraesentationis decisionis est intquae repraesentari potest ut valorem 32-bit vel 64-minutum, secundum compilator et scopo compilationem, accipitur cum LLVM codicem IR generaverit. Haec una ex multis causis LLVM IR codici non, ut multi putant, suggestum independentem est. Talis signum, pro uno suggestu creatum, pro alio suggestu simpliciter sumi et compilari non potest (nisi idoneus sis ad hanc quaestionem solvendam. summa cautela).

Alius interesting punctum memorabile est quod genus i64 integer signatus non est: neutrum est pro signo numeri repraesentando. Secundum disciplinam, numeros et signatos et non signatos repraesentare potest. In repraesentatione autem operationis additionis, hoc non est in materia, ideo non est differentia operativarum numerorum signatorum vel non signatorum. Hic notare velim quod in lingua C, inundans signati integri variabilis ad mores indefinitos ducit, sic Clang frontend vexillum operationi addit. nsw (nullum signatum velare), quod narrat LLVM id assumere posse quod additio numquam redundat.

Aliquam sit amet ipsum est. Exempli gratia addit duos valores i16 in XXXII frenum suggestum (cum XXXII-frenum registra) requirit, cum additione, signum expansionis operatio ut in range i16. Propter hoc, saepe magis efficax est ad integras operationes faciendas secundum machinas magnitudinum actis.

Quid deinde cum hoc IR codice acciderit, nunc nobis non est peculiaris cura. Codex optimized est (sed in casu simplici exempli sicut nostri nihil optimized est) et postea in machinam convertitur.

Secundum exemplum

Proximum exemplum paulo multiplex erit. Nempe de functione quae segmentum integrorum sumit;

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

Hoc signum ad sequentia Go SS.

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

Hic iam videre potes plura constructiones typicas ad codicem repraesentandum in forma SSA. Fortasse notissimum notum huius codicis est eo quod nullae structae manant imperium imperat. Ad calculorum fluxum moderandum, adsilit tantum conditionalis et sine exceptione, et, si mandatum hoc consideramus ut imperium fluere refrenare, reditio mandati.

Reapse hic animadvertere potes programmata non divisa in caudices utentes adstringit crispos (sicut in C linguarum familia). Dividitur per pittacia, reminiscens linguarum conventuum, et in forma stipitiorum fundamentalium exhibita. In SSA, caudices basici definiuntur series contiguae codicis incipientis a pittacio et clausulis peractis instructionum fundamentalium desinentibus, ut βˆ’ return ΠΈ jump.

Alius interesting detail huius codicis significatur per instructionem phi. Praecepta admodum inusitata sunt et aliqua temporis ad intellegendum sumenda sunt. Meminisse SATB brevis ad Static Single adsignatione. Haec est media repraesentatio codicis a compilatoribus adhibitis, in qua unumquodque variabile semel tantum valorem assignatur. Hoc magnum est ad exprimendas simplices functiones sicut nostrum munus myAddsupra ostensum est, sed non competit functionibus multiplicioribus ut munus in hac sectione discussum sum. Praesertim variabiles per ansa executionem mutantur i ΠΈ n.

SATB restrictionem praetermittit assignans valores variabiles semel utendo, ut aiunt, disciplinam phi (nomen a graeco sumptum est). Ita res est, ut ad repraesentationem SSA de codice generandam pro linguis similibus C, aliquibus strophis adhibitum est. Effectus vocationis huius instructionis est praesens valor variabilis (i aut n) et indicem caudicum fundamentalium pro parametris eius usurpatur. Verbi gratia:

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

Sensus eius talis est: si prior stipes fundamentalis erat truncus entry (input), tunc t0 est constans 0Ac si prior basic scandalum erat for.bodyErgo debes accipere valorem t6 ab hoc scandalo. Haec omnia satis arcana videntur, sed haec mechanismus est quae efficit opus SATB. Ex humano prospectu, haec omnia ad intellegendum difficile facit codicem, sed quod unumquodque valorem semel tantum tribuitur, multas optimizationes multo facilius efficit.

Nota quod si compilatorem tuum scribis, non solere cum huiusmodi materia agere. Etiam Clang non generat omnia mandata phi, utitur mechanismo alloca (similis laborat cum variantibus loci variantibus). Deinde, cum currendo LLVM ipsum transitum vocatum est mem2reg, instructiones alloca SSA converti ad formam. TinyGo autem input ab Go SSA accipit, quod commode iam ad formam SSA convertitur.

Alia innovatio fragmenti codicis intermedii sub consideratione est quod accessus ad segmenta elementa per indicem repraesentatur in forma operationis computandi inscriptionis et operationis monstratorem indeficientem. Hic videre potes accessionem directam constantium ad IR codicem (exempli gratia - 1:int). In exemplum cum functione myAdd hoc non est adhibitum. Nunc notas illas extra viam tenemus, inspicemus quid in LLVM IR forma fiat huius codicis conversa:

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
}

Hic, ut ante, videre possumus eandem structuram, quae alias structuras syntacticas includit. Exempli gratia in vocat phi valores ac pittacia tela. Sed est hic aliquid sedulo operae pretium.

Incipere, hic videre potes signaturam prorsus diversam functionis. LLVM pecias non sustinet, et inde, ut optimizatio, compilator TinyGo qui generavit hunc codicem intermedium descriptionem huius notitiae structurae in partes dividit. Posset tria segmentum elementa (ptr, len ΠΈ cap) ut structura (struct), sed eas repraesentans ut tria entia distincta permittit aliquot optimizationes. Alii compilatores scalpere possunt aliis modis, secundum vocationum conventiones functionum tribunalium scopo.

Alia interesting notatio huius codicis est usus instructionis getelementptr (Saepe abbreviata ut GEP).

Haec instructio cum indicibus laborat et monstratorem ad scalpere elementum obtinet. Exempli gratia comparemus cum sequenti codice in C scriptum:

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

Vel with the following equivalent to hoc ;

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

Hoc primum est quod mandatum getelementptr dereferencing res non facit. Modo novum monstratorem determinat ex existente. Potest accipi iussus mul ΠΈ add at odio massa. Plus legere potes de instructiones GEP hic.

Alia interesting notatio huius codicis intermedii est usus instructionis icmp. Haec est generalis propositi institutio ad integras comparationes efficiendas. Effectus huius instructionis semper valorem generis i1 β€” valorem logicum. In hoc casu comparatio facta est utendo keyword slt (minus signati), cum duos numeros prius per genus repraesentatos comparemus int. Si duos integros unsigned comparemus, tum utemur icmpac keyword in comparatione esset ult. Ut punctum numeri conferam, alia doctrina adhibetur; fcmpqui similiter operatur.

results

Credo in hac materia praecipuas lineas LLVM IR obtexisse. Donec sit amet volutpat est. Praesertim media repraesentatio codicis multas adnotationes continere potest, quae optimam condicionem admittunt, notae notae codici notae compilator, quae aliter in IR exprimi non possunt. Exempli gratia, hoc signum est inbounds GEP instructiones vel vexilla nsw ΠΈ nuwquae addi ad instructiones possunt add. Idem valet de keyword privateostendens optimizer munus quod notat ab extra hodiernam compilationem unitas non referenda. Hoc permittit multum interesting optimizationes interprocedurales sicut rationes insuetas eliminare.

Plura legere potes de LLVM in documentumquas saepe cum LLVM substructio compilator enucleare feres. Hic ducisquae spectat ad elaborandum compilator linguae simplicissimae. Utrumque horum informationum tibi utile erit cum tuum compilator creaveris.

Carissimi legentibus! Uteris LLVM?

LLVM a Go perspective

Source: www.habr.com