Saya telah menggunakan LLVM untuk beberapa waktu sekarang dan saya memecahkan masalah sendiri dengan mencari dan menggunakan dentang untuk menampilkan kode yang dihasilkan, tetapi saya tidak pernah bisa menyelesaikan masalah ini. Saat ini saya sedang mengembangkan kompiler dengan frontend kustom saya dan LLVM sebagai backend. Karena saya menginginkan fungsi println khusus yang berfungsi persis seperti fungsi printf (pemformatan argumen varadik), saya mencoba mengimplementasikannya menggunakan instrisic @llvm.va_start dan @llvm.va_end.

Masalahnya sekarang adalah semuanya dikompilasi dengan baik, tetapi ketika saya menjalankan program, itu menunjukkan kepada saya angka-angka aneh alih-alih argumen nyata yang digunakan, misalnya:

Memasukkan:

println("Hello World %i, %i?", 1, 2)

Keluaran:

Hello World 1447122753, 1280590165?

Juga terlihat bahwa angka-angka tidak berubah bahkan jika program dijalankan lagi.

Beberapa informasi sistem dan perpustakaan yang digunakan:

  • LLVM-10
  • Ubuntu 18.04.4 LTS
  • Intel(R) Core(TM) i5-8400 CPU

Output tertaut dari program saya di LLVM-IR oleh kompiler saya:

; ModuleID = 'merged.bc'
source_filename = "ld-temp.o"
target triple = "x86_64-unknown-linux-gnu"

%0 = type { i32, i32, i8*, i8* }

@0 = private unnamed_addr constant [20 x i8] c"Hello World %i, %i?\00", align 1
@1 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1

define i32 @main() {
  %1 = alloca i32, align 4
  %2 = call i32 (i8*, ...) @println(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @0, i32 0, i32 0), i32 1, i32 2)
  store i32 0, i32* %1, align 4
  br label %3

3:                                                ; preds = %0
  %4 = load i32, i32* %1, align 4
  ret i32 %4
}

define internal i32 @println(i8* %0, ...) {
  %2 = alloca i32, align 4
  %3 = call %0* @va_start()
  %4 = alloca %0*
  store %0* %3, %0** %4
  %5 = alloca i8*, align 1
  store i8* %0, i8** %5, align 1
  %6 = load i8*, i8** %5, align 1
  %7 = load %0*, %0** %4
  %8 = call i32 @vprintf(i8* %6, %0* %7)
  %9 = alloca i32, align 4
  store i32 %8, i32* %9, align 4
  %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0))
  %11 = load %0*, %0** %4
  call void @va_end(%0* %11)
  %12 = load i32, i32* %9, align 4
  store i32 %12, i32* %2, align 4
  br label %13

13:                                               ; preds = %1
  %14 = load i32, i32* %2, align 4
  ret i32 %14
}

declare i32 @vprintf(i8*, %0*)

declare i32 @printf(i8*, ...)

define internal %0* @va_start() {
  %1 = alloca %0, align 8
  %2 = bitcast %0* %1 to i8*
  call void @llvm.va_start(i8* %2)
  ret %0* %1
}

; Function Attrs: nounwind
declare void @llvm.va_start(i8*) #0

define internal void @va_end(%0* %0) {
  %2 = bitcast %0* %0 to i8*
  call void @llvm.va_end(i8* %2)
  ret void
}

; Function Attrs: nounwind
declare void @llvm.va_end(i8*) #0

attributes #0 = { nounwind }

!llvm.module.flags = !{!0}

!0 = !{i32 1, !"LTOPostLink", i32 1}

Perubahan:

  • Alih-alih menggunakan fungsi eksternal, sekarang buat IR secara langsung saat dipanggil (ini menyebabkan masalah, karena Anda akan mendapatkan argumen dari fungsi va_start(), yang tidak memiliki argumen apa pun). IR yang berfungsi sekarang adalah:
; ModuleID = 'merged.bc'
source_filename = "ld-temp.o"
target triple = "x86_64-unknown-linux-gnu"

%0 = type { i32, i32, i8*, i8* }

@0 = private unnamed_addr constant [20 x i8] c"Hello World %i, %i?\00", align 1
@1 = private unnamed_addr constant [2 x i8] c"\0A\00", align 1

define i32 @main() {
  %1 = alloca i32, align 4
  %2 = call i32 (i8*, ...) @println(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @0, i32 0, i32 0), i32 1, i32 2)
  store i32 0, i32* %1, align 4
  br label %3

3:                                                ; preds = %0
  %4 = load i32, i32* %1, align 4
  ret i32 %4
}

define internal i32 @println(i8* %0, ...) {
  %2 = alloca i32, align 4
  %3 = alloca %0, align 8
  %4 = bitcast %0* %3 to i8*
  call void @llvm.va_start(i8* %4)
  %5 = load %0, %0* %3, align 8
  %6 = alloca %0
  store %0 %5, %0* %6
  %7 = alloca i8*, align 1
  store i8* %0, i8** %7, align 1
  %8 = load i8*, i8** %7, align 1
  %9 = getelementptr %0, %0* %6
  %10 = call i32 @vprintf(i8* %8, %0* %9)
  %11 = alloca i32, align 4
  store i32 %10, i32* %11, align 4
  %12 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0))
  %13 = getelementptr %0, %0* %6
  %14 = getelementptr %0, %0* %13
  %15 = bitcast %0* %14 to i8*
  call void @llvm.va_end(i8* %15)
  store i32 0, i32* %2, align 4
  br label %16

16:                                               ; preds = %1
  %17 = load i32, i32* %2, align 4
  ret i32 %17
}

; Function Attrs: nounwind
declare void @llvm.va_start(i8*) #0

declare i32 @vprintf(i8*, %0*)

declare i32 @printf(i8*, ...)

; Function Attrs: nounwind
declare void @llvm.va_end(i8*) #0

attributes #0 = { nounwind }

!llvm.module.flags = !{!0}

!0 = !{i32 1, !"LTOPostLink", i32 1}
1
Excse 8 Juli 2020, 23:36

1 menjawab

Jawaban Terbaik

Masalah utama Anda tampaknya adalah Anda mengembalikan pointer ke alloca-memori yang dialokasikan (yaitu memori lokal) dari @va_start. Anda harus membuatnya mengambil pointer sebagai argumen seperti yang dilakukan @llvm.va_start atau menghilangkan fungsi sama sekali dan memanggil @llvm.va_start langsung dari @println.

PS: Saya tidak mengerti apa gunanya tipe %0 Anda. Jika seharusnya mewakili daftar argumen, mengapa tidak menggunakan i8* (yang diharapkan oleh fungsi vararg LLVM) secara langsung daripada melakukan bitcasting.

PPS: String format dan jumlah argumen dalam kode sumber Anda tidak cocok dengan yang ada di LLVM yang Anda buat. Saya menduga LLVM sebenarnya dihasilkan dari kode sumber yang berbeda (khususnya println("Hello World %i?", 1)).

2
sepp2k 9 Juli 2020, 09:08