如何使用没有运行时库的VC++内部函数

Adr*_*thy 32 c++ demoscene memset intrinsics visual-c++

我参与了其中一个挑战,你试图生成尽可能小的二进制文件,所以我在没有 C或C++运行时库(RTL)的情况下构建我的程序.我没有链接到DLL版本或静态版本.我甚#include至没有头文件.我有这个工作正常.

有些RTL函数memset()可能很有用,所以我尝试添加自己的实现.它在Debug构建中工作正常(即使对于编译器生成隐式调用的那些地方memset()).但是在Release版本中,我得到一个错误,说我无法定义内部函数.您可以看到,在发布版本中,内部函数已启用,并且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;
}
Run Code Online (Sandbox Code Playgroud)

我这样构建:

cl /c /W4 /WX /GL /Ob2 /Oi /Oy /Gs- /GF /Gy intrinsic.cpp
link /SUBSYSTEM:CONSOLE /LTCG /DEBUG /NODEFAULTLIB /ENTRY:main intrinsic.obj
Run Code Online (Sandbox Code Playgroud)

如果我用我的实现编译memset(),我得到一个编译器错误:

error C2169: 'memset' : intrinsic function, cannot be defined
Run Code Online (Sandbox Code Playgroud)

如果我在没有实现的情况下编译它memset(),我会收到链接器错误:

error LNK2001: unresolved external symbol _memset
Run Code Online (Sandbox Code Playgroud)

Adr*_*thy 21

我想我终于找到了一个解决方案:

首先,在头文件中,memset()使用pragma 声明,如下所示:

extern "C" void * __cdecl memset(void *, int, size_t);
#pragma intrinsic(memset)
Run Code Online (Sandbox Code Playgroud)

这允许您的代码调用memset().在大多数情况下,编译器将内联内部版本.

其次,在单独的实现文件中,提供实现.防止编译器抱怨重新定义内部函数的技巧是首先使用另一个pragma.像这样:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这为优化程序决定不使用内部版本的情况提供了实现.

缺点是您必须禁用整个程序优化(/ GL和/ LTCG).我不知道为什么.如果有人在没有禁用全局优化的情况下找到了解决方法,请插入.

  • 通过将这些内在函数编译到单独的静态库中,您可以将整个程序优化的禁用限制为仅限内部函数. (2认同)

egr*_*nin 5

  1. 我很确定有一个编译器标志告诉VC++不要使用内在函数

  2. 运行时库的源代码随编译器一起安装.您可以选择您想要/需要的摘录功能,但通常您必须对它们进行广泛修改(因为它们包含您不需要/不需要的功能和/或依赖项).

  3. 还有其他开源运行时库可用,可能需要较少的自定义.

  4. 如果你真的很认真,你需要知道(也许使用)汇编语言.

编辑添加:

我得到了你的新测试代码来编译和链接.这些是相关设置:

Enable Intrinsic Functions: No
Whole Program Optimization: No
Run Code Online (Sandbox Code Playgroud)

这是最后一个抑制"编译器帮助器"的内置memset.

编辑添加:

现在它已经解耦,你可以将memm.asm中的asm代码复制到你的程序中 - 它有一个全局引用,但你可以删除它.它足够大,所以它没有内联,但如果你删除它用来获得速度的所有技巧,你可能能够使它足够小.

我拿了你上面的例子,用以下代替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;
}
Run Code Online (Sandbox Code Playgroud)

它有效,但图书馆的版本要快得多.

  • 你写的一切都是真的,但它并没有真正解决我的问题.这可能是我在问题中不够清楚的错.关闭优化是为了保持程序小(这就是为什么我试图首先省略RTL)和快速(这是次要目标).似乎没有必要将汇编插入到我的代码中,当它与编译器生成的内容几乎完全相同时.感谢您的投入. (3认同)