我对Microsoft Visual C ++编译器如何处理/优化静态变量感兴趣。
我的代码:
#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;
}
这是代码的程序集(使用优化编译):
我感兴趣的主要领域是最后一个案例。
这里,第一个语句得到了全面优化,第二个语句的两个调用被内联,它们实际上代表了类似的代码块。
如果测试不成功,他们每个都会test something something
然后缩短jump
(这些跳跃显然指向相应例程的结尾)。
如果第一次调用该函数,编译器是否对每个函数调用进行显式检查?
编译器实际上是否有flag
,表明这是否是第一次调用该函数?
它存放在哪里(我猜所有test
的东西都是关于它的,但我不确定)?
答案 0 :(得分:8)
是的,编译器必须添加一个隐藏标志来测试它是否是第一次调用函数并初始化或取决于该函数。在两个片段中,它都在测试标志,如果它被提升,它将跳转到函数的末尾,否则它将初始化静态变量。请注意,由于编译器已内联函数,因此可以优化第二次测试,知道该标志仅在第一次调用时进行测试。
该标志似乎位于地址0x00403374,并且占用一个字节,而变量本身位于地址0x00403370。
答案 1 :(得分:3)
我喜欢使用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