Compiler construction LLVM中的部分应用

Compiler construction LLVM中的部分应用,compiler-construction,llvm,llvm-ir,compiler-development,Compiler Construction,Llvm,Llvm Ir,Compiler Development,我试图创建一个函数“add”,它可以应用于单个参数,然后应用于另一个参数。我不知道如何用LLVM IR表示这一点,因为我不知道如何使用单个值调用函数,然后将该值保存在内存中的某个位置,然后返回应用于该val的另一个函数。我需要LLVM中的某种闭包机制 我已经在C中搜索了它的实现,这样我就可以通过clang查看发出的LLVM,但是我找到的解决方案非常复杂,所以我想我可以直接研究LLVM 这将是未结婚的版本 define i8 @add(i8 %a, i8 %b) { entry: %res =

我试图创建一个函数“add”,它可以应用于单个参数,然后应用于另一个参数。我不知道如何用LLVM IR表示这一点,因为我不知道如何使用单个值调用函数,然后将该值保存在内存中的某个位置,然后返回应用于该val的另一个函数。我需要LLVM中的某种闭包机制

我已经在C中搜索了它的实现,这样我就可以通过clang查看发出的LLVM,但是我找到的解决方案非常复杂,所以我想我可以直接研究LLVM

这将是未结婚的版本

define i8 @add(i8 %a, i8 %b) {
entry:
  %res = add i8 %a, %b
  ret i8 %res
}
我想让
add(1)
返回
i8(i8)
类型。我想我得想办法把这个函数分开

另外,我之所以研究这个问题,是因为我正在为一种小型函数式语言编写一个编译器,所以我正在寻找任何关于编译器设计中部分应用程序/语法实现的建议

更新: 我现在有了下面的代码,但这是一个相当复杂的过程,我认为自动生成它并不容易

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

define { i8, i8 (i8, i8) * } @add1(i8 %a) {
  ; allocate the struct containing the supplied argument 
  ; and a function ptr to the actual function
  %nextPtr = alloca { i8, i8 (i8, i8) * }
  store { i8, i8 (i8, i8) * } { i8 undef, i8 (i8, i8) * @add2 }, { i8, i8 (i8, i8) * } * %nextPtr
  %next0 = load { i8, i8 (i8, i8) * } * %nextPtr

  ; insert the supplied arg into the struct
  %next1 = insertvalue { i8, i8 (i8, i8) * } %next0, i8 %a, 0
  ret { i8, i8 (i8, i8) * } %next1
}

define i8 @add2(i8 %a, i8 %b) {
  %res = add i8 %a, %b
  ret i8 %res
}

define i8 @main() {
  ; call add(35) resulting in 'fn' of type {35, &add2}
  %res1 = call { i8, i8 (i8, i8) * } @add1(i8 35)

  ; get the arg of the first call, ie element 0 of the resulting struct
  %arg = extractvalue { i8, i8 (i8, i8) * } %res1, 0
  ; similarily get the function ptr
  %fn = extractvalue { i8, i8 (i8, i8) * } %res1, 1

  ; apply the argument to the function
  %res = call i8 %fn(i8 %arg, i8 30)

  ; print result  
  %ptr = alloca i8
  store i8 %res, i8* %ptr
  call i32 (i8*, ...)* @printf(i8* %ptr)

  ret i8 0
}
我写这篇文章是为了模拟我的函数式语言编译器对“部分应用程序”(lambda演算)的支持。我不发出“C”代码,而是直接向LLVM-IR发出。您只需从样本源发射即可看到LLVM-IR透视图:

clang -S -emit-llvm partial.c
当编译器经过AST节点时,会触发它发出LLVM-IR中的部分机制,该节点反映了一个插入式包装(给出或获取一些附加细节)表达式


希望这能有所帮助。

您是否设置了
i8(i8)
类型,或者这是一个示例?要真正获得诚实的LLVM函数指针,您必须在运行时动态生成机器代码。如果创建一个将已提供的参数与函数指针存储在一起的聚合,则会更容易。好吧,这就是Rust实现闭包的方式。我想让你看看他们的输出,但根据我有限的经验,IR也很难阅读。这是可行的(见上文),但如果我能从第一次调用中得到
i8(i8)
类型,就会容易得多:我真的怀疑这会更容易。正如我所说,要获得实际的函数指针,需要为每个部分应用程序生成thunk。这需要生成以下代码:(1)分配内存,(2)相应地设置权限,(3)写出不仅特定于平台而且特定于参数类型的机器代码,可能还有更多。即使您为此使用libffi,您仍然需要与libffi通信并将所有这些嵌入到LLVM-IR中。机械地生成fat闭包指针不会很简单,但应该是可行的。这是有意义的,我将研究使用这种方法。谢谢,我可能会设置一个要点,用伪代码或其他东西来完成它。它是编译时构造与RTL支持的组合,但确实利用了函数引用的概念,该函数引用具有参数存储,这些参数将在调用函数之前不断累积到函数签名阈值。这就是我获得咖喱/部分功能的方式。作为旁注,我对lambda/闭包使用了细微的变化。