Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -4948,6 +4948,16 @@ See :doc:`TypeMetadata`. +'``associated``' Metadata +^^^^^^^^^^^^^^^^^^^ + +The ``associated`` metadata may be attached to a global variable +declaration. It tells the optimizer (DCE in particular) to treat all +global references in that variable's initializer as reverse +dependencies. + +On ELF targets this is modelled by placing the global in a section +with SHF_ASSOCIATED flag. Module Flags Metadata ===================== Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -78,6 +78,7 @@ MD_type = 19, // "type" MD_section_prefix = 20, // "section_prefix" MD_absolute_symbol = 21, // "absolute_symbol" + MD_associated = 22, // "associated" }; /// Known operand bundle tag IDs, which always have the same value. All Index: include/llvm/Transforms/IPO/GlobalDCE.h =================================================================== --- include/llvm/Transforms/IPO/GlobalDCE.h +++ include/llvm/Transforms/IPO/GlobalDCE.h @@ -34,11 +34,15 @@ SmallPtrSet SeenConstants; std::unordered_multimap ComdatMembers; + // Maps global to a set of !associated globals that depend on it. + std::unordered_multimap AssociatedGlobals; + /// Mark the specific global value as needed, and /// recursively mark anything that it uses as also needed. void GlobalIsNeeded(GlobalValue *GV); void MarkUsedGlobalsAsNeeded(Constant *C); bool RemoveUnusedGlobalValue(GlobalValue &GV); + void AddAssociatedGlobal(GlobalVariable *AG); }; } Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -224,6 +224,10 @@ Group = C->getName(); Flags |= ELF::SHF_GROUP; } + + if (GO->getMetadata(LLVMContext::MD_associated)) + Flags |= ELF::SHF_ASSOCIATED; + return getContext().getELFSection(SectionName, getELFSectionType(SectionName, Kind), Flags, /*EntrySize=*/0, Group); Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -58,6 +58,7 @@ {MD_type, "type"}, {MD_section_prefix, "section_prefix"}, {MD_absolute_symbol, "absolute_symbol"}, + {MD_associated, "associated"}, }; for (auto &MDKind : MDKinds) { Index: lib/Transforms/IPO/GlobalDCE.cpp =================================================================== --- lib/Transforms/IPO/GlobalDCE.cpp +++ lib/Transforms/IPO/GlobalDCE.cpp @@ -102,6 +102,10 @@ if (Comdat *C = GA.getComdat()) ComdatMembers.insert(std::make_pair(C, &GA)); + for (GlobalVariable &GV : M.globals()) + if (GV.getMetadata(LLVMContext::MD_associated)) + AddAssociatedGlobal(&GV); + // Loop over the module, adding globals which are obviously necessary. for (GlobalObject &GO : M.global_objects()) { Changed |= RemoveUnusedGlobalValue(GO); @@ -197,6 +201,7 @@ AliveGlobals.clear(); SeenConstants.clear(); ComdatMembers.clear(); + AssociatedGlobals.clear(); if (Changed) return PreservedAnalyses::none(); @@ -215,6 +220,9 @@ GlobalIsNeeded(CM.second); } + for (auto &&AG : make_range(AssociatedGlobals.equal_range(G))) + GlobalIsNeeded(AG.second); + if (GlobalVariable *GV = dyn_cast(G)) { // If this is a global variable, we must make sure to add any global values // referenced by the initializer to the alive set. @@ -243,6 +251,27 @@ } } +void GlobalDCEPass::AddAssociatedGlobal(GlobalVariable *AG) { + SmallPtrSet Seen; + SmallVector WorkList; + WorkList.push_back(AG->getInitializer()); + + while (!WorkList.empty()) { + Constant *C = WorkList.pop_back_val(); + if (GlobalValue *GV = dyn_cast(C)) { + AssociatedGlobals.insert(std::make_pair(GV, AG)); + continue; + } + + for (Use &U : C->operands()) { + // If we've already processed this constant there's no need to do it again. + Constant *Op = dyn_cast(U); + if (Op && Seen.insert(Op).second) + WorkList.push_back(Op); + } + } +} + void GlobalDCEPass::MarkUsedGlobalsAsNeeded(Constant *C) { if (GlobalValue *GV = dyn_cast(C)) return GlobalIsNeeded(GV); Index: test/ThinLTO/X86/lazyload_metadata.ll =================================================================== --- test/ThinLTO/X86/lazyload_metadata.ll +++ test/ThinLTO/X86/lazyload_metadata.ll @@ -10,13 +10,13 @@ ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=LAZY -; LAZY: 49 bitcode-reader - Number of Metadata records loaded +; LAZY: 51 bitcode-reader - Number of Metadata records loaded ; LAZY: 2 bitcode-reader - Number of MDStrings loaded ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -disable-ondemand-mds-loading -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=NOTLAZY -; NOTLAZY: 58 bitcode-reader - Number of Metadata records loaded +; NOTLAZY: 60 bitcode-reader - Number of Metadata records loaded ; NOTLAZY: 7 bitcode-reader - Number of MDStrings loaded @@ -55,4 +55,4 @@ !6 = !{!9} !7 = !{!"7"} !8 = !{!"8"} -!9 = !{!6} \ No newline at end of file +!9 = !{!6} Index: test/Transforms/GlobalDCE/associated.ll =================================================================== --- /dev/null +++ test/Transforms/GlobalDCE/associated.ll @@ -0,0 +1,26 @@ +; RUN: opt < %s -globaldce -S | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.A = type { i32, i32* } + +; CHECK: @x = +@x = global i32 0, align 4 + +; CHECK-NOT: @y0 = +@y0 = internal global i32* @x, align 8 +; CHECK: @y1 = +@y1 = internal global i32* @x, align 8, !associated !0 + +; CHECK-NOT: @z0 = +@z0 = internal global %struct.A { i32 5, i32* @x }, align 8 +; CHECK: @z1 = +@z1 = internal global %struct.A { i32 5, i32* @x }, align 8, !associated !0 + +; CHECK-NOT: @zz0 = +@zz0 = internal global %struct.A* @z1, align 8 +; CHECK: @zz1 = +@zz1 = internal global %struct.A* @z1, align 8, !associated !0 + +!0 = !{}