[ArgPromotion] Remove dead code produced by removing dead arguments
authorJeff Byrnes <jeffrey.byrnes@amd.com>
Wed, 15 Mar 2023 19:11:20 +0000 (12:11 -0700)
committerJeff Byrnes <jeffrey.byrnes@amd.com>
Thu, 23 Mar 2023 16:43:35 +0000 (09:43 -0700)
ArgPromotion currently produces phantom / dead loads. A good example of this is store-into-inself.ll. First, ArgPromo finds the promotable argument %p in @l. Then it inserts a load of %p in the caller, and passes instead the loaded value / transforms the function body. PromoteMem2Reg is able to optimize out the entire function body, resulting in an unused argument. In a subsequent ArgPromotion pass, it removes the dead argument, resulting in a dead load in the caller. These dead loads may reduce effectiveness of other transformations (e.g. SimplifyCFG, MergedLoadStoreMotion).

This patch removes loads and geps that are made dead in the caller after removal of dead args.

Differential Revision: https://reviews.llvm.org/D146327

llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
llvm/test/Transforms/ArgumentPromotion/propagate-remove-dead-args.ll
llvm/test/Transforms/ArgumentPromotion/store-into-inself.ll
llvm/test/Transforms/PhaseOrdering/dce-after-argument-promotion-loads.ll

index dd1a3b7..3b1a174 100644 (file)
@@ -67,6 +67,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/PromoteMemToReg.h"
 #include <algorithm>
 #include <cassert>
@@ -220,6 +221,8 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
   // pass in the loaded pointers.
   SmallVector<Value *, 16> Args;
   const DataLayout &DL = F->getParent()->getDataLayout();
+  SmallVector<WeakTrackingVH, 16> DeadArgs;
+
   while (!F->use_empty()) {
     CallBase &CB = cast<CallBase>(*F->user_back());
     assert(CB.getCalledFunction() == F);
@@ -255,6 +258,9 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
           Args.push_back(LI);
           ArgAttrVec.push_back(AttributeSet());
         }
+      } else {
+        assert(ArgsToPromote.count(&*I) && I->use_empty());
+        DeadArgs.emplace_back(AI->get());
       }
     }
 
@@ -297,6 +303,8 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
     CB.eraseFromParent();
   }
 
+  RecursivelyDeleteTriviallyDeadInstructionsPermissive(DeadArgs);
+
   // Since we have now created the new function, splice the body of the old
   // function right into the new function, leaving the old rotting hulk of the
   // function empty.
