Files
clang-p2996/llvm/test/Transforms/SimpleLoopUnswitch/memssa-readnone-access.ll
Nikita Popov d02c7931d1 [MSSA] Don't require clone creation to succeed (#76819)
Sometimes, we create a MemoryAccess for an instruction, which is later
simplified (e.g. via devirtualization) such that the new instruction has
no memory effects anymore.

If we later clone the instruction (e.g. during unswitching), then MSSA
will not create a MemoryAccess for the new instruction, triggering an
assert.

Disable the assertion (by passing CreationMustSucceed=false) and adjust
getDefiningAccessForClone() to work correctly in that case.

This PR implements the alternative suggestion by alinas from
https://github.com/llvm/llvm-project/pull/76142.
2024-01-08 10:21:25 +01:00

118 lines
3.3 KiB
LLVM

; 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
}