Частичное применение в LLVM

Я пытаюсь создать функцию "добавить", которая может быть применена к одному аргументу, а затем к другому. Я не могу понять, как представить это с помощью 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) тип. Я полагаю, мне придется как-то разделить функцию.

пс. Я смотрю на это, потому что я работаю над компилятором для небольшого функционального языка, поэтому я ищу любой совет, касающийся реализации частичного применения / каррирования в дизайне компилятора в целом.

Обновление: теперь у меня работает следующий код, но он довольно сложный, и я не думаю, что его будет легко сгенерировать автоматически.

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
}

1 ответ

Решение

Я написал этот пример кода C, чтобы имитировать то, что мой компилятор функционального языка поддерживает для "частичного применения" (лямбда-исчисление). Я не излучаю код 'C', а вместо этого напрямую излучаю в LLVM-IR. Вы можете видеть перспективу LLVM-IR, просто испуская из исходного источника:

clang -S -emit-llvm partial.c

Компилятор запускается для генерации частичного механизма в LLVM-IR, когда он проходит через узел AST, отражая заключенное в скобки (дать или взять несколько дополнительных деталей) выражение.

Надеюсь это поможет.

Другие вопросы по тегам