diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp --- a/llvm/lib/Transforms/Scalar/SCCP.cpp +++ b/llvm/lib/Transforms/Scalar/SCCP.cpp @@ -1456,6 +1456,14 @@ /// This scan also checks for values that use undefs. It conservatively marks /// them as overdefined. bool SCCPSolver::ResolvedUndefsIn(Function &F) { + // Keep track of values that dependent on an yet unknown tracked function + // call. It only makes sense to resolve them once the call is resolved. + SmallPtrSet DependsOnSkipped; + for (auto &A : F.args()) { + if (A.getType()->isStructTy() || !getValueState(&A).isUnknown()) + continue; + DependsOnSkipped.insert(&A); + } for (BasicBlock &BB : F) { if (!BBExecutable.count(&BB)) continue; @@ -1470,8 +1478,10 @@ // Tracked calls must never be marked overdefined in ResolvedUndefsIn. if (CallSite CS = CallSite(&I)) if (Function *F = CS.getCalledFunction()) - if (MRVFunctionsTracked.count(F)) + if (MRVFunctionsTracked.count(F)) { + DependsOnSkipped.insert(&I); continue; + } // extractvalue and insertvalue don't need to be marked; they are // tracked as precisely as their operands. @@ -1498,8 +1508,21 @@ // never be marked overdefined in ResolvedUndefsIn. if (CallSite CS = CallSite(&I)) if (Function *F = CS.getCalledFunction()) - if (TrackedRetVals.count(F)) + if (TrackedRetVals.count(F)) { + DependsOnSkipped.insert(&I); continue; + } + + // Skip instructions that depend on results of calls we skipped earlier. + // Otherwise we might mark I as overdefined to early when we would end up + // discovering a constant value for I, if the call later resolves to a + // constant. + if (any_of(I.operands(), [&DependsOnSkipped](Value *V) { + return DependsOnSkipped.find(V) != DependsOnSkipped.end(); + })) { + DependsOnSkipped.insert(&I); + continue; + } markOverdefined(&I); return true; diff --git a/llvm/test/Transforms/SCCP/ipsccp-basic.ll b/llvm/test/Transforms/SCCP/ipsccp-basic.ll --- a/llvm/test/Transforms/SCCP/ipsccp-basic.ll +++ b/llvm/test/Transforms/SCCP/ipsccp-basic.ll @@ -240,7 +240,7 @@ ; CHECK-LABEL: define i32 @test10a( ; CHECK-NEXT: entry: ; CHECK-NEXT: %call = call i32 @test10b(i32 undef) -; CHECK-NEXT: ret i32 %call +; CHECK-NEXT: ret i32 undef } define internal i32 @test10b(i32 %x) nounwind { @@ -249,8 +249,7 @@ ret i32 %r ; CHECK-LABEL: define internal i32 @test10b( ; CHECK-NEXT: entry: -; CHECK-NEXT: %r = and i32 undef, 1 -; CHECK-NEXT: ret i32 %r +; CHECK-NEXT: ret i32 undef } ;;======================== test11 diff --git a/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll b/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll --- a/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll +++ b/llvm/test/Transforms/SCCP/resolvedundefsin-tracked-fn.ll @@ -1,4 +1,4 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature ; RUN: opt -ipsccp -S %s | FileCheck %s %t1 = type opaque @@ -8,9 +8,10 @@ ; Test that we a skip unknown values depending on a unknown tracked call, until the call gets resolved. The @test1 and @test2 variants are very similar, they just check 2 different kinds of users (icmp and zext) define i32 @test1_m(i32 %h) { -; CHECK-LABEL: define i32 @test1_m( +; CHECK-LABEL: define {{[^@]+}}@test1_m +; CHECK-SAME: (i32 [[H:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H:%.*]] to i8 +; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H]] to i8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @test1_k(i8 [[CONV]], i32 0) ; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[H]] to i64 ; CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[CONV1]] to %t1* @@ -32,15 +33,14 @@ declare void @use.1(i1) define internal i32 @test1_k(i8 %h, i32 %i) { -; CHECK-LABEL: define {{.*}} @test1_k( +; CHECK-LABEL: define {{[^@]+}}@test1_k +; CHECK-SAME: (i8 [[H:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @e, align 4 ; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64 ; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to %t1* ; CHECK-NEXT: [[CALL:%.*]] = call i1 @test1_g(%t1* [[TMP1]], i32 0) -; CHECK-NEXT: [[FROMBOOL_1:%.*]] = zext i1 false to i8 -; CHECK-NEXT: [[TOBOOL_1:%.*]] = trunc i8 [[FROMBOOL_1]] to i1 -; CHECK-NEXT: call void @use.1(i1 [[TOBOOL_1]]) +; CHECK-NEXT: call void @use.1(i1 false) ; CHECK-NEXT: ret i32 undef ; entry: @@ -55,9 +55,10 @@ } define internal i1 @test1_g(%t1* %h, i32 %i) #0 { -; CHECK-LABEL: define {{.*}} @test1_g( +; CHECK-LABEL: define {{[^@]+}}@test1_g +; CHECK-SAME: (%t1* [[H:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[I:%.*]], 0 +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[I]], 0 ; CHECK-NEXT: br i1 [[TOBOOL]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] ; CHECK: land.rhs: ; CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @test1_j() @@ -84,9 +85,10 @@ declare i32 @test1_j(...) define i32 @test2_m(i32 %h) #0 { -; CHECK-LABEL: define {{.*}} @test2_m( +; CHECK-LABEL: define {{[^@]+}}@test2_m +; CHECK-SAME: (i32 [[H:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H:%.*]] to i8 +; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H]] to i8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @test2_k(i8 [[CONV]], i32 0) ; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[H]] to i64 ; CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[CONV1]] to %t1* @@ -108,15 +110,14 @@ ; TODO: We could do better for the return value of call i1 @test3_g, if we ; resolve the unknown values there first. define internal i32 @test2_k(i8 %h, i32 %i) { -; CHECK-LABEL: define {{.*}} @test2_k( +; CHECK-LABEL: define {{[^@]+}}@test2_k +; CHECK-SAME: (i8 [[H:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @e, align 4 ; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64 ; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to %t1* ; CHECK-NEXT: [[CALL:%.*]] = call i1 @test3_g(%t1* [[TMP1]], i32 0) -; CHECK-NEXT: [[FROMBOOL:%.*]] = icmp slt i1 false, true -; CHECK-NEXT: [[ADD:%.*]] = add i1 [[FROMBOOL]], [[FROMBOOL]] -; CHECK-NEXT: call void @use.1(i1 [[FROMBOOL]]) +; CHECK-NEXT: call void @use.1(i1 false) ; CHECK-NEXT: ret i32 undef ; entry: @@ -132,7 +133,8 @@ } define internal i1 @test2_g(%t1* %h, i32 %i) { -; CHECK-LABEL: define {{.*}} @test2_g( +; CHECK-LABEL: define {{[^@]+}}@test2_g +; CHECK-SAME: (%t1* [[H:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: br i1 true, label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] ; CHECK: land.rhs: @@ -163,9 +165,10 @@ ; Same as test_2*, but with a PHI node depending on a tracked call result. define i32 @test3_m(i32 %h) #0 { -; CHECK-LABEL: define {{.*}} @test3_m( +; CHECK-LABEL: define {{[^@]+}}@test3_m +; CHECK-SAME: (i32 [[H:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H:%.*]] to i8 +; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[H]] to i8 ; CHECK-NEXT: [[CALL:%.*]] = call i32 @test3_k(i8 [[CONV]], i32 0) ; CHECK-NEXT: [[CONV1:%.*]] = sext i32 [[H]] to i64 ; CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[CONV1]] to %t1* @@ -185,7 +188,8 @@ } define internal i32 @test3_k(i8 %h, i32 %i) { -; CHECK-LABEL: define {{.*}} @test3_k( +; CHECK-LABEL: define {{[^@]+}}@test3_k +; CHECK-SAME: (i8 [[H:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @e, align 4 ; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64 @@ -218,9 +222,10 @@ } define internal i1 @test3_g(%t1* %h, i32 %i) { -; CHECK-LABEL: define {{.*}} @test3_g( +; CHECK-LABEL: define {{[^@]+}}@test3_g +; CHECK-SAME: (%t1* [[H:%.*]], i32 [[I:%.*]]) ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[I:%.*]], 0 +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[I]], 0 ; CHECK-NEXT: br i1 [[TOBOOL]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] ; CHECK: land.rhs: ; CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @test3_j() @@ -254,7 +259,7 @@ declare void @use.8(i8*) define void @test4_a() { -; CHECK-LABEL: define {{.*}} @test4_a( +; CHECK-LABEL: define {{[^@]+}}@test4_a() ; CHECK-NEXT: bb: ; CHECK-NEXT: [[TMP:%.*]] = call i8* @test4_c(i8* null) ; CHECK-NEXT: call void @test4_b(i8* null) @@ -267,10 +272,10 @@ } define internal void @test4_b(i8* %arg) { -; CHECK-LABEL: define {{.*}} @test4_b( +; CHECK-LABEL: define {{[^@]+}}@test4_b +; CHECK-SAME: (i8* [[ARG:%.*]]) ; CHECK-NEXT: bb: -; CHECK-NEXT: [[TMP:%.*]] = bitcast i8* null to i16* -; CHECK-NEXT: call void @use.16(i16* [[TMP]]) +; CHECK-NEXT: call void @use.16(i16* null) ; CHECK-NEXT: call void @use.8(i8* null) ; CHECK-NEXT: ret void ; @@ -283,7 +288,8 @@ } define internal i8* @test4_c(i8* %arg) { -; CHECK-LABEL: define {{.*}} @test4_c( +; CHECK-LABEL: define {{[^@]+}}@test4_c +; CHECK-SAME: (i8* [[ARG:%.*]]) ; CHECK-NEXT: bb1: ; CHECK-NEXT: [[TMP:%.*]] = and i1 undef, undef ; CHECK-NEXT: br i1 [[TMP]], label [[BB3:%.*]], label [[BB2:%.*]] @@ -306,7 +312,7 @@ ; TODO: Same as test4, but with a select instead of a bitcast. define void @test5_a() { -; CHECK-LABEL: define {{.*}} @test5_a( +; CHECK-LABEL: define {{[^@]+}}@test5_a() ; CHECK-NEXT: bb: ; CHECK-NEXT: [[TMP:%.*]] = call i8* @test5_c(i8* null) ; CHECK-NEXT: call void @test5_b(i8* null) @@ -319,10 +325,10 @@ } define internal void @test5_b(i8* %arg) { -; CHECK-LABEL: define {{.*}} @test5_b( +; CHECK-LABEL: define {{[^@]+}}@test5_b +; CHECK-SAME: (i8* [[ARG:%.*]]) ; CHECK-NEXT: bb: -; CHECK-NEXT: [[SEL:%.*]] = select i1 false, i8* null, i8* null -; CHECK-NEXT: call void @use.8(i8* [[SEL]]) +; CHECK-NEXT: call void @use.8(i8* null) ; CHECK-NEXT: ret void ; bb: @@ -332,7 +338,8 @@ } define internal i8* @test5_c(i8* %arg) { -; CHECK-LABEL: define {{.*}} @test5_c( +; CHECK-LABEL: define {{[^@]+}}@test5_c +; CHECK-SAME: (i8* [[ARG:%.*]]) ; CHECK-NEXT: bb1: ; CHECK-NEXT: [[TMP:%.*]] = and i1 undef, undef ; CHECK-NEXT: br i1 [[TMP]], label [[BB3:%.*]], label [[BB2:%.*]] @@ -359,7 +366,7 @@ @maxposslen = external dso_local local_unnamed_addr global i32, align 4 define void @test3() { -; CHECK-LABEL: define {{.*}} @test3( +; CHECK-LABEL: define {{[^@]+}}@test3() ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[IF_END16:%.*]] ; CHECK: if.end16: