202 lines
6.2 KiB
LLVM
202 lines
6.2 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
|
|
; Test if the !invariant.load metadata is maintained by GVN.
|
|
; RUN: opt -passes=gvn -S < %s | FileCheck %s
|
|
|
|
define i32 @test1(ptr nocapture %p, ptr nocapture %q) {
|
|
; CHECK-LABEL: define i32 @test1
|
|
; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr nocapture [[Q:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !0
|
|
; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[X]] to i8
|
|
; CHECK-NEXT: store i8 [[CONV]], ptr [[Q]], align 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X]], 1
|
|
; CHECK-NEXT: ret i32 [[ADD]]
|
|
;
|
|
entry:
|
|
%x = load i32, ptr %p, align 4, !invariant.load !0
|
|
%conv = trunc i32 %x to i8
|
|
store i8 %conv, ptr %q, align 1
|
|
%y = load i32, ptr %p, align 4, !invariant.load !0
|
|
%add = add i32 %y, 1
|
|
ret i32 %add
|
|
}
|
|
|
|
define i32 @test2(ptr nocapture %p, ptr nocapture %q) {
|
|
; CHECK-LABEL: define i32 @test2
|
|
; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr nocapture [[Q:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[X]] to i8
|
|
; CHECK-NEXT: store i8 [[CONV]], ptr [[Q]], align 1
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X]], 1
|
|
; CHECK-NEXT: ret i32 [[ADD]]
|
|
;
|
|
entry:
|
|
%x = load i32, ptr %p, align 4
|
|
%conv = trunc i32 %x to i8
|
|
store i8 %conv, ptr %q, align 1
|
|
%y = load i32, ptr %p, align 4, !invariant.load !0
|
|
%add = add i32 %y, 1
|
|
ret i32 %add
|
|
}
|
|
|
|
; With the invariant.load metadata, what would otherwise
|
|
; be a case for PRE becomes a full redundancy.
|
|
define i32 @test3(i1 %cnd, ptr %p, ptr %q) {
|
|
; CHECK-LABEL: define i32 @test3
|
|
; CHECK-SAME: (i1 [[CND:%.*]], ptr [[P:%.*]], ptr [[Q:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[CND]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: store i32 5, ptr [[Q]], align 4
|
|
; CHECK-NEXT: br label [[BB2]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%v1 = load i32, ptr %p
|
|
br i1 %cnd, label %bb1, label %bb2
|
|
|
|
bb1:
|
|
store i32 5, ptr %q
|
|
br label %bb2
|
|
|
|
bb2:
|
|
%v2 = load i32, ptr %p, !invariant.load !0
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
; This test is here to document a case which doesn't optimize
|
|
; as well as it could.
|
|
define i32 @test4(i1 %cnd, ptr %p, ptr %q) {
|
|
; CHECK-LABEL: define i32 @test4
|
|
; CHECK-SAME: (i1 [[CND:%.*]], ptr [[P:%.*]], ptr [[Q:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !0
|
|
; CHECK-NEXT: br i1 [[CND]], label [[BB1:%.*]], label [[BB2:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: store i32 5, ptr [[Q]], align 4
|
|
; CHECK-NEXT: [[V2_PRE:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: br label [[BB2]]
|
|
; CHECK: bb2:
|
|
; CHECK-NEXT: [[V2:%.*]] = phi i32 [ [[V2_PRE]], [[BB1]] ], [ [[V1]], [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
; %v2 is redundant, but GVN currently doesn't catch that
|
|
entry:
|
|
%v1 = load i32, ptr %p, !invariant.load !0
|
|
br i1 %cnd, label %bb1, label %bb2
|
|
|
|
bb1:
|
|
store i32 5, ptr %q
|
|
br label %bb2
|
|
|
|
bb2:
|
|
%v2 = load i32, ptr %p
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
; Checks that we return the mustalias store as a def
|
|
; so that it contributes to value forwarding. Note
|
|
; that we could and should remove the store too.
|
|
define i32 @test5(i1 %cnd, ptr %p) {
|
|
; CHECK-LABEL: define i32 @test5
|
|
; CHECK-SAME: (i1 [[CND:%.*]], ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: store i32 5, ptr [[P]], align 4
|
|
; CHECK-NEXT: ret i32 5
|
|
;
|
|
entry:
|
|
%v1 = load i32, ptr %p, !invariant.load !0
|
|
store i32 5, ptr %p ;; must alias store, want to exploit
|
|
%v2 = load i32, ptr %p, !invariant.load !0
|
|
ret i32 %v2
|
|
}
|
|
|
|
|
|
declare void @foo()
|
|
|
|
; Clobbering (mayalias) stores, even in function calls, can be ignored
|
|
define i32 @test6(i1 %cnd, ptr %p) {
|
|
; CHECK-LABEL: define i32 @test6
|
|
; CHECK-SAME: (i1 [[CND:%.*]], ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: call void @foo()
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%v1 = load i32, ptr %p, !invariant.load !0
|
|
call void @foo()
|
|
%v2 = load i32, ptr %p, !invariant.load !0
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
declare noalias ptr @bar(...)
|
|
|
|
; Same as previous, but a function with a noalias result (since they're handled
|
|
; differently in MDA)
|
|
define i32 @test7(i1 %cnd, ptr %p) {
|
|
; CHECK-LABEL: define i32 @test7
|
|
; CHECK-SAME: (i1 [[CND:%.*]], ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[TMP0:%.*]] = call ptr (...) @bar(ptr [[P]])
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%v1 = load i32, ptr %p, !invariant.load !0
|
|
call ptr (...) @bar(ptr %p)
|
|
%v2 = load i32, ptr %p, !invariant.load !0
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
define i32 @test8(i1 %cnd, ptr %p) {
|
|
; CHECK-LABEL: define i32 @test8
|
|
; CHECK-SAME: (i1 [[CND:%.*]], ptr [[P:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !0
|
|
; CHECK-NEXT: br i1 [[CND]], label [[TAKEN:%.*]], label [[MERGE:%.*]]
|
|
; CHECK: taken:
|
|
; CHECK-NEXT: [[P2:%.*]] = call ptr (...) @bar(ptr [[P]])
|
|
; CHECK-NEXT: [[V2_PRE:%.*]] = load i32, ptr [[P2]], align 4, !invariant.load !0
|
|
; CHECK-NEXT: br label [[MERGE]]
|
|
; CHECK: merge:
|
|
; CHECK-NEXT: [[V2:%.*]] = phi i32 [ [[V1]], [[ENTRY:%.*]] ], [ [[V2_PRE]], [[TAKEN]] ]
|
|
; CHECK-NEXT: [[P3:%.*]] = phi ptr [ [[P]], [[ENTRY]] ], [ [[P2]], [[TAKEN]] ]
|
|
; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
entry:
|
|
%v1 = load i32, ptr %p, !invariant.load !0
|
|
br i1 %cnd, label %taken, label %merge
|
|
taken:
|
|
%p2 = call ptr (...) @bar(ptr %p)
|
|
br label %merge
|
|
merge:
|
|
%p3 = phi ptr [%p, %entry], [%p2, %taken]
|
|
%v2 = load i32, ptr %p3, !invariant.load !0
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
define i32 @metadata_preservation(ptr nocapture %p, ptr nocapture %q) {
|
|
; CHECK-LABEL: define i32 @metadata_preservation
|
|
; CHECK-SAME: (ptr nocapture [[P:%.*]], ptr nocapture [[Q:%.*]]) {
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X:%.*]] = load i32, ptr [[P]], align 4, !invariant.load !0
|
|
; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X]], [[X]]
|
|
; CHECK-NEXT: ret i32 [[ADD]]
|
|
;
|
|
entry:
|
|
%x = load i32, ptr %p, align 4, !invariant.load !0
|
|
%y = load i32, ptr %p, align 4
|
|
%add = add i32 %x, %y
|
|
ret i32 %add
|
|
}
|
|
|
|
!0 = !{ }
|
|
|