5

単一の引数に適用でき、続いて別の引数に適用できる関数「add」を作成しようとしています。単一の値で関数を呼び出し、その値をメモリのどこかに保存し、その値に適用される別の関数を返す方法がわからないため、LLVM IR でこれを表す方法がわかりません。LLVM にはある種の閉鎖メカニズムが必要です。

C での実装を検索して、出力された LLVM を clang で表示できるようにしましたが、見つかった解決策は非常に複雑だったので、LLVM を直接調査するだけでよいと考えました。

これはカリー化されていないバージョンになります

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

そして、どういうわけか、型add(1)を返したいと思います。i8 (i8)どうにかして機能を分割する必要があると思います。

ps。私は小さな関数型言語のコンパイラに取り組んでいるので、これを調べています。そのため、一般的なコンパイラ設計における部分的なアプリケーション/カリー化の実装に関するアドバイスを探しています。

更新: 次のコードが機能するようになりましたが、非常に複雑で、自動的に生成するのは簡単ではないと思います

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

1 に答える 1