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
439 lines
14 KiB
LLVM
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}
|
|
;.
|