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
#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>
// 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);
Args.push_back(LI);
ArgAttrVec.push_back(AttributeSet());
}
+ } else {
+ assert(ArgsToPromote.count(&*I) && I->use_empty());
+ DeadArgs.emplace_back(AI->get());
}
}
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.
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
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:
; 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
;
-; 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
}
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: