web-dev-qa-db-ja.com

ランタイムライブラリなしでVC ++組み込み関数を使用する方法

私は、可能な限り最小のバイナリを生成しようとするこれらの課題の1つに関与しているので、プログラムを構築していますなし CまたはC++ランタイムライブラリ(RTL)。 DLLバージョンまたは静的バージョンにリンクしていません。ヘッダーファイルも_#include_していません。これは正常に機能しています。

memset()などの一部のRTL関数は便利な場合があるため、独自の実装を追加してみました。デバッグビルドでは正常に機能します(コンパイラが暗黙的memset()の呼び出しを生成する場所でも)。しかし、リリースビルドでは、組み込み関数を定義できないというエラーが表示されます。ご覧のとおり、リリースビルドでは、組み込み関数が有効になっており、memset()は組み込み関数です。

memset()の組み込み関数は、おそらくインライン化されており、実装よりも小さく高速であるため、リリースビルドで使用したいと思います。しかし、私はキャッチ22にいるようです。 memset()を定義しないと、リンカーは未定義であると文句を言います。定義すると、コンパイラは組み込み関数を定義できないと文句を言います。

定義、宣言、_#pragma_、コンパイラフラグとリンカフラグの正しい組み合わせを知っている人はいますか?RTLオーバーヘッドを取得せずに組み込み関数を取得しますか?

Visual Studio 2008、x86、Windows XP +。

問題をもう少し具体的にするには:

_extern "C" void * __cdecl memset(void *, int, size_t);

#ifdef IMPLEMENT_MEMSET
void * __cdecl memset(void *pTarget, int value, size_t cbTarget) {
    char *p = reinterpret_cast<char *>(pTarget);
    while (cbTarget > 0) {
        *p++ = static_cast<char>(value);
        --cbTarget;
    }
    return pTarget;
}
#endif

struct MyStruct {
    int foo[10];
    int bar;
};

int main() {
    MyStruct blah;
    memset(&blah, 0, sizeof(blah));
    return blah.bar;
}
_

そして私はこのように構築します:

_cl /c /W4 /WX /GL /Ob2 /Oi /Oy /Gs- /GF /Gy intrinsic.cpp
link /SUBSYSTEM:CONSOLE /LTCG /DEBUG /NODEFAULTLIB /ENTRY:main intrinsic.obj
_

memset()の実装でコンパイルすると、コンパイラエラーが発生します。

_error C2169: 'memset' : intrinsic function, cannot be defined
_

memset()を実装せずにこれをコンパイルすると、リンカーエラーが発生します。

_error LNK2001: unresolved external symbol _memset
_
34
Adrian McCarthy

私はついに解決策を見つけたと思います:

まず、ヘッダーファイルで、次のようにプラグマを使用してmemset()を宣言します。

_extern "C" void * __cdecl memset(void *, int, size_t);
#pragma intrinsic(memset)
_

これにより、コードでmemset()を呼び出すことができます。ほとんどの場合、コンパイラは組み込みバージョンをインライン化します。

次に、別の実装ファイルで、実装を提供します。コンパイラが組み込み関数の再定義について文句を言うのを防ぐ秘訣は、最初に別のプラグマを使用することです。このような:

_#pragma function(memset)
void * __cdecl memset(void *pTarget, int value, size_t cbTarget) {
    unsigned char *p = static_cast<unsigned char *>(pTarget);
    while (cbTarget-- > 0) {
        *p++ = static_cast<unsigned char>(value);
    }
    return pTarget;
}
_

これは、オプティマイザーが組み込みバージョンを使用しないことを決定した場合の実装を提供します。

顕著な欠点は、プログラム全体の最適化(/ GLおよび/ LTCG)を無効にする必要があることです。理由はわかりません。グローバル最適化を無効にせずにこれを行う方法を誰かが見つけた場合は、チャイムを鳴らしてください。

22
Adrian McCarthy
  1. 組み込み関数を使用しないようにVC++に指示するコンパイラフラグがあると確信しています

  2. ランタイムライブラリのソースは、コンパイラとともにインストールされます。必要な/必要な機能の抜粋を選択できますが、多くの場合、それらを大幅に変更する必要があります(不要な/必要な機能や依存関係が含まれているため)。

  3. 他にも利用可能なオープンソースのランタイムライブラリがあり、カスタマイズが少なくて済む場合があります。

  4. これについて真剣に考えている場合は、アセンブリ言語を知っている(そしておそらく使用する)必要があります。

追加するために編集:

コンパイルしてリンクするための新しいテストコードを入手しました。関連する設定は次のとおりです。

_Enable Intrinsic Functions: No
Whole Program Optimization: No
_

組み込みのmemsetのような「コンパイラヘルパー」を抑制するのは最後の1つです。

追加するために編集:

分離されたので、asmコードをmemset.asmからプログラムにコピーできます。グローバル参照が1つありますが、それを削除できます。 notインライン化されるほど十分に大きいですが、速度を上げるために使用するすべてのトリックを削除すると、そのために十分に小さくできる可能性があります。

上記の例を取り上げて、memset()を次のように置き換えました。

_void * __cdecl memset(void *pTarget, char value, size_t cbTarget) {
    _asm {
    Push ecx
    Push edi

    mov al, value
    mov ecx, cbTarget
    mov edi, pTarget
    rep stosb

    pop edi
    pop ecx
    }
    return pTarget;
}
_

それは機能しますが、ライブラリのバージョンははるかに高速です。

4
egrunin

リリース構成をコンパイルするには、最適化を「サイズの最小化(/ O1)」または「無効(/ Od)」に設定する必要があると思います。少なくとも、これがVS 2005でのトリックでした。組み込み関数は速度を重視して設計されているため、他の最適化レベル(速度とフル)で有効にするのは理にかなっています。

1
Luke

関数に少し違う名前を付けるだけです。

0
Puppy

これはVS2015で確実に機能します。コマンドラインオプション/ Oi-を追加します。これは、組み込み関数の「いいえ」がスイッチではなく、指定されていないために機能します。/Oi-そしてあなたのすべての問題はなくなります(それはプログラム全体の最適化で動作するはずですが、私はこれを適切にテストしていません)。

0
Solocle