代码之家  ›  专栏  ›  技术社区  ›  Yippie-Ki-Yay

MSVC如何优化静态变量的使用?

  •  13
  • Yippie-Ki-Yay  · 技术社区  · 14 年前
    我对微软Visual C++编译器如何处理/优化静态变量很感兴趣。

    <我的代码:

    include<cstdlib>
    
    void no_static_初始化())
    {
    静态int值=3;
    }
    
    void static_初始化(int new_value)
    {
    静态int值=新的_值;
    }
    
    int()
    {
    无“静态”初始化();
    静态初始化(1);
    静态_初始化(std::rand());
    
    返回0;
    }
    < /代码> 
    
    

    下面是代码的程序集(使用优化进行编译):。

    我感兴趣的主要领域是最后一个案例。

    这里,第一条语句得到了完全优化,第二条语句的两个调用是内联的,它们实际上表示相似的代码块。

    如果测试不成功(这些跳转显然指向相应例程的结尾),则它们中的每一个都会执行测试某些东西->code>然后执行一个短的跳转->code>。

    如果函数是第一次调用的,编译器是否会对每个函数调用进行显式检查?< BR> 编译器是否有一个flag>code>,它指示是否是第一次调用函数?< BR> 它存储在哪里(我猜所有这些都是关于它的,但我不完全确定)?

    我的代码:

    #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关于这件事,但我不太确定?

    2 回复  |  直到 7 年前
        1
  •  8
  •   David Rodríguez - dribeas    14 年前

    是的,编译器必须添加一个隐藏的标志来测试它是否是对函数的第一次调用,并根据这一点进行初始化。在这两个代码段中,它都在测试标志,如果它被提升,它将跳到函数的末尾,否则它将初始化静态变量。请注意,由于编译器已经内联了函数,所以它还可以优化第二个测试,因为知道只在第一个调用上测试标志。

    该标志似乎位于地址0x00403374处,取一个字节,而变量本身位于地址0x00403370处。

        2
  •  3
  •   SingleNegationElimination    14 年前

    我喜欢使用llvm,因为它生成的代码更明确地告诉您它在做什么:

    实际代码在下面,因为它有点长。是的,LLVM为静态值创建保护条件变量。注意如何 static_initialization / bb: 获取guard,检查它的某个值是否与已经初始化的值对应,如果需要初始化则分支到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