Выполнение вывода 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?
Ответы
Ответ 1
Вы пробовали эти две вещи?
1 - Используйте атрибут функции alwaysinline: http://llvm.org/docs/LangRef.html#function-attributes
2 - Использование соглашения о вызове GHC (cc 10 вместо fastcc):
http://llvm.org/docs/LangRef.html#calling-conventions
define cc 10 i64 @llvminc(i64 inreg %x) alwaysinline {
%r = add i64 %x, 1
ret i64 %r
}