1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
declare noalias ptr @calloc(i32, i32) nounwind allockind("alloc,zeroed") allocsize(0,1) "alloc-family"="malloc"
declare void @free(ptr) allockind("free") "alloc-family"="malloc"
; Test load from uninitialized alloca - should be removed and replaced with undef
define i32 @test_load_uninitialized_alloca() {
; CHECK-LABEL: @test_load_uninitialized_alloca(
; CHECK-NEXT: ret i32 undef
;
%a = alloca i32
%v = load i32, ptr %a
ret i32 %v
}
; Test load from zero-initialized malloc - should be removed and replaced with zero
define i32 @test_load_zero_initialized_malloc() {
; CHECK-LABEL: @test_load_zero_initialized_malloc(
; CHECK-NEXT: ret i32 0
;
%a = call ptr @calloc(i32 1, i32 4)
%v = load i32, ptr %a
call void @free(ptr %a)
ret i32 %v
}
; Test memcpy from uninitialized source - should be removed
define void @test_memcpy_from_uninitialized_alloca(ptr %dest) {
; CHECK-LABEL: @test_memcpy_from_uninitialized_alloca(
; CHECK-NEXT: ret void
;
%src = alloca i32, align 1
call void @llvm.memcpy.p0.p0.i32(ptr %src, ptr %src, i32 4, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr %dest, ptr %src, i32 4, i1 false)
ret void
}
; Test memcpy from zeroed source - should transform to memset with zero
define void @test_memcpy_from_uninitialized_calloc(ptr %dest) {
; CHECK-LABEL: @test_memcpy_from_uninitialized_calloc(
; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(16) [[DEST:%.*]], i8 0, i32 16, i1 false)
; CHECK-NEXT: ret void
;
%src = call ptr @calloc(i32 1, i32 16)
call void @llvm.memcpy.p0.p0.i32(ptr %dest, ptr %src, i32 16, i1 false)
call void @free(ptr %src)
ret void
}
; Test mixed read/write pattern - should not be removable due to write before read
define i32 @test_write_then_read_alloca() {
; CHECK-LABEL: @test_write_then_read_alloca(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: store i8 42, ptr [[A]], align 1
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: ret i32 [[V]]
;
%a = alloca i32
store i8 42, ptr %a
%v = load i32, ptr %a
ret i32 %v
}
; Test read then write pattern - should not be removable due to conflicting access
define void @test_read_then_write_alloca() {
; CHECK-LABEL: @test_read_then_write_alloca(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[A]], align 4
; CHECK-NEXT: [[V8:%.*]] = trunc i32 [[V]] to i8
; CHECK-NEXT: store i8 [[V8]], ptr [[A]], align 1
; CHECK-NEXT: ret void
;
%a = alloca i32
%v = load i32, ptr %a
%v8 = trunc i32 %v to i8
store i8 %v8, ptr %a
ret void
}
; Test load through GEP from uninitialized alloca
define i8 @test_load_gep_uninitialized_alloca() {
; CHECK-LABEL: @test_load_gep_uninitialized_alloca(
; CHECK-NEXT: ret i8 undef
;
%a = alloca [4 x i8]
%gep = getelementptr [4 x i8], ptr %a, i32 0, i32 2
%v = load i8, ptr %gep
ret i8 %v
}
; Test load through bitcast from uninitialized alloca
define i16 @test_load_bitcast_uninitialized_alloca() {
; CHECK-LABEL: @test_load_bitcast_uninitialized_alloca(
; CHECK-NEXT: ret i16 undef
;
%a = alloca i32
%bc = bitcast ptr %a to ptr
%v = load i16, ptr %bc
ret i16 %v
}
; Test memmove from zero-initialized malloc
define void @test_memmove_from_zero_initialized_malloc(ptr %dest) {
; CHECK-LABEL: @test_memmove_from_zero_initialized_malloc(
; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(32) [[DEST:%.*]], i8 0, i32 32, i1 false)
; CHECK-NEXT: ret void
;
%src = call ptr @calloc(i32 8, i32 4)
call void @llvm.memmove.p0.p0.i32(ptr %dest, ptr %src, i32 32, i1 false)
call void @free(ptr %src)
ret void
}
; Test multiple loads from same uninitialized alloca
define { i32, i32 } @test_multiple_loads_uninitialized_alloca() {
; CHECK-LABEL: @test_multiple_loads_uninitialized_alloca(
; CHECK-NEXT: ret { i32, i32 } undef
;
%a = alloca [2 x i32]
%gep1 = getelementptr [2 x i32], ptr %a, i32 0, i32 0
%gep2 = getelementptr [2 x i32], ptr %a, i32 0, i32 1
%v1 = load i32, ptr %gep1
%v2 = load i32, ptr %gep2
%ret = insertvalue { i32, i32 } { i32 undef, i32 poison }, i32 %v1, 0
%ret2 = insertvalue { i32, i32 } %ret, i32 %v2, 1
ret { i32, i32 } %ret2
}
; Test that volatile operations prevent removal
define i32 @test_volatile_load_prevents_removal() {
; CHECK-LABEL: @test_volatile_load_prevents_removal(
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT: [[V:%.*]] = load volatile i32, ptr [[A]], align 4
; CHECK-NEXT: ret i32 [[V]]
;
%a = alloca i32
%v = load volatile i32, ptr %a
ret i32 %v
}
|