Index: include/llvm/IR/Constant.h =================================================================== --- include/llvm/IR/Constant.h +++ include/llvm/IR/Constant.h @@ -148,7 +148,7 @@ /// them. This method is useful for clients that want to check to see if a /// global is unused, but don't want to deal with potentially dead constants /// hanging off of the globals. - void removeDeadConstantUsers() const; + void removeDeadConstantUsers(bool SkipMetadataUses = true) const; Constant *stripPointerCasts() { return cast(Value::stripPointerCasts()); Index: lib/IR/Constants.cpp =================================================================== --- lib/IR/Constants.cpp +++ lib/IR/Constants.cpp @@ -466,16 +466,19 @@ /// removeDeadUsersOfConstant - If the specified constantexpr is dead, remove /// it. This involves recursively eliminating any dead users of the /// constantexpr. -static bool removeDeadUsersOfConstant(const Constant *C) { +static bool removeDeadUsersOfConstant(const Constant *C, bool SkipMetadataUses) { if (isa(C)) return false; // Cannot remove this while (!C->use_empty()) { const Constant *User = dyn_cast(C->user_back()); if (!User) return false; // Non-constant usage; - if (!removeDeadUsersOfConstant(User)) + if (!removeDeadUsersOfConstant(User, SkipMetadataUses)) return false; // Constant wasn't dead } + if (!SkipMetadataUses && C->isUsedByMetadata()) + return false; + const_cast(C)->destroyConstant(); return true; } @@ -485,7 +488,7 @@ /// off of this constant, remove them. This method is useful for clients /// that want to check to see if a global is unused, but don't want to deal /// with potentially dead constants hanging off of the globals. -void Constant::removeDeadConstantUsers() const { +void Constant::removeDeadConstantUsers(bool SkipMetadataUses) const { Value::const_user_iterator I = user_begin(), E = user_end(); Value::const_user_iterator LastNonDeadUser = E; while (I != E) { @@ -496,7 +499,7 @@ continue; } - if (!removeDeadUsersOfConstant(User)) { + if (!removeDeadUsersOfConstant(User, SkipMetadataUses)) { // If the constant wasn't dead, remember that this was the last live use // and move on to the next constant. LastNonDeadUser = I; Index: lib/Transforms/IPO/GlobalOpt.cpp =================================================================== --- lib/Transforms/IPO/GlobalOpt.cpp +++ lib/Transforms/IPO/GlobalOpt.cpp @@ -1683,8 +1683,19 @@ return true; } +/// Return true if GV is only used by constants which are only used by metadata. +static bool onlyUsedByMetadata(GlobalValue &GV) { + for (auto *U : GV.users()) + if (auto *C = dyn_cast(U)) { + if (!(C->use_empty() && C->isUsedByMetadata())) + return false; + } else + return false; + return true; +} + bool GlobalOpt::deleteIfDead(GlobalValue &GV) { - GV.removeDeadConstantUsers(); + GV.removeDeadConstantUsers(/* skip metadata uses */false); if (!GV.isDiscardableIfUnused()) return false; @@ -1697,7 +1708,7 @@ if (auto *F = dyn_cast(&GV)) Dead = F->isDefTriviallyDead(); else - Dead = GV.use_empty(); + Dead = GV.use_empty() || onlyUsedByMetadata(GV); if (!Dead) return false; Index: test/Transforms/GlobalOpt/debug-info-global-vars.ll =================================================================== --- /dev/null +++ test/Transforms/GlobalOpt/debug-info-global-vars.ll @@ -0,0 +1,34 @@ +; RUN: opt %s -globalopt -S -o - | FileCheck %s +; Built and reduced from 'const char version[] = "test";' with -fsanitize=address. + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx" + +; CHECK: @version = constant +@version = constant { [5 x i8], [59 x i8] } { [5 x i8] c"test\00", [59 x i8] zeroinitializer }, align 32 +@unused = internal global { [5 x i8], [59 x i8] } { [5 x i8] c"test\00", [59 x i8] zeroinitializer }, align 32 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!10, !11, !12} +!llvm.ident = !{!13} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, globals: !3) +!1 = !DIFile(filename: "version.c", directory: "/Volumes/Data/radar/24899262") +!2 = !{} +!3 = !{!4, !14} +; Check that we don't drop the debug info for this global variable. +; CHECK: !DIGlobalVariable(name: "version", {{.*}}, variable: [5 x i8]* +!4 = !DIGlobalVariable(name: "version", scope: !0, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, variable: [5 x i8]* getelementptr inbounds ({ [5 x i8], [59 x i8] }, { [5 x i8], [59 x i8] }* @version, i32 0, i32 0)) +!5 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 40, align: 8, elements: !8) +!6 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !7) +!7 = !DIBasicType(name: "char", size: 8, align: 8, encoding: DW_ATE_signed_char) +!8 = !{!9} +!9 = !DISubrange(count: 5) +!10 = !{i32 2, !"Dwarf Version", i32 2} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"PIC Level", i32 2} +!13 = !{!"clang version 3.9.0 "} +; Check that we do drop the debug info for this global variable. +; CHECK: !DIGlobalVariable(name: "unused", +; CHECK-NOT: variable: [5 x i8]* +!14 = !DIGlobalVariable(name: "unused", scope: !0, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, variable: [5 x i8]* getelementptr inbounds ({ [5 x i8], [59 x i8] }, { [5 x i8], [59 x i8] }* @unused, i32 0, i32 0))