如何实现数组 - llvm

How to implement arrays - llvm

我正在研究一种使用 llvm c++ api 编写的玩具语言,我正在尝试实现数组。我尝试了几种不同的方法 none,其中效果很好。

这是我想要的:

理想情况下,它们类似于 swift 中的数组。

当前解决方案

目前我正在使用basic array type。这将检查所有的框,除了虽然一维数组可以传递给函数并从函数返回,但多维(嵌套)数组必须通过指针传递。这意味着我不仅要将返回的指针位转换为正确的类型,还要遍历每个元素并将其存储在适当的位置。很快,这不仅变得非常混乱,而且变得很慢。

向量

据我所知,你不能有嵌套向量。例如。这不起作用:

<8 x <8 x i32>>

使用 malloc

最后,我尝试使用 malloc 分配适当的 space 然后只对所有内容使用指针。这与我当前的解决方案存在许多相同的问题。

C++之类的东西?

举个例子:

  %1 = alloca [5 x i32], align 16
  %2 = alloca i32*, align 8
  %3 = getelementptr inbounds [5 x i32], [5 x i32]* %1, i32 0, i32 0
  %4 = call i32* @_Z8getArrayPi(i32* %3)
  store i32* %4, i32** %2, align 8

无法将指针 %4 存储到 %1 中,因此必须创建新分配。否则(使用嵌套数组)每个元素都需要复制,并且会出现与我当前解决方案相同的问题。此外,理想情况下,编译器会处理所有指针,因此对于该语言的用户来说,它看起来就像一个数组。

问题

万一不清楚 - 我的问题是如何实现数组,以便我可以完成上面列出的所有事情(而不必复制每个元素)。

序言


我认为 alloca 是正确的方法,如果它们要固定大小,但你必须编写相当多的样板来实现有保证的(C++ 风格)省略以优化函数 returns.

现在问题出在所有权语义上,如果你想完美地做到这一点,你将需要确定它们的生命周期并确定它们是否需要堆分配或只能通过堆栈分配。但是让我们暂时忽略优化。 Swift/ARC 实现在幕后相当混乱并且使用了大量优化,因此您也不一定要对其建模,更不用说 Swift/ObjC ARC 代码和 "unmanaged" 代码之间的差距了.


简单的解决方案"like Swift"

如果您不想处理这个问题,假设您知道一个函数的所有可能退出路径,您可以在堆上使用某种形式的运行时来分配您的数组,并提供侵入式引用计数语义。这是 Swift 使用的模型(顶部有大量优化)——你堆分配每个数组并实现一个控制块来跟踪数组的引用计数和大小(例如,如果你想要动态边界检查) .在这种情况下,您将使用简单的按引用传递语义,例如:

  • 数组只能由对堆对象的引用来表示。
  • 当你输入一个函数时,如果你的特殊数组类型(你可以注释 LLVM 的 Value 来跟踪它们)被传入,你会增加引用计数。
  • 在终止的基本块中,您发出对运行时函数的调用,该函数会为每个跟踪对象减少函数内跟踪对象的引用计数(那里有很大的优化空间)。
  • 确保上面有一个例外,当从函数 returned 的数组不会减少引用计数以确保它在 return 后仍然存在时,你必须确保你保持在弹出调用帧时跟踪那些(为简单起见,我们假设您只能 return 一个参考)。
  • 最难的部分是 retain/release 当您无法静态确定数组的生命周期时的语义,这就是引用计数变得有用的地方。每次引用存储在任何类型的数据结构中时,您都需要保留运行时(增加引用计数)。销毁所有者后,将释放其中的任何引用计数对象(如果没有引用,则减少引用计数并释放)。
  • 当你的数组引用被存储时,它被保留(引用计数增加),这假设存储对象,为了简单起见,一个存储对其他数组的引用的数组要么是静态的(在编译器中更难实现)要么它存储的对象类型的动态感知,并且它知道如果它正在存储引用计数的对象,则在其销毁时它需要释放所有引用。

这就是它的基本原理,是 ARC 工作原理的非常非常简单的版本。现在需要考虑以下事项:

  • 非线性控制流(线程、异常、协程、闭包)。放松特别有趣。
  • 让你的语言中的每一个重要类型基本上都具有相似的特征(refcounted 对象),只要你不允许原始指针,这会让事情变得容易得多。您可以这样处理闭包(事实上,这就是块运行时所做的)。
  • 当您开始使用指针或通过非托管代码封送托管对象时,一切都会变得一团糟。
  • 根据您选择的设计方式,需要特别注意非拥有引用(指针)。
  • 正在复制对象。

我建议从一个引用计数对象开始,它将成为用您的语言管理的所有内容的基础,包括您的数组类型,并带有一些类型信息。 (就像 libobjc4 中的 NSObject 的一个简单得多的版本)。