2016-12-18 13 views
2

私は4単語を返し、4単語を返し、GHCがサポートしていないいくつかのプリムプを最適化して出力する単純なCルーチンを持っています。私はこの手順を呼び出す様々な方法をベンチマークしようとしており、技術を適応させることを試みることに問題がありますdescribed hereforeign import primを使用してください。STG呼び出し規約を使用してC関数で `foreign import prim`を使用する

以下は、各入力単語に1を追加するだけですが、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); 
} 

私は本当に私がやっているのか分かりません。そのブログ記事から技術を適用する方法はありますか?そしてこれは悪い考えですか?

+1

これは非常に低レベルです。あなたは本当にこのレベルのパフォーマンスが必要ですか? AFAICSのブログ記事では、clangを使ってLLVMを生成し、Cの呼び出し規則をLLVM cc10(GHCのもの)に変更して出力をパッチし、llcでコンパイルします。恐ろしい。これはコンフォートゾーンを超えています(つまり、このレベルで何が起こっているかについての知識はほとんどありません)。しかし、cc10呼び出し規約を使用することは非常に重要です。 – chi

+0

@chi通常の外国コールから4ワードの構造体を返すベンチマーキングも進行中ですが、オーバーヘッドがそれほど価値があると思っています(でも驚くかもしれません)。私は、LLVMバックエンドを使用していないときに生成されたローテート命令を取得しようとしています。しかし、これもまた不思議です – jberryman

+2

これはうまくいかないでしょう。ブログの投稿は次のように述べています。「これはまだccallの機能ですが、後でそれを修正します。現在、clangのcc10(GHCの呼び出し規約のLLVMの内部名)として定義する方法はありません。 Cの呼び出し規約はGHCのものとは異なります。例えば、Cは最初の引数 'baseReg'が' rdi'(x86_64を前提としている)でなければならないと考えていますが、GHCは 'r13'に' baseReg'を渡しています。 –

答えて

3

手書きで書く場合は、このようにすることができます(x86_64用)。これを拡張子が.sのファイルに入れ、それをghcコマンドラインの引数として指定します。

.global sipRound 
sipRound: 
    inc %rbx 
    inc %r14 
    inc %rsi 
    inc %rdi 
    jmp *(%rbp) 

STGレジスタとマシン・レジスタ間のマッピングはhttps://github.com/ghc/ghc/blob/master/includes/stg/MachRegs.h#L159に定義されています。

まだ関数呼び出しが含まれるため、LLVMから取得したコードほど効率的ではありません。

+0

私はこれをはるかに良く理解しています、ありがとう! 2つの迅速なフォローアップ:これをghcに公開するには、 'void sipRound(){asm(...)} 'のようなacファイルでインラインasmを使う必要がありますか、それを行う良い方法があります「関連する関数呼び出しがありますか?」)?そして、これを安定したAPIと考えることができますか?マッピングがあまり変更されておらず、llvmなどと連携するために安定していなければなりません。 – jberryman

+0

C関数を追加するので、 'void sipRound(){asm(...)}'を使用することはできませんプロローグ。他の入力と同じようにアセンブラ入力を提供するだけです。私が話している関数呼び出しは 'sipRound_c#'に書く呼び出しです。要点は、アセンブリで実装されているため、GHCは "sipRound'を"インライン "できないということです。これとは対照的に、 '+#'を呼び出すと、GHCはトップレベル関数の呼び出しを生成せず、add命令を出します。 –

+1

ABIはあまり変更されませんが、安定しているとは限りません。異なるバージョンのGHCとの間にABI互換性はなく、各バージョンのGHCは1つのバージョンのLLVMでしか動作しないため、修正された呼び出し規約を次のバージョンのLLVMに取り込むことは難しくありません。 –

関連する問題