Таҳияи компилятор кори хеле душвор аст. Аммо, хушбахтона, бо таҳияи лоиҳаҳо ба монанди LLVM, ҳалли ин мушкилот хеле содда карда мешавад, ки ҳатто ба як барномасоз имкон медиҳад, ки забони наверо эҷод кунад, ки аз ҷиҳати иҷроиш ба C наздик аст. Кор бо LLVM аз он сабаб мушкил аст, ки ин Система бо миқдори зиёди код, ки бо ҳуҷҷатҳои кам муҷаҳҳаз шудааст, муаррифӣ карда мешавад. Барои ислоҳи ин камбуд, муаллифи мавод, ки тарҷумаи онро имрӯз нашр мекунем, мехоҳад намунаҳои кодҳои дар Go навишташударо намоиш диҳад ва нишон диҳад, ки чӣ гуна онҳо бори аввал ба забони англисӣ тарҷума шудаанд.
Мисоли аввал
Аввалин функсияе, ки ман дар ин ҷо мебинам, як механизми оддии илова кардани рақамҳост:
func myAdd(a, b int) int{
return a + b
}
Ин функсия хеле содда аст ва, шояд, ҳеҷ чиз соддатар буда наметавонад. Он ба рамзи зерини Go SSA тарҷума мешавад:
func myAdd(a int, b int) int:
entry:
t0 = a + b int
return t0
Бо ин назар, маслиҳатҳои навъи додаҳо дар тарафи рост ҷойгир шудаанд ва онҳоро дар аксари ҳолатҳо нодида гирифтан мумкин аст.
Ин мисоли хурд аллакай ба шумо имкон медиҳад, ки моҳияти як ҷанбаи SSA-ро бубинед. Маҳз, ҳангоми табдил додани код ба шакли SSA, ҳар як ифода ба қисмҳои ибтидоӣ, ки аз онҳо иборат аст, тақсим карда мешавад. Дар вазъияти мо, фармон return a + b
, дар асл, ду амалро ифода мекунад: илова кардани ду адад ва баргардонидани натиҷа.
Илова бар ин, дар ин ҷо шумо метавонед блокҳои асосии барномаро бинед, дар ин код танҳо як блок мавҷуд аст - блоки вуруд. Мо дар бораи блокҳо дар зер бештар сӯҳбат хоҳем кард.
Рамзи Go SSA ба осонӣ ба LLVM IR табдил меёбад:
define i64 @myAdd(i64 %a, i64 %b) {
entry:
%0 = add i64 %a, %b
ret i64 %0
}
Он чизе ки шумо мушоҳида карда метавонед, ин аст, ки гарчанде дар ин ҷо сохторҳои гуногуни синтаксисӣ истифода мешаванд, сохтори функсия асосан бетағйир аст. Рамзи LLVM IR нисбат ба рамзи Go SSA каме қавитар аст, ки ба C монанд аст. Дар ин ҷо, дар эъломияи функсия аввал тавсифи навъи додаи он бармегардад, навъи аргумент пеш аз номи аргумент нишон дода мешавад. Илова бар ин, барои содда кардани таҳлили IR, дар пеши номҳои объектҳои ҷаҳонӣ аломат гузошта мешавад @
, ва пеш аз номњои мањаллї аломат гузошта шудааст %
(функсия инчунин объекти глобалӣ ҳисобида мешавад).
Як чизро бояд дар бораи ин код қайд кард, ки қарори намояндагии навъи Go int
, ки метавонад ҳамчун арзиши 32-бит ё 64-бит муаррифӣ шавад, вобаста ба компилятор ва ҳадафи компиляция, ҳангоми тавлиди LLVM рамзи IR қабул карда мешавад. Ин яке аз сабабҳои зиёди он аст, ки рамзи LLVM IR, тавре ки бисёриҳо фикр мекунанд, аз платформа мустақил нест. Чунин кодеро, ки барои як платформа сохта шудааст, барои платформаи дигар гирифтан ва тартиб додан мумкин нест (агар шумо барои ҳалли ин мушкилот мувофиқ набошед
Боз як нуктаи ҷолиби диққати он аст, ки навъи i64
адади бутуни имзошуда нест: аз чихати ифодаи аломати адад бетараф аст. Вобаста аз дастур, он метавонад рақамҳои имзошуда ва беимзоро ифода кунад. Дар сурати ифодаи амалиёти изофӣ, ин аҳамият надорад, бинобар ин дар кор бо рақамҳои имзошуда ё беимзо тафовуте вуҷуд надорад. Дар ин ҷо ман мехоҳам қайд намоям, ки дар забони C зиёд кардани як тағирёбандаи бутуни имзошуда ба рафтори номуайян оварда мерасонад, аз ин рӯ фронти Clang ба амалиёт парчам илова мекунад. nsw
(ҳеҷ бастабандии имзошуда), ки ба LLVM мегӯяд, ки он метавонад тахмин кунад, ки илова ҳеҷ гоҳ пур намешавад.
Ин метавонад барои баъзе оптимизатсияҳо муҳим бошад. Масалан, илова кардани ду арзиш i16
дар платформаи 32-битӣ (бо регистрҳои 32-битӣ) пас аз илова, амалиёти васеъкунии аломатро талаб мекунад, то дар диапазон боқӣ монад. i16
. Аз ин сабаб, иҷрои амалиёти бутун дар асоси андозаи реестри мошин аксар вақт самараноктар аст.
Он чизе, ки дар оянда бо ин рамзи IR рӯй медиҳад, ҳоло барои мо таваҷҷӯҳи хоса надорад. Рамз оптимизатсия карда шудааст (аммо дар сурати мисоли оддии мо, ҳеҷ чиз оптимизатсия карда намешавад) ва сипас ба рамзи мошин табдил дода мешавад.
Мисоли дуюм
Мисоли оянда, ки мо онро дида мебароем, каме мураккабтар хоҳад буд. Махз, мо дар бораи функсияе сухан меронем, ки як буридаи бутунро ҷамъ мекунад:
func sum(numbers []int) int {
n := 0
for i := 0; i < len(numbers); i++ {
n += numbers[i]
}
return n
}
Ин код ба рамзи зерини Go SSA табдил меёбад:
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
Дар ин ҷо шумо аллакай биноҳои бештареро мебинед, ки барои муаррифии код дар шакли SSA хосанд. Эҳтимол, хусусияти равшантарини ин код он аст, ки ягон фармонҳои сохтории назорати ҷараёни мавҷуд нестанд. Барои идора кардани ҷараёни ҳисобҳо, танҳо ҷаҳишҳои шартӣ ва бешартӣ вуҷуд доранд ва агар ин фармонро ҳамчун фармони идоракунии ҷараён баррасӣ кунем, фармони бозгашт.
Дар асл, дар ин ҷо шумо метавонед ба он диққат диҳед, ки барнома бо истифода аз қавсҳои ҷингила ба блокҳо тақсим карда нашудааст (чун дар оилаи забонҳои C). Он аз ҷониби тамғакоғазҳо тақсим карда мешавад, ки забонҳои ассембро ба хотир меорад ва дар шакли блокҳои асосӣ пешниҳод карда мешавад. Дар SSA, блокҳои асосӣ ҳамчун пайдарпаии ҳамбастаи код муайян карда мешаванд, ки аз нишона сар карда, бо дастурҳои асосии анҷом додани блок анҷом мешаванд, ба монанди − return
и jump
.
Боз як ҷузъиёти ҷолиби ин код бо дастур нишон дода шудааст phi
. Дастурҳо хеле ғайриоддӣ мебошанд ва барои фаҳмидани он шояд чанд вақт лозим шавад. дар хотир доред, ки myAdd
дар боло нишон дода шудааст, аммо барои вазифаҳои мураккабтар ба монанди функсияе, ки дар ин бахш баррасӣ мешавад, мувофиқ нест sum
. Махсусан, тағирёбандаҳо ҳангоми иҷрои давра тағир меёбанд i
и n
.
SSA маҳдудияти таъини арзишҳои тағирёбандаро як маротиба бо истифода аз дастури номбурда мегузарад phi
(номи он аз алифбои юнонӣ гирифта шудааст). Гап дар он аст, ки барои он ки рамзи SSA барои забонҳо ба монанди C тавлид шавад, шумо бояд ба баъзе ҳилаҳо муроҷиат кунед. Натиҷаи даъвати ин дастур арзиши ҷории тағирёбанда (i
ё n
) ва рӯйхати блокҳои асосӣ ҳамчун параметрҳои он истифода мешавад. Масалан, ин дастурро баррасӣ кунед:
t0 = phi [entry: 0:int, for.body: t6] #n
Маънои он чунин аст: агар блоки асосии пештара блок бошад entry
(ворид), пас t0
доимист 0
, ва агар блоки асосии пештара буд for.body
, пас шумо бояд арзишро гиред t6
аз ин блок. Ин ҳама метавонад хеле пурасрор ба назар расад, аммо ин механизм он чизест, ки SSA кор кунад. Аз нуқтаи назари инсон, ин ҳама фаҳмидани рамзро душвор мегардонад, аммо он, ки ҳар як арзиш танҳо як маротиба таъин карда мешавад, бисёр оптимизатсияҳоро хеле осон мекунад.
Дар хотир доред, ки агар шумо компилятори шахсии худро нависед, ба шумо одатан лозим нест, ки бо ин гуна мавод сарукор дошта бошед. Ҳатто Clang ҳамаи ин дастурҳоро тавлид намекунад phi
, механизмро истифода мебарад alloca
(он ба кор бо тағирёбандаҳои оддии маҳаллӣ шабоҳат дорад). Сипас, ҳангоми иҷро кардани гузариши оптимизатсияи LLVM даъват карда мешавад alloca
ба шакли SSA табдил дода мешавад. Аммо, TinyGo аз Go SSA маълумот мегирад, ки ба таври мувофиқ аллакай ба шакли SSA табдил дода шудааст.
Навоварии дигари фрагменти коди мобайнии мавриди баррасишаванда дар он аст, ки дастрасӣ ба унсурҳои бурида аз рӯи индекс дар шакли амалиёти ҳисобкунии суроға ва амали аз истинод аз нишондиханда ҳосилшуда муаррифӣ мешавад. Дар ин ҷо шумо метавонед иловаи мустақими константаҳоро ба рамзи IR дидан кунед (масалан - 1:int
). Дар мисол бо функсия myAdd
ин истифода нашудааст. Акнун, ки мо ин хусусиятҳоро аз роҳ дур кардем, биёед бубинем, ки ин код ҳангоми табдил ба шакли 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
}
Дар ин чо хам мисли пештара хамон сохтеро мушохида кардан мумкин аст, ки дигар сохторхои синтаксисиро дар бар мегирад. Масалан, дар зангҳо phi
арзишҳо ва тамғакоғазҳо иваз карда шуданд. Бо вуҷуди ин, дар ин ҷо чизе ҳаст, ки ба он диққати махсус додан лозим аст.
Барои оғоз, дар ин ҷо шумо метавонед як имзои функсияи тамоман дигарро бинед. LLVM буридаҳоро дастгирӣ намекунад ва дар натиҷа, компилятори TinyGo, ки ин рамзи фосилавиро тавлид кардааст, тавсифи ин сохтори додаҳоро ба қисмҳо тақсим мекунад. Он метавонад се унсури буридаро намояндагӣ кунад (ptr
, len
и cap
) ҳамчун сохтор (сохтор), аммо муаррифии онҳо ҳамчун се объекти алоҳида имкон медиҳад, ки баъзе оптимизатсияҳо. Дигар компиляторҳо метавонанд буридаро бо роҳҳои дигар, вобаста ба конвенсияҳои даъвати функсияҳои платформаи мақсаднок намояндагӣ кунанд.
Хусусияти дигари ҷолиби ин код истифодаи дастур аст getelementptr
(аксар вақт ҳамчун GEP ихтисор карда мешавад).
Ин дастур бо нишондиҳандаҳо кор мекунад ва барои ба даст овардани ишора ба элементи бурида истифода мешавад. Масалан, биёед онро бо рамзи зерини дар C навишташуда муқоиса кунем:
int* sliceptr(int *ptr, int index) {
return &ptr[index];
}
Ё бо муодили зерини ин:
int* sliceptr(int *ptr, int index) {
return ptr + index;
}
Муҳимтар аз ҳама ин аст, ки дастурҳо getelementptr
амалиёти бекоркуниро ичро намекунад. Он танҳо як нишондиҳандаи навро дар асоси мавҷудаи мавҷуда ҳисоб мекунад. Онро метавон ҳамчун дастур қабул кард mul
и add
дар сатҳи сахтафзор. Шумо метавонед дар бораи дастурҳои GEP бештар хонед
Хусусияти дигари ҷолиби ин рамзи мобайнӣ истифодаи дастур аст icmp
. Ин дастури умумӣ аст, ки барои татбиқи муқоисаи ададҳо истифода мешавад. Натиҷаи ин дастур ҳамеша арзиши навъи аст i1
- арзиши мантиқӣ. Дар ин ҳолат бо истифода аз калимаи калидӣ муқоиса карда мешавад slt
(камтар аз имзо шудааст), зеро мо ду рақами қаблан аз рӯи намудро муқоиса мекунем int
. Агар мо ду адади бутуни беимзоро мукоиса мекардем, он гох истифода мебурдем icmp
, ва калимаи калидӣ дар муқоиса истифода мешавад ult
. Барои муқоисаи рақамҳои нуқтаи шинокунанда, дастури дигар истифода мешавад, fcmp
, ки ба хамин тарз кор мекунад.
Натиҷаҳо
Ман боварӣ дорам, ки дар ин мавод ман муҳимтарин хусусиятҳои LLVM IR-ро фаро гирифтаам. Албатта, дар ин ҷо бисёр чизҳои дигар вуҷуд доранд. Махсусан, муаррифии фосилавии код метавонад эзоҳҳои зиёде дошта бошад, ки имкон медиҳад, ки оптимизатсия ба инобат гирифтани хусусиятҳои муайяни коди ба компилятор маълум, ки ба таври дигар дар IR ифода карда нашавад. Масалан, ин парчам аст inbounds
Дастурҳои GEP, ё парчамҳо nsw
и nuw
, ки онро ба дастурхо илова кардан мумкин аст add
. Ҳамин чиз барои калимаи калидӣ меравад private
, ба оптимизатор нишон медиҳад, ки функсияе, ки он қайд мекунад, аз берун аз воҳиди компиляцияи ҷорӣ истинод карда намешавад. Ин имкон медиҳад, ки бисёр оптимизатсияҳои ҷолиби байнипросессуалӣ ба монанди аз байн бурдани далелҳои истифоданашуда.
Шумо метавонед дар бораи LLVM бештар хонед
Хонандагони азиз! Оё шумо LLVM-ро истифода мебаред?
Манбаъ: will.com