LLVM передает va_list другой функции - PullRequest
1 голос
/ 08 июля 2020

Я уже довольно давно использую LLVM и сам решил проблемы, выполнив поиск и используя clang для вывода сгенерированного кода, но я так и не смог решить эту проблему. В настоящее время я разрабатываю компилятор с моим пользовательским интерфейсом и LLVM в качестве бэкэнда. Поскольку мне нужна специальная функция println, которая работает точно так же, как функция printf (форматирование аргументов varadi c), я попытался реализовать ее, используя instrisi c @ llvm.va_start и @ llvm.va_end.

Проблема теперь в том, что все компилируется хорошо, но когда я запускаю программу, она показывает мне странные числа вместо реальных аргументов, которые используются, например:

Ввод:

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

Вывод:

Hello World 1447122753, 1280590165?

Также примечательно, что числа не меняются даже при повторном запуске программы.

Некоторая системная информация и используемые библиотеки:

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

Связанный вывод моей программы в LLVM-IR от моего компилятора:

; 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}

Изменения:

  • Вместо использования внешних функций теперь генерирует IR напрямую при его вызове (это вызвало проблемы, потому что вы получите аргументы функции va_start (), у которой нет любые аргументы). Рабочий ИК теперь:
; 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 Ответ

2 голосов
/ 08 июля 2020

Похоже, ваша основная проблема заключается в том, что вы возвращаете указатель на память, выделенную alloca (т.е. локальную память), из @va_start. Вы должны либо заставить его принимать указатель в качестве аргумента, например @llvm.va_start, либо вообще избавиться от функции и вызвать @llvm.va_start непосредственно из @println.

PS: Я не понимаю, в чем смысл вашего типа %0. Если предполагается, что он представляет список аргументов, почему бы не использовать i8* (это то, что ожидают функции LLVM vararg) напрямую вместо его побитового преобразования.

PPS: строка формата и количество аргументов в исходном коде не соответствует таковым в созданном вами LLVM. Я предполагаю, что LLVM на самом деле был сгенерирован из другого исходного кода (в частности, println("Hello World %i?", 1)).

...