clang的优化是否被打破?

时间:2016-08-20 19:11:42

标签: c++ optimization clang llvm

如果我将此c ++程序插入clang(版本3.7)

///*
#include "stdio.h"
#include "stdint.h"
//extern int printf(const unsigned char*, ...);


extern "C" void __cxa_pure_virtual() { }

struct A
{
    virtual void foo() = 0;
};

struct B : A
{
    uint32_t x;
    B(int x) : x(x) { }
    virtual void foo()
    {
        printf("This is a test %d\n", x);
    }
};
//*/
uint64_t thing = 0;
float other = 10.0f;
B b(12345);
int main()
{

    thing++;
    A* a = &b;
    other *= 3.14159f;
    a->foo();
}

使用clang -emit-llvm main.cpp -fno-rtti -O3 -S进行编译,然后我得到以下字节代码:

; ModuleID = 'main.cpp'
target datalayout = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686-pc-linux-gnu"

%struct.B = type { %struct.A, i32 }
%struct.A = type { i32 (...)** }

$_ZN1B3fooEv = comdat any

$_ZTV1B = comdat any

@thing = global i64 0, align 8
@other = global float 1.000000e+01, align 4
@b = global %struct.B { %struct.A { i32 (...)** bitcast (i8** getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTV1B, i64 0, i64 2) to i32 (...)**) }, i32 12345 }, align 4
@_ZTV1B = linkonce_odr unnamed_addr constant [3 x i8*] [i8* null, i8* null, i8* bitcast (void (%struct.B*)* @_ZN1B3fooEv to i8*)], comdat, align 4
@.str = private unnamed_addr constant [19 x i8] c"This is a test %d\0A\00", align 1
@llvm.global_ctors = appending global [0 x { i32, void ()*, i8* }] zeroinitializer

; Function Attrs: nounwind readnone
define void @__cxa_pure_virtual() #0 {
entry:
  ret void
}

define i32 @main() #1 {
entry:
  %0 = load i64, i64* @thing, align 8, !tbaa !1
  %inc = add i64 %0, 1
  store i64 %inc, i64* @thing, align 8, !tbaa !1
  %1 = load float, float* @other, align 4, !tbaa !5
  %mul = fmul float %1, 0x400921FA00000000
  store float %mul, float* @other, align 4, !tbaa !5
  %vtable = load void (%struct.A*)**, void (%struct.A*)*** bitcast (%struct.B* @b to void (%struct.A*)***), align 4, !tbaa !7
  %2 = load void (%struct.A*)*, void (%struct.A*)** %vtable, align 4
  tail call void %2(%struct.A* getelementptr inbounds (%struct.B, %struct.B* @b, i32 0, i32 0))
  ret i32 0
}

; Function Attrs: nounwind
define linkonce_odr void @_ZN1B3fooEv(%struct.B* nocapture readonly %this) unnamed_addr #2 comdat align 2 {
entry:
  %x = getelementptr inbounds %struct.B, %struct.B* %this, i32 0, i32 1
  %0 = load i32, i32* %x, align 4, !tbaa !9
  %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str, i32 0, i32 0), i32 %0)
  ret void
}

; Function Attrs: nounwind
declare i32 @printf(i8* nocapture readonly, ...) #2

attributes #0 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"clang version 3.7.1 "}
!1 = !{!2, !2, i64 0}
!2 = !{!"long long", !3, i64 0}
!3 = !{!"omnipotent char", !4, i64 0}
!4 = !{!"Simple C/C++ TBAA"}
!5 = !{!6, !6, i64 0}
!6 = !{!"float", !3, i64 0}
!7 = !{!8, !8, i64 0}
!8 = !{!"vtable pointer", !4, i64 0}
!9 = !{!10, !11, i64 4}
!10 = !{!"_ZTS1B", !11, i64 4}
!11 = !{!"int", !3, i64 0}

如果你看一下main函数,我有两个无用的变量。当然我增加一个,我在另一个上做一些乘法,但我从来没有使用它们中的值。

但是如果你看一下字节代码的输出,看起来它仍然在进行无用的数学运算。

只是我还是这个错误?

2 个答案:

答案 0 :(得分:6)

这些变量是全局范围内的变量。编译器根本无法弄清楚这些变量是否可以在其他翻译单元中声明和引用。

如果任何现代C ++编译器足够复杂以确定执行流程无法以定义的方式逃避此转换单元,那么我会感到惊讶,因此在此优化掉未使用的全局变量是安全的翻译单位。

答案 1 :(得分:3)

不,我不相信这是一个错误,因为你的变量是全局变量。 Clang无法删除此数学,因为它无法知道任何外部调用的函数(如printf函数,在不同的翻译单元中)不会声明extern float other;并以某种方式使用它。

尝试写作:

int main()
{
    uint64_t thing = 0;
    float other = 10.0f;
    B b(12345);

    thing++;
    A* a = &b;
    other *= 3.14159f;
    a->foo();
}