Как использовать VC ++ встроенные функции w/o библиотека времени выполнения

Я вовлечен в одну из тех проблем, где Вы пытаетесь произвести самый маленький двоичный файл, таким образом, я создаю свою программу без библиотек времени выполнения (RTL) C++ или C. Я не связываюсь с версией DLL или статической версией. Я не делаю даже #include заголовочные файлы. У меня есть это хорошо работать.

Некоторые функции RTL, как memset(), может быть полезным, таким образом, я пытался добавить свою собственную реализацию. Это хорошо работает в сборках Отладки (даже для тех мест, где компилятор генерирует неявный вызов к 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
32
задан Adrian McCarthy 17 August 2017 в 14:54
поделиться

5 ответов

Кажется, я наконец нашел решение:

Во-первых, в заголовочном файле объявите 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). Я не уверен, зачем это нужно. Если кто-нибудь найдет способ сделать это без отключения глобальной оптимизации, пожалуйста, сообщите.

21
ответ дан 27 November 2019 в 21:14
поделиться
  1. Я почти уверен, что есть флаг компилятора, который говорит VC++ не использовать intrinsics

  2. Исходники библиотеки времени выполнения устанавливаются вместе с компилятором. У вас есть возможность вырезать нужные вам функции, хотя часто вам придется их сильно модифицировать (потому что они включают функции и/или зависимости, которые вам не нужны).

  3. Существуют и другие библиотеки времени выполнения с открытым исходным кодом, которые могут потребовать меньшей настройки.

  4. Если вы действительно серьезно настроены, вам потребуется знание (и, возможно, использование) языка ассемблера.

Отредактировано для добавления:

Я получил ваш новый тестовый код для компиляции и линковки. Вот соответствующие настройки:

Enable Intrinsic Functions: No
Whole Program Optimization: No

Это последняя, которая подавляет "помощников компилятора", таких как встроенный memset.

Отредактировано для добавления:

Теперь, когда все развязано, вы можете скопировать asm-код из memset.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;
}

Это работает, но версия библиотеки намного быстрее.

4
ответ дан 27 November 2019 в 21:14
поделиться

Я думаю, вам нужно установить Оптимизацию на «Минимальный размер (/ O1)» или «Отключено (/ Od)», чтобы получить конфигурацию выпуска для компиляции; по крайней мере, это то, что помогло мне с VS 2005. Внутренние функции разработаны для скорости, поэтому имеет смысл, что они будут включены для других уровней оптимизации (скорость и полный).

1
ответ дан 27 November 2019 в 21:14
поделиться

Просто назовите функцию немного иначе.

0
ответ дан 27 November 2019 в 21:14
поделиться

«Обычная» библиотека времени выполнения делает это путем компиляции файла сборки с определением memset и связывания его со средой выполнения. (Вы можете найти файл сборки в папке C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src\intel\memset.asm или рядом с ней). Такие вещи прекрасно работают даже при оптимизации всей программы.

Также обратите внимание, что компилятор будет использовать встроенный memset только в некоторых особых случаях (когда размер постоянен и мал?). Обычно он будет использовать предоставленную вами функцию memset, поэтому вам, вероятно, следует использовать оптимизированную функцию в memset.asm, если только вы не собираетесь писать что-то столь же оптимизированное.

-1
ответ дан 27 November 2019 в 21:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: