| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -passes='simplifycfg<sink-common-insts>,verify' -S %s | FileCheck %s | |
| declare void @clobber1() | |
| declare void @clobber2() | |
| declare void @f() | |
| ; Both bundle operands are variable, so the calls can be sunk and the bundle | |
| ; operand merged by a PHI. | |
| define void @sink_variable_bundle_operands(i1 %c, i32 %a, i32 %b) { | |
| ; CHECK-LABEL: define void @sink_variable_bundle_operands( | |
| ; CHECK-SAME: i1 [[C:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[BB:.*:]] | |
| ; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]] | |
| ; CHECK: [[THEN]]: | |
| ; CHECK-NEXT: call void @clobber1() | |
| ; CHECK-NEXT: br label %[[EXIT:.*]] | |
| ; CHECK: [[ELSE]]: | |
| ; CHECK-NEXT: call void @clobber2() | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: [[B_SINK:%.*]] = phi i32 [ [[B]], %[[ELSE]] ], [ [[A]], %[[THEN]] ] | |
| ; CHECK-NEXT: call void @f() [ "deopt"(i32 [[B_SINK]]) ] | |
| ; CHECK-NEXT: ret void | |
| ; | |
| bb: | |
| br i1 %c, label %then, label %else | |
| then: | |
| call void @clobber1() | |
| call void @f() [ "deopt"(i32 %a) ] | |
| br label %exit | |
| else: | |
| call void @clobber2() | |
| call void @f() [ "deopt"(i32 %b) ] | |
| br label %exit | |
| exit: | |
| ret void | |
| } | |
| ; The bundle operand of the call in %then is constant, so the calls must not be | |
| ; sunk. | |
| define void @no_sink_constant_bundle_operand_in_then(i1 %c, i32 %b) { | |
| ; CHECK-LABEL: define void @no_sink_constant_bundle_operand_in_then( | |
| ; CHECK-SAME: i1 [[C:%.*]], i32 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[BB:.*:]] | |
| ; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]] | |
| ; CHECK: [[THEN]]: | |
| ; CHECK-NEXT: call void @clobber1() | |
| ; CHECK-NEXT: call void @f() [ "deopt"(i32 7) ] | |
| ; CHECK-NEXT: br label %[[EXIT:.*]] | |
| ; CHECK: [[ELSE]]: | |
| ; CHECK-NEXT: call void @clobber2() | |
| ; CHECK-NEXT: call void @f() [ "deopt"(i32 [[B]]) ] | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| bb: | |
| br i1 %c, label %then, label %else | |
| then: | |
| call void @clobber1() | |
| call void @f() [ "deopt"(i32 7) ] | |
| br label %exit | |
| else: | |
| call void @clobber2() | |
| call void @f() [ "deopt"(i32 %b) ] | |
| br label %exit | |
| exit: | |
| ret void | |
| } | |
| ; Same as above, with the constant bundle operand in the other predecessor. | |
| define void @no_sink_constant_bundle_operand_in_else(i1 %c, i32 %b) { | |
| ; CHECK-LABEL: define void @no_sink_constant_bundle_operand_in_else( | |
| ; CHECK-SAME: i1 [[C:%.*]], i32 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[BB:.*:]] | |
| ; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]] | |
| ; CHECK: [[THEN]]: | |
| ; CHECK-NEXT: call void @clobber1() | |
| ; CHECK-NEXT: call void @f() [ "deopt"(i32 [[B]]) ] | |
| ; CHECK-NEXT: br label %[[EXIT:.*]] | |
| ; CHECK: [[ELSE]]: | |
| ; CHECK-NEXT: call void @clobber2() | |
| ; CHECK-NEXT: call void @f() [ "deopt"(i32 7) ] | |
| ; CHECK-NEXT: br label %[[EXIT]] | |
| ; CHECK: [[EXIT]]: | |
| ; CHECK-NEXT: ret void | |
| ; | |
| bb: | |
| br i1 %c, label %then, label %else | |
| then: | |
| call void @clobber1() | |
| call void @f() [ "deopt"(i32 %b) ] | |
| br label %exit | |
| else: | |
| call void @clobber2() | |
| call void @f() [ "deopt"(i32 7) ] | |
| br label %exit | |
| exit: | |
| ret void | |
| } |