; 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)" < %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 }