c ++ disassembly local static variables

vs2017 test

34:     for (int i = 0; i < 5; i++)
0029734E C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0  
00297355 EB 09                jmp         main+30h (0297360h)  
00297357 8B 45 F8             mov         eax,dword ptr [ebp-8]  
0029735A 83 C0 01             add         eax,1  
0029735D 89 45 F8             mov         dword ptr [ebp-8],eax  
00297360 83 7D F8 05          cmp         dword ptr [ebp-8],5  
00297364 7D 0E                jge         main+44h (0297374h)  
    35:     {
    36:         ShowStatic(i);
00297366 8B 45 F8             mov         eax,dword ptr [ebp-8]  
00297369 50                   push        eax  
0029736A E8 4B C4 FF FF       call        ShowStatic (02937BAh)  
0029736F 83 C4 04             add         esp,4  
    37:     }
00297372 EB E3                jmp         main+27h (0297357h)  

Local static variables are initialized only once.

 
 

void ShowStatic(int nNumber)
{
static int g_snNumber1 = nNumber;
static int g_snNumber2 = nNumber;
printf("%d \r\n", g_snNumber1);
printf("%d \r\n", g_snNumber2);
}

11: void ShowStatic(int nNumber)
    12: {
00297160 55                   push        ebp  
00297161 8B EC                mov         ebp,esp  
00297163 81 EC C0 00 00 00    sub         esp,0C0h  
00297169 53                   push        ebx  
0029716A 56                   push        esi  
0029716B 57                   push        edi  
0029716C 8D BD 40 FF FF FF    lea         edi,[ebp-0C0h]  
00297172 B9 30 00 00 00       mov         ecx,30h  
00297177 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
0029717C F3 AB                rep stos    dword ptr es:[edi]  
    13:     static int g_snNumber1 = nNumber;
0029717E A1 AC 8E 35 00       mov         eax,dword ptr [_tls_index (0358EACh)]  
00297183 64 8B 0D 2C 00 00 00 mov         ecx,dword ptr fs:[2Ch]  
0029718A 8B 14  81              MOV          EDX, DWORD PTR [EAX + ECX * . 4 ]  
 0029718D A1 68 8E 35  00        MOV          EAX, DWORD PTR DS: [00358E68h] // initialization flag this thread-local static variables, initialized to 0, 
 00,297,192 3B 82  04  01  00  00     CMP          EAX, DWORD PTR [EDX + 104H] // [EDX + 104] i.e. [[ecx + eax * 4] +104] -> [[[fs: [2c] + _ tls_index] +104] , 
// TLS stored in the global static initialization local counter, an initial value INT_MIN, 0x80000000 (-2147483647 -. 1);
00,297,198 7E 2B JLE ShowStatic + 65h (02971C5h) // determines whether initialization
0029719A 68 68 8E 35 00 Push 358E68h 0029719F E8 43 is the A8 the FF the FF Call __Init_thread_header (02919E7h) // (the 358E68h (this thread-local static initialization flag), if it is 0, the counter is not initialized ffffffff (-1), is represented by initialization) 002971A4 83 C4 04 the Add ESP, . 4 002971A7 83 3D 68 8E 35 00 the FF CMP DWORD PTR DS: [358E68h], // 0FFFFFFFFh is determined whether the initialization state 002971AE 75 15 JNE ShowStatic + 65h (02971C5h) 002971B08B 45 08 MOV EAX, DWORD PTR [nNumber] 002971B3 A3 64 8E 35 00 MOV DWORD PTR [g_snNumber1 (0358E64h)], EAX // initialize 002971B8 68 68 8E 35 00 Push 358E68h 002971BD E8 the FF the FF Bl. 3A Call __Init_thread_footer (02922FCh ) // modify tls stored in a global counter, and 358E6Eh, i.e., this thread-local static initialization flag, which is assigned to the same global counters 002971C2 83 C4 04 the Add ESP, . 4 14 : static int g_snNumber2 = nNumber ; 002971C5 A1 AC 8E 35 00 mov eax,dword ptr [_tls_index (0358EACh)] 002971CA 64 8B 0D 2C 00 00 00 mov ecx,dword ptr fs:[2Ch] 002971D1 8B 14 81 mov edx,dword ptr [ecx+eax*4] 002971D4 A1 70 8E 35 00 mov eax,dword ptr ds:[00358E70h] 002971D9 3B 82 04 01 00 00 cmp eax,dword ptr [edx+104h] 002971DF 7E 2B jle ShowStatic+0ACh (029720Ch) 14: static int g_snNumber2 = nNumber; 002971E1 68 70 8E 35 00 push 358E70h 002971E6 E8 FC A7 FF FF call __Init_thread_header (02919E7h) 002971EB 83 C4 04 add esp,4 002971EE 83 3D 70 8E 35 00 FF cmp dword ptr ds:[358E70h],0FFFFFFFFh 002971F5 75 15 jne ShowStatic+0ACh (029720Ch) 002971F7 8B 45 08 mov eax,dword ptr [nNumber] 002971FA A3 6C 8E 35 00 mov dword ptr [g_snNumber2 (0358E6Ch)],eax 002971FF 68 70 8E 35 00 push 358E70h 00297204 E8 F3 B0 FF FF call __Init_thread_footer (02922FCh) 00297209 83 C4 04 add esp,4 15: printf("%d \r\n", g_snNumber1); 0029720C A1 64 8E 35 00 mov eax,dword ptr [g_snNumber1 (0358E64h)] 00297211 50 push eax 00297212 68 60 3E 33 00 push offset string "%d \r\n" (0333E60h) 00297217 E8 AE A1 FF FF call _printf (02913CAh) 0029721C 83 C4 08 add esp,8 16: printf("%d \r\n", g_snNumber2); 0029721F A1 6C 8E 35 00 mov eax,dword ptr [g_snNumber2 (0358E6Ch)] 00297224 50 push eax 00297225 68 60 3E 33 00 push offset string "%d \r\n" (0333E60h) 0029722A E8 9B A1 FF FF call _printf (02913CAh) 0029722F 83 C4 08 add esp,8 17: } 00297232 5F pop edi 00297233 5E pop esi 00297234 5B pop ebx 00297235 81 C4 C0 00 00 00 add esp,0C0h 0029723B 3B EC cmp ebp,esp 0029723D E8 54 BC FF FF call __RTC_CheckEsp (0292E96h) 00297242 8B E5 mov esp,ebp 00297244 5D pop ebp 00297245 C3 ret
Which _Init_thread_header and _Init_thread_footer initialize the thread function is to ensure the safety of local static objects.
_Init_thread_header
// Control access to the initialization expression.  Only one thread may leave
// this function before the variable has completed initialization, this thread
// will perform initialization.  All other threads are blocked until the
// initialization completes or fails due to an exception.

// control access to initialize expression. Only one thread can leave

// This is the function before initialized variable, this thread

// initialization will be performed. All other threads are blocked until

// initialization is complete or failed due to an exception.

extern "C" void __cdecl _Init_thread_header(int* const pOnce) noexcept
{
    _Init_thread_lock();

    if (*pOnce == Uninitialized)//int const Uninitialized = 0;
    {
        *pOnce = BeingInitialized;//int const BeingInitialized = -1;
    }
    else
    {
        while (*pOnce == BeingInitialized)
        {
            // Timeout can be replaced with an infinite wait when XP support is
            // removed or the XP-based condition variable is sophisticated enough
            // to guarantee all waiting threads will be woken when the variable is
            // signalled.
            _Init_thread_wait(XpTimeout);

            if (*pOnce == Uninitialized)
            {
                *pOnce = BeingInitialized;
                _Init_thread_unlock();
                return;
            }
        }
        _Init_thread_epoch = _Init_global_epoch;
    }

    _Init_thread_unlock();
}
_Init_thread_footer
// Called by the thread that completes initialization of a variable.
// Increment the global and per thread counters, mark the variable as
// initialized, and release waiting threads.

// called by the completion of variable initialization thread.

// increment counter global and per-thread counter variable labeled

// initialize and release the waiting thread.

extern "C" void __cdecl _Init_thread_footer(int* const pOnce) noexcept
{
    _Init_thread_lock();
    ++_Init_global_epoch;
    *pOnce = _Init_global_epoch;
    _Init_thread_epoch = _Init_global_epoch;
    _Init_thread_unlock();
    _Init_thread_notify();
}

 

_Init_thread_header and _Init_thread_footer function where the source file:
VS installation directory \ VC \ Tools \ MSVC \ 14.16.27023 \ crt \ src \ vcruntime \ thread_safe_statics.cpp
//
// thread_safe_statics.cpp
//
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// Helper functions used by thread-safe static initialization.
//
#ifdef _M_CEE
    #error This file cannot be built as managed
#endif

#include <vcstartup_internal.h>
#include <vcruntime_internal.h>
#include <awint.h>
#include <limits.h>

int const Uninitialized    = 0;
int const BeingInitialized = -1;
int const EpochStart       = INT_MIN;

// Access to these variables is guarded in the below functions.  They may only
// be modified while the lock is held.  _Tss_epoch is readable from user
// code and is read without taking the lock.
extern "C"
{
    int _Init_global_epoch = EpochStart;
    __declspec(thread) int _Init_thread_epoch = EpochStart;
}

static CRITICAL_SECTION   _Tss_mutex;
static CONDITION_VARIABLE _Tss_cv;
static HANDLE             _Tss_event;

static decltype(SleepConditionVariableCS)* encoded_sleep_condition_variable_cs;
static decltype(WakeAllConditionVariable)* encoded_wake_all_condition_variable;



// Initializer for synchronization data structures.
// On Vista or newer, the native CONDITION_VARIABLE type is used.  On XP, we use a simple
// Windows event.  This is not safe to use as a complete condition variable, but for the purposes
// of this feature the event is sufficient but not optimal.  See the code in _Tss_wait
// below.
//
// For Windows OS components:  The OS supports APISets downlevel to Windows 7,
// and OS components that run downlevel to Windows 7 may build against APISets.
// However, these components cannot use CONDITION_VARIABLE directly because it
// is not available via APISets until Windows 8.  Thus, for Windows OS components,
// we use the "ancient" code path and first try the APISet and then fall back to
// kernel32.dll.
// The helper __scrt_is_event_api_used signals the usage of the event API for the
// rest of the code (allows it to be hardcoded to false when guaranteed to not be used).
#if defined _SCRT_ENCLAVE_BUILD || defined _CRT_APP || \
    (!defined _CRT_WINDOWS && (defined _ONECORE || defined _KERNELX || defined _M_ARM || defined _M_ARM64))
    static void __cdecl __scrt_initialize_thread_safe_statics_platform_specific() noexcept
    {
        // This can only fail due to invalid parameters (flags) so ignoring error is ok.
        InitializeCriticalSectionEx(&_Tss_mutex, 4000, 0);

        InitializeConditionVariable(&_Tss_cv);

        encoded_sleep_condition_variable_cs = __crt_fast_encode_pointer(&SleepConditionVariableCS);
        encoded_wake_all_condition_variable = __crt_fast_encode_pointer(&WakeAllConditionVariable);
    }

    constexpr bool __scrt_is_event_api_used(HANDLE) { return false; }
#else // ^^^ Modern Platforms ^^^ // vvv Ancient Platforms vvv //
    static void __cdecl __scrt_initialize_thread_safe_statics_platform_specific() noexcept
    {
        // This can fail pre-Vista and that is ignored.
        InitializeCriticalSectionAndSpinCount(&_Tss_mutex, 4000);

        // CONDITION_VARIABLE is available via this APISet starting on Windows 8.
        HMODULE kernel_dll = GetModuleHandleW(L"api-ms-win-core-synch-l1-2-0.dll");
        if (kernel_dll == nullptr)
        {
            kernel_dll = GetModuleHandleW(L"kernel32.dll");
        }

        if (kernel_dll == nullptr)
        {
            __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
        }

        #define GET_PROC_ADDRESS(m, f) reinterpret_cast<decltype(f)*>(GetProcAddress(m, _CRT_STRINGIZE(f)))

        auto const initialize_condition_variable = GET_PROC_ADDRESS(kernel_dll, InitializeConditionVariable);
        auto const sleep_condition_variable_cs   = GET_PROC_ADDRESS(kernel_dll, SleepConditionVariableCS);
        auto const wake_all_condition_variable   = GET_PROC_ADDRESS(kernel_dll, WakeAllConditionVariable);

        #undef GET_PROC_ADDRESS

        if (initialize_condition_variable && sleep_condition_variable_cs && wake_all_condition_variable)
        {
            _Tss_event = nullptr;
            initialize_condition_variable(&_Tss_cv);

            encoded_sleep_condition_variable_cs = __crt_fast_encode_pointer(sleep_condition_variable_cs);
            encoded_wake_all_condition_variable = __crt_fast_encode_pointer(wake_all_condition_variable);
        }
        else
        {
            _Tss_event = CreateEventW(NULL, TRUE, FALSE, NULL);
            if (_Tss_event == nullptr)
            {
                __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
            }
        }
    }

    constexpr bool __scrt_is_event_api_used(HANDLE const _Event) { return _Event != nullptr; }
#endif // Ancient Platforms

// Terminator for synchronization data structures.
static void __cdecl __scrt_uninitialize_thread_safe_statics() noexcept
{
    DeleteCriticalSection(&_Tss_mutex);
    if (__scrt_is_event_api_used(_Tss_event))
    {
        CloseHandle(_Tss_event);
    }
}

// Initializer for synchronization data structures.
static int __cdecl __scrt_initialize_thread_safe_statics() noexcept
{
    __scrt_initialize_thread_safe_statics_platform_specific();

    // If CRT initialization was skipped then we should initialize the atexit tables.
    // This will only be needed when using a managed DLL with /NOENTRY specified.
    if (!__scrt_initialize_onexit_tables(__scrt_module_type::dll))
    {
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
    }
    atexit(__scrt_uninitialize_thread_safe_statics);
    return 0;
}

_CRTALLOC(".CRT$XIC") static _PIFV __scrt_initialize_tss_var = __scrt_initialize_thread_safe_statics;

// Helper functions for accessing the mutex and condition variable.  Can be replaced with
// more suitable data structures provided by the CRT, preferably ones that use the most
// efficient synchronization primitives available on the platform.
extern "C" void __cdecl _Init_thread_lock()
{
    EnterCriticalSection(&_Tss_mutex);
}

extern "C" void __cdecl _Init_thread_unlock()
{
    LeaveCriticalSection(&_Tss_mutex);
}

// Wait on the condition variable.  In the XP implementation using only a Windows event
// we can't guarantee that we'll ever actually receive the notification signal, so we
// must use a non-infinite timeout.  This is not optimal: we may wake up early if the
// initializer is long-running, or we may miss the signal and not wake up until the
// timeout expires.  The signal may be missed because the sleeping threads may be
// stolen by the kernel to service an APC, or due to the race condition between the
// unlock call and the WaitForSingleObject call.
extern "C" void __cdecl _Init_thread_wait(DWORD const timeout)
{
    if (!__scrt_is_event_api_used(_Tss_event))
    {
        __crt_fast_decode_pointer(encoded_sleep_condition_variable_cs)(&_Tss_cv, &_Tss_mutex, timeout);
        return;
    }

    _ASSERT(timeout != INFINITE);
    _Init_thread_unlock();
    WaitForSingleObjectEx(_Tss_event, timeout, FALSE);
    _Init_thread_lock();
}

extern "C" void __cdecl _Init_thread_notify()
{
    if (!__scrt_is_event_api_used(_Tss_event))
    {
        __crt_fast_decode_pointer(encoded_wake_all_condition_variable)(&_Tss_cv);
    }
    else
    {
        SetEvent(_Tss_event);
        ResetEvent(_Tss_event);
    }
}

DWORD const XpTimeout = 100; // ms



// Control access to the initialization expression.  Only one thread may leave
// this function before the variable has completed initialization, this thread
// will perform initialization.  All other threads are blocked until the
// initialization completes or fails due to an exception.
extern "C" void __cdecl _Init_thread_header(int* const pOnce) noexcept
{
    _Init_thread_lock();

    if (*pOnce == Uninitialized)
    {
        *pOnce = BeingInitialized;
    }
    else
    {
        while (*pOnce == BeingInitialized)
        {
            // Timeout can be replaced with an infinite wait when XP support is
            // removed or the XP-based condition variable is sophisticated enough
            // to guarantee all waiting threads will be woken when the variable is
            // signalled.
            _Init_thread_wait(XpTimeout);

            if (*pOnce == Uninitialized)
            {
                *pOnce = BeingInitialized;
                _Init_thread_unlock();
                return;
            }
        }
        _Init_thread_epoch = _Init_global_epoch;
    }

    _Init_thread_unlock();
}

// Abort processing of the initializer due to an exception.  Reset the state
// to uninitialized and release waiting threads (one of which will take over
// initialization, any remaining will again sleep).
extern "C" void __cdecl _Init_thread_abort(int* const pOnce) noexcept
{
    _Init_thread_lock();
    *pOnce = Uninitialized;
    _Init_thread_unlock();
    _Init_thread_notify();
}

// Called by the thread that completes initialization of a variable.
// Increment the global and per thread counters, mark the variable as
// initialized, and release waiting threads.
extern "C" void __cdecl _Init_thread_footer(int* const pOnce) noexcept
{
    _Init_thread_lock();
    ++_Init_global_epoch;
    *pOnce = _Init_global_epoch;
    _Init_thread_epoch = _Init_global_epoch;
    _Init_thread_unlock();
    _Init_thread_notify();
}
View Code

 

 

Reference links:

C language variables and their life cycle

Local static variable is initialized only once? It is how to achieve

Guess you like

Origin www.cnblogs.com/DirWang/p/12167475.html