【发布时间】:2017-05-03 23:03:56
【问题描述】:
我有一个简单的 C 例程,它接受四个单词并返回四个单词,gcc 可以优化并发出 GHC 不支持的一些 primops。我正在尝试对调用此过程的各种方法进行基准测试,但在尝试将技术 described here 调整为使用 foreign import prim 时遇到了麻烦。
下面的意思只是给每个输入单词加 1,但是有段错误。
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);
}
我真的不知道我在做什么。有没有办法调整该博客文章中的技术?这是个坏主意吗?
【问题讨论】:
-
这是非常非常低的级别。你真的需要这种水平的性能吗? AFAICS,博客文章使用 clang 生成 LLVM,然后通过将 C 调用约定更改为 LLVM cc10(GHC 之一)来修补输出,然后使用 llc 编译结果。可怕的。这远远超出了舒适区(即,我对这个级别发生的事情知之甚少),但使用 cc10 调用约定似乎至关重要!
-
@chi 我正在对从正常的外国 ccall 返回 4 个单词的结构进行基准测试,但我预计开销太大而不值得(但可能惊讶);对于我正在处理的库,我正在经历所有这些尝试在不使用 LLVM 后端时生成旋转指令。但这也是出于好奇
-
对,这行不通。正如博客文章所说:“这仍然是一个 ccall 函数,但我们稍后会修复它。目前无法在 clang 中将其定义为 cc10(LLVM 的 GHC 调用约定的内部名称)。” C 调用约定与 GHC 不同。例如,C 认为第一个参数
baseReg应该在rdi中(假设 x86_64),但 GHC 在r13中传递baseReg。
标签: c haskell segmentation-fault ghc ffi