| ; RUN: opt -passes=ipsccp -S %s | FileCheck %s |
| |
| ; Test 1. |
| ; Both arguments and return value of @callee can be tracked. The inferred range |
| ; can be added to call sites. |
| define internal noundef i32 @callee(i32 %x) { |
| ; CHECK-LABEL: @callee( |
| ; CHECK-NEXT: ret i32 [[X:%.*]] |
| ; |
| ret i32 %x |
| } |
| |
| define i32 @caller1() { |
| ; CHECK-LABEL: @caller1( |
| ; CHECK-NEXT: [[C1:%.*]] = call i32 @callee(i32 10), !range [[RANGE_10_21:![0-9]+]] |
| ; CHECK-NEXT: [[C2:%.*]] = call i32 @callee(i32 20), !range [[RANGE_10_21]] |
| ; CHECK-NEXT: [[A:%.*]] = add i32 [[C1]], [[C2]] |
| ; CHECK-NEXT: ret i32 [[A]] |
| ; |
| %c1 = call i32 @callee(i32 10) |
| %c2 = call i32 @callee(i32 20) |
| %a = add i32 %c1, %c2 |
| ret i32 %a |
| } |
| |
| define i32 @caller2(i32 %x) { |
| ; CHECK-LABEL: @caller2( |
| ; CHECK-NEXT: [[X_15:%.*]] = and i32 [[X:%.*]], 15 |
| ; CHECK-NEXT: [[C:%.*]] = call i32 @callee(i32 [[X_15]]), !range [[RANGE_10_21]] |
| ; CHECK-NEXT: ret i32 [[C]] |
| ; |
| %x.15 = and i32 %x, 15 |
| %c = call i32 @callee(i32 %x.15) |
| ret i32 %c |
| } |
| |
| ; Test 2. |
| ; The return value of @callee2 can be tracked, but arguments cannot, because |
| ; it is passed to @use_cb1. We cannot infer a range for the return value, no |
| ; metadata should be added. |
| |
| declare void @use_cb1(i32 (i32)*) |
| |
| define internal noundef i32 @callee2(i32 %x) { |
| ; CHECK-LABEL: @callee2( |
| ; CHECK-NEXT: ret i32 [[X:%.*]] |
| ; |
| ret i32 %x |
| } |
| |
| define void @caller_cb1() { |
| ; CHECK-LABEL: @caller_cb1( |
| ; CHECK-NEXT: [[C1:%.*]] = call i32 @callee2(i32 9) |
| ; CHECK-NOT: !range |
| ; CHECK-NEXT: [[C2:%.*]] = call i32 @callee2(i32 10) |
| ; CHECK-NOT: !range |
| ; CHECK-NEXT: call void @use_cb1(i32 (i32)* @callee2) |
| ; CHECK-NEXT: ret void |
| ; |
| %c1 = call i32 @callee2(i32 9) |
| %c2 = call i32 @callee2(i32 10) |
| call void @use_cb1(i32 (i32)* @callee2) |
| ret void |
| } |
| |
| ; Test 3. |
| ; The return value can be tracked and it the result range ([500, 601) does not |
| ; depend on the arguments, which cannot be tracked because @callee3 is passed |
| ; to @use_cb2. The result range can be added to the call sites of @callee. |
| |
| declare void @use_cb2(i32 (i32)*) |
| |
| define internal noundef i32 @callee3(i32 %x) { |
| ; CHECK-LABEL: @callee3( |
| ; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X:%.*]], 10 |
| ; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i32 500, i32 600 |
| ; CHECK-NEXT: ret i32 [[S]] |
| ; |
| %c = icmp eq i32 %x, 10 |
| %s = select i1 %c, i32 500, i32 600 |
| ret i32 %s |
| } |
| |
| define void @caller_cb2() { |
| ; CHECK-LABEL: @caller_cb2( |
| ; CHECK-NEXT: [[C1:%.*]] = call i32 @callee3(i32 9), !range [[RANGE_500_601:![0-9]+]] |
| ; CHECK-NEXT: [[C2:%.*]] = call i32 @callee3(i32 10), !range [[RANGE_500_601]] |
| ; CHECK-NEXT: call void @use_cb2(i32 (i32)* @callee3) |
| ; CHECK-NEXT: ret void |
| ; |
| %c1 = call i32 @callee3(i32 9) |
| %c2 = call i32 @callee3(i32 10) |
| call void @use_cb2(i32 (i32)* @callee3) |
| ret void |
| } |
| |
| ; Test 4. |
| ; The return value of @callee4 can be tracked, but depends on an argument which |
| ; cannot be tracked. No result range can be inferred. |
| |
| declare void @use_cb3(i32 (i32, i32)*) |
| |
| define internal noundef i32 @callee4(i32 %x, i32 %y) { |
| ; CHECK-LABEL: @callee4( |
| ; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[X:%.*]], 10 |
| ; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i32 500, i32 [[Y:%.*]] |
| ; CHECK-NEXT: ret i32 [[S]] |
| ; |
| %c = icmp eq i32 %x, 10 |
| %s = select i1 %c, i32 500, i32 %y |
| ret i32 %s |
| } |
| |
| define void @caller_cb3() { |
| ; CHECK-LABEL: @caller_cb3( |
| ; CHECK-NEXT: [[C1:%.*]] = call i32 @callee4(i32 11, i32 30) |
| ; CHECK-NOT: !range |
| ; CHECK-NEXT: [[C2:%.*]] = call i32 @callee4(i32 12, i32 40) |
| ; CHECK-NOT: !range |
| ; CHECK-NEXT: call void @use_cb3(i32 (i32, i32)* @callee4) |
| ; CHECK-NEXT: ret void |
| ; |
| %c1 = call i32 @callee4(i32 11, i32 30) |
| %c2 = call i32 @callee4(i32 12, i32 40) |
| call void @use_cb3(i32 (i32, i32)* @callee4) |
| ret void |
| } |
| |
| ; Test 5. |
| ; Range for the return value of callee5 includes undef. No range metadata |
| ; should be added at call sites. |
| define internal noundef i32 @callee5(i32 %x, i32 %y) { |
| ; CHECK-LABEL: @callee5( |
| ; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[X:%.*]], 15 |
| ; CHECK-NEXT: br i1 [[C]], label [[BB1:%.*]], label [[BB2:%.*]] |
| ; CHECK: bb1: |
| ; CHECK-NEXT: br label [[EXIT:%.*]] |
| ; CHECK: bb2: |
| ; CHECK-NEXT: br label [[EXIT]] |
| ; CHECK: exit: |
| ; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[Y:%.*]], [[BB1]] ], [ undef, [[BB2]] ] |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %c = icmp slt i32 %x, 15 |
| br i1 %c, label %bb1, label %bb2 |
| |
| bb1: |
| br label %exit |
| |
| bb2: |
| br label %exit |
| |
| exit: |
| %res = phi i32 [ %y, %bb1 ], [ undef, %bb2] |
| ret i32 %res |
| } |
| |
| define i32 @caller5() { |
| ; CHECK-LABEL: @caller5( |
| ; CHECK-NEXT: [[C1:%.*]] = call i32 @callee5(i32 10, i32 100) |
| ; CHECK-NOT: !range |
| ; CHECK-NEXT: [[C2:%.*]] = call i32 @callee5(i32 20, i32 200) |
| ; CHECK-NOT: !range |
| ; CHECK-NEXT: [[A:%.*]] = add i32 [[C1]], [[C2]] |
| ; CHECK-NEXT: ret i32 [[A]] |
| ; |
| %c1 = call i32 @callee5(i32 10, i32 100) |
| %c2 = call i32 @callee5(i32 20, i32 200) |
| %a = add i32 %c1, %c2 |
| ret i32 %a |
| } |
| |
| ; CHECK: [[RANGE_10_21]] = !{i32 0, i32 21} |
| ; CHECK: [[RANGE_500_601]] = !{i32 500, i32 601} |