Как MSVC оптимизирует использование статических переменных?
Мне интересно, как компилятор Microsoft Visual С++ обрабатывает/оптимизирует статические переменные.
Мой код:
#include <cstdlib>
void no_static_initialization()
{
static int value = 3;
}
void static_initialization(int new_value)
{
static int value = new_value;
}
int main()
{
no_static_initialization();
static_initialization(1);
static_initialization(std::rand());
return 0;
}
Здесь сборка для кода (скомпилирована с оптимизацией):
![picture of the assembly listing]()
Моя основная область интересов - последний случай.
Здесь первое утверждение полностью оптимизировалось, и два вызова второго оператора были встроены, и они фактически представляют собой аналогичные фрагменты кода.
Каждый из них выполняет test something something
, а затем делает короткий jump
, если тест не был успешным (эти прыжки, очевидно, указывают на конец соответствующей процедуры).
Является ли компилятор явной проверкой для каждого вызова функции, если функция вызывается в первый раз?
Есть ли у компилятора flag
, который указывает, является ли это впервые вызовом функции или нет?
Где он хранится (я думаю, что все, что test
материал об этом, но я не совсем уверен)?
Ответы
Ответ 1
Да, компилятор должен добавить скрытый флаг, чтобы проверить, является ли это первым вызовом функции и инициализируется или нет, в зависимости от этого. В обоих фрагментах он проверяет флаг, если он поднят, он перейдет к концу функции, иначе инициализирует статическую переменную. Обратите внимание, что поскольку компилятор встроил функцию, он мог бы также оптимизировать второй тест, зная, что флаг должен быть проверен только при первом вызове.
Флаг, по-видимому, находится по адресу 0x00403374 и берет байт, а сама переменная находится по адресу 0x00403370.
Ответ 2
Мне нравится использовать LLVM, потому что генерируемый им код говорит вам более явно, что он делает:
Фактический код ниже, потому что он долго читается. Да, LLVM создает защитные условия для статических значений. обратите внимание, как static_initialization
/bb:
получает охрану, проверяет, соответствует ли это определенному значению, уже инициализированному, и либо веткится на bb1, если требуется инициализировать, либо bb2, если это не так. Это не единственный способ решить одно требование инициализации, но это обычный способ.
; ModuleID = '/tmp/webcompile/_31867_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"
@guard variable for static_initialization(int)::value = internal global i64 0 ; <i64*> [#uses=3]
@static_initialization(int)::value = internal global i32 0 ; <i32*> [#uses=1]
define void @no_static_initialization()() nounwind {
entry:
br label %return
return: ; preds = %entry
ret void
}
define void @static_initialization(int)(i32 %new_value) nounwind {
entry:
%new_value_addr = alloca i32 ; <i32*> [#uses=2]
%0 = alloca i8 ; <i8*> [#uses=2]
%retval.1 = alloca i8 ; <i8*> [#uses=2]
%"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0]
store i32 %new_value, i32* %new_value_addr
%1 = load i8* bitcast (i64* @guard variable for static_initialization(int)::value to i8*), align 1 ; <i8> [#uses=1]
%2 = icmp eq i8 %1, 0 ; <i1> [#uses=1]
br i1 %2, label %bb, label %bb2
bb: ; preds = %entry
%3 = call i32 @__cxa_guard_acquire(i64* @guard variable for static_initialization(int)::value) nounwind ; <i32> [#uses=1]
%4 = icmp ne i32 %3, 0 ; <i1> [#uses=1]
%5 = zext i1 %4 to i8 ; <i8> [#uses=1]
store i8 %5, i8* %retval.1, align 1
%6 = load i8* %retval.1, align 1 ; <i8> [#uses=1]
%toBool = icmp ne i8 %6, 0 ; <i1> [#uses=1]
br i1 %toBool, label %bb1, label %bb2
bb1: ; preds = %bb
store i8 0, i8* %0, align 1
%7 = load i32* %new_value_addr, align 4 ; <i32> [#uses=1]
store i32 %7, i32* @static_initialization(int)::value, align 4
store i8 1, i8* %0, align 1
call void @__cxa_guard_release(i64* @guard variable for static_initialization(int)::value) nounwind
br label %bb2
bb2: ; preds = %bb1, %bb, %entry
br label %return
return: ; preds = %bb2
ret void
}
declare i32 @__cxa_guard_acquire(i64*) nounwind
declare void @__cxa_guard_release(i64*) nounwind
define i32 @main() nounwind {
entry:
%retval = alloca i32 ; <i32*> [#uses=2]
%0 = alloca i32 ; <i32*> [#uses=2]
%"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0]
call void @no_static_initialization()() nounwind
call void @static_initialization(int)(i32 1) nounwind
%1 = call i32 @rand() nounwind ; <i32> [#uses=1]
call void @static_initialization(int)(i32 %1) nounwind
store i32 0, i32* %0, align 4
%2 = load i32* %0, align 4 ; <i32> [#uses=1]
store i32 %2, i32* %retval, align 4
br label %return
return: ; preds = %entry
%retval1 = load i32* %retval ; <i32> [#uses=1]
ret i32 %retval1
}
declare i32 @rand() nounwind