Jak używać funkcji wewnętrznych VC++ bez biblioteki run-time

Jestem zaangażowany w jedno z tych wyzwań, w którym próbujesz stworzyć najmniejszy możliwy plik binarny, więc buduję mój program BEZ bibliotek run-time C lub C++ (RTL). Nie linkuję do wersji DLL lub wersji statycznej. Nie mam nawet #include plików nagłówkowych. Mam to działa dobrze.

Niektóre funkcje RTL, jak memset(), mogą być użyteczne, więc próbowałem dodać własną implementację. Działa dobrze w kompilacjach debugowania (nawet w tych miejscach, w których kompilator generuje implicit call to memset()). Ale w Release builds, dostaję błąd mówiąc, że nie mogę zdefiniować wewnętrznej funkcji. Widzisz, w Release buildach, funkcje wewnętrzne są włączone i memset() jest wewnętrznymi.

Chciałbym użyć intrinsic for memset() W moich release buildach, ponieważ prawdopodobnie jest wbudowany, mniejszy i szybszy niż moja implementacja. Ale wydaje mi się, że jestem w catch-22. Jeśli nie zdefiniuję memset(), linker narzeka, że jest niezdefiniowany. Jeśli to zdefiniuję, kompilator skarży się, że nie mogę zdefiniować funkcji wewnętrznej.

Czy ktoś zna właściwą kombinację definicji, deklaracji, #pragma, oraz flag kompilatora i linkera, aby uzyskać wewnętrzną funkcję bez przeciągania RTL nad głową?

Visual Studio 2008, x86, Windows XP+.

Aby problem był bardziej konkretny:

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

A ja buduję Tak:

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

Jeśli kompiluję z moją implementacją memset(), otrzymuję kompilator błąd:

error C2169: 'memset' : intrinsic function, cannot be defined

Jeśli skompiluję to bez mojej implementacji memset(), dostaję błąd linkera:

error LNK2001: unresolved external symbol _memset
Author: Adrian McCarthy, 2010-05-30

6 answers

Myślę, że w końcu znalazłem rozwiązanie:

Najpierw w pliku nagłówkowym zadeklaruj memset() za pomocą pragmy, Tak:

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

, który pozwala na wywołanie kodu memset(). W większości przypadków kompilator wbuduje wewnętrzną wersję.

Po drugie, w oddzielnym pliku implementacji, podaj implementację. Sztuczka uniemożliwiająca kompilatorowi narzekanie na ponowne zdefiniowanie funkcji wewnętrznej polega na pierwszym użyciu innej pragmy. Tak:

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

To zapewnia implementacja dla tych przypadków, w których optymalizator decyduje się nie używać wersji wewnętrznej.

Wyjątkową wadą jest to, że musisz wyłączyć optymalizację całego programu (/GL i /LTCG). Nie wiem dlaczego. Jeśli ktoś znajdzie sposób, aby to zrobić bez wyłączania global optimization, proszę o kontakt.

 19
Author: Adrian McCarthy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-16 17:47:26
  1. Jestem prawie pewien, że jest flaga kompilatora, który mówi VC++ nie używać intrinsics

  2. Źródło do biblioteki runtime jest instalowane wraz z kompilatorem. Masz do wyboru fragmenty funkcji, które chcesz / potrzebujesz, choć często będziesz musiał je szeroko modyfikować(ponieważ zawierają one funkcje i/lub zależności, których nie chcesz / potrzebujesz).

  3. Dostępne są również inne biblioteki runtime open source, które mogą wymagać mniej Personalizacja.

  4. Jeśli myślisz o tym poważnie, musisz znać (i być może używać) język asemblacji.

Edited to add:

Mam Twój nowy kod testowy do skompilowania i linkowania. Są to odpowiednie ustawienia:

Enable Intrinsic Functions: No
Whole Program Optimization: No

To ten ostatni, który tłumi "pomocników kompilatora", takich jak wbudowany memset.

Edited to add:

Teraz, gdy jest odsprzęgnięty, możesz skopiować kod asm z memsetu.asm do twojego program -- ma jedno globalne odniesienie, ale możesz to usunąć. Jest wystarczająco duży, aby był nie inlined, chociaż jeśli usuniesz wszystkie sztuczki, których używa, aby zyskać prędkość, możesz być w stanie zrobić go wystarczająco małym.

Wzięłam powyższy przykład i zastąpiłam memset() tym:

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

To działa, ale wersja biblioteki jest znacznie szybsza.

 5
Author: egrunin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-31 20:01:27

Myślę, że musisz ustawić optymalizację na " Minimalizuj Rozmiar (/O1)" lub "wyłączony (/Od)", Aby uzyskać konfigurację Wydania do kompilacji; przynajmniej to, co mi się udało Z VS 2005. Intrinsics są zaprojektowane dla prędkości, więc ma sens, że będą one włączone dla innych poziomów optymalizacji(prędkość i pełne).

 1
Author: Luke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-30 18:04:37

Po prostu nazwij funkcję czymś nieco innym.

 0
Author: Puppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-31 17:35:51

To zdecydowanie działa z VS 2015: Dodaj opcję wiersza poleceń / Oi-. Działa to, ponieważ "nie" na wewnętrznych funkcjach nie jest przełącznikiem, jest nieokreślone. / Oi - i wszystkie Twoje problemy znikną (powinno działać z optymalizacją całego programu, ale nie testowałem tego poprawnie).

 0
Author: Solocle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 20:48:03

Sposób, w jaki "zwykła" biblioteka runtime robi to poprzez kompilację pliku asemblera z definicją memset i połączenie go z biblioteką runtime (plik asemblera można znaleźć w lub wokół niego C:\Program Files \ Microsoft Visual Studio 10.0 \ VC \ crt \ src \ intel \ memset.asm). Takie rzeczy działają dobrze nawet przy optymalizacji całego programu.

Zauważ również, że kompilator będzie używał memset intrinsic tylko w niektórych szczególnych przypadkach (gdy rozmiar jest stały i mały?). Zwykle będzie użyj funkcji memset dostarczonej przez Ciebie, więc prawdopodobnie powinieneś użyć zoptymalizowanej funkcji w memset.asm, chyba, że masz zamiar napisać coś tak zoptymalizowanego.

 -1
Author: namey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-06 23:33:35