Выполнение вывода LLVM GHC через линкер LLVM для биткода

Я хочу иметь возможность вызывать код LLVM из Haskell без накладных расходов на полный вызов функции. Например:

-- Main.hs --

{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE GHCForeignImportPrim #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE BangPatterns #-}

import GHC.Exts(Word(W#))
import GHC.Prim(Word#)

foreign import ccall llvminc :: Word# -> Word#

main = do
  line1 <- getLine
  let !(W# x1) = read line1
  let !r1 = llvminc x1
  print (W# r1)


-- funcs.ll --

define fastcc i64 @llvminc(i64 inreg %x) {
  %r = add i64 %x, 1
  ret i64 %r
}

Я могу скомпилировать и связать это, чтобы создать исполняемый исполняемый файл, выполнив:

ghc -O2 -fllvm Main.hs funcs.ll

Действительно, даже удаление -fllvm по-прежнему приводит к функционированию исполняемого файла, например.

ghc -O2 Main.hs funcs.ll

Это приводит меня к серьезному подозрению в том, что GHC связывает эти файлы отдельно в обоих случаях, используя обычную C-связь.

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

ghc -O2 -fllvm -keep-s-files Main.hs funcs.ll

Я вижу следующее в Main.s:

callq   suspendThread
movq    %rax, %rbp
movq    %rbx, %rdi
callq   llvminc
movq    %rax, %rbx
movq    %rbp, %rdi
callq   resumeThread

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

Вместо этого я бы хотел, чтобы GHC отправлял исходные файлы LLVM (как из GHC, так и из указанного пользователя) в llvm-link, что в отличие от системного компоновщика, просто объединяет несколько файлов бит LLVM в один файл бит-кода LLVM. Было бы лучше, если бы этот результат был скомпилирован в собственный файл объекта кода и отправлен в системный компоновщик вместо нескольких объектных файлов, отправляемых в системный компоновщик.

В самом деле, когда я попробовал это вручную, собрав .ll человекочитаемые файлы в LLVM .bc bitcode, llvm-link с помощью полученного битового кода, затем разобрав биткод следующим образом:

llvm-as Main.ll
llvm-as funcs.ll
llvm-link funcs.bc Main.bc -o combined.bc
llvm-dis combined.bc

Я нашел следующее в полученном коде LLVM

%ln59M = add i64 %ln59L, 1

непосредственно после вызова для чтения без "вызова" или возврата. Фактическая функция все еще находится в LLVM, но не выполняется.

Итак, я попробовал поручить GHC связываться с LLVM-компоновщиком, добавив -pgml llvm-link в командную строку, но это не удалось эффектно, а llvm-link отбросило многие ошибки об неизвестных параметрах. Это неудивительно, так как llvm-link не является реальным компоновщиком, он просто объединяет файлы LLVM.

Итак, есть ли в любом случае, чтобы GHC отправил все промежуточные файлы LLVM через LLVM-компоновщик, чтобы включить оптимизацию интермодуля на уровне LLVM?

Ответы