; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
; RUN: opt -S -passes="loop-mssa(loop-instsimplify,simple-loop-unswitch<nontrivial>)" < %s | FileCheck %s
@vtable = constant ptr @foo
declare void @foo() memory(none)
declare void @bar()
; The call becomes known readnone after simplification, but still have a
; MemoryAccess. Make sure this does not lead to an assertion failure.
define void @test(i1 %c) {
; CHECK-LABEL: define void @test(
; CHECK-SAME: i1 [[C:%.*]]) {
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
; CHECK: .split.us:
; CHECK-NEXT: br label [[LOOP_US:%.*]]
; CHECK: loop.us:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT_SPLIT_US:%.*]]
; CHECK: exit.split.us:
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: .split:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
br label %loop
loop:
%fn = load ptr, ptr @vtable, align 8
call void %fn()
br i1 %c, label %exit, label %loop
exit:
ret void
}
; Variant with another access after the call.
define void @test2(i1 %c, ptr %p) {
; CHECK-LABEL: define void @test2(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
; CHECK: .split.us:
; CHECK-NEXT: br label [[LOOP_US:%.*]]
; CHECK: loop.us:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[EXIT_SPLIT_US:%.*]]
; CHECK: exit.split.us:
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: .split:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
br label %loop
loop:
%fn = load ptr, ptr @vtable, align 8
call void %fn()
call void @bar()
br i1 %c, label %exit, label %loop
exit:
ret void
}
; Variant with another access after the call and no access before the call.
define void @test3(i1 %c, ptr %p) {
; CHECK-LABEL: define void @test3(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
; CHECK: .split.us:
; CHECK-NEXT: br label [[LOOP_US:%.*]]
; CHECK: loop.us:
; CHECK-NEXT: br label [[SPLIT_US:%.*]]
; CHECK: split.us:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[EXIT_SPLIT_US:%.*]]
; CHECK: exit.split.us:
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: .split:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br label [[SPLIT:%.*]]
; CHECK: split:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LOOP]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
br label %loop
loop:
%fn = load ptr, ptr @vtable, align 8
br label %split
split:
call void %fn()
call void @bar()
br i1 %c, label %exit, label %loop
exit:
ret void
}
; Variants of the above test with swapped branch destinations.
define void @test1_swapped(i1 %c) {
; CHECK-LABEL: define void @test1_swapped(
; CHECK-SAME: i1 [[C:%.*]]) {
; CHECK-NEXT: start:
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
; CHECK-NEXT: br i1 [[C_FR]], label [[START_SPLIT_US:%.*]], label [[START_SPLIT:%.*]]
; CHECK: start.split.us:
; CHECK-NEXT: br label [[LOOP_US:%.*]]
; CHECK: loop.us:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[LOOP_US]]
; CHECK: start.split:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
start:
br label %loop
loop:
%fn = load ptr, ptr @vtable, align 8
call void %fn()
br i1 %c, label %loop, label %exit
exit:
ret void
}
define void @test2_swapped(i1 %c, ptr %p) {
; CHECK-LABEL: define void @test2_swapped(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
; CHECK: .split.us:
; CHECK-NEXT: br label [[LOOP_US:%.*]]
; CHECK: loop.us:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LOOP_US]]
; CHECK: .split:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
br label %loop
loop:
%fn = load ptr, ptr @vtable, align 8
call void %fn()
call void @bar()
br i1 %c, label %loop, label %exit
exit:
ret void
}
define void @test3_swapped(i1 %c, ptr %p) {
; CHECK-LABEL: define void @test3_swapped(
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
; CHECK: .split.us:
; CHECK-NEXT: br label [[LOOP_US:%.*]]
; CHECK: loop.us:
; CHECK-NEXT: br label [[SPLIT_US:%.*]]
; CHECK: split.us:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[LOOP_US]]
; CHECK: .split:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: br label [[SPLIT:%.*]]
; CHECK: split:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @bar()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
br label %loop
loop:
%fn = load ptr, ptr @vtable, align 8
br label %split
split:
call void %fn()
call void @bar()
br i1 %c, label %loop, label %exit
exit:
ret void
}