Mengapa LLVM mengalokasikan variabel redundan?

9

Berikut adalah file C sederhana dengan definisi enum dan mainfungsi:

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    return 0;
}

Ini mentranspile ke LLVM IR berikut:

define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 2, i32* %2, align 4
  ret i32 0
}

%2jelas dvariabel, yang mendapat 2 ditugaskan untuk itu. Apa yang %1berhubungan dengan jika nol dikembalikan secara langsung?

macleginn
sumber
1
Bendera apa yang Anda gunakan untuk menghasilkan IR ini?
panah
@ Arrowd, saya menginstal suite LLVM stabil terbaru dan menjalankanclang-9 -S -emit-llvm simple.c
macleginn
1
Saya pikir ini ada hubungannya dengan inisialisasi sebelumnya main( godbolt.org/z/kEtS-s ). Tautan menunjukkan bagaimana perakitan dipetakan ke sumber
Pradeep Kumar
2
@PradeepKumar: Memang, jika Anda mengubah nama fungsi menjadi sesuatu selain main, variabel ekstra misterius menghilang. Menariknya, itu juga menghilang jika Anda menghilangkan returnpernyataan sepenuhnya (yang legal untuk maindalam C dan setara dengan return 0;).
Nate Eldredge
1
@ macleginn: Saya tidak begitu yakin. Jika Anda menyatakan mainseperti yang int main(int argc, char **argv)Anda lihat argcdan argvdisalin ke tumpukan, tetapi variabel nol misterius masih ada di samping mereka.
Nate Eldredge

Jawaban:

3

Ini %1mendaftar dihasilkan oleh dentang untuk menangani beberapa pernyataan kembali dalam fungsi . Bayangkan Anda memiliki fungsi untuk menghitung faktorial bilangan bulat. Alih-alih menulisnya seperti ini

int factorial(int n){
    int result;
    if(n < 2)
      result = 1;
    else{
      result = n * factorial(n-1);
    }
    return result;
}

Anda mungkin akan melakukan ini

int factorial(int n){
    if(n < 2)
      return 1;
    return n * factorial(n-1);
}

Mengapa? Karena Dentang akan memasukkan resultvariabel yang memegang nilai pengembalian untuk Anda. Yay. Itulah tujuan persisnya %1. Lihatlah ir untuk versi kode Anda yang sedikit dimodifikasi.

Kode yang dimodifikasi,

enum days {MON, TUE, WED, THU};

int main() {
    enum days d;
    d = WED;
    if(d) return 1;
    return 0;
}

IR,

define dso_local i32 @main() #0 !dbg !15 {
    %1 = alloca i32, align 4
    %2 = alloca i32, align 4
    store i32 0, i32* %1, align 4
    store i32 2, i32* %2, align 4, !dbg !22
    %3 = load i32, i32* %2, align 4, !dbg !23
    %4 = icmp ne i32 %3, 0, !dbg !23
    br i1 %4, label %5, label %6, !dbg !25

 5:                                                ; preds = %0
   store i32 1, i32* %1, align 4, !dbg !26
   br label %7, !dbg !26

 6:                                                ; preds = %0
  store i32 0, i32* %1, align 4, !dbg !27
  br label %7, !dbg !27

 7:                                                ; preds = %6, %5
  %8 = load i32, i32* %1, align 4, !dbg !28
  ret i32 %8, !dbg !28
}

Sekarang Anda lihat itu %1membuat dirinya berguna ya? Seperti yang lain telah tunjukkan, untuk fungsi dengan hanya satu pernyataan pengembalian, variabel ini mungkin akan dilucuti oleh salah satu operan optimal llvm.

droptop
sumber
1

Mengapa ini penting - apa masalah sebenarnya?

Saya pikir jawaban yang lebih dalam yang Anda cari mungkin: Arsitektur LLVM didasarkan di sekitar antarmuka yang cukup sederhana dan banyak lintasan. Frontend harus menghasilkan kode yang benar, tetapi tidak harus berupa kode yang baik. Mereka dapat melakukan hal paling sederhana yang berhasil.

Dalam hal ini, Dentang menghasilkan beberapa instruksi yang ternyata tidak dapat digunakan untuk apa pun. Itu umumnya bukan masalah, karena beberapa bagian dari LLVM akan menyingkirkan instruksi yang berlebihan. Dentang percaya itu akan terjadi. Dentang tidak perlu menghindari memancarkan kode mati; implementasinya dapat fokus pada kebenaran, kesederhanaan, testabilitas, dll.

bukan
sumber
1

Karena Dentang dilakukan dengan analisis sintaks tetapi LLVM bahkan belum mulai dengan optimasi.

Ujung depan Dentang telah menghasilkan IR (Representasi Menengah) dan bukan kode mesin. Variabel-variabel tersebut adalah SSA (Tugas Statis Tunggal); mereka belum terikat register dan sebenarnya setelah optimasi, tidak akan pernah terjadi karena mereka berlebihan.

Kode itu adalah representasi sumber yang agak literal. Inilah yang bergantung pada LLVM untuk optimisasi. Pada dasarnya, LLVM mulai dengan itu dan mengoptimalkan dari sana. Memang, untuk versi 10 dan x86_64, llc -O2 pada akhirnya akan menghasilkan:

main: # @main
  xor eax, eax
  ret
Olsonis
sumber
Saya mengerti proses pada level ini. Saya ingin tahu mengapa IR ini dibuat untuk memulai.
macleginn
Anda mungkin menganggap kompiler sebagai satu pass. Ada pipa saluran mulai dengan ujung depan Dentang yang menghasilkan IR. Bahkan tidak menghasilkan IR tekstual ini yang malah diminta oleh seseorang dengan dentang -emit-llvm -S file.cpp Deng sebenarnya menghasilkan versi bitcode biner IR yang bisa serial. LLVM disusun sebagai beberapa lintasan, masing-masing mengambil dan mengoptimalkan IR. Pass LLVM pertama mengambil IR dari Dentang. Dibutuhkan IR karena Anda dapat mengganti Dentang dengan Fortran FE untuk mendukung bahasa lain dengan optimizer + code generator yang sama.
Olsonist