Go ရှုထောင့်မှ LLVM

compiler တစ်ခုကို တီထွင်ခြင်းသည် အလွန်ခက်ခဲသော အလုပ်ဖြစ်သည်။ သို့သော် ကံကောင်းထောက်မစွာ၊ LLVM ကဲ့သို့သော ပရောဂျက်များ၏ ဖွံ့ဖြိုးတိုးတက်မှုနှင့်အတူ၊ ဤပြဿနာအတွက် ဖြေရှင်းချက်သည် အလွန်ရိုးရှင်းသွားသည်၊ ၎င်းသည် ပရိုဂရမ်မာတစ်ဦးတည်းပင်လျှင် C နှင့် စွမ်းဆောင်ရည်နီးစပ်သော ဘာသာစကားအသစ်ကို ဖန်တီးနိုင်စေပါသည်။ LLVM နှင့်အလုပ်လုပ်ခြင်းသည် ဤအချက်ကြောင့် ရှုပ်ထွေးပါသည်။ စာရွက်စာတမ်းအနည်းငယ်ပါရှိသော စနစ်အား ကုဒ်အများအပြားဖြင့် ကိုယ်စားပြုပါသည်။ ဤချို့ယွင်းချက်ကို ပြုပြင်ရန် ကြိုးစားရန်အတွက် ယနေ့ ကျွန်ုပ်တို့ထုတ်ဝေနေသော ဘာသာပြန်ဆိုထားသည့် အကြောင်းအရာကို ရေးသားသူသည် Go တွင်ရေးထားသောကုဒ်၏နမူနာများကို သရုပ်ပြသပြီး ၎င်းတို့ကို မည်သို့ဘာသာပြန်ဆိုထားသည်ကို ပြသသွားမည်ဖြစ်သည်။ SSA ကိုသွားပါ။ထို့နောက် compiler ကို အသုံးပြု၍ LLVM IR တွင်၊ မင်္ဂလာပါ. Go SSA နှင့် LLVM IR ကုဒ်အား ဤနေရာတွင် ဖော်ပြထားသော ရှင်းလင်းချက်များနှင့် မသက်ဆိုင်သောအရာများကို ဖယ်ရှားရန်၊ ရှင်းလင်းချက်များကို ပိုမိုနားလည်နိုင်စေရန်အတွက် အနည်းငယ်တည်းဖြတ်ထားပါသည်။

Go ရှုထောင့်မှ LLVM

ပထမဥပမာ

ဒီမှာကြည့်မယ့် ပထမဆုံး function က နံပါတ်တွေထည့်တဲ့ ရိုးရှင်းတဲ့ ယန္တရားတစ်ခုပါ။

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တကယ်တော့၊ လုပ်ဆောင်ချက်နှစ်ခုကို ကိုယ်စားပြုသည်- နံပါတ်နှစ်ခုပေါင်းခြင်းနှင့် ရလဒ်ကို ပြန်ပေးခြင်းဖြစ်သည်။

ထို့အပြင်၊ ဤနေရာတွင် ပရိုဂရမ်၏ အခြေခံလုပ်ကွက်များကို သင်တွေ့မြင်နိုင်သည်၊ ဤကုဒ်တွင် ဘလောက်တစ်ခုသာ ရှိသည်- entry block. အောက်ဖော်ပြပါ ဘလောက်များအကြောင်း နောက်ထပ်ပြောပါမည်။

Go SSA ကုဒ်သည် LLVM IR သို့ အလွယ်တကူပြောင်းသည်-

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

သင်သတိပြုမိနိုင်သည်မှာ ဤနေရာတွင် မတူညီသော syntactic တည်ဆောက်ပုံများကို အသုံးပြုထားသော်လည်း function ၏ဖွဲ့စည်းပုံသည် အခြေခံအားဖြင့် မပြောင်းလဲကြောင်းဖြစ်သည်။ LLVM IR ကုဒ်သည် C နှင့်ဆင်တူသော Go SSA ကုဒ်ထက် အနည်းငယ် ပိုအားကောင်းသည်။ ဤတွင်၊ လုပ်ဆောင်ချက် ကြေငြာချက်တွင်၊ ဦးစွာ ၎င်းပြန်လာသော ဒေတာအမျိုးအစား၏ ဖော်ပြချက်ပါရှိပြီး၊ အငြင်းအခုံအမျိုးအစားကို အကြောင်းပြချက်အမည်ရှေ့တွင် ညွှန်ပြထားသည်။ ထို့အပြင်၊ IR ခွဲခြမ်းစိတ်ဖြာမှုကို ရိုးရှင်းစေရန်၊ ကမ္ဘာလုံးဆိုင်ရာ အဖွဲ့အစည်းများ၏ အမည်များကို သင်္ကေတဖြင့် ရှေ့တွင် ထားရှိသည်။ @ဒေသနာမည်တွေရှေ့မှာ သင်္ကေတတစ်ခုရှိတယ်။ % (လုပ်ဆောင်ချက်တစ်ခုကို ကမ္ဘာလုံးဆိုင်ရာ entity တစ်ခုအဖြစ်လည်း ယူဆသည်)။

ဤကုဒ်နှင့်ပတ်သက်၍ သတိထားရမည့်အချက်မှာ Go ၏ အမျိုးအစားကိုယ်စားပြုဆုံးဖြတ်ချက်ဖြစ်သည်။ intcompiler နှင့် compilation ၏ပစ်မှတ်ပေါ်မူတည်၍ 32-bit သို့မဟုတ် 64-bit တန်ဖိုးအဖြစ် ကိုယ်စားပြုနိုင်သည့်၊ LLVM သည် IR ကုဒ်ကိုထုတ်ပေးသောအခါ လက်ခံသည်။ ဤသည်မှာ LLVM IR ကုဒ်သည် လူအများထင်သည့်အတိုင်း ပလက်ဖောင်းလွတ်လပ်မှုမရှိသည့် အကြောင်းရင်းများစွာထဲမှ တစ်ခုဖြစ်သည်။ ပလပ်ဖောင်းတစ်ခုအတွက် ဖန်တီးထားသည့် ထိုကုဒ်ကို ရိုးရှင်းစွာယူ၍ အခြားပလပ်ဖောင်းအတွက် စုစည်း၍မရပါ (ဤပြဿနာကို ဖြေရှင်းရန် သင်မသင့်လျော်ပါက၊ အလွန်ဂရုစိုက်မှုနှင့်).

နောက်ထပ် စိတ်ဝင်စားစရာ အချက်ကတော့ အမျိုးအစားပါ။ i64 ရေးထိုးထားသော ကိန်းပြည့်မဟုတ်ပါ။ ညွှန်ကြားချက်အပေါ် မူတည်၍ ၎င်းသည် လက်မှတ်မထိုးထားသော နံပါတ်များနှင့် လက်မှတ်မထိုးထားသော နံပါတ်များကို ကိုယ်စားပြုနိုင်သည်။ ထပ်လောင်းလုပ်ဆောင်မှုကို ဖော်ပြခြင်းကိစ္စတွင်၊ ၎င်းသည် အရေးမကြီးပါ၊ ထို့ကြောင့် လက်မှတ်မထိုးထားသော သို့မဟုတ် လက်မှတ်မထိုးထားသော နံပါတ်များဖြင့် လုပ်ဆောင်ရာတွင် ကွာခြားချက်မရှိပါ။ ဤနေရာတွင် ကျွန်ုပ်မှတ်သားလိုသည်မှာ C language တွင်၊ ရေးထိုးထားသော ကိန်းပြည့်ကိန်းရှင်ကို ပြည့်လျှံစေခြင်းသည် သတ်မှတ်မထားသော အပြုအမူကို ဖြစ်စေသည်၊ ထို့ကြောင့် Clang frontend သည် လုပ်ဆောင်မှုတွင် အလံတစ်ခု ထပ်ထည့်သည် nsw (လက်မှတ်မထိုးထားသော ထုပ်ပိုးခြင်း) သည် LLVM ကိုပြောသည် ၊ ၎င်းသည် ထပ်လောင်းသည် မည်သည့်အခါမျှ မလျှံဟု ယူဆနိုင်သည်။

ပိုမိုကောင်းမွန်အောင်လုပ်ဆောင်မှုအချို့အတွက် ၎င်းသည် အရေးကြီးပါသည်။ ဥပမာအားဖြင့်၊ တန်ဖိုးနှစ်ခုပေါင်းထည့်ခြင်း။ i16 32-bit platform (32-bit registers များနှင့်အတူ) တွင်၊ ထို့အပြင် range တွင်ဆက်လက်တည်ရှိရန်အတွက် sign expansion operation တစ်ခုလိုအပ်သည် i16. ထို့အတွက်ကြောင့်၊ စက်မှတ်ပုံတင်အရွယ်အစားပေါ်မူတည်၍ ကိန်းပြည့်လည်ပတ်မှုများကို လုပ်ဆောင်ရန် မကြာခဏ ပိုမိုထိရောက်သည်။

ဤ IR ကုဒ်နှင့် နောက်ဆက်တွဲဖြစ်ပျက်မည့်အရာသည် ကျွန်ုပ်တို့အတွက် အထူးစိတ်ဝင်စားစရာမဟုတ်ပါ။ ကုဒ်ကို အကောင်းဆုံးဖြစ်အောင် ပြုလုပ်ထားသည် (သို့သော် ကျွန်ုပ်တို့၏ ဥပမာကဲ့သို့ ရိုးရှင်းသော ကိစ္စမျိုးတွင်၊ မည်သည့်အရာကမျှ optimized မဖြစ်ပါ) ထို့နောက် စက်ကုဒ်အဖြစ်သို့ ပြောင်းလဲသွားသည်။

ဒုတိယဥပမာ

နောက်ဥပမာကိုကြည့်မယ် ဆိုရင် နည်းနည်းပိုရှုပ်ထွေးပါလိမ့်မယ်။ ဆိုလိုသည်မှာ၊ ကျွန်ုပ်တို့သည် ကိန်းပြည့်များကို ပေါင်းစည်းထားသော လုပ်ဆောင်ချက်တစ်ခုအကြောင်း ပြောနေပါသည်။

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 ဖောင်တွင် ကုဒ်ကို ကိုယ်စားပြုရန်အတွက် ပုံမှန်တည်ဆောက်မှုများကို ပိုမိုမြင်တွေ့နိုင်ပါသည်။ ဤကုဒ်၏ အထင်ရှားဆုံးသော အင်္ဂါရပ်မှာ ဖွဲ့စည်းတည်ဆောက်ထားသော စီးဆင်းမှု ထိန်းချုပ်မှု အမိန့်များ မရှိခြင်း ဖြစ်သည် ။ တွက်ချက်မှုစီးဆင်းမှုကို ထိန်းချုပ်ရန်၊ အခြေအနေနှင့် ခြွင်းချက်မရှိ jumps များသာ ရှိပြီး၊ ဤ command ကို flow ကို ထိန်းချုပ်ရန် command တစ်ခုအဖြစ် ယူဆပါက၊ return command ဖြစ်သည်။

တကယ်တော့၊ ပရိုဂရမ်ကို curly braces (ဘာသာစကား C မိသားစုတွင်ကဲ့သို့) သုံးပြီး တုံးများအဖြစ် ခွဲခြားမထားသည်ကို ဤနေရာတွင် အာရုံစိုက်နိုင်ပါသည်။ ၎င်းကို အညွှန်းများဖြင့် ပိုင်းခြားကာ စုစည်းထားသော ဘာသာစကားများကို အမှတ်ရစေကာ အခြေခံလုပ်ကွက်များပုံစံဖြင့် ဖော်ပြသည်။ SSA တွင်၊ အခြေခံလုပ်ကွက်များကို အညွှန်းတစ်ခုဖြင့်စတင်ကာ − ကဲ့သို့သော အခြေခံလုပ်ကွက်ပြီးစီးမှုလမ်းညွှန်ချက်များဖြင့်အဆုံးသတ်သည့် ကုဒ်၏ဆက်နွှယ်သော sequences များအဖြစ် သတ်မှတ်သည်။ return и jump.

ဤကုဒ်၏ နောက်ထပ်စိတ်ဝင်စားဖွယ်အသေးစိတ်အသေးစိတ်ကို ညွှန်ကြားချက်ဖြင့် ကိုယ်စားပြုပါသည်။ phi. ညွှန်ကြားချက်များသည် အထူးအဆန်းဖြစ်ပြီး နားလည်ရန် အချိန်အနည်းငယ်ကြာနိုင်သည်။ သတိရပါ။ ရှမ်းပြည်တောင်ပိုင်း Static Single Assignment ၏ အတိုကောက်ဖြစ်သည်။ ဤသည်မှာ compilers များအသုံးပြုသော ကုဒ်၏အလယ်အလတ်ကိုယ်စားပြုမှုဖြစ်ပြီး၊ ကိန်းရှင်တစ်ခုစီကို တစ်ကြိမ်သာတန်ဖိုးသတ်မှတ်ထားသည်။ ကျွန်ုပ်တို့၏လုပ်ဆောင်ချက်ကဲ့သို့ ရိုးရှင်းသောလုပ်ဆောင်ချက်များကို ဖော်ပြရန်အတွက် ဤအရာသည် ကောင်းမွန်ပါသည်။ myAddအထက်တွင်ပြသထားသော်လည်း ဤကဏ္ဍတွင် ဆွေးနွေးထားသော လုပ်ဆောင်ချက်ကဲ့သို့သော ပိုမိုရှုပ်ထွေးသောလုပ်ဆောင်ချက်များအတွက် မသင့်လျော်ပါ။ sum. အထူးသဖြင့်၊ ကွင်းဆက်ကိုလုပ်ဆောင်နေစဉ်အတွင်း ကိန်းရှင်များသည် ပြောင်းလဲပါသည်။ i и n.

SSA သည် ညွှန်ကြားချက်ကို အသုံးပြု၍ ပြောင်းလဲနိုင်သော တန်ဖိုးများကို သတ်မှတ်ခြင်းဆိုင်ရာ ကန့်သတ်ချက်ကို ကျော်ဖြတ်သည် phi (၎င်း၏အမည်ကိုဂရိအက္ခရာမှယူသည်) ။ အမှန်မှာ၊ C ကဲ့သို့သော ဘာသာစကားများအတွက် SSA ကိုယ်စားပြုကုဒ်ကို ထုတ်ပေးရန်အတွက်၊ အချို့သောလှည့်ကွက်များကို အားကိုးရန် လိုအပ်ပါသည်။ ဤညွှန်ကြားချက်ကိုခေါ်ဆိုခြင်း၏ရလဒ်သည် variable ၏လက်ရှိတန်ဖိုးဖြစ်သည် (i သို့မဟုတ် n) နှင့် အခြေခံလုပ်ကွက်များစာရင်းကို ၎င်း၏ ကန့်သတ်ချက်များအဖြစ် အသုံးပြုသည်။ ဥပမာ၊ ဤညွှန်ကြားချက်ကို သုံးသပ်ကြည့်ပါ-

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

၎င်း၏ အဓိပ္ပါယ်မှာ အောက်ပါအတိုင်းဖြစ်သည်- အကယ်၍ ယခင်အခြေခံဘလောက်သည် ဘလောက်ဖြစ်သည်။ entry (input) ထို့နောက် t0 ကိန်းသေတစ်ခုဖြစ်သည်။ 0အရင်တုံးက ဖြစ်ခဲ့မယ်ဆိုရင်၊ for.bodyဒါဆို မင်းတန်ဖိုးကို ယူရမယ်။ t6 ဤဘလောက်မှ ဤအရာအားလုံးသည် လျှို့ဝှက်ဆန်းကြယ်ပုံပေါက်နိုင်သော်လည်း ဤယန္တရားသည် SSA ကို အလုပ်ဖြစ်စေသည်။ လူ့ရှုထောင့်မှကြည့်လျှင် ဤအရာအားလုံးသည် ကုဒ်ကို နားလည်ရန် ခက်ခဲစေသည်၊ သို့သော် တန်ဖိုးတစ်ခုစီကို တစ်ကြိမ်သာ သတ်မှတ်ပေးသည့်အတွက် များစွာသော optimizations များစွာကို ပိုမိုလွယ်ကူစေသည်။

သင့်ကိုယ်ပိုင် compiler ရေးပါက၊ သင်သည် ဤကဲ့သို့သော အကြောင်းအရာများကို ကိုင်တွယ်ဖြေရှင်းရန် မလိုအပ်ကြောင်း သတိပြုပါ။ Clang ပင်လျှင် ဤညွှန်ကြားချက်အားလုံးကို မထုတ်ပေးပါ။ phiယန္တရားတစ်ခုကို အသုံးပြုသည်။ alloca (၎င်းသည် သာမန် local variable များနှင့် အလုပ်လုပ်ပုံတူသည်)။ ထို့နောက် LLVM optimization pass ကို run သောအခါ ခေါ်သည်။ mem2reg၊ ညွှန်ကြားချက် alloca SSA ပုံစံသို့ ပြောင်းလဲခဲ့သည်။ TinyGo သည် Go SSA မှ ထည့်သွင်းမှုကို လက်ခံရရှိသည်၊ ၎င်းသည် အဆင်ပြေစွာဖြင့် SSA ပုံစံသို့ ပြောင်းလဲပြီးဖြစ်သည်။

ထည့်သွင်းစဉ်းစားထားသည့် အလယ်အလတ်ကုဒ်၏အပိုင်းအစ၏ နောက်ထပ်ဆန်းသစ်တီထွင်မှုတစ်ခုမှာ အညွှန်းအားဖြင့် အချပ်ဒြပ်စင်များကို ဝင်ရောက်အသုံးပြုခြင်းကို လိပ်စာတွက်ချက်ခြင်းနှင့် ရလဒ်ညွှန်ပြခြင်း၏ လုပ်ဆောင်မှုပုံစံဖြင့် ကိုယ်စားပြုခြင်းဖြစ်ပါသည်။ ဤနေရာတွင် IR ကုဒ်သို့ ကိန်းသေများ တိုက်ရိုက်ထပ်ထည့်ခြင်းကို သင်တွေ့နိုင်သည် (ဥပမာ- 1:int) function နှင့်အတူဥပမာ 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
}

ဤတွင်၊ ယခင်ကဲ့သို့ပင်၊ အခြား syntactic တည်ဆောက်ပုံများပါ၀င်သော တူညီသောဖွဲ့စည်းပုံကို ကျွန်ုပ်တို့တွေ့နိုင်ပါသည်။ ဥပမာအားဖြင့် ခေါ်ဆိုမှုများ phi တန်ဖိုးများနှင့် တံဆိပ်များ ဖလှယ်ကြသည်။ သို့သော် ဤနေရာတွင် အထူးသတိထားရမည့် အရာတစ်ခုရှိသည်။

စတင်ရန်၊ ဤနေရာတွင် လုံးဝကွဲပြားခြားနားသော လုပ်ဆောင်ချက်လက်မှတ်ကို သင်တွေ့နိုင်ပါသည်။ LLVM သည် အချပ်များကို မပံ့ပိုးပါ၊ ထို့ကြောင့် ပိုမိုကောင်းမွန်အောင်ပြုလုပ်ခြင်းဖြင့်၊ ဤအလယ်အလတ်ကုဒ်ကိုထုတ်ပေးသော TinyGo compiler သည် ဤဒေတာဖွဲ့စည်းပုံ၏ဖော်ပြချက်ကို အပိုင်းများအဖြစ် ပိုင်းခြားထားသည်။ ၎င်းသည် အပိုင်းသုံးပိုင်းကို ကိုယ်စားပြုနိုင်သည် (ptr, len и cap) ဖွဲ့စည်းပုံ (struct) တစ်ခုအနေဖြင့်၊ သို့သော် ၎င်းတို့ကို သီးခြား entities သုံးခုအဖြစ် ကိုယ်စားပြု၍ အချို့သော optimization များကို ခွင့်ပြုသည်။ ပစ်မှတ်ပလက်ဖောင်း၏ လုပ်ဆောင်ချက်များကို ခေါ်ဆိုခြင်းဆိုင်ရာ သဘောတူညီချက်များပေါ်မူတည်၍ အခြားသော compilers များသည် အချပ်ကို အခြားနည်းလမ်းများဖြင့် ကိုယ်စားပြုနိုင်ပါသည်။

ဤကုဒ်၏ နောက်ထပ် စိတ်ဝင်စားဖွယ် အင်္ဂါရပ်မှာ ညွှန်ကြားချက်ကို အသုံးပြုခြင်း ဖြစ်သည်။ 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. Floating Point နံပါတ်များကို နှိုင်းယှဉ်ရန် အခြားသော ညွှန်ကြားချက်ကို အသုံးပြုသည်၊ fcmpအလားတူနည်းလမ်းဖြင့်အလုပ်လုပ်သည်။

ရလဒ်များကို

ဤပစ္စည်းတွင် LLVM IR ၏ အရေးကြီးဆုံးအင်္ဂါရပ်များကို အကျုံးဝင်သည်ဟု ကျွန်ုပ်ယုံကြည်ပါသည်။ ဟုတ်ပါတယ်၊ ဒီမှာ အများကြီးရှိပါသေးတယ်။ အထူးသဖြင့်၊ ကုဒ်၏အလယ်အလတ်ကိုယ်စားပြုမှုတွင် IR တွင် အခြားနည်းဖြင့်ဖော်ပြ၍မရသော compiler မှသိထားသော code ၏အချို့သောအင်္ဂါရပ်များကိုထည့်သွင်းစဉ်းစားရန် optimization passes များကိုထည့်သွင်းခွင့်ပြုသည့်မှတ်ချက်များစွာပါဝင်နိုင်သည်။ ဥပမာ၊ ဒါက အလံတစ်ခုပါ။ inbounds GEP ညွှန်ကြားချက်များ သို့မဟုတ် အလံများ nsw и nuwညွှန်ကြားချက်များတွင် ထည့်သွင်းနိုင်သည်။ add. keyword မှာလည်း အလားတူပါပဲ။ private၎င်းအမှတ်အသားပြုသည့်လုပ်ဆောင်ချက်သည် လက်ရှိစုစည်းမှုယူနစ်ပြင်ပမှ ရည်ညွှန်းမည်မဟုတ်ကြောင်း ပိုမိုကောင်းမွန်အောင်ပြုလုပ်သူအား ညွှန်ပြသည်။ ၎င်းသည် အသုံးမပြုသော ငြင်းခုံမှုများကို ဖယ်ရှားခြင်းကဲ့သို့ စိတ်ဝင်စားဖွယ် လုပ်ငန်းစဉ် အပြန်အလှန် ပိုမိုကောင်းမွန်အောင် လုပ်ဆောင်မှုများ များစွာကို ပြုလုပ်နိုင်စေပါသည်။

LLVM အကြောင်းပိုမိုဖတ်ရှုနိုင်ပါသည်။ စာရွက်စာတမ်းသင်၏ကိုယ်ပိုင် LLVM-based compiler ကိုတီထွင်သောအခါတွင် သင်မကြာခဏရည်ညွှန်းလေ့ရှိသော၊ ဒီမှာ ခေါင်းဆောင်မှုအလွန်ရိုးရှင်းသောဘာသာစကားအတွက် compiler ကိုတီထွင်ရန်ကြည့်ရှုသည်။ သင်၏ကိုယ်ပိုင် compiler ကိုဖန်တီးသောအခါ ဤသတင်းရင်းမြစ်နှစ်ခုစလုံးသည် သင့်အတွက် အသုံးဝင်ပါလိမ့်မည်။

ချစ်ခင်ရပါသောစာဖတ်သူများ! သင်သည် LLVM ကိုအသုံးပြုနေပါသလား။

Go ရှုထောင့်မှ LLVM

source: www.habr.com

မှတ်ချက် Add