diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp --- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp +++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/EquivalenceClasses.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" @@ -1820,6 +1821,15 @@ Old->replaceUsesWithIf(New, isDirectCall); } +// Returns if the Value is only used by assumes, permitting indirectly via phis. +static bool hasOnlyAssumeUsers(Value *V, SmallPtrSet &Visited) { + return all_of(V->uses(), [&Visited](Use &U) { + if (auto *P = dyn_cast(U.getUser())) + return !Visited.insert(P).second || hasOnlyAssumeUsers(P, Visited); + return isa(U.getUser()); + }); +} + bool LowerTypeTestsModule::lower() { Function *TypeTestFunc = M.getFunction(Intrinsic::getName(Intrinsic::type_test)); @@ -2101,14 +2111,8 @@ // lower it here. We also don't want to rewrite any associated globals // unnecessarily. These will be removed by a subsequent LTT invocation // with the DropTypeTests flag set. - bool OnlyAssumeUses = !CI->use_empty(); - for (const Use &CIU : CI->uses()) { - if (isa(CIU.getUser())) - continue; - OnlyAssumeUses = false; - break; - } - if (OnlyAssumeUses) + SmallPtrSet Visited; + if (hasOnlyAssumeUsers(CI, Visited)) continue; auto TypeIdMDVal = dyn_cast(CI->getArgOperand(1)); diff --git a/llvm/test/Transforms/LowerTypeTests/assume-phi.ll b/llvm/test/Transforms/LowerTypeTests/assume-phi.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/LowerTypeTests/assume-phi.ll @@ -0,0 +1,85 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -passes=lowertypetests -whole-program-visibility %s 2>&1 | FileCheck %s + +; Make sure to skip type tests that feed into assumes perhaps indirectly via phis. +define void @call(ptr %obj, i1 %i) { +; CHECK-LABEL: @call( +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[I:%.*]], label [[BB1:%.*]], label [[BB2:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[OBJ:%.*]], align 8 +; CHECK-NEXT: [[FPTR:%.*]] = load ptr, ptr [[VTABLE]], align 8 +; CHECK-NEXT: call void [[FPTR]](ptr [[OBJ]]) +; CHECK-NEXT: [[P:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"typeid") +; CHECK-NEXT: br label [[FIN:%.*]] +; CHECK: bb2: +; CHECK-NEXT: [[VTABLE2:%.*]] = load ptr, ptr [[OBJ]], align 8 +; CHECK-NEXT: [[FPTR2:%.*]] = load ptr, ptr [[VTABLE2]], align 8 +; CHECK-NEXT: call void [[FPTR2]](ptr [[OBJ]]) +; CHECK-NEXT: [[P2:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE2]], metadata !"typeid") +; CHECK-NEXT: br label [[FIN]] +; CHECK: fin: +; CHECK-NEXT: [[PN:%.*]] = phi i1 [ [[P]], [[BB1]] ], [ [[P2]], [[BB2]] ] +; CHECK-NEXT: call void @llvm.assume(i1 [[PN]]) +; CHECK-NEXT: ret void +; +entry: + br i1 %i, label %bb1, label %bb2 +bb1: + %vtable = load ptr, ptr %obj + %fptr = load ptr, ptr %vtable + call void %fptr(ptr %obj) + %p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid") + br label %fin +bb2: + %vtable2 = load ptr, ptr %obj + %fptr2 = load ptr, ptr %vtable2 + call void %fptr2(ptr %obj) + %p2 = call i1 @llvm.type.test(ptr %vtable2, metadata !"typeid") + br label %fin +fin: + %pn = phi i1 [ %p, %bb1 ], [ %p2, %bb2 ] + call void @llvm.assume(i1 %pn) + ret void +} + +; Make sure we handle phi cycles. +define void @call2(ptr %obj, i1 %i) { +; CHECK-LABEL: @call2( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[BB1:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[Z:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[P:%.*]], [[BB1]] ], [ [[PN:%.*]], [[BB2:%.*]] ] +; CHECK-NEXT: [[VTABLE:%.*]] = load ptr, ptr [[OBJ:%.*]], align 8 +; CHECK-NEXT: [[FPTR:%.*]] = load ptr, ptr [[VTABLE]], align 8 +; CHECK-NEXT: call void [[FPTR]](ptr [[OBJ]]) +; CHECK-NEXT: [[P]] = call i1 @llvm.type.test(ptr [[VTABLE]], metadata !"typeid") +; CHECK-NEXT: br i1 [[I:%.*]], label [[BB2]], label [[BB1]] +; CHECK: bb2: +; CHECK-NEXT: [[PN]] = phi i1 [ [[Z]], [[BB1]] ] +; CHECK-NEXT: br i1 [[I]], label [[FIN:%.*]], label [[BB1]] +; CHECK: fin: +; CHECK-NEXT: call void @llvm.assume(i1 [[PN]]) +; CHECK-NEXT: ret void +; +entry: + br label %bb1 +bb1: + %z = phi i1 [ false, %entry ], [ %p, %bb1 ], [ %pn, %bb2 ] + %vtable = load ptr, ptr %obj + %fptr = load ptr, ptr %vtable + call void %fptr(ptr %obj) + %p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid") + br i1 %i, label %bb2, label %bb1 +bb2: + %pn = phi i1 [ %z, %bb1 ] + br i1 %i, label %fin, label %bb1 +fin: + call void @llvm.assume(i1 %pn) + ret void +} + +declare i1 @llvm.type.test(ptr, metadata) +declare void @llvm.assume(i1) + +!0 = !{i32 0, !"typeid"} diff --git a/llvm/test/Transforms/LowerTypeTests/blockaddress-2.ll b/llvm/test/Transforms/LowerTypeTests/blockaddress-2.ll --- a/llvm/test/Transforms/LowerTypeTests/blockaddress-2.ll +++ b/llvm/test/Transforms/LowerTypeTests/blockaddress-2.ll @@ -16,8 +16,11 @@ ret void } +declare void @use(i1) + define internal fastcc void @do_f() unnamed_addr !type !2 { %1 = tail call i1 @llvm.type.test(i8* undef, metadata !"_ZTSFiP4fileP3uioP5ucrediP6threadE"), !nosanitize !3 + call void @use(i1 %1) ret void } diff --git a/llvm/test/Transforms/LowerTypeTests/blockaddress.ll b/llvm/test/Transforms/LowerTypeTests/blockaddress.ll --- a/llvm/test/Transforms/LowerTypeTests/blockaddress.ll +++ b/llvm/test/Transforms/LowerTypeTests/blockaddress.ll @@ -9,9 +9,12 @@ target triple = "x86_64-unknown-linux" +declare void @use(i1) + define void @f1() { entry: %0 = call i1 @llvm.type.test(i8* bitcast (i8* ()* @f2 to i8*), metadata !"_ZTSFvP3bioE") + call void @use(i1 %0) ret void } diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll b/llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll --- a/llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll +++ b/llvm/test/Transforms/LowerTypeTests/cfi-direct-call1.ll @@ -21,6 +21,7 @@ declare !type !3 !type !4 extern_weak i32 @extern_weak() declare !type !3 !type !4 i32 @extern_decl() declare i1 @llvm.type.test(i8*, metadata) +declare void @use(i1) define hidden i32 @main(i32 %argc) { entry: @@ -29,10 +30,12 @@ %fptr2 = select i1 %cmp.i, i32 ()* @extern_weak, i32 ()* @extern_decl %0 = bitcast i32 ()* %fptr1 to i8* %1 = tail call i1 @llvm.type.test(i8* nonnull %0, metadata !"_ZTSFivE") + call void @use(i1 %1) %call2 = tail call i32 %fptr1() %2 = bitcast i32 ()* %fptr2 to i8* %3 = tail call i1 @llvm.type.test(i8* %2, metadata !"_ZTSFivE") + call void @use(i1 %3) %call4 = tail call i32 %fptr2() %call5 = tail call i32 @extern_decl() diff --git a/llvm/test/Transforms/LowerTypeTests/layout.ll b/llvm/test/Transforms/LowerTypeTests/layout.ll --- a/llvm/test/Transforms/LowerTypeTests/layout.ll +++ b/llvm/test/Transforms/LowerTypeTests/layout.ll @@ -18,10 +18,14 @@ !2 = !{i32 0, !"typeid3"} declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone +declare void @use(i1) define void @foo() { %x = call i1 @llvm.type.test(i8* undef, metadata !"typeid1") + call void @use(i1 %x) %y = call i1 @llvm.type.test(i8* undef, metadata !"typeid2") + call void @use(i1 %y) %z = call i1 @llvm.type.test(i8* undef, metadata !"typeid3") + call void @use(i1 %z) ret void } diff --git a/llvm/test/Transforms/LowerTypeTests/simple.ll b/llvm/test/Transforms/LowerTypeTests/simple.ll --- a/llvm/test/Transforms/LowerTypeTests/simple.ll +++ b/llvm/test/Transforms/LowerTypeTests/simple.ll @@ -44,6 +44,7 @@ ; CHECK: @bits.{{[0-9]*}} = private alias i8, getelementptr inbounds ([68 x i8], [68 x i8]* [[BA]], i32 0, i32 0) declare i1 @llvm.type.test(i8* %ptr, metadata %bitset) nounwind readnone +declare void @use(i1) ; CHECK: @foo(i32* [[A0:%[^ ]*]]) define i1 @foo(i32* %p) { @@ -66,9 +67,11 @@ ; CHECK: [[R16:%[^ ]*]] = phi i1 [ false, {{%[^ ]*}} ], [ [[R11]], {{%[^ ]*}} ] %x = call i1 @llvm.type.test(i8* %pi8, metadata !"typeid1") + call void @use(i1 %x) ; CHECK-NOT: llvm.type.test %y = call i1 @llvm.type.test(i8* %pi8, metadata !"typeid1") + call void @use(i1 %y) ; CHECK: ret i1 [[R16]] ret i1 %x @@ -85,6 +88,7 @@ ; CHECK: [[S5:%[^ ]*]] = or i32 [[S3]], [[S4]] ; CHECK: [[S6:%[^ ]*]] = icmp ule i32 [[S5]], 1 %x = call i1 @llvm.type.test(i8* %pi8, metadata !"typeid2") + call void @use(i1 %x) ; CHECK: ret i1 [[S6]] ret i1 %x