Files
clang-p2996/llvm/test/Transforms/GVN/metadata.ll
Guozhi Wei 84bcfa0e1b [GVN] Improve PRE on load instructions
This patch implements the enhancement proposed by
https://github.com/llvm/llvm-project/issues/59312.

Suppose we have following code

   v0 = load %addr
   br %LoadBB

LoadBB:
   v1 = load %addr
   ...

PredBB:
   ...
   br %cond, label %LoadBB, label %SuccBB

SuccBB:
   v2 = load %addr
   ...

Instruction v1 in LoadBB is partially redundant, edge (PredBB, LoadBB) is a
critical edge. SuccBB is another successor of PredBB, it contains another load
v2 which is identical to v1. Current GVN splits the critical edge
(PredBB, LoadBB) and inserts a new load in it. A better method is move the load
of v2 into PredBB, then v1 can be changed to a PHI instruction.

If there are two or more similar predecessors, like the test case in the bug
entry, current GVN simply gives up because otherwise it needs to split multiple
critical edges. But we can move all loads in successor blocks into predecessors.

Differential Revision: https://reviews.llvm.org/D141712
2023-06-06 19:45:34 +00:00

439 lines
14 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --version 2
; RUN: opt -passes=gvn -S < %s | FileCheck %s
declare void @use.ptr(ptr) memory(none)
declare void @use.i64(i64) memory(none)
declare void @use.i32(i32) memory(none)
define i32 @test1(ptr %p) {
; CHECK-LABEL: define i32 @test1
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !0
%b = load i32, ptr %p, !range !0
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test2(ptr %p) {
; CHECK-LABEL: define i32 @test2
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !0
%b = load i32, ptr %p
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test3(ptr %p) {
; CHECK-LABEL: define i32 @test3
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !0
%b = load i32, ptr %p, !range !1
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test4(ptr %p) {
; CHECK-LABEL: define i32 @test4
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG2:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !0
%b = load i32, ptr %p, !range !2
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test5(ptr %p) {
; CHECK-LABEL: define i32 @test5
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG3:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !3
%b = load i32, ptr %p, !range !4
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test6(ptr %p) {
; CHECK-LABEL: define i32 @test6
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG4:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !5
%b = load i32, ptr %p, !range !6
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test7(ptr %p) {
; CHECK-LABEL: define i32 @test7
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG5:![0-9]+]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !7
%b = load i32, ptr %p, !range !8
%c = add i32 %a, %b
ret i32 %c
}
define i32 @test8(ptr %p) {
; CHECK-LABEL: define i32 @test8
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !9
%b = load i32, ptr %p, !range !10
%c = add i32 %a, %b
ret i32 %c
}
define i32 @load_noundef_load(ptr %p) {
; CHECK-LABEL: define i32 @load_noundef_load
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG0]], !noundef !6
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !0, !noundef !11
%b = load i32, ptr %p, !range !1
%c = add i32 %a, %b
ret i32 %c
}
define i32 @load_load_noundef(ptr %p) {
; CHECK-LABEL: define i32 @load_load_noundef
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P]], align 4, !range [[RNG1]]
; CHECK-NEXT: [[C:%.*]] = add i32 [[A]], [[A]]
; CHECK-NEXT: ret i32 [[C]]
;
%a = load i32, ptr %p, !range !0
%b = load i32, ptr %p, !range !1, !noundef !11
%c = add i32 %a, %b
ret i32 %c
}
define void @load_dereferenceable_dominating(ptr %p) {
; CHECK-LABEL: define void @load_dereferenceable_dominating
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable !7
; CHECK-NEXT: call void @use.ptr(ptr [[A]])
; CHECK-NEXT: call void @use.ptr(ptr [[A]])
; CHECK-NEXT: ret void
;
%a = load ptr, ptr %p, !dereferenceable !{i64 10}
%b = load ptr, ptr %p
call void @use.ptr(ptr %a)
call void @use.ptr(ptr %b)
ret void
}
define void @load_dereferenceable_not_dominating(ptr %p) {
; CHECK-LABEL: define void @load_dereferenceable_not_dominating
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[A:%.*]] = load ptr, ptr [[P]], align 8
; CHECK-NEXT: call void @use.ptr(ptr [[A]])
; CHECK-NEXT: call void @use.ptr(ptr [[A]])
; CHECK-NEXT: ret void
;
%a = load ptr, ptr %p
%b = load ptr, ptr %p, !dereferenceable !{i64 10}
call void @use.ptr(ptr %a)
call void @use.ptr(ptr %b)
ret void
}
define void @load_ptr_nonnull_to_i64(ptr %p) {
; CHECK-LABEL: define void @load_ptr_nonnull_to_i64
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load ptr, ptr [[P]], align 8
; CHECK-NEXT: [[VAL_INT:%.*]] = ptrtoint ptr [[VAL]] to i64
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: ret void
;
%val = load ptr, ptr %p, align 8, !nonnull !{}
%val.int = ptrtoint ptr %val to i64
%val2 = load i64, ptr %p, align 8
call void @use.i64(i64 %val.int)
call void @use.i64(i64 %val2)
ret void
}
define void @load_ptr_nonnull_noundef_to_i64(ptr %p) {
; CHECK-LABEL: define void @load_ptr_nonnull_noundef_to_i64
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load ptr, ptr [[P]], align 8, !nonnull !6, !noundef !6
; CHECK-NEXT: [[VAL_INT:%.*]] = ptrtoint ptr [[VAL]] to i64
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: ret void
;
%val = load ptr, ptr %p, align 8, !nonnull !{}, !noundef !{}
%val.int = ptrtoint ptr %val to i64
%val2 = load i64, ptr %p, align 8
call void @use.i64(i64 %val.int)
call void @use.i64(i64 %val2)
ret void
}
define void @load_ptr_invariant_load_to_i64(ptr %p) {
; CHECK-LABEL: define void @load_ptr_invariant_load_to_i64
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load ptr, ptr [[P]], align 8, !invariant.load !6
; CHECK-NEXT: [[VAL_INT:%.*]] = ptrtoint ptr [[VAL]] to i64
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: ret void
;
%val = load ptr, ptr %p, align 8, !invariant.load !{}
%val.int = ptrtoint ptr %val to i64
%val2 = load i64, ptr %p, align 8
call void @use.i64(i64 %val.int)
call void @use.i64(i64 %val2)
ret void
}
define void @load_ptr_dereferenceable_to_i64(ptr %p) {
; CHECK-LABEL: define void @load_ptr_dereferenceable_to_i64
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable !7
; CHECK-NEXT: [[VAL_INT:%.*]] = ptrtoint ptr [[VAL]] to i64
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: ret void
;
%val = load ptr, ptr %p, align 8, !dereferenceable !{i64 10}
%val.int = ptrtoint ptr %val to i64
%val2 = load i64, ptr %p, align 8
call void @use.i64(i64 %val.int)
call void @use.i64(i64 %val2)
ret void
}
define void @load_ptr_dereferenceable_or_null_to_i64(ptr %p) {
; CHECK-LABEL: define void @load_ptr_dereferenceable_or_null_to_i64
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load ptr, ptr [[P]], align 8, !dereferenceable_or_null !7
; CHECK-NEXT: [[VAL_INT:%.*]] = ptrtoint ptr [[VAL]] to i64
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: ret void
;
%val = load ptr, ptr %p, align 8, !dereferenceable_or_null !{i64 10}
%val.int = ptrtoint ptr %val to i64
%val2 = load i64, ptr %p, align 8
call void @use.i64(i64 %val.int)
call void @use.i64(i64 %val2)
ret void
}
define void @load_ptr_nonnull_to_i32(ptr %p) {
; CHECK-LABEL: define void @load_ptr_nonnull_to_i32
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load ptr, ptr [[P]], align 8
; CHECK-NEXT: [[VAL_INT:%.*]] = ptrtoint ptr [[VAL]] to i64
; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[VAL_INT]] to i32
; CHECK-NEXT: call void @use.i64(i64 [[VAL_INT]])
; CHECK-NEXT: call void @use.i32(i32 [[TMP1]])
; CHECK-NEXT: ret void
;
%val = load ptr, ptr %p, align 8, !nonnull !{}
%val.int = ptrtoint ptr %val to i64
%val2 = load i32, ptr %p, align 8
call void @use.i64(i64 %val.int)
call void @use.i32(i32 %val2)
ret void
}
define void @load_i64_range_to_i32_range(ptr %p) {
; CHECK-LABEL: define void @load_i64_range_to_i32_range
; CHECK-SAME: (ptr [[P:%.*]]) {
; CHECK-NEXT: [[VAL:%.*]] = load i64, ptr [[P]], align 8
; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[VAL]] to i32
; CHECK-NEXT: call void @use.i64(i64 [[VAL]])
; CHECK-NEXT: call void @use.i32(i32 [[TMP1]])
; CHECK-NEXT: ret void
;
%val = load i64, ptr %p, align 8, !range !{i64 0, i64 10}
%val2 = load i32, ptr %p, align 8, !range !{i32 0, i32 10}
call void @use.i64(i64 %val)
call void @use.i32(i32 %val2)
ret void
}
define i64 @load_is_stored(ptr %p, ptr %p2) {
; CHECK-LABEL: define i64 @load_is_stored
; CHECK-SAME: (ptr [[P:%.*]], ptr [[P2:%.*]]) {
; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P]], align 4, !range [[RNG8:![0-9]+]]
; CHECK-NEXT: store i64 [[V1]], ptr [[P2]], align 4
; CHECK-NEXT: ret i64 [[V1]]
;
%v1 = load i64, ptr %p, !range !{i64 0, i64 10}
store i64 %v1, ptr %p2
%v2 = load i64, ptr %p2
ret i64 %v2
}
define void @non_local_dominating(i1 %c, ptr %p) {
; CHECK-LABEL: define void @non_local_dominating
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P]], align 4, !range [[RNG9:![0-9]+]]
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
; CHECK: if:
; CHECK-NEXT: br label [[JOIN]]
; CHECK: join:
; CHECK-NEXT: call void @use.i64(i64 [[V1]])
; CHECK-NEXT: call void @use.i64(i64 [[V1]])
; CHECK-NEXT: ret void
;
%v1 = load i64, ptr %p, !range !{i64 0, i64 10}
br i1 %c, label %if, label %join
if:
br label %join
join:
%v2 = load i64, ptr %p, !range !{i64 20, i64 30}
call void @use.i64(i64 %v1)
call void @use.i64(i64 %v2)
ret void
}
define void @non_local_non_dominating(i1 %c, ptr %p) {
; CHECK-LABEL: define void @non_local_non_dominating
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P]], align 4, !range [[RNG9]]
; CHECK-NEXT: call void @use.i64(i64 [[V1]])
; CHECK-NEXT: br label [[JOIN:%.*]]
; CHECK: else:
; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[P]], align 4, !range [[RNG10:![0-9]+]]
; CHECK-NEXT: call void @use.i64(i64 [[V2]])
; CHECK-NEXT: br label [[JOIN]]
; CHECK: join:
; CHECK-NEXT: [[V3:%.*]] = phi i64 [ [[V2]], [[ELSE]] ], [ [[V1]], [[IF]] ]
; CHECK-NEXT: call void @use.i64(i64 [[V3]])
; CHECK-NEXT: ret void
;
br i1 %c, label %if, label %else
if:
%v1 = load i64, ptr %p, !range !{i64 0, i64 10}
call void @use.i64(i64 %v1)
br label %join
else:
%v2 = load i64, ptr %p, !range !{i64 10, i64 20}
call void @use.i64(i64 %v2)
br label %join
join:
%v3 = load i64, ptr %p, !range !{i64 20, i64 30}
call void @use.i64(i64 %v3)
ret void
}
define void @non_local_coerced(i1 %c, ptr %p) {
; CHECK-LABEL: define void @non_local_coerced
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[V1_PTR:%.*]] = load ptr, ptr [[P]], align 8
; CHECK-NEXT: [[V1:%.*]] = ptrtoint ptr [[V1_PTR]] to i64
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
; CHECK: if:
; CHECK-NEXT: br label [[JOIN]]
; CHECK: join:
; CHECK-NEXT: call void @use.i64(i64 [[V1]])
; CHECK-NEXT: call void @use.i64(i64 [[V1]])
; CHECK-NEXT: ret void
;
%v1.ptr = load ptr, ptr %p, !nonnull !{}
%v1 = ptrtoint ptr %v1.ptr to i64
br i1 %c, label %if, label %join
if:
br label %join
join:
%v2 = load i64, ptr %p, !range !{i64 20, i64 30}
call void @use.i64(i64 %v1)
call void @use.i64(i64 %v2)
ret void
}
define void @non_local_pre(i1 %c, ptr %p) {
; CHECK-LABEL: define void @non_local_pre
; CHECK-SAME: (i1 [[C:%.*]], ptr [[P:%.*]]) {
; CHECK-NEXT: [[V2_PRE:%.*]] = load i64, ptr [[P]], align 4, !range [[RNG9]]
; CHECK-NEXT: br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
; CHECK: if:
; CHECK-NEXT: call void @use.i64(i64 [[V2_PRE]])
; CHECK-NEXT: br label [[JOIN]]
; CHECK: join:
; CHECK-NEXT: call void @use.i64(i64 [[V2_PRE]])
; CHECK-NEXT: ret void
;
br i1 %c, label %if, label %join
if:
%v1 = load i64, ptr %p, !range !{i64 0, i64 10}
call void @use.i64(i64 %v1)
br label %join
join:
%v2 = load i64, ptr %p, !range !{i64 20, i64 30}
call void @use.i64(i64 %v2)
ret void
}
!0 = !{i32 0, i32 2}
!1 = !{i32 3, i32 5}
!2 = !{i32 2, i32 5}
!3 = !{i32 -5, i32 -2}
!4 = !{i32 1, i32 5}
!5 = !{i32 10, i32 1}
!6 = !{i32 12, i32 16}
!7 = !{i32 1, i32 2, i32 3, i32 4}
!8 = !{i32 5, i32 1}
!9 = !{i32 1, i32 5}
!10 = !{i32 5, i32 1}
!11 = !{}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { memory(none) }
;.
; CHECK: [[RNG0]] = !{i32 0, i32 2}
; CHECK: [[RNG1]] = !{i32 0, i32 2, i32 3, i32 5}
; CHECK: [[RNG2]] = !{i32 0, i32 5}
; CHECK: [[RNG3]] = !{i32 -5, i32 -2, i32 1, i32 5}
; CHECK: [[RNG4]] = !{i32 10, i32 1}
; CHECK: [[RNG5]] = !{i32 3, i32 4, i32 5, i32 2}
; CHECK: [[META6:![0-9]+]] = !{}
; CHECK: [[META7:![0-9]+]] = !{i64 10}
; CHECK: [[RNG8]] = !{i64 0, i64 10}
; CHECK: [[RNG9]] = !{i64 0, i64 10, i64 20, i64 30}
; CHECK: [[RNG10]] = !{i64 10, i64 30}
;.