Подтвердить что ты не робот

Как использовать встроенные функции VС++ без библиотеки времени выполнения

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

Некоторые функции RTL, такие как memset(), могут быть полезны, поэтому я попытался добавить свою собственную реализацию. Он отлично работает в сборках Debug (даже для тех мест, где компилятор генерирует неявный вызов memset()). Но в версиях Release я получаю сообщение об ошибке, говоря, что я не могу определить внутреннюю функцию. Вы видите, что в версиях Release встроены внутренние функции, а memset() является внутренним.

Я хотел бы использовать встроенный для memset() в своих выпусках, поскольку он, вероятно, встроен и меньше и быстрее, чем моя реализация. Но я, похоже, поймаю-22. Если я не определяю memset(), компоновщик жалуется, что он undefined. Если я его определяю, компилятор жалуется, что я не могу определить внутреннюю функцию.

Кто-нибудь знает правильную комбинацию определения, объявления, #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
4b9b3361

Ответ 1

Думаю, я наконец нашел решение:

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

Ответ 2

  • Я уверен, что есть флаг компилятора, который сообщает VС++ не использовать intrinsics

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

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

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

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

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

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;
}

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

Ответ 3

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

Ответ 4

Просто называйте функцию чем-то немного отличающимся.

Ответ 5

Это определенно работает с VS 2015: Добавьте параметр командной строки /Oi -. Это работает, потому что "Нет" по внутренним функциям не является переключателем, это неуказано. /Oi, и все ваши проблемы уходят (он должен работать с оптимизацией всей программы, но я не проверил это правильно).

Ответ 6

То, как это делает обычная библиотека времени выполнения, заключается в компиляции файла сборки с определением memset и привязки его к библиотеке времени выполнения (вы можете найти файл сборки в C:\Program Files\Microsoft Visual Studio 10,0\VC\элт\SRC\Intel\memset.asm). Подобная работа прекрасно работает даже при оптимизации всей программы.

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