我正在构建一种带有 LLVM 后端的编程语言,并开发对多线程的支持。
为了弄清楚如何在 LLVM 中进行适当的函数调用,我正在编译以下简单的 C 程序:
#include <pthread.h>
int main() {
pthread_t my_thread;
pthread_mutex_t my_mutex;
pthread_mutex_init(&my_mutex, NULL);
pthread_mutex_lock(&my_mutex);
pthread_mutex_unlock(&my_mutex);
pthread_join(my_thread, NULL);
return 0;
}
当我运行
clang -S -emit-llvm example.c
时,我得到如下所示的 LLVM IR:
%struct._opaque_pthread_t = type { i64, %struct.__darwin_pthread_handler_rec*, [8176 x i8] }
%struct.__darwin_pthread_handler_rec = type { void (i8*)*, i8*, %struct.__darwin_pthread_handler_rec* }
%struct._opaque_pthread_mutex_t = type { i64, [56 x i8] }
%struct._opaque_pthread_mutexattr_t = type { i64, [8 x i8] }
define i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca %struct._opaque_pthread_t*, align 8
%3 = alloca %struct._opaque_pthread_mutex_t, align 8
store i32 0, i32* %1, align 4
%4 = call i32 @pthread_mutex_init(%struct._opaque_pthread_mutex_t* noundef %3, %struct._opaque_pthread_mutexattr_t* noundef null)
%5 = call i32 @pthread_mutex_lock(%struct._opaque_pthread_mutex_t* noundef %3)
%6 = call i32 @pthread_mutex_unlock(%struct._opaque_pthread_mutex_t* noundef %3)
%7 = load %struct._opaque_pthread_t*, %struct._opaque_pthread_t** %2, align 8
%8 = call i32 @"\01_pthread_join"(%struct._opaque_pthread_t* noundef %7, i8** noundef null)
ret i32 0
}
declare i32 @pthread_mutex_init(%struct._opaque_pthread_mutex_t* noundef, %struct._opaque_pthread_mutexattr_t* noundef) #1
declare i32 @pthread_mutex_lock(%struct._opaque_pthread_mutex_t* noundef) #1
declare i32 @pthread_mutex_unlock(%struct._opaque_pthread_mutex_t* noundef) #1
declare i32 @"\01_pthread_join"(%struct._opaque_pthread_t* noundef, i8** noundef) #1
尽管在顶部声明的结构被称为“
_opaque_pthread_mutex_t
”之类的东西,但它们 look 并不十分不透明;事实上,它们似乎明确引用了 pthread
对象的内部布局,甚至包括特定于平台的标签(“darwin
”)。使用 clang
在不同的系统上编译相同的程序会产生截然不同的结果。
对于我自己在 OCaml 中实现的编译器,我怎么知道我需要生成什么 LLVM IR? 或者这些特定于平台的定义是否不必要?
clang
如何知道 pthread
对象的内部格式以便进行这些定义?