index cc1f7fb..4176a8a 100644 (file)
@@ -18,18 +18,12 @@ entry:
 
 define internal void @parent(ptr %this, ptr %p1, ptr %p2) {
 ; CHECK-LABEL: define internal void @parent
-; CHECK-SAME: (ptr [[THIS:%.*]], ptr [[P1:%.*]], ptr [[P2:%.*]]) {
+; CHECK-SAME: (ptr [[P1:%.*]], ptr [[P2:%.*]]) {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[SRC_ELEMENT_OP_0:%.*]] = getelementptr ptr, ptr [[THIS]], i64 0
-; CHECK-NEXT:    [[LOAD0:%.*]] = load ptr, ptr [[SRC_ELEMENT_OP_0]], align 8
 ; CHECK-NEXT:    [[P2_VAL2:%.*]] = load half, ptr [[P2]], align 2
 ; CHECK-NEXT:    call void @child(ptr [[P1]], half [[P2_VAL2]])
-; CHECK-NEXT:    [[SRC_ELEMENT_OP_1:%.*]] = getelementptr ptr, ptr [[THIS]], i64 1
-; CHECK-NEXT:    [[LOAD1:%.*]] = load ptr, ptr [[SRC_ELEMENT_OP_1]], align 8
 ; CHECK-NEXT:    [[P2_VAL1:%.*]] = load half, ptr [[P2]], align 2
 ; CHECK-NEXT:    call void @child(ptr [[P1]], half [[P2_VAL1]])
-; CHECK-NEXT:    [[SRC_ELEMENT_OP_2:%.*]] = getelementptr ptr, ptr [[THIS]], i64 2
-; CHECK-NEXT:    [[LOAD2:%.*]] = load ptr, ptr [[SRC_ELEMENT_OP_2]], align 8
 ; CHECK-NEXT:    [[P2_VAL:%.*]] = load half, ptr [[P2]], align 2
 ; CHECK-NEXT:    call void @child(ptr [[P1]], half [[P2_VAL]])
 ; CHECK-NEXT:    ret void
@@ -50,10 +44,9 @@ entry:
 define  void @grandparent() {
 ; CHECK-LABEL: define void @grandparent() {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[F:%.*]] = alloca [[PTR_STRUCT:%.*]], align 8
 ; CHECK-NEXT:    [[XPTR:%.*]] = alloca i32, align 4
 ; CHECK-NEXT:    [[YPTR:%.*]] = alloca i32, align 4
-; CHECK-NEXT:    call void @parent(ptr [[F]], ptr [[XPTR]], ptr [[YPTR]])
+; CHECK-NEXT:    call void @parent(ptr [[XPTR]], ptr [[YPTR]])
 ; CHECK-NEXT:    ret void
 ;
 entry:
index 7d70990..be94af6 100644 (file)
@@ -83,7 +83,6 @@ define i32 @main() nounwind  {
 ; CHECK-NEXT:    call void @g(ptr byval(ptr) align 4 [[S]]) #[[ATTR0]]
 ; CHECK-NEXT:    call void @h(ptr byval(ptr) align 4 [[S]]) #[[ATTR0]]
 ; CHECK-NEXT:    call void @k(ptr byval(ptr) align 4 [[S]]) #[[ATTR0]]
-; CHECK-NEXT:    [[S_VAL:%.*]] = load ptr, ptr [[S]], align 8
 ; CHECK-NEXT:    call void @l() #[[ATTR0]]
 ; CHECK-NEXT:    ret i32 0
 ;
index 2bdd42b..2fe8f39 100644 (file)
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
 ; RUN: opt -O3 -S < %s | FileCheck %s
 
 ; Arg promotion eliminates the struct argument, and eliminates dead arguments, but introduces and leaves dead loads of the eliminated dead arg in callers
@@ -13,24 +13,17 @@ entry:
 }
 
 define ptr @parent(ptr align 8 dereferenceable(72) %f, i16 %val1, i16 %val2, i32 %val3) align 2 {
-; CHECK-LABEL: define {{[^@]+}}@parent
+; CHECK-LABEL: define nonnull ptr @parent
 ; CHECK-SAME: (ptr readonly returned align 8 dereferenceable(72) [[F:%.*]], i16 [[VAL1:%.*]], i16 [[VAL2:%.*]], i32 [[VAL3:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] align 2 {
 ; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr i8, ptr [[F]], i64 64
+; CHECK-NEXT:    [[F_VAL:%.*]] = load ptr, ptr [[TMP0]], align 8
 ; CHECK-NEXT:    [[CMP_NOT_NOT_I:%.*]] = icmp eq i32 [[VAL3]], 0
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [[STRUCT_SS:%.*]], ptr [[F]], i64 0, i32 8
-; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8
-; CHECK-NEXT:    br i1 [[CMP_NOT_NOT_I]], label [[IF_THEN_I:%.*]], label [[IF_ELSE_I:%.*]]
-; CHECK:       if.then.i:
-; CHECK-NEXT:    store i16 [[VAL1]], ptr [[TMP1]], align 2
-; CHECK-NEXT:    [[ADD_PTR_I_I_I_I_I:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 16
-; CHECK-NEXT:    br label [[BADCHILD_EXIT:%.*]]
-; CHECK:       if.else.i:
-; CHECK-NEXT:    [[ADD_PTR_I_I_I_I7_I:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 16
-; CHECK-NEXT:    store i16 [[VAL1]], ptr [[ADD_PTR_I_I_I_I7_I]], align 2
-; CHECK-NEXT:    br label [[BADCHILD_EXIT]]
-; CHECK:       badChild.exit:
-; CHECK-NEXT:    [[DOTSINK_I:%.*]] = phi ptr [ [[TMP1]], [[IF_ELSE_I]] ], [ [[ADD_PTR_I_I_I_I_I]], [[IF_THEN_I]] ]
-; CHECK-NEXT:    store i16 [[VAL2]], ptr [[DOTSINK_I]], align 2
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[CMP_NOT_NOT_I]], i16 [[VAL1]], i16 [[VAL2]]
+; CHECK-NEXT:    [[SPEC_SELECT2_I:%.*]] = select i1 [[CMP_NOT_NOT_I]], i16 [[VAL2]], i16 [[VAL1]]
+; CHECK-NEXT:    store i16 [[SPEC_SELECT_I]], ptr [[F_VAL]], align 2
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[F_VAL]], i64 16
+; CHECK-NEXT:    store i16 [[SPEC_SELECT2_I]], ptr [[TMP1]], align 2
 ; CHECK-NEXT:    ret ptr [[F]]
 ;
 entry: