J'ai une routine C simple qui prend quatre mots et retourne quatre mots, et pour lequel gcc peut optimiser et émettre des primops que GHC ne supporte pas. J'essaie de comparer différentes façons d'appeler cette procédure, et j'ai du mal à essayer d'adapter la technique described here pour utiliser foreign import prim
.Utilisation de `import import prim` avec une fonction C utilisant la convention d'appel STG
Ce qui suit est juste pour ajouter 1 à chaque mot d'entrée, mais segfaults.
Main.hs:
{-# LANGUAGE GHCForeignImportPrim #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedFFITypes #-}
import Foreign.C
import GHC.Prim
import GHC.Int
import GHC.Word
foreign import prim "sipRound"
sipRound_c# :: Word# -> Word# -> Word# -> Word# -> (# Word#, Word#, Word#, Word# #)
sipRound_c :: Word64 -> Word64 -> Word64 -> Word64 -> (Word64, Word64, Word64, Word64)
sipRound_c (W64# v0) (W64# v1) (W64# v2) (W64# v3) = case sipRound_c# v0 v1 v2 v3 of
(# v0', v1', v2', v3' #) -> (W64# v0', W64# v1', W64# v2', W64# v3')
main = do
print $ sipRound_c 1 2 3 4
sip.c:
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
// define a function pointer type that matches the STG calling convention
typedef void (*HsCall)(int64_t*, int64_t*, int64_t*, int64_t, int64_t, int64_t, int64_t,
int64_t, int64_t, int64_t*, float, float, float, float, double, double);
extern void
sipRound(
int64_t* restrict baseReg,
int64_t* restrict sp,
int64_t* restrict hp,
uint64_t v0, // R1
uint64_t v1, // R2
uint64_t v2, // R3
uint64_t v3, // R4
int64_t r5,
int64_t r6,
int64_t* restrict spLim,
float f1,
float f2,
float f3,
float f4,
double d1,
double d2)
{
v0 += 1;
v1 += 1;
v2 += 1;
v3 += 1;
// create undefined variables, clang will emit these as a llvm undef literal
const int64_t iUndef;
const float fUndef;
const double dUndef;
const HsCall fun = (HsCall)sp[0];
return fun(
baseReg,
sp,
hp,
v0,
v1,
v2,
v3,
iUndef,
iUndef,
spLim,
fUndef,
fUndef,
fUndef,
fUndef,
dUndef,
dUndef);
}
Je ne sais pas vraiment ce que je fais. Existe-t-il un moyen d'adapter la technique à partir de ce blog? Et est-ce une mauvaise idée?
Ceci est très, très bas niveau. Avez-vous vraiment besoin de ce niveau de performance? AFAICS, l'article de blog génère LLVM en utilisant clang, puis corrige la sortie en changeant la convention d'appel C en LLVM cc10 (le GHC), puis compile le résultat avec llc. Effrayant. Ceci est bien au-delà de la zone de confort (c'est-à-dire que j'ai très peu de connaissances sur ce qui se passe à ce niveau), mais l'utilisation de la convention d'appel cc10 semble être cruciale! – chi
@chi Je suis en train de faire un benchmarking en retournant une structure de 4 mots d'un ccall étranger normal, mais je m'attends à ce que les frais généraux soient trop importants pour en valoir la peine (mais ils pourraient être surpris); Je passe par tout cela pour essayer d'obtenir des instructions de rotation générées en n'utilisant pas le backend LLVM, pour une bibliothèque sur laquelle je travaille. Mais c'est aussi par curiosité – jberryman
Droit, cela ne fonctionnera pas. Comme le dit l'article du blog: "C'est toujours une fonction ccall mais nous y remédierons plus tard, il n'y a actuellement aucun moyen de définir ceci comme cc10 (nom interne de LLVM pour la convention d'appel de GHC) dans clang." La convention d'appel C est différente de celle de GHC. Par exemple, C pense que le premier argument 'baseReg' devrait être dans' rdi' (en supposant que x86_64) mais que GHC passe 'baseReg' dans' r13'. –