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.
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 + b
duas 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 int
quae 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.
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 myAdd
supra 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 0
Ac si prior basic scandalum erat for.body
Ergo 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 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
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 icmp
ac keyword in comparatione esset ult
. Ut punctum numeri conferam, alia doctrina adhibetur; fcmp
qui 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
ΠΈ nuw
quae addi ad instructiones possunt add
. Idem valet de keyword private
ostendens 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
Carissimi legentibus! Uteris LLVM?
Source: www.habr.com