[llvm] aa9bdd5 - llvm-reduce: Fix invalid reductions with llvm.used

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 14 12:06:27 PST 2022


Author: Matt Arsenault
Date: 2022-12-14T15:06:22-05:00
New Revision: aa9bdd50c25aa6a6dc58f2af397bdfcfad417244

URL: https://github.com/llvm/llvm-project/commit/aa9bdd50c25aa6a6dc58f2af397bdfcfad417244
DIFF: https://github.com/llvm/llvm-project/commit/aa9bdd50c25aa6a6dc58f2af397bdfcfad417244.diff

LOG: llvm-reduce: Fix invalid reductions with llvm.used

Fixes issue 59413.

Added: 
    llvm/test/tools/llvm-reduce/remove-functions-used.ll
    llvm/test/tools/llvm-reduce/remove-global-used.ll

Modified: 
    llvm/include/llvm/Transforms/Utils/ModuleUtils.h
    llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
    llvm/lib/Transforms/Utils/ModuleUtils.cpp
    llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp
    llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
index a307a3a7e7669..47a618647f1d9 100644
--- a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_TRANSFORMS_UTILS_MODULEUTILS_H
 #define LLVM_TRANSFORMS_UTILS_MODULEUTILS_H
 
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Alignment.h"
 #include "llvm/Support/MemoryBufferRef.h"
@@ -80,6 +81,12 @@ void appendToUsed(Module &M, ArrayRef<GlobalValue *> Values);
 /// Adds global values to the llvm.compiler.used list.
 void appendToCompilerUsed(Module &M, ArrayRef<GlobalValue *> 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,
+                         function_ref<bool(Constant *)> ShouldRemove);
+
 /// Filter out potentially dead comdat functions where other entries keep the
 /// entire comdat group alive.
 ///

diff  --git a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
index 3b84f30b50d15..2570a9123ffdd 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerModuleLDSPass.cpp
@@ -186,54 +186,20 @@ bool isKernelLDS(const Function *F) {
 
 class AMDGPULowerModuleLDS : public ModulePass {
 
-  static void removeFromUsedList(Module &M, StringRef Name,
-                                 SmallPtrSetImpl<Constant *> &ToRemove) {
-    GlobalVariable *GV = M.getNamedGlobal(Name);
-    if (!GV || ToRemove.empty()) {
-      return;
-    }
-
-    SmallVector<Constant *, 16> Init;
-    auto *CA = cast<ConstantArray>(GV->getInitializer());
-    for (auto &Op : CA->operands()) {
-      // ModuleUtils::appendToUsed only inserts Constants
-      Constant *C = cast<Constant>(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<GlobalVariable *> &LocalVars) {
+  static void
+  removeLocalVarsFromUsedLists(Module &M,
+                               const DenseSet<GlobalVariable *> &LocalVars) {
     // The verifier rejects used lists containing an inttoptr of a constant
     // so remove the variables from these lists before replaceAllUsesWith
+    SmallPtrSet<Constant *, 8> LocalVarsSet;
+    for (GlobalVariable *LocalVar : LocalVars)
+      LocalVarsSet.insert(cast<Constant>(LocalVar->stripPointerCasts()));
+
+    removeFromUsedLists(
+        M, [&LocalVarsSet](Constant *C) { return LocalVarsSet.count(C); });
 
-    SmallPtrSet<Constant *, 32> LocalVarsSet;
     for (GlobalVariable *LocalVar : LocalVars)
-      if (Constant *C = dyn_cast<Constant>(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 @@ class AMDGPULowerModuleLDS : public ModulePass {
                                    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 @@ class AMDGPULowerModuleLDS : public ModulePass {
           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

diff  --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index fbdc3fd9f9cc2..a1e13708163ef 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -74,35 +74,35 @@ void llvm::appendToGlobalDtors(Module &M, Function *F, int Priority, Constant *D
   appendToGlobalArray("llvm.global_dtors", M, F, Priority, Data);
 }
 
+static void collectUsedGlobals(GlobalVariable *GV,
+                               SmallSetVector<Constant *, 16> &Init) {
+  if (!GV || !GV->hasInitializer())
+    return;
+
+  auto *CA = cast<ConstantArray>(GV->getInitializer());
+  for (Use &Op : CA->operands())
+    Init.insert(cast<Constant>(Op));
+}
+
 static void appendToUsedList(Module &M, StringRef Name, ArrayRef<GlobalValue *> Values) {
   GlobalVariable *GV = M.getGlobalVariable(Name);
-  SmallPtrSet<Constant *, 16> InitAsSet;
-  SmallVector<Constant *, 16> Init;
-  if (GV) {
-    if (GV->hasInitializer()) {
-      auto *CA = cast<ConstantArray>(GV->getInitializer());
-      for (auto &Op : CA->operands()) {
-        Constant *C = cast_or_null<Constant>(Op);
-        if (InitAsSet.insert(C).second)
-          Init.push_back(C);
-      }
-    }
+
+  SmallSetVector<Constant *, 16> 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,42 @@ void llvm::appendToCompilerUsed(Module &M, ArrayRef<GlobalValue *> Values) {
   appendToUsedList(M, "llvm.compiler.used", Values);
 }
 
+static void removeFromUsedList(Module &M, StringRef Name,
+                               function_ref<bool(Constant *)> ShouldRemove) {
+  GlobalVariable *GV = M.getNamedGlobal(Name);
+  if (!GV)
+    return;
+
+  SmallSetVector<Constant *, 16> Init;
+  collectUsedGlobals(GV, Init);
+
+  Type *ArrayEltTy = cast<ArrayType>(GV->getValueType())->getElementType();
+
+  SmallVector<Constant *, 16> NewInit;
+  for (Constant *MaybeRemoved : Init) {
+    if (!ShouldRemove(MaybeRemoved->stripPointerCasts()))
+      NewInit.push_back(MaybeRemoved);
+  }
+
+  if (!NewInit.empty()) {
+    ArrayType *ATy = ArrayType::get(ArrayEltTy, NewInit.size());
+    GlobalVariable *NewGV =
+        new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
+                           ConstantArray::get(ATy, NewInit), "", GV,
+                           GV->getThreadLocalMode(), GV->getAddressSpace());
+    NewGV->setSection(GV->getSection());
+    NewGV->takeName(GV);
+  }
+
+  GV->eraseFromParent();
+}
+
+void llvm::removeFromUsedLists(Module &M,
+                               function_ref<bool(Constant *)> 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;

diff  --git a/llvm/test/tools/llvm-reduce/remove-functions-used.ll b/llvm/test/tools/llvm-reduce/remove-functions-used.ll
new file mode 100644
index 0000000000000..afe59491c2a51
--- /dev/null
+++ b/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
+
+ at llvm.used = appending global [2 x ptr] [ptr @kept_used, ptr @removed_used ]
+ at 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
+}
+

diff  --git a/llvm/test/tools/llvm-reduce/remove-global-used.ll b/llvm/test/tools/llvm-reduce/remove-global-used.ll
new file mode 100644
index 0000000000000..e2e31632e4f20
--- /dev/null
+++ b/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
+ at kept_used = global i32 0
+
+ at removed_used = global i32 1
+
+; CHECK-INTERESTINGNESS: @kept_compiler_used = global
+; CHECK-FINAL: @kept_compiler_used = global
+ at kept_compiler_used = global i32 2
+
+ at removed_compiler_used = global i32 3
+
+; CHECK-OTHER: @foo = global
+; CHECK-REMOVED: @foo = global
+ at foo = global i32 4
+
+ at llvm.used = appending global [2 x ptr] [ptr @kept_used, ptr @removed_used ]
+ at 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]

diff  --git a/llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp b/llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp
index 61f7af924cb19..ef208ad347019 100644
--- a/llvm/tools/llvm-reduce/deltas/ReduceFunctions.cpp
+++ b/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 <iterator>
 #include <vector>
 
@@ -25,27 +27,31 @@ using namespace llvm;
 /// that aren't inside any of the desired Chunks.
 static void extractFunctionsFromModule(Oracle &O, Module &Program) {
   // Record all out-of-chunk functions.
-  std::vector<std::reference_wrapper<Function>> 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<Constant *, 8> 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<Function>(F)->eraseFromParent();
   }
 }
 

diff  --git a/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp b/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp
index bc45f69361cb4..69d7391eb364f 100644
--- a/llvm/tools/llvm-reduce/deltas/ReduceGlobalVars.cpp
+++ b/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 <set>
 
 using namespace llvm;
@@ -21,29 +22,33 @@ using namespace llvm;
 /// 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<GlobalVariable *> InitGVsToKeep;
-  for (auto &GV : Program.globals())
-    if (O.shouldKeep())
+  std::vector<Constant *> 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<GlobalVariable *> GVsToKeep(InitGVsToKeep.begin(),
-                                       InitGVsToKeep.end());
+  std::set<Constant *> GVsToKeep(InitGVsToKeep.begin(), InitGVsToKeep.end());
 
   // Delete out-of-chunk GVs and their uses
-  std::vector<GlobalVariable *> ToRemove;
+  DenseSet<Constant *> ToRemove;
   std::vector<WeakVH> 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<Instruction>(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 @@ static void extractGVsFromModule(Oracle &O, Module &Program) {
     Inst->eraseFromParent();
   }
 
-  for (auto *GV : ToRemove)
-    GV->eraseFromParent();
+  for (auto *GV : ToRemove) {
+    GV->replaceAllUsesWith(getDefaultValue(GV->getType()));
+    cast<GlobalVariable>(GV)->eraseFromParent();
+  }
 }
 
 void llvm::reduceGlobalsDeltaPass(TestRunner &Test) {


        


More information about the llvm-commits mailing list