Index: llvm/include/llvm/Transforms/Utils/ModuleUtils.h =================================================================== --- llvm/include/llvm/Transforms/Utils/ModuleUtils.h +++ llvm/include/llvm/Transforms/Utils/ModuleUtils.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/MemoryBufferRef.h" +#include #include // for std::pair namespace llvm { @@ -80,6 +81,12 @@ /// Adds global values to the llvm.compiler.used list. void appendToCompilerUsed(Module &M, ArrayRef Values); +/// Removes global values from the llvm.used and llvm.compiler.used arrays. \p +/// ShouldRemove should return true for any initializer field that should not be +/// included in the replacement global. +void removeFromUsedLists(Module &M, + std::function ShouldRemove); + /// Filter out potentially dead comdat functions where other entries keep the /// entire comdat group alive. /// Index: llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp =================================================================== --- llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp +++ llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp @@ -186,54 +186,20 @@ class AMDGPULowerModuleLDS : public ModulePass { - static void removeFromUsedList(Module &M, StringRef Name, - SmallPtrSetImpl &ToRemove) { - GlobalVariable *GV = M.getNamedGlobal(Name); - if (!GV || ToRemove.empty()) { - return; - } - - SmallVector Init; - auto *CA = cast(GV->getInitializer()); - for (auto &Op : CA->operands()) { - // ModuleUtils::appendToUsed only inserts Constants - Constant *C = cast(Op); - if (!ToRemove.contains(C->stripPointerCasts())) { - Init.push_back(C); - } - } - - if (Init.size() == CA->getNumOperands()) { - return; // none to remove - } - - GV->eraseFromParent(); - - for (Constant *C : ToRemove) { - C->removeDeadConstantUsers(); - } - - if (!Init.empty()) { - ArrayType *ATy = - ArrayType::get(Type::getInt8PtrTy(M.getContext()), Init.size()); - GV = - new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage, - ConstantArray::get(ATy, Init), Name); - GV->setSection("llvm.metadata"); - } - } - - static void removeFromUsedLists(Module &M, - const DenseSet &LocalVars) { + static void + removeLocalVarsFromUsedLists(Module &M, + const DenseSet &LocalVars) { // The verifier rejects used lists containing an inttoptr of a constant // so remove the variables from these lists before replaceAllUsesWith + SmallPtrSet LocalVarsSet; + for (GlobalVariable *LocalVar : LocalVars) + LocalVarsSet.insert(cast(LocalVar->stripPointerCasts())); + + removeFromUsedLists( + M, [&LocalVarsSet](Constant *C) { return LocalVarsSet.count(C); }); - SmallPtrSet LocalVarsSet; for (GlobalVariable *LocalVar : LocalVars) - if (Constant *C = dyn_cast(LocalVar->stripPointerCasts())) - LocalVarsSet.insert(C); - removeFromUsedList(M, "llvm.used", LocalVarsSet); - removeFromUsedList(M, "llvm.compiler.used", LocalVarsSet); + LocalVar->removeDeadConstantUsers(); } static void markUsedByKernel(IRBuilder<> &Builder, Function *Func, @@ -804,7 +770,7 @@ Type::getInt8PtrTy(Ctx)))}); // historic - removeFromUsedLists(M, ModuleScopeVariables); + removeLocalVarsFromUsedLists(M, ModuleScopeVariables); // Replace all uses of module scope variable from non-kernel functions replaceLDSVariablesWithStruct( @@ -891,7 +857,7 @@ createLDSVariableReplacement(M, VarName, KernelUsedVariables); // remove preserves existing codegen - removeFromUsedLists(M, KernelUsedVariables); + removeLocalVarsFromUsedLists(M, KernelUsedVariables); KernelToReplacement[&Func] = Replacement; // Rewrite uses within kernel to the new struct Index: llvm/lib/Transforms/Utils/ModuleUtils.cpp =================================================================== --- llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ llvm/lib/Transforms/Utils/ModuleUtils.cpp @@ -74,35 +74,35 @@ appendToGlobalArray("llvm.global_dtors", M, F, Priority, Data); } +static void collectUsedGlobals(GlobalVariable *GV, + SmallSetVector &Init) { + if (!GV || !GV->hasInitializer()) + return; + + auto *CA = cast(GV->getInitializer()); + for (Use &Op : CA->operands()) + Init.insert(cast(Op)); +} + static void appendToUsedList(Module &M, StringRef Name, ArrayRef Values) { GlobalVariable *GV = M.getGlobalVariable(Name); - SmallPtrSet InitAsSet; - SmallVector Init; - if (GV) { - if (GV->hasInitializer()) { - auto *CA = cast(GV->getInitializer()); - for (auto &Op : CA->operands()) { - Constant *C = cast_or_null(Op); - if (InitAsSet.insert(C).second) - Init.push_back(C); - } - } + + SmallSetVector Init; + collectUsedGlobals(GV, Init); + if (GV) GV->eraseFromParent(); - } - Type *Int8PtrTy = llvm::Type::getInt8PtrTy(M.getContext()); - for (auto *V : Values) { - Constant *C = ConstantExpr::getPointerBitCastOrAddrSpaceCast(V, Int8PtrTy); - if (InitAsSet.insert(C).second) - Init.push_back(C); - } + Type *ArrayEltTy = llvm::Type::getInt8PtrTy(M.getContext()); + for (auto *V : Values) + Init.insert(ConstantExpr::getPointerBitCastOrAddrSpaceCast(V, ArrayEltTy)); if (Init.empty()) return; - ArrayType *ATy = ArrayType::get(Int8PtrTy, Init.size()); + ArrayType *ATy = ArrayType::get(ArrayEltTy, Init.size()); GV = new llvm::GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage, - ConstantArray::get(ATy, Init), Name); + ConstantArray::get(ATy, Init.getArrayRef()), + Name); GV->setSection("llvm.metadata"); } @@ -114,6 +114,44 @@ appendToUsedList(M, "llvm.compiler.used", Values); } +static void removeFromUsedList(Module &M, StringRef Name, + std::function ShouldRemove) { + GlobalVariable *GV = M.getNamedGlobal(Name); + if (!GV) + return; + + SmallSetVector Init; + collectUsedGlobals(GV, Init); + + StringRef Section = GV->getSection(); + unsigned AddrSpace = GV->getAddressSpace(); + GlobalValue::ThreadLocalMode TLM = GV->getThreadLocalMode(); + + Type *ArrayEltTy = cast(GV->getValueType())->getElementType(); + + GV->eraseFromParent(); + + SmallVector NewInit; + for (Constant *MaybeRemoved : Init.getArrayRef()) { + if (!ShouldRemove(MaybeRemoved->stripPointerCasts())) + NewInit.push_back(MaybeRemoved); + } + + if (!NewInit.empty()) { + ArrayType *ATy = ArrayType::get(ArrayEltTy, NewInit.size()); + GV = new llvm::GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage, + ConstantArray::get(ATy, NewInit), Name, + nullptr, TLM, AddrSpace); + GV->setSection(Section); + } +} + +void llvm::removeFromUsedLists(Module &M, + std::function ShouldRemove) { + removeFromUsedList(M, "llvm.used", ShouldRemove); + removeFromUsedList(M, "llvm.compiler.used", ShouldRemove); +} + static void setKCFIType(Module &M, Function &F, StringRef MangledType) { if (!M.getModuleFlag("kcfi")) return; Index: llvm/test/CodeGen/AMDGPU/lower-kernel-and-module-lds.ll =================================================================== --- llvm/test/CodeGen/AMDGPU/lower-kernel-and-module-lds.ll +++ llvm/test/CodeGen/AMDGPU/lower-kernel-and-module-lds.ll @@ -15,11 +15,11 @@ ;. ; CHECK: @llvm.amdgcn.module.lds = internal addrspace(3) global %llvm.amdgcn.module.lds.t undef, align 8 -; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 0, i32 0) to i8*)], section "llvm.metadata" ; CHECK: @llvm.amdgcn.kernel.k0.lds = internal addrspace(3) global %llvm.amdgcn.kernel.k0.lds.t undef, align 16 ; CHECK: @llvm.amdgcn.kernel.k1.lds = internal addrspace(3) global %llvm.amdgcn.kernel.k1.lds.t undef, align 16 ; CHECK: @llvm.amdgcn.kernel.k2.lds = internal addrspace(3) global %llvm.amdgcn.kernel.k2.lds.t undef, align 2 ; CHECK: @llvm.amdgcn.kernel.k3.lds = internal addrspace(3) global %llvm.amdgcn.kernel.k3.lds.t undef, align 4 +; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* addrspacecast (i8 addrspace(3)* getelementptr inbounds (%llvm.amdgcn.module.lds.t, %llvm.amdgcn.module.lds.t addrspace(3)* @llvm.amdgcn.module.lds, i32 0, i32 0, i32 0) to i8*)], section "llvm.metadata" ;. define amdgpu_kernel void @k0() #0 { ; CHECK-LABEL: @k0( Index: llvm/test/tools/llvm-reduce/remove-functions-used.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-functions-used.ll @@ -0,0 +1,44 @@ +; RUN: llvm-reduce -abort-on-invalid-reduction --delta-passes=functions --test FileCheck --test-arg --check-prefix=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t.0 +; RUN: FileCheck --implicit-check-not=define --check-prefix=CHECK-FINAL %s < %t.0 + +; Check a case where llvm.used is fully deleted +; RUN: llvm-reduce -abort-on-invalid-reduction --delta-passes=functions --test FileCheck --test-arg --check-prefixes=CHECK-OTHER --test-arg %s --test-arg --input-file %s -o %t.1 +; RUN: FileCheck --implicit-check-not=define --check-prefix=CHECK-REMOVED %s < %t.1 + +@llvm.used = appending global [2 x ptr] [ptr @kept_used, ptr @removed_used ] +@llvm.compiler.used = appending global [2 x ptr] [ptr @kept_compiler_used, ptr @removed_compiler_used ] + + +; CHECK-REMOVED-NOT: @llvm.used +; CHECK-REMOVED-NOT: @llvm.compiler.used + +; CHECK-FINAL: @llvm.used = appending global [1 x ptr] [ptr @kept_used] +; CHECK-FINAL: @llvm.compiler.used = appending global [1 x ptr] [ptr @kept_compiler_used] + + +; CHECK-INTERESTINGNESS: define void @kept_used( +; CHECK-FINAL: define void @kept_used( +define void @kept_used() { + ret void +} + +define void @removed_used() { + ret void +} + +; CHECK-INTERESTINGNESS: define void @kept_compiler_used( +; CHECK-FINAL: define void @kept_compiler_used( +define void @kept_compiler_used() { + ret void +} + +define void @removed_compiler_used() { + ret void +} + +; CHECK-OTHER: define void @foo( +; CHECK-REMOVED: define void @foo( +define void @foo() { + ret void +} + Index: llvm/test/tools/llvm-reduce/remove-global-used.ll =================================================================== --- /dev/null +++ llvm/test/tools/llvm-reduce/remove-global-used.ll @@ -0,0 +1,32 @@ +; RUN: llvm-reduce -abort-on-invalid-reduction --delta-passes=global-variables --test FileCheck --test-arg --check-prefix=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t.0 +; RUN: FileCheck --implicit-check-not=define --check-prefix=CHECK-FINAL %s < %t.0 + +; Check a case where llvm.used is fully deleted +; RUN: llvm-reduce -abort-on-invalid-reduction --delta-passes=global-variables --test FileCheck --test-arg --check-prefixes=CHECK-OTHER --test-arg %s --test-arg --input-file %s -o %t.1 +; RUN: FileCheck --implicit-check-not=define --check-prefix=CHECK-REMOVED %s < %t.1 + +; CHECK-INTERESTINGNESS: @kept_used = global +; CHECK-FINAL: @kept_used = global +@kept_used = global i32 0 + +@removed_used = global i32 1 + +; CHECK-INTERESTINGNESS: @kept_compiler_used = global +; CHECK-FINAL: @kept_compiler_used = global +@kept_compiler_used = global i32 2 + +@removed_compiler_used = global i32 3 + +; CHECK-OTHER: @foo = global +; CHECK-REMOVED: @foo = global +@foo = global i32 4 + +@llvm.used = appending global [2 x ptr] [ptr @kept_used, ptr @removed_used ] +@llvm.compiler.used = appending global [2 x ptr] [ptr @kept_compiler_used, ptr @removed_compiler_used ] + + +; CHECK-REMOVED-NOT: @llvm.used +; CHECK-REMOVED-NOT: @llvm.compiler.used + +; CHECK-FINAL: @llvm.used = appending global [1 x ptr] [ptr @kept_used] +; CHECK-FINAL: @llvm.compiler.used = appending global [1 x ptr] [ptr @kept_compiler_used] Index: llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp =================================================================== --- llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp +++ llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp @@ -16,6 +16,8 @@ #include "Delta.h" #include "Utils.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" #include #include @@ -25,27 +27,31 @@ /// that aren't inside any of the desired Chunks. static void extractFunctionsFromModule(Oracle &O, Module &Program) { // Record all out-of-chunk functions. - std::vector> FuncsToRemove; - copy_if(Program.functions(), std::back_inserter(FuncsToRemove), - [&O](Function &F) { - // Intrinsics don't have function bodies that are useful to - // reduce. Additionally, intrinsics may have additional operand - // constraints. But, do drop intrinsics that are not referenced. - return (!F.isIntrinsic() || F.use_empty()) && !hasAliasUse(F) && - !O.shouldKeep(); - }); + SmallPtrSet FuncsToRemove; + for (Function &F : Program.functions()) { + // Intrinsics don't have function bodies that are useful to + // reduce. Additionally, intrinsics may have additional operand + // constraints. But, do drop intrinsics that are not referenced. + if ((!F.isIntrinsic() || F.use_empty()) && !hasAliasUse(F) && + !O.shouldKeep()) + FuncsToRemove.insert(&F); + } + + removeFromUsedLists(Program, [&FuncsToRemove](Constant *C) { + return FuncsToRemove.count(C); + }); // Then, drop body of each of them. We want to batch this and do nothing else // here so that minimal number of remaining exteranal uses will remain. - for (Function &F : FuncsToRemove) - F.dropAllReferences(); + for (Constant *F : FuncsToRemove) + F->dropAllReferences(); // And finally, we can actually delete them. - for (Function &F : FuncsToRemove) { + for (Constant *F : FuncsToRemove) { // Replace all *still* remaining uses with the default value. - F.replaceAllUsesWith(getDefaultValue(F.getType())); + F->replaceAllUsesWith(getDefaultValue(F->getType())); // And finally, fully drop it. - F.eraseFromParent(); + cast(F)->eraseFromParent(); } } Index: llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp =================================================================== --- llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp +++ llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp @@ -14,6 +14,7 @@ #include "ReduceGlobalVars.h" #include "Utils.h" #include "llvm/IR/Constants.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" #include using namespace llvm; @@ -21,29 +22,33 @@ /// Removes all the GVs that aren't inside the desired Chunks. static void extractGVsFromModule(Oracle &O, Module &Program) { // Get GVs inside desired chunks - std::vector InitGVsToKeep; - for (auto &GV : Program.globals()) - if (O.shouldKeep()) + std::vector InitGVsToKeep; + for (auto &GV : Program.globals()) { + if ((GV.getName() == "llvm.used" || GV.getName() == "llvm.compiler.used") || + O.shouldKeep()) InitGVsToKeep.push_back(&GV); + } // We create a vector first, then convert it to a set, so that we don't have // to pay the cost of rebalancing the set frequently if the order we insert // the elements doesn't match the order they should appear inside the set. - std::set GVsToKeep(InitGVsToKeep.begin(), - InitGVsToKeep.end()); + std::set GVsToKeep(InitGVsToKeep.begin(), InitGVsToKeep.end()); // Delete out-of-chunk GVs and their uses - std::vector ToRemove; + DenseSet ToRemove; std::vector InstToRemove; - for (auto &GV : Program.globals()) + for (auto &GV : Program.globals()) { if (!GVsToKeep.count(&GV)) { for (auto *U : GV.users()) if (auto *Inst = dyn_cast(U)) InstToRemove.push_back(Inst); - GV.replaceAllUsesWith(getDefaultValue(GV.getType())); - ToRemove.push_back(&GV); + ToRemove.insert(&GV); } + } + + removeFromUsedLists(Program, + [&ToRemove](Constant *C) { return ToRemove.count(C); }); // Delete (unique) Instruction uses of unwanted GVs for (Value *V : InstToRemove) { @@ -54,8 +59,10 @@ Inst->eraseFromParent(); } - for (auto *GV : ToRemove) - GV->eraseFromParent(); + for (auto *GV : ToRemove) { + GV->replaceAllUsesWith(getDefaultValue(GV->getType())); + cast(GV)->eraseFromParent(); + } } void llvm::reduceGlobalsDeltaPass(TestRunner &Test) {