; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature ; RUN: opt -passes=ipsccp -S %s | FileCheck %s %t1 = type opaque @e = common global i32 0, align 4 ; 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 {{[^@]+}}@test1_m ; CHECK-SAME: (i32 [[H:%.*]]) { ; CHECK-NEXT: entry: ; 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 ptr ; CHECK-NEXT: [[CALL2:%.*]] = call i1 @test1_g(ptr [[TMP0]], i32 1) ; CHECK-NEXT: ret i32 undef ; entry: %conv = trunc i32 %h to i8 %call = call i32 @test1_k(i8 %conv, i32 0) %conv1 = sext i32 %h to i64 %0 = inttoptr i64 %conv1 to ptr %call2 = call i1 @test1_g(ptr %0, i32 1) ret i32 undef ; uselistorder directives uselistorder i32 %h, { 1, 0 } } declare void @use.1(i1) define internal i32 @test1_k(i8 %h, i32 %i) { ; CHECK-LABEL: define {{[^@]+}}@test1_k ; CHECK-SAME: (i8 [[H:%.*]], i32 [[I:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @e, align 4 ; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64 ; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to ptr ; CHECK-NEXT: [[CALL:%.*]] = call i1 @test1_g(ptr [[TMP1]], i32 0) ; CHECK-NEXT: call void @use.1(i1 false) ; CHECK-NEXT: ret i32 undef ; entry: %0 = load i32, ptr @e, align 4 %conv = sext i32 %0 to i64 %1 = inttoptr i64 %conv to ptr %call = call i1 @test1_g(ptr %1, i32 %i) %frombool.1 = zext i1 %call to i8 %tobool.1 = trunc i8 %frombool.1 to i1 call void @use.1(i1 %tobool.1) ret i32 undef } define internal i1 @test1_g(ptr %h, i32 %i) #0 { ; CHECK-LABEL: define {{[^@]+}}@test1_g ; CHECK-SAME: (ptr [[H:%.*]], i32 [[I:%.*]]) { ; CHECK-NEXT: entry: ; 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() ; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i32 [[CALL]], 0 ; CHECK-NEXT: br label [[LAND_END]] ; CHECK: land.end: ; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[TOBOOL1]], [[LAND_RHS]] ] ; CHECK-NEXT: ret i1 undef ; entry: %tobool = icmp ne i32 %i, 0 br i1 %tobool, label %land.rhs, label %land.end land.rhs: ; preds = %entry %call = call i32 (...) @test1_j() %tobool1 = icmp ne i32 %call, 0 br label %land.end land.end: ; preds = %land.rhs, %entry %0 = phi i1 [ false, %entry ], [ %tobool1, %land.rhs ] ret i1 false } declare i32 @test1_j(...) define i32 @test2_m(i32 %h) #0 { ; CHECK-LABEL: define {{[^@]+}}@test2_m ; CHECK-SAME: (i32 [[H:%.*]]) { ; CHECK-NEXT: entry: ; 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 ptr ; CHECK-NEXT: [[CALL2:%.*]] = call i1 @test2_g(ptr [[TMP0]], i32 1) ; CHECK-NEXT: ret i32 undef ; entry: %conv = trunc i32 %h to i8 %call = call i32 @test2_k(i8 %conv, i32 0) %conv1 = sext i32 %h to i64 %0 = inttoptr i64 %conv1 to ptr %call2 = call i1 @test2_g(ptr %0, i32 1) ret i32 undef ; uselistorder directives uselistorder i32 %h, { 1, 0 } } ; 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-SAME: (i8 [[H:%.*]], i32 [[I:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @e, align 4 ; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64 ; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to ptr ; CHECK-NEXT: [[CALL:%.*]] = call i1 @test3_g(ptr [[TMP1]], i32 0) ; CHECK-NEXT: call void @use.1(i1 false) ; CHECK-NEXT: ret i32 undef ; entry: %0 = load i32, ptr @e, align 4 %conv = sext i32 %0 to i64 %1 = inttoptr i64 %conv to ptr %call = call i1 @test3_g(ptr %1, i32 %i) %frombool = icmp slt i1 %call, 1 %add = add i1 %frombool, %frombool call void @use.1(i1 %frombool) ret i32 undef } define internal i1 @test2_g(ptr %h, i32 %i) { ; CHECK-LABEL: define {{[^@]+}}@test2_g ; CHECK-SAME: (ptr [[H:%.*]], i32 [[I:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[LAND_RHS:%.*]] ; CHECK: land.rhs: ; CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @test2_j() ; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i32 [[CALL]], 0 ; CHECK-NEXT: br label [[LAND_END:%.*]] ; CHECK: land.end: ; CHECK-NEXT: ret i1 undef ; entry: %tobool = icmp ne i32 %i, 0 br i1 %tobool, label %land.rhs, label %land.end land.rhs: ; preds = %entry %call = call i32 (...) @test2_j() %tobool1 = icmp ne i32 %call, 0 br label %land.end land.end: ; preds = %land.rhs, %entry %0 = phi i1 [ false, %entry ], [ %tobool1, %land.rhs ] ret i1 false } declare i32 @test2_j(...) ; 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-SAME: (i32 [[H:%.*]]) { ; CHECK-NEXT: entry: ; 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 ptr ; CHECK-NEXT: [[CALL2:%.*]] = call i1 @test3_g(ptr [[TMP0]], i32 1) ; CHECK-NEXT: ret i32 undef ; entry: %conv = trunc i32 %h to i8 %call = call i32 @test3_k(i8 %conv, i32 0) %conv1 = sext i32 %h to i64 %0 = inttoptr i64 %conv1 to ptr %call2 = call i1 @test3_g(ptr %0, i32 1) ret i32 undef ; uselistorder directives uselistorder i32 %h, { 1, 0 } } define internal i32 @test3_k(i8 %h, i32 %i) { ; CHECK-LABEL: define {{[^@]+}}@test3_k ; CHECK-SAME: (i8 [[H:%.*]], i32 [[I:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @e, align 4 ; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64 ; CHECK-NEXT: [[TMP1:%.*]] = inttoptr i64 [[CONV]] to ptr ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[CALL:%.*]] = call i1 @test3_g(ptr [[TMP1]], i32 0) ; CHECK-NEXT: call void @use.1(i1 false) ; CHECK-NEXT: br label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: ret i32 undef ; entry: %0 = load i32, ptr @e, align 4 %conv = sext i32 %0 to i64 %1 = inttoptr i64 %conv to ptr br label %loop loop: %phi = phi i1 [ undef, %entry], [ %call, %loop ] %call = call i1 @test3_g(ptr %1, i32 %i) %frombool = icmp slt i1 %call, 1 %add = add i1 %frombool, %frombool call void @use.1(i1 %frombool) br i1 %call, label %loop, label %exit exit: ret i32 undef } define internal i1 @test3_g(ptr %h, i32 %i) { ; CHECK-LABEL: define {{[^@]+}}@test3_g ; CHECK-SAME: (ptr [[H:%.*]], i32 [[I:%.*]]) { ; CHECK-NEXT: entry: ; 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() ; CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i32 [[CALL]], 0 ; CHECK-NEXT: br label [[LAND_END]] ; CHECK: land.end: ; CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[TOBOOL1]], [[LAND_RHS]] ] ; CHECK-NEXT: ret i1 undef ; entry: %tobool = icmp ne i32 %i, 0 br i1 %tobool, label %land.rhs, label %land.end land.rhs: ; preds = %entry %call = call i32 (...) @test3_j() %tobool1 = icmp ne i32 %call, 0 br label %land.end land.end: ; preds = %land.rhs, %entry %0 = phi i1 [ false, %entry ], [ %tobool1, %land.rhs ] ret i1 false } declare i32 @test3_j(...) ; TODO: We can eliminate the bitcast, if we resolve the unknown argument of ; @test4_b first. declare void @use.16(ptr) declare void @use.8(ptr) define void @test4_a() { ; CHECK-LABEL: define {{[^@]+}}@test4_a() { ; CHECK-NEXT: bb: ; CHECK-NEXT: [[TMP:%.*]] = call ptr @test4_c(ptr null) ; CHECK-NEXT: call void @test4_b(ptr null) ; CHECK-NEXT: ret void ; bb: %tmp = call ptr @test4_c(ptr null) call void @test4_b(ptr %tmp) ret void } define internal void @test4_b(ptr %arg) { ; CHECK-LABEL: define {{[^@]+}}@test4_b ; CHECK-SAME: (ptr [[ARG:%.*]]) { ; CHECK-NEXT: bb: ; CHECK-NEXT: [[SEL:%.*]] = select i1 false, ptr null, ptr null ; CHECK-NEXT: call void @use.16(ptr null) ; CHECK-NEXT: call void @use.8(ptr [[SEL]]) ; CHECK-NEXT: ret void ; bb: %sel = select i1 false, ptr %arg, ptr %arg call void @use.16(ptr %arg) call void @use.8(ptr %sel) ret void } define internal ptr @test4_c(ptr %arg) { ; CHECK-LABEL: define {{[^@]+}}@test4_c ; CHECK-SAME: (ptr [[ARG:%.*]]) { ; CHECK-NEXT: bb1: ; CHECK-NEXT: [[TMP:%.*]] = and i1 undef, undef ; CHECK-NEXT: br i1 [[TMP]], label [[BB3:%.*]], label [[BB2:%.*]] ; CHECK: bb2: ; CHECK-NEXT: unreachable ; CHECK: bb3: ; CHECK-NEXT: ret ptr undef ; bb1: ; preds = %bb %tmp = and i1 undef, undef br i1 %tmp, label %bb3, label %bb2 bb2: ; preds = %bb1 unreachable bb3: ; preds = %bb1 ret ptr null } ; TODO: Same as test4, but with a select instead of a bitcast. define void @test5_a() { ; CHECK-LABEL: define {{[^@]+}}@test5_a() { ; CHECK-NEXT: bb: ; CHECK-NEXT: [[TMP:%.*]] = call ptr @test5_c(ptr null) ; CHECK-NEXT: call void @test5_b(ptr null) ; CHECK-NEXT: ret void ; bb: %tmp = call ptr @test5_c(ptr null) call void @test5_b(ptr %tmp) ret void } define internal void @test5_b(ptr %arg) { ; CHECK-LABEL: define {{[^@]+}}@test5_b ; CHECK-SAME: (ptr [[ARG:%.*]]) { ; CHECK-NEXT: bb: ; CHECK-NEXT: [[SEL:%.*]] = select i1 false, ptr null, ptr null ; CHECK-NEXT: call void @use.8(ptr [[SEL]]) ; CHECK-NEXT: ret void ; bb: %sel = select i1 false, ptr %arg, ptr %arg call void @use.8(ptr %sel) ret void } define internal ptr @test5_c(ptr %arg) { ; CHECK-LABEL: define {{[^@]+}}@test5_c ; CHECK-SAME: (ptr [[ARG:%.*]]) { ; CHECK-NEXT: bb1: ; CHECK-NEXT: [[TMP:%.*]] = and i1 undef, undef ; CHECK-NEXT: br i1 [[TMP]], label [[BB3:%.*]], label [[BB2:%.*]] ; CHECK: bb2: ; CHECK-NEXT: unreachable ; CHECK: bb3: ; CHECK-NEXT: ret ptr undef ; bb1: ; preds = %bb %tmp = and i1 undef, undef br i1 %tmp, label %bb3, label %bb2 bb2: ; preds = %bb1 unreachable bb3: ; preds = %bb1 ret ptr null } @contextsize = external dso_local local_unnamed_addr global i32, align 4 @pcount = internal local_unnamed_addr global i32 0, align 4 @maxposslen = external dso_local local_unnamed_addr global i32, align 4 define void @test3() { ; CHECK-LABEL: define {{[^@]+}}@test3() { ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[IF_END16:%.*]] ; CHECK: if.end16: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @contextsize, align 4 ; CHECK-NEXT: [[SUB18:%.*]] = sub i32 undef, [[TMP0]] ; CHECK-NEXT: [[SUB19:%.*]] = sub i32 [[SUB18]], undef ; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @maxposslen, align 4 ; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], 8 ; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 undef, [[ADD]] ; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr @pcount, align 4 ; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[DIV]], [[SUB19]] ; CHECK-NEXT: [[CMP20:%.*]] = icmp sgt i32 [[TMP2]], [[MUL]] ; CHECK-NEXT: br i1 [[CMP20]], label [[IF_THEN22:%.*]], label [[IF_END24:%.*]] ; CHECK: if.then22: ; CHECK-NEXT: store i32 [[MUL]], ptr @pcount, align 4 ; CHECK-NEXT: ret void ; CHECK: if.end24: ; CHECK-NEXT: [[CMP25474:%.*]] = icmp sgt i32 [[TMP2]], 0 ; CHECK-NEXT: br i1 [[CMP25474]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] ; CHECK: for.body: ; CHECK-NEXT: ret void ; CHECK: for.end: ; CHECK-NEXT: ret void ; entry: br label %if.end16 if.end16: ; preds = %entry %0 = load i32, ptr @contextsize, align 4 %sub18 = sub i32 undef, %0 %sub19 = sub i32 %sub18, undef %1 = load i32, ptr @maxposslen, align 4 %add = add nsw i32 %1, 8 %div = sdiv i32 undef, %add %2 = load i32, ptr @pcount, align 4 %mul = mul nsw i32 %div, %sub19 %cmp20 = icmp sgt i32 %2, %mul br i1 %cmp20, label %if.then22, label %if.end24 if.then22: ; preds = %if.end16 store i32 %mul, ptr @pcount, align 4 ret void if.end24: ; preds = %if.end16 %cmp25474 = icmp sgt i32 %2, 0 br i1 %cmp25474, label %for.body, label %for.end for.body: ; preds = %if.end24 %3 = trunc i64 0 to i32 %div30 = sdiv i32 %3, %sub19 ret void for.end: ; preds = %if.end24 ret void }