gpt4 book ai didi

c++ - 了解内存分配的工作原理 (LLVM)

转载 作者:可可西里 更新时间:2023-11-01 17:31:06 29 4
gpt4 key购买 nike

我正在玩具编译器(第一次)上取得进展,并试图了解如何分配/构造 LLVM 结构类型。Kaleidoscope 教程没有包括甚至没有提到这一点,我不知道我在 LLVM 源/测试中寻找什么来找到可能的例子。

所以我写了一个简单的 C++ 示例,用 clang 转储了 IR,试图理解它产生了什么,但老实说,我并没有完全遵循它。对我来说显而易见的是函数定义/声明和一些函数调用以及 memset 调用,所以我得到了它的一部分,但它还没有全部组合在一起。 (P.S 我对 alloca 指令文档的解释是它创建的任何东西在返回时都会被释放所以我不能使用那个权利,它基本上只用于局部变量?)

我所做的是:

alloc.cpp

struct Alloc {
int age;
};

//Alloc allocCpy() {
// return *new Alloc();
//}

Alloc *allocPtr() {
return new Alloc();
}

int main() {
Alloc *ptr = allocPtr();
// ptr->name = "Courtney";
// Alloc cpy = allocCpy();
// cpy.name = "Robinson";
// std::cout << ptr->name << std::endl;
// std::cout << cpy.name << std::endl;
return 0;
}

然后运行clang -S -emit-llvm alloc.cpp生成alloc.ll

; ModuleID = 'alloc.cpp'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.11.0"

%struct.Alloc = type { i32 }

; Function Attrs: ssp uwtable
define %struct.Alloc* @_Z8allocPtrv() #0 {
entry:
%call = call noalias i8* @_Znwm(i64 4) #3
%0 = bitcast i8* %call to %struct.Alloc*
%1 = bitcast %struct.Alloc* %0 to i8*
call void @llvm.memset.p0i8.i64(i8* %1, i8 0, i64 4, i32 4, i1 false)
ret %struct.Alloc* %0
}

; Function Attrs: nobuiltin
declare noalias i8* @_Znwm(i64) #1

; Function Attrs: nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) #2

; Function Attrs: ssp uwtable
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%ptr = alloca %struct.Alloc*, align 8
store i32 0, i32* %retval
%call = call %struct.Alloc* @_Z8allocPtrv()
store %struct.Alloc* %call, %struct.Alloc** %ptr, align 8
ret i32 0
}

attributes #0 = { ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nobuiltin "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }
attributes #3 = { builtin }

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

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"clang version 3.7.0 (tags/RELEASE_370/final)"}

有人能解释一下这个 IR 中发生了什么以及它如何映射回 C++ 吗?或者忽略这个具体的例子,人们会/应该如何为一个 LLVM StructType 分配堆内存,这个 LLVM StructType 存在于创建它的函数中(如果你觉得慷慨,以后如何释放内存)。

我注释掉的部分来 self 原来的例子,但作为一个完全的新手,IR 的洞察力甚至更差......

最佳答案

my interpretation of the alloca instruction docs is that it anything created from that gets freed on return so I can't use that right, it's essentially only for local variables?

是的。此外,目前关于 LLVM IR 的建议是,尽管 alloca 可以按您预期的那样工作,但优化是另一种情况。他们建议您立即alloca 入口 block 中的所有本地人,即使您不允许用户访问它们或它们并不总是包含有意义的数据。

堆分配是一个库特性。它不是 LLVM 或编译器的特性。当您使用 new T() 时,编译器只需调用 operator new 来获取内存,然后在那里构造 T。没有魔法参与。你看到的大多数垃圾都是特定于 C++-ABI 的,而不是 LLVM 的任何要求。它最终会降低为类似 void* p = malloc(size);新(p)T();。对于几乎所有类型 T,这几乎可以归结为将一系列存储到 p 或调用用户定义的函数。

您可以使用您选择的运行时库中的内存分配函数。

trying to understand how to allocate/construct an LLVM struct type

LLVM 类型系统不包含构造的概念。这是源语言的概念。

就 LLVM 而言,结构只是一堆位,所有内存位置或多或少都相同。如果您希望这些位是特定的东西,请将您想要的位存储到该位置。如果要将这些位放在堆上,则调用运行时库堆分配函数并将这些位存储到该位置。

请注意,垃圾回收是一个稍微不同的故事,因为 w.r.t 发生了一些尴尬的事情。在堆栈中查找局部变量以进行标记。

郑重声明,您不会试图理解 Clang 的 LLVM IR。我已经这样做了好几年了,这太疯狂了,你会花那么长时间才能开始掌握,更不用说你不想知道的所有 C++ 特定的 ABI 细节了。与尝试对其进行逆向工程相比,您会在他们的 IRC channel 的 #llvm 中进一步询问或在这里询问具体问题。

关于c++ - 了解内存分配的工作原理 (LLVM),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33533608/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com