[llvm-branch-commits] [llvm] llvm-reduce: Support exotic terminators in instructions-to-return (PR #134794)

Matt Arsenault via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Apr 7 23:48:16 PDT 2025


https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/134794

Use splitBasicBlock and avoid directly dealing with the specific of
how to trim the existing terminators. We just need to deal with
unconditional branch to return.

>From c1e5029de5918b2317f6d01517e5f2d50cf676ad Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 8 Apr 2025 11:16:01 +0700
Subject: [PATCH] llvm-reduce: Support exotic terminators in
 instructions-to-return

Use splitBasicBlock and avoid directly dealing with the specific of
how to trim the existing terminators. We just need to deal with
unconditional branch to return.
---
 .../reduce-values-to-return-callbr.ll         | 54 ++++++++++++++++++
 .../reduce-values-to-return-invoke.ll         | 56 +++++++++++++++++++
 .../deltas/ReduceValuesToReturn.cpp           | 50 ++++-------------
 llvm/tools/llvm-reduce/deltas/Utils.cpp       |  2 +-
 4 files changed, 122 insertions(+), 40 deletions(-)
 create mode 100644 llvm/test/tools/llvm-reduce/reduce-values-to-return-callbr.ll
 create mode 100644 llvm/test/tools/llvm-reduce/reduce-values-to-return-invoke.ll

diff --git a/llvm/test/tools/llvm-reduce/reduce-values-to-return-callbr.ll b/llvm/test/tools/llvm-reduce/reduce-values-to-return-callbr.ll
new file mode 100644
index 0000000000000..da2f225f0405b
--- /dev/null
+++ b/llvm/test/tools/llvm-reduce/reduce-values-to-return-callbr.ll
@@ -0,0 +1,54 @@
+; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=instructions-to-return --test FileCheck --test-arg --check-prefix=INTERESTING --test-arg %s --test-arg --input-file %s -o %t
+; RUN: FileCheck --check-prefix=RESULT %s < %t
+
+ at gv = global i32 0, align 4
+
+; INTERESTING-LABEL: @callbr0(
+; INTERESTING: %load0 = load i32, ptr %arg0
+; INTERESTING: store i32 %load0, ptr @gv
+
+; RESULT-LABEL: define void @callbr0(ptr %arg0) {
+; RESULT: %load0 = load i32, ptr %arg0, align 4
+; RESULT-NEXT: %callbr = callbr i32 asm
+define void @callbr0(ptr %arg0) {
+entry:
+  %load0 = load i32, ptr %arg0
+  %callbr = callbr i32 asm "", "=r,r,!i,!i"(i32 %load0)
+              to label %one [label %two, label %three]
+one:
+  store i32 %load0, ptr @gv
+  ret void
+
+two:
+  store i32 %load0, ptr @gv
+  ret void
+
+three:
+  store i32 %load0, ptr @gv
+  ret void
+}
+
+; INTERESTING-LABEL: @callbr1(
+; INTERESTING: %load0 = load i32, ptr %arg0
+
+; RESULT-LABEL: define i32 @callbr1(ptr %arg0) {
+; RESULT-NEXT: entry:
+; RESULT-NEXT: %load0 = load i32, ptr %arg0
+; RESULT-NEXT: ret i32 %load0
+define void @callbr1(ptr %arg0) {
+entry:
+  %load0 = load i32, ptr %arg0
+  %callbr = callbr i32 asm "", "=r,r,!i,!i"(i32 %load0)
+              to label %one [label %two, label %three]
+one:
+  store i32 %load0, ptr @gv
+  ret void
+
+two:
+  store i32 %load0, ptr @gv
+  ret void
+
+three:
+  store i32 %load0, ptr @gv
+  ret void
+}
diff --git a/llvm/test/tools/llvm-reduce/reduce-values-to-return-invoke.ll b/llvm/test/tools/llvm-reduce/reduce-values-to-return-invoke.ll
new file mode 100644
index 0000000000000..efa1e5377160e
--- /dev/null
+++ b/llvm/test/tools/llvm-reduce/reduce-values-to-return-invoke.ll
@@ -0,0 +1,56 @@
+; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=instructions-to-return --test FileCheck --test-arg --check-prefix=INTERESTING --test-arg %s --test-arg --input-file %s -o %t
+; RUN: FileCheck --check-prefix=RESULT %s < %t
+
+ at gv = global i32 0, align 4
+
+
+define i32 @has_invoke_user(ptr %arg) {
+  %load = load i32, ptr %arg
+  store i32 %load, ptr @gv
+  ret i32 9
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+; INTERESTING-LABEL: @invoker_keep_invoke(
+; INTERESTING: %invoke
+; RESULT:   %invoke = invoke i32 @has_invoke_user(ptr %arg)
+define void @invoker_keep_invoke(ptr %arg) personality ptr @__gxx_personality_v0 {
+bb:
+  %invoke = invoke i32 @has_invoke_user(ptr %arg)
+    to label %bb3 unwind label %bb1
+
+bb1:
+  landingpad { ptr, i32 }
+  catch ptr null
+  ret void
+
+bb3:
+  store i32 %invoke, ptr null
+  ret void
+}
+
+; INTERESTING-LABEL: @invoker_drop_invoke(
+; INTERESTING: %add = add i32
+
+; RESULT-LABEL: define i32 @invoker_drop_invoke(i32 %arg0, ptr %arg1) personality ptr @__gxx_personality_v0 {
+; RESULT-NEXT: bb:
+; RESULT-NEXT: %add = add i32 %arg0, 9
+; RESULT-NEXT: ret i32 %add
+; RESULT-NEXT: }
+define void @invoker_drop_invoke(i32 %arg0, ptr %arg1) personality ptr @__gxx_personality_v0 {
+bb:
+  %add = add i32 %arg0, 9
+  %invoke = invoke i32 @has_invoke_user(ptr %arg1)
+    to label %bb3 unwind label %bb1
+
+bb1:
+  landingpad { ptr, i32 }
+  catch ptr null
+  br label %bb3
+
+bb3:
+  %phi = phi i32 [ %invoke, %bb ], [ %add, %bb1 ]
+  store i32 %phi, ptr null
+  ret void
+}
diff --git a/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp b/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
index 72cfa830579a2..d2adce8381044 100644
--- a/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
+++ b/llvm/tools/llvm-reduce/deltas/ReduceValuesToReturn.cpp
@@ -22,7 +22,6 @@
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/Instructions.h"
-#include "llvm/Transforms/Utils/BasicBlockUtils.h"
 #include "llvm/Transforms/Utils/Cloning.h"
 
 using namespace llvm;
@@ -55,7 +54,7 @@ static void rewriteFuncWithReturnType(Function &OldF, Value *NewRetValue) {
   BasicBlock *NewRetBlock = NewRetI ? NewRetI->getParent() : &EntryBB;
 
   BasicBlock::iterator NewValIt =
-      NewRetI ? NewRetI->getIterator() : EntryBB.end();
+      NewRetI ? std::next(NewRetI->getIterator()) : EntryBB.begin();
 
   Type *OldRetTy = OldFuncTy->getReturnType();
 
@@ -73,28 +72,16 @@ static void rewriteFuncWithReturnType(Function &OldF, Value *NewRetValue) {
     }
   }
 
-  // Now prune any CFG edges we have to deal with.
-  //
-  // Use KeepOneInputPHIs in case the instruction we are using for the return is
-  // that phi.
-  // TODO: Could avoid this with fancier iterator management.
-  for (BasicBlock *Succ : successors(NewRetBlock))
-    Succ->removePredecessor(NewRetBlock, /*KeepOneInputPHIs=*/true);
-
-  // Now delete the tail of this block, in reverse to delete uses before defs.
-  for (Instruction &I : make_early_inc_range(
-           make_range(NewRetBlock->rbegin(), NewValIt.getReverse()))) {
-    Value *Replacement = getDefaultValue(I.getType());
-    I.replaceAllUsesWith(Replacement);
-    I.eraseFromParent();
-  }
+  // If we're returning an instruction, split the basic block so we can let
+  // EliminateUnreachableBlocks cleanup the successors.
+  BasicBlock *TailBB = NewRetBlock->splitBasicBlock(NewValIt);
 
+  // Replace the unconditional branch splitBasicBlock created
+  NewRetBlock->getTerminator()->eraseFromParent();
   ReturnInst::Create(Ctx, NewRetValue, NewRetBlock);
 
-  // TODO: We may be eliminating blocks that were originally unreachable. We
-  // probably ought to only be pruning blocks that became dead directly as a
-  // result of our pruning here.
-  EliminateUnreachableBlocks(OldF);
+  // Now prune any CFG edges we have to deal with.
+  simpleSimplifyCFG(OldF, {TailBB}, /*FoldBlockIntoPredecessor=*/false);
 
   // Drop the incompatible attributes before we copy over to the new function.
   if (OldRetTy != NewRetTy) {
@@ -199,20 +186,6 @@ static bool shouldReplaceNonVoidReturnValue(const BasicBlock &BB,
   return true;
 }
 
-static bool canHandleSuccessors(const BasicBlock &BB) {
-  // TODO: Handle invoke and other exotic terminators
-  if (!isa<ReturnInst, UnreachableInst, BranchInst, SwitchInst>(
-          BB.getTerminator()))
-    return false;
-
-  for (const BasicBlock *Succ : successors(&BB)) {
-    if (!Succ->canSplitPredecessors())
-      return false;
-  }
-
-  return true;
-}
-
 static bool shouldForwardValueToReturn(const BasicBlock &BB, const Value *V,
                                        Type *RetTy) {
   if (!isReallyValidReturnType(V->getType()))
@@ -231,10 +204,9 @@ static bool tryForwardingInstructionsToReturn(
   Type *RetTy = F.getReturnType();
 
   for (BasicBlock &BB : F) {
-    if (!canHandleSuccessors(BB))
-      continue;
-
-    for (Instruction &I : BB) {
+    // Skip the terminator, we can't insert a second terminator to return its
+    // value.
+    for (Instruction &I : make_range(BB.begin(), std::prev(BB.end()))) {
       if (shouldForwardValueToReturn(BB, &I, RetTy) && !O.shouldKeep()) {
         FuncsToReplace.emplace_back(&F, &I);
         return true;
diff --git a/llvm/tools/llvm-reduce/deltas/Utils.cpp b/llvm/tools/llvm-reduce/deltas/Utils.cpp
index a980a0f9fad2f..e2ebeb5eefc3c 100644
--- a/llvm/tools/llvm-reduce/deltas/Utils.cpp
+++ b/llvm/tools/llvm-reduce/deltas/Utils.cpp
@@ -79,7 +79,7 @@ void llvm::simpleSimplifyCFG(Function &F, ArrayRef<BasicBlock *> BBs,
   for (BasicBlock *BB : Unreachable) {
     for (BasicBlock *Successor : successors(&*BB))
       if (Visited.count(Successor))
-        Successor->removePredecessor(&*BB);
+        Successor->removePredecessor(&*BB, /*KeepOneInputPHIs=*/true);
     BB->dropAllReferences();
   }
 



More information about the llvm-branch-commits mailing list