diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp --- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp +++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp @@ -2271,6 +2271,24 @@ ThreadEdge(BB, PredsToFactor, SuccBB); } +namespace { + +/// isUsedBy - Returns 'true' if the instruction is used based on the predicate. +template +bool isUsedBy(const Instruction *I, UnaryPredicate Predicate) { + for (const Use &U : I->uses()) { + const Instruction *User = cast(U.getUser()); + if (Predicate(User)) + return true; + if (isa(User)) + return isUsedBy(User, Predicate); + } + + return false; +} + +} // end anonymous namespace + /// TryThreadEdge - Thread an edge if it's safe and profitable to do so. bool JumpThreadingPass::TryThreadEdge( BasicBlock *BB, const SmallVectorImpl &PredBBs, @@ -2304,6 +2322,44 @@ return false; } + // If the PHI node has a constant value and the PHI is used by an is.constant + // intrinsic, then don't thread the edge. Otherwise, we could end up in a + // situation where the intrinsic will resolve to 'true' when it should resolve + // to 'false'. For example, %z should be 'false' in this: + // + // A: + // %v = phi i32 [ 3, %X ], [ %v, %Y ] + // %z = call i1 @llvm.is.constant(i32 %v) + // + // but when jump threading happens we get two different values: + // + // A.thread: + // %v.thread = phi i32 [ 3, %X ] + // %z.thread = call i1 @llvm.is.constant(i32 %v.thread) + // br ... + // + // A: + // %v = phi i32 [ %v, %Y ] + // %z = call i1 @llvm.is.constant(i32 %v) + // br ... + // + // One consequence, if %z and %z.thread are used in a PHI, we cannot perform + // DCE on any dead paths. This is an issue if the programmer relies on DCE + // occurring. + for (PHINode &PN : BB->phis()) { + if (llvm::all_of(PN.incoming_values(), + [](const Value *V) { return !isa(V); })) + // We are only concerned about PHI nodes with a constant incoming value. + continue; + + for (Use &U : PN.uses()) + if (isUsedBy(cast(U.getUser()), [](const Instruction *I) { + using namespace PatternMatch; + return match(I, m_Intrinsic()); + })) + return false; + } + ThreadEdge(BB, PredBBs, SuccBB); return true; } diff --git a/llvm/test/Transforms/JumpThreading/is_constant.ll b/llvm/test/Transforms/JumpThreading/is_constant.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/JumpThreading/is_constant.ll @@ -0,0 +1,302 @@ +; RUN: opt -jump-threading -S -verify < %s | FileCheck %s + +; This is reduced from the Linux net/ipv4/tcp.c file built with ASAN. We +; don't want jump threading to split up a basic block which has a PHI node with +; at least one constant incoming value, whose value is used by an is.constant +; intrinsic with non-local uses. It could lead to later passes no DCE'ing +; invalid paths. E.g. the "__bad_copy_*" functions should be DCE'ed, because +; the llvm.is.constant call should return "false". + +; CHECK-LABEL: define i32 @do_tcp_getsockopt( +; CHECK-LABEL: if.end616: +; CHECK-NOT: %len.0 = phi i32 [ %conv592, %if.end603 ] +; CHECK-NEXT: %len.0 = phi i32 [ 24, %if.then607 ], [ %conv592, %if.end603 ] +; CHECK: %tmp8.i.i222 = call i1 @llvm.is.constant.i64(i64 %conv617) +; CHECK-LABEL: if.then.i.i223: +; CHECK-NEXT: br i1 %tmp8.i.i222, label %if.else.i.i225, label %if.then7.i.i224 +; CHECK-LABEL: if.else.i.i225: +; CHECK-NEXT: call void @__bad_copy_to() +; CHECK-LABEL: if.then.i.i49: +; CHECK-NEXT: br i1 %tmp8.i.i222, label %if.else.i.i51, label %if.then7.i.i50 +; CHECK-LABEL: if.else.i.i51: +; CHECK-NEXT: call void @__bad_copy_from() + +%struct.sock = type { i32, i64 } +%struct.tcp_zerocopy_receive = type { i64, i32, i32, i32, i32 } + +@.str.17 = external hidden unnamed_addr constant [30 x i8], align 1 + +define i32 @do_tcp_getsockopt(%struct.sock* %sk, i32 %optname, i8* %optval, i32* %optlen) { +entry: + %val = alloca i32, align 4 + %zc = alloca %struct.tcp_zerocopy_receive, align 8 + %tmp8 = bitcast i32* %val to i8* + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %tmp8) + %tmp12 = call i64 @llvm.read_register.i64(metadata !0) + %tmp14 = call { i32*, i64, i64 } asm sideeffect "", "={ax},={rdx},={rsp},0,i,2,~{dirflag},~{fpsr},~{flags}"(i32* %optlen, i64 4, i64 %tmp12) + %asmresult = extractvalue { i32*, i64, i64 } %tmp14, 0 + %asmresult3 = extractvalue { i32*, i64, i64 } %tmp14, 1 + %asmresult4 = extractvalue { i32*, i64, i64 } %tmp14, 2 + %tmp15 = ptrtoint i32* %asmresult to i64 + call void @llvm.write_register.i64(metadata !0, i64 %asmresult4) + %conv = trunc i64 %asmresult3 to i32 + %sext.mask = and i64 %tmp15, 4294967295 + %tobool = icmp eq i64 %sext.mask, 0 + br i1 %tobool, label %if.end11, label %cleanup662, !prof !2, !misexpect !3 + +if.end11: ; preds = %entry + %cmp = icmp ult i32 %conv, 4 + %conv. = select i1 %cmp, i32 %conv, i32 4 + br label %sw.bb586 + +sw.bb586: ; preds = %if.end11 + %tmp554 = bitcast %struct.tcp_zerocopy_receive* %zc to i8* + call void @llvm.lifetime.start.p0i8(i64 24, i8* nonnull %tmp554) + %tmp558 = call i64 @llvm.read_register.i64(metadata !0) + %tmp561 = call { i32*, i64, i64 } asm sideeffect "", "={ax},={rdx},={rsp},0,i,2,~{dirflag},~{fpsr},~{flags}"(i32* %optlen, i64 4, i64 %tmp558) + %asmresult589 = extractvalue { i32*, i64, i64 } %tmp561, 0 + %asmresult590 = extractvalue { i32*, i64, i64 } %tmp561, 1 + %asmresult591 = extractvalue { i32*, i64, i64 } %tmp561, 2 + %tmp562 = ptrtoint i32* %asmresult589 to i64 + call void @llvm.write_register.i64(metadata !0, i64 %asmresult591) + %conv592 = trunc i64 %asmresult590 to i32 + %sext.mask1 = and i64 %tmp562, 4294967295 + %tobool596 = icmp eq i64 %sext.mask1, 0 + br i1 %tobool596, label %if.end598, label %cleanup644, !prof !2, !misexpect !3 + +if.end598: ; preds = %sw.bb586 + %sext = shl i64 %asmresult590, 32 + %conv599 = ashr exact i64 %sext, 32 + %cmp600 = icmp ult i64 %conv599, 12 + br i1 %cmp600, label %cleanup644, label %if.end603 + +if.end603: ; preds = %if.end598 + %cmp605 = icmp ugt i64 %conv599, 24 + br i1 %cmp605, label %if.then607, label %if.end616 + +if.then607: ; preds = %if.end603 + %tmp576 = call i32 asm sideeffect "", "={ax},0,{cx},~{ebx},~{dirflag},~{fpsr},~{flags}"(i32 24, i32* %optlen) + %tobool613 = icmp eq i32 %tmp576, 0 + br i1 %tobool613, label %if.end616, label %cleanup644, !prof !2, !misexpect !3 + +if.end616: ; preds = %if.then607, %if.end603 + %len.0 = phi i32 [ 24, %if.then607 ], [ %conv592, %if.end603 ] + %conv617 = sext i32 %len.0 to i64 + %cmp3.i.i220 = icmp ugt i32 %len.0, 24 + %tmp8.i.i222 = call i1 @llvm.is.constant.i64(i64 %conv617) + br i1 %cmp3.i.i220, label %if.then.i.i223, label %if.end12.i.i226, !prof !12, !misexpect !3 + +if.then.i.i223: ; preds = %if.end616 + br i1 %tmp8.i.i222, label %if.else.i.i225, label %if.then7.i.i224, !prof !13 + +if.then7.i.i224: ; preds = %if.then.i.i223 + call void asm sideeffect "", "i,i,i,i,~{dirflag},~{fpsr},~{flags}"(i8* getelementptr inbounds ([30 x i8], [30 x i8]* @.str.17, i64 0, i64 0), i32 127, i32 2305, i64 12) + br label %copy_from_user.exit + +if.else.i.i225: ; preds = %if.then.i.i223 + call void @__bad_copy_to() + br label %copy_from_user.exit + +if.end12.i.i226: ; preds = %if.end616 + br i1 %tmp8.i.i222, label %if.then.i229, label %if.then.i.i.i227 + +if.then.i.i.i227: ; preds = %if.end12.i.i226 + call void @__check_object_size(i8* nonnull %tmp554, i64 %conv617, i1 zeroext false) + br label %if.then.i229 + +if.then.i229: ; preds = %if.then.i.i.i227, %if.end12.i.i226 + %call2.i228 = call i64 @_copy_from_user(i8* nonnull %tmp554, i8* %optval, i64 %conv617) + br label %copy_from_user.exit + +copy_from_user.exit: ; preds = %if.then.i229, %if.else.i.i225, %if.then7.i.i224 + %n.addr.0.i230 = phi i64 [ %call2.i228, %if.then.i229 ], [ %conv617, %if.then7.i.i224 ], [ %conv617, %if.else.i.i225 ] + %tobool619 = icmp eq i64 %n.addr.0.i230, 0 + br i1 %tobool619, label %if.end621, label %cleanup644 + +if.end621: ; preds = %copy_from_user.exit + %call622 = call fastcc i32 @tcp_zerocopy_receive() + switch i32 %len.0, label %zerocopy_rcv_out [ + i32 24, label %zerocopy_rcv_sk_err + i32 20, label %zerocopy_rcv_inq + ] + +zerocopy_rcv_sk_err: ; preds = %if.end621 + %tobool631 = icmp eq i32 %call622, 0 + br i1 %tobool631, label %if.then632, label %zerocopy_rcv_inq + +if.then632: ; preds = %zerocopy_rcv_sk_err + %sk_err.i = getelementptr inbounds %struct.sock, %struct.sock* %sk, i64 0, i32 0 + %tmp3.i = load i32, i32* %sk_err.i, align 8 + %tobool.i231 = icmp eq i32 %tmp3.i, 0 + br i1 %tobool.i231, label %sock_error.exit, label %if.end.i, !prof !2, !misexpect !31 + +if.end.i: ; preds = %if.then632 + %tmp7.i = call i32 asm sideeffect "", "=r,=*m,0,*m,~{memory},~{cc},~{dirflag},~{fpsr},~{flags}"(i32* %sk_err.i, i32 0, i32* %sk_err.i) + %sub.i = sub i32 0, %tmp7.i + br label %sock_error.exit + +sock_error.exit: ; preds = %if.end.i, %if.then632 + %retval.0.i = phi i32 [ %sub.i, %if.end.i ], [ 0, %if.then632 ] + %err634 = getelementptr inbounds %struct.tcp_zerocopy_receive, %struct.tcp_zerocopy_receive* %zc, i64 0, i32 4 + store i32 %retval.0.i, i32* %err634, align 4 + br label %zerocopy_rcv_inq + +zerocopy_rcv_inq: ; preds = %sock_error.exit, %zerocopy_rcv_sk_err, %if.end621 + %0 = getelementptr inbounds %struct.sock, %struct.sock* %sk, i64 0, i32 0 + %tmp11.i.i = load volatile i32, i32* %0, align 4 + %1 = getelementptr inbounds %struct.sock, %struct.sock* %sk, i64 0, i32 0 + %tmp11.i4.i = load volatile i32, i32* %1, align 4 + %sub.i232 = sub i32 %tmp11.i4.i, %tmp11.i.i + %cmp.i233 = icmp slt i32 %sub.i232, 0 + br i1 %cmp.i233, label %if.then.i234, label %lor.rhs.i, !prof !12 + +lor.rhs.i: ; preds = %zerocopy_rcv_inq + %tmp11.i2.i = load volatile i32, i32* %0, align 4 + %cmp20.i = icmp eq i32 %tmp11.i.i, %tmp11.i2.i + br i1 %cmp20.i, label %if.end.i235, label %if.then.i234, !prof !2, !misexpect !3 + +if.then.i234: ; preds = %lor.rhs.i, %zerocopy_rcv_inq + %tmp33.i = load i32, i32* %1, align 8 + %tmp35.i = load i32, i32* %0, align 4 + %sub24.i = sub i32 %tmp33.i, %tmp35.i + br label %if.end.i235 + +if.end.i235: ; preds = %if.then.i234, %lor.rhs.i + %inq.0.i = phi i32 [ %sub24.i, %if.then.i234 ], [ %sub.i232, %lor.rhs.i ] + %cmp25.i = icmp eq i32 %inq.0.i, 0 + br i1 %cmp25.i, label %land.lhs.true.i, label %tcp_inq_hint.exit + +land.lhs.true.i: ; preds = %if.end.i235 + %skc_flags.i.i = getelementptr inbounds %struct.sock, %struct.sock* %sk, i64 0, i32 1 + %tmp3.i.i.i = load volatile i64, i64* %skc_flags.i.i, align 8 + %and1.i.i.i = lshr i64 %tmp3.i.i.i, 1 + %2 = trunc i64 %and1.i.i.i to i32 + %3 = and i32 %2, 1 + br label %tcp_inq_hint.exit + +tcp_inq_hint.exit: ; preds = %land.lhs.true.i, %if.end.i235 + %call636236 = phi i32 [ %3, %land.lhs.true.i ], [ %inq.0.i, %if.end.i235 ] + %inq = getelementptr inbounds %struct.tcp_zerocopy_receive, %struct.tcp_zerocopy_receive* %zc, i64 0, i32 3 + store i32 %call636236, i32* %inq, align 8 + br label %zerocopy_rcv_out + +zerocopy_rcv_out: ; preds = %tcp_inq_hint.exit, %if.end621 + %tobool637 = icmp eq i32 %call622, 0 + br i1 %tobool637, label %land.lhs.true638, label %cleanup644 + +land.lhs.true638: ; preds = %zerocopy_rcv_out + br i1 %cmp3.i.i220, label %if.then.i.i49, label %if.end12.i.i52, !prof !12, !misexpect !3 + +if.then.i.i49: ; preds = %land.lhs.true638 + br i1 %tmp8.i.i222, label %if.else.i.i51, label %if.then7.i.i50, !prof !13 + +if.then7.i.i50: ; preds = %if.then.i.i49 + call void asm sideeffect "", "i,i,i,i,~{dirflag},~{fpsr},~{flags}"(i8* getelementptr inbounds ([30 x i8], [30 x i8]* @.str.17, i64 0, i64 0), i32 127, i32 2305, i64 12) + br label %copy_to_user.exit57 + +if.else.i.i51: ; preds = %if.then.i.i49 + call void @__bad_copy_from() + br label %copy_to_user.exit57 + +if.end12.i.i52: ; preds = %land.lhs.true638 + br i1 %tmp8.i.i222, label %if.then.i55, label %if.then.i.i.i53 + +if.then.i.i.i53: ; preds = %if.end12.i.i52 + call void @__check_object_size(i8* nonnull %tmp554, i64 %conv617, i1 zeroext true) + br label %if.then.i55 + +if.then.i55: ; preds = %if.then.i.i.i53, %if.end12.i.i52 + %call2.i54 = call i64 @_copy_to_user(i8* %optval, i8* nonnull %tmp554, i64 %conv617) + br label %copy_to_user.exit57 + +copy_to_user.exit57: ; preds = %if.then.i55, %if.else.i.i51, %if.then7.i.i50 + %n.addr.0.i56 = phi i64 [ %call2.i54, %if.then.i55 ], [ %conv617, %if.then7.i.i50 ], [ %conv617, %if.else.i.i51 ] + %tobool641 = icmp eq i64 %n.addr.0.i56, 0 + %spec.select = select i1 %tobool641, i32 0, i32 -14 + br label %cleanup644 + +cleanup644: ; preds = %copy_to_user.exit57, %zerocopy_rcv_out, %copy_from_user.exit, %if.then607, %if.end598, %sw.bb586 + %retval.6 = phi i32 [ -14, %sw.bb586 ], [ -22, %if.end598 ], [ -14, %if.then607 ], [ -14, %copy_from_user.exit ], [ %call622, %zerocopy_rcv_out ], [ %spec.select, %copy_to_user.exit57 ] + call void @llvm.lifetime.end.p0i8(i64 24, i8* nonnull %tmp554) + br label %cleanup662 + +sw.bb646: ; preds = %if.end11 + %tmp607 = call i32 asm sideeffect "", "={ax},0,{cx},~{ebx},~{dirflag},~{fpsr},~{flags}"(i32 %conv., i32* %optlen) + %tobool654 = icmp eq i32 %tmp607, 0 + br i1 %tobool654, label %if.end656, label %cleanup662, !prof !2, !misexpect !3 + +if.end656: + %conv657 = zext i32 %conv. to i64 + %tmp8.i.i = call i1 @llvm.is.constant.i64(i64 %conv657) + br i1 %tmp8.i.i, label %if.then.i, label %if.then.i.i.i + +if.then.i.i.i: ; preds = %if.end656 + call void @__check_object_size(i8* nonnull %tmp8, i64 %conv657, i1 zeroext true) + br label %if.then.i + +if.then.i: ; preds = %if.then.i.i.i, %if.end656 + %call2.i = call i64 @_copy_to_user(i8* %optval, i8* nonnull %tmp8, i64 %conv657) + %tobool659 = icmp eq i64 %call2.i, 0 + %.25 = select i1 %tobool659, i32 0, i32 -14 + br label %cleanup662 + +cleanup662: + ret i32 0 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) + +declare i1 @llvm.is.constant.i64(i64) + +declare dso_local void @__bad_copy_from() local_unnamed_addr + +declare dso_local void @__bad_copy_to() local_unnamed_addr + +declare dso_local void @__check_object_size(i8*, i64, i1 zeroext) local_unnamed_addr + +declare i64 @llvm.read_register.i64(metadata) + +declare void @llvm.write_register.i64(metadata, i64) + +declare dso_local i64 @_copy_from_user(i8*, i8*, i64) local_unnamed_addr + +declare hidden fastcc i32 @tcp_zerocopy_receive() + +declare dso_local i64 @_copy_to_user(i8*, i8*, i64) local_unnamed_addr + +!0 = !{!"rsp"} +!1 = !{i32 -2138498757} +!2 = !{!"branch_weights", i32 2000, i32 1} +!3 = !{!"misexpect", i64 1, i64 2000, i64 1} +!4 = distinct !{!4, !5} +!5 = !{!"llvm.loop.unroll.disable"} +!6 = !{i32 -2138496028} +!7 = !{i32 -2138492645} +!8 = !{i32 -2138491979} +!9 = !{i32 -2138488596} +!10 = !{i32 -2138487932} +!11 = !{i32 -2138484679} +!12 = !{!"branch_weights", i32 1, i32 2000} +!13 = !{!"branch_weights", i32 1073205, i32 2146410443} +!14 = !{i32 -2146020474, i32 -2146020444, i32 -2146020398, i32 -2146020340, i32 -2146020286, i32 -2146020232, i32 -2146020177, i32 -2146020146} +!15 = !{i32 -2138484012} +!16 = !{i32 -2138480756} +!17 = !{i32 -2138480084} +!18 = !{i32 -2138476789} +!19 = !{i32 -2138476125} +!20 = !{i32 -2138472871} +!21 = !{i32 -2138471375} +!22 = !{i32 -2138470633} +!23 = !{i32 -2145640372} +!24 = !{i32 -2145640231} +!25 = !{i32 -2138465901} +!26 = !{i32 -2138465222} +!27 = !{i32 -2138464417} +!28 = !{i32 -2138459916} +!29 = !{i32 -2138459215} +!30 = !{i32 -2138457404} +!31 = !{!"misexpect", i64 0, i64 2000, i64 1} +!32 = !{i32 -2139801272} +!33 = !{i32 -2138454970}