[BOLT] Add --pad-funcs-before=func:n (#117924)
[llvm-project.git] / llvm / test / Instrumentation / MemorySanitizer / msan_asm_conservative.ll
blob1b9ffaf9e505a552f78ed1638b78d7ea70a4c251
1 ; Test for handling of asm constraints in MSan instrumentation.
2 ; RUN: opt < %s -msan-check-access-address=0 -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck %s
3 ; RUN: opt < %s -msan-check-access-address=0 -S -passes=msan 2>&1 | FileCheck --check-prefixes=CHECK,USER-CONS %s
4 ; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck --check-prefixes=CHECK,KMSAN %s
5 ; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck --check-prefixes=CHECK,KMSAN,CHECK-CONS %s
7 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
8 target triple = "x86_64-unknown-linux-gnu"
10 %struct.pair = type { i32, i32 }
11 %struct.large = type { [33 x i8] }
13 @id1 = common dso_local global i32 0, align 4
14 @is1 = common dso_local global i32 0, align 4
15 @id2 = common dso_local global i32 0, align 4
16 @is2 = common dso_local global i32 0, align 4
17 @id3 = common dso_local global i32 0, align 4
18 @pair2 = common dso_local global %struct.pair zeroinitializer, align 4
19 @pair1 = common dso_local global %struct.pair zeroinitializer, align 4
20 @c2 = common dso_local global i8 0, align 1
21 @c1 = common dso_local global i8 0, align 1
22 @memcpy_d1 = common dso_local global ptr null, align 8
23 @memcpy_d2 = common dso_local global ptr null, align 8
24 @memcpy_s1 = common dso_local global ptr null, align 8
25 @memcpy_s2 = common dso_local global ptr null, align 8
26 @large = common dso_local global %struct.pair zeroinitializer, align 4
28 ; The functions below were generated from a C source that contains declarations like follows:
29 ;   void f1() {
30 ;     asm("" : "=r" (id1) : "r" (is1));
31 ;   }
32 ; with corresponding input/output constraints.
33 ; Note that the assembly statement is always empty, as MSan doesn't look at it anyway.
35 ; One input register, one output register:
36 ;   asm("" : "=r" (id1) : "r" (is1));
37 define dso_local void @f_1i_1o_reg() sanitize_memory {
38 entry:
39   %0 = load i32, ptr @is1, align 4
40   %1 = call i32 asm "", "=r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0)
41   store i32 %1, ptr @id1, align 4
42   ret void
45 ; CHECK-LABEL: @f_1i_1o_reg
46 ; CHECK: [[IS1_F1:%.*]] = load i32, ptr @is1, align 4
47 ; CHECK: call void @__msan_warning
48 ; CHECK: call i32 asm "",{{.*}}(i32 [[IS1_F1]])
49 ; KMSAN: [[PACK1_F1:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
50 ; KMSAN: [[EXT1_F1:%.*]] = extractvalue { ptr, ptr } [[PACK1_F1]], 0
51 ; KMSAN: store i32 0, ptr [[EXT1_F1]]
54 ; Two input registers, two output registers:
55 ;   asm("" : "=r" (id1), "=r" (id2) : "r" (is1), "r"(is2));
56 define dso_local void @f_2i_2o_reg() sanitize_memory {
57 entry:
58   %0 = load i32, ptr @is1, align 4
59   %1 = load i32, ptr @is2, align 4
60   %2 = call { i32, i32 } asm "", "=r,=r,r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1)
61   %asmresult = extractvalue { i32, i32 } %2, 0
62   %asmresult1 = extractvalue { i32, i32 } %2, 1
63   store i32 %asmresult, ptr @id1, align 4
64   store i32 %asmresult1, ptr @id2, align 4
65   ret void
68 ; CHECK-LABEL: @f_2i_2o_reg
69 ; CHECK: [[IS1_F2:%.*]] = load i32, ptr @is1, align 4
70 ; CHECK: [[IS2_F2:%.*]] = load i32, ptr @is2, align 4
71 ; CHECK: call void @__msan_warning
72 ; KMSAN: call void @__msan_warning
73 ; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[IS1_F2]], i32 [[IS2_F2]])
74 ; KMSAN: [[PACK1_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
75 ; KMSAN: [[EXT1_F2:%.*]] = extractvalue { ptr, ptr } [[PACK1_F2]], 0
76 ; KMSAN: store i32 0, ptr [[EXT1_F2]]
77 ; KMSAN: [[PACK2_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
78 ; KMSAN: [[EXT2_F2:%.*]] = extractvalue { ptr, ptr } [[PACK2_F2]], 0
79 ; KMSAN: store i32 0, ptr [[EXT2_F2]]
81 ; Input same as output, used twice:
82 ;   asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r" (id2));
83 define dso_local void @f_2i_2o_reuse2_reg() sanitize_memory {
84 entry:
85   %0 = load i32, ptr @id1, align 4
86   %1 = load i32, ptr @id2, align 4
87   %2 = call { i32, i32 } asm "", "=r,=r,r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1)
88   %asmresult = extractvalue { i32, i32 } %2, 0
89   %asmresult1 = extractvalue { i32, i32 } %2, 1
90   store i32 %asmresult, ptr @id1, align 4
91   store i32 %asmresult1, ptr @id2, align 4
92   ret void
95 ; CHECK-LABEL: @f_2i_2o_reuse2_reg
96 ; CHECK: [[ID1_F3:%.*]] = load i32, ptr @id1, align 4
97 ; CHECK: [[ID2_F3:%.*]] = load i32, ptr @id2, align 4
98 ; CHECK: call void @__msan_warning
99 ; KMSAN: call void @__msan_warning
100 ; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F3]], i32 [[ID2_F3]])
101 ; KMSAN: [[PACK1_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
102 ; KMSAN: [[EXT1_F3:%.*]] = extractvalue { ptr, ptr } [[PACK1_F3]], 0
103 ; KMSAN: store i32 0, ptr [[EXT1_F3]]
104 ; KMSAN: [[PACK2_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
105 ; KMSAN: [[EXT2_F3:%.*]] = extractvalue { ptr, ptr } [[PACK2_F3]], 0
106 ; KMSAN: store i32 0, ptr [[EXT2_F3]]
109 ; One of the input registers is also an output:
110 ;   asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r"(is1));
111 define dso_local void @f_2i_2o_reuse1_reg() sanitize_memory {
112 entry:
113   %0 = load i32, ptr @id1, align 4
114   %1 = load i32, ptr @is1, align 4
115   %2 = call { i32, i32 } asm "", "=r,=r,r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1)
116   %asmresult = extractvalue { i32, i32 } %2, 0
117   %asmresult1 = extractvalue { i32, i32 } %2, 1
118   store i32 %asmresult, ptr @id1, align 4
119   store i32 %asmresult1, ptr @id2, align 4
120   ret void
123 ; CHECK-LABEL: @f_2i_2o_reuse1_reg
124 ; CHECK: [[ID1_F4:%.*]] = load i32, ptr @id1, align 4
125 ; CHECK: [[IS1_F4:%.*]] = load i32, ptr @is1, align 4
126 ; CHECK: call void @__msan_warning
127 ; KMSAN: call void @__msan_warning
128 ; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F4]], i32 [[IS1_F4]])
129 ; KMSAN: [[PACK1_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
130 ; KMSAN: [[EXT1_F4:%.*]] = extractvalue { ptr, ptr } [[PACK1_F4]], 0
131 ; KMSAN: store i32 0, ptr [[EXT1_F4]]
132 ; KMSAN: [[PACK2_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
133 ; KMSAN: [[EXT2_F4:%.*]] = extractvalue { ptr, ptr } [[PACK2_F4]], 0
134 ; KMSAN: store i32 0, ptr [[EXT2_F4]]
137 ; One input register, three output registers:
138 ;   asm("" : "=r" (id1), "=r" (id2), "=r" (id3) : "r" (is1));
139 define dso_local void @f_1i_3o_reg() sanitize_memory {
140 entry:
141   %0 = load i32, ptr @is1, align 4
142   %1 = call { i32, i32, i32 } asm "", "=r,=r,=r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0)
143   %asmresult = extractvalue { i32, i32, i32 } %1, 0
144   %asmresult1 = extractvalue { i32, i32, i32 } %1, 1
145   %asmresult2 = extractvalue { i32, i32, i32 } %1, 2
146   store i32 %asmresult, ptr @id1, align 4
147   store i32 %asmresult1, ptr @id2, align 4
148   store i32 %asmresult2, ptr @id3, align 4
149   ret void
152 ; CHECK-LABEL: @f_1i_3o_reg
153 ; CHECK: [[IS1_F5:%.*]] = load i32, ptr @is1, align 4
154 ; CHECK: call void @__msan_warning
155 ; CHECK: call { i32, i32, i32 } asm "",{{.*}}(i32 [[IS1_F5]])
156 ; KMSAN: [[PACK1_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
157 ; KMSAN: [[EXT1_F5:%.*]] = extractvalue { ptr, ptr } [[PACK1_F5]], 0
158 ; KMSAN: store i32 0, ptr [[EXT1_F5]]
159 ; KMSAN: [[PACK2_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
160 ; KMSAN: [[EXT2_F5:%.*]] = extractvalue { ptr, ptr } [[PACK2_F5]], 0
161 ; KMSAN: store i32 0, ptr [[EXT2_F5]]
162 ; KMSAN: [[PACK3_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id3{{.*}})
163 ; KMSAN: [[EXT3_F5:%.*]] = extractvalue { ptr, ptr } [[PACK3_F5]], 0
164 ; KMSAN: store i32 0, ptr [[EXT3_F5]]
167 ; 2 input memory args, 2 output memory args:
168 ;  asm("" : "=m" (id1), "=m" (id2) : "m" (is1), "m"(is2))
169 define dso_local void @f_2i_2o_mem() sanitize_memory {
170 entry:
171   call void asm "", "=*m,=*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, ptr elementtype(i32) @id2, ptr elementtype(i32) @is1, ptr elementtype(i32) @is2)
172   ret void
175 ; CHECK-LABEL: @f_2i_2o_mem
176 ; USER-CONS:  store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
177 ; USER-CONS:  store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
178 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
179 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id2{{.*}}, i64 4)
180 ; CHECK: call void asm "", "=*m,=*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, ptr elementtype(i32) @id2, ptr elementtype(i32) @is1, ptr elementtype(i32) @is2)
183 ; Same input and output passed as both memory and register:
184 ;  asm("" : "=r" (id1), "=m"(id1) : "r"(is1), "m"(is1));
185 define dso_local void @f_1i_1o_memreg() sanitize_memory {
186 entry:
187   %0 = load i32, ptr @is1, align 4
188   %1 = call i32 asm "", "=r,=*m,r,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, i32 %0, ptr elementtype(i32) @is1)
189   store i32 %1, ptr @id1, align 4
190   ret void
193 ; CHECK-LABEL: @f_1i_1o_memreg
194 ; CHECK: [[IS1_F7:%.*]] = load i32, ptr @is1, align 4
195 ; USER-CONS:  store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
196 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
197 ; CHECK: call void @__msan_warning
198 ; CHECK: call i32 asm "", "=r,=*m,r,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, i32 [[IS1_F7]], ptr elementtype(i32) @is1)
200 ; Three outputs, first and last returned via regs, second via mem:
201 ;  asm("" : "=r" (id1), "=m"(id2), "=r" (id3):);
202 define dso_local void @f_3o_reg_mem_reg() sanitize_memory {
203 entry:
204   %0 = call { i32, i32 } asm "", "=r,=*m,=r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id2)
205   %asmresult = extractvalue { i32, i32 } %0, 0
206   %asmresult1 = extractvalue { i32, i32 } %0, 1
207   store i32 %asmresult, ptr @id1, align 4
208   store i32 %asmresult1, ptr @id3, align 4
209   ret void
212 ; CHECK-LABEL: @f_3o_reg_mem_reg
213 ; USER-CONS:  store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
214 ; CHECK-CONS: call void @__msan_instrument_asm_store(ptr @id2, i64 4)
215 ; CHECK: call { i32, i32 } asm "", "=r,=*m,=r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id2)
218 ; Three inputs and three outputs of different types: a pair, a char, a function pointer.
219 ; Everything is meant to be passed in registers, but LLVM chooses to return the integer pair by pointer:
220 ;  asm("" : "=r" (pair2), "=r" (c2), "=r" (memcpy_d1) : "r"(pair1), "r"(c1), "r"(memcpy_s1));
221 define dso_local void @f_3i_3o_complex_reg() sanitize_memory {
222 entry:
223   %0 = load i64, ptr @pair1, align 4
224   %1 = load i8, ptr @c1, align 1
225   %2 = load ptr, ptr @memcpy_s1, align 8
226   %3 = call { i8, ptr } asm "", "=*r,=r,=r,r,r,r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.pair) @pair2, i64 %0, i8 %1, ptr %2)
227   %asmresult = extractvalue { i8, ptr } %3, 0
228   %asmresult1 = extractvalue { i8, ptr } %3, 1
229   store i8 %asmresult, ptr @c2, align 1
230   store ptr %asmresult1, ptr @memcpy_d1, align 8
231   ret void
234 ; CHECK-LABEL: @f_3i_3o_complex_reg
235 ; CHECK: [[PAIR1_F9:%.*]] = load {{.*}} @pair1
236 ; CHECK: [[C1_F9:%.*]] = load {{.*}} @c1
237 ; CHECK: [[MEMCPY_S1_F9:%.*]] = load {{.*}} @memcpy_s1
238 ; USER-CONS:  store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
239 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
240 ; CHECK: call void @__msan_warning
241 ; KMSAN: call void @__msan_warning
242 ; KMSAN: call void @__msan_warning
243 ; CHECK: call { i8, ptr } asm "", "=*r,=r,=r,r,r,r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.pair) @pair2, {{.*}}[[PAIR1_F9]], i8 [[C1_F9]], {{.*}} [[MEMCPY_S1_F9]])
245 ; Three inputs and three outputs of different types: a pair, a char, a function pointer.
246 ; Everything is passed in memory:
247 ;  asm("" : "=m" (pair2), "=m" (c2), "=m" (memcpy_d1) : "m"(pair1), "m"(c1), "m"(memcpy_s1));
248 define dso_local void @f_3i_3o_complex_mem() sanitize_memory {
249 entry:
250   call void asm "", "=*m,=*m,=*m,*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.pair) @pair2, ptr elementtype(i8) @c2, ptr elementtype(ptr) @memcpy_d1, ptr elementtype(%struct.pair) @pair1, ptr elementtype(i8) @c1, ptr elementtype(ptr) @memcpy_s1)
251   ret void
254 ; CHECK-LABEL: @f_3i_3o_complex_mem
255 ; USER-CONS:       store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
256 ; USER-CONS-NEXT:  store i8 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @c2 to i64), i64 87960930222080) to ptr), align 1
257 ; USER-CONS-NEXT:  store i64 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @memcpy_d1 to i64), i64 87960930222080) to ptr), align 1
258 ; CHECK-CONS:      call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
259 ; CHECK-CONS:      call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
260 ; CHECK-CONS:      call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
261 ; CHECK: call void asm "", "=*m,=*m,=*m,*m,*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.pair) @pair2, ptr elementtype(i8) @c2, ptr elementtype(ptr) @memcpy_d1, ptr elementtype(%struct.pair) @pair1, ptr elementtype(i8) @c1, ptr elementtype(ptr) @memcpy_s1)
263 ; Use memset when the size is larger.
264 define dso_local void @f_1i_1o_mem_large() sanitize_memory {
265 entry:
266   %0 = call i32 asm "", "=r,=*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.large) @large, ptr elementtype(%struct.large) @large)
267   store i32 %0, ptr @id1, align 4
268   ret void
270 ; CHECK-LABEL: @f_1i_1o_mem_large(
271 ; USER-CONS:  call void @llvm.memset.p0.i64(ptr align 1 inttoptr (i64 xor (i64 ptrtoint (ptr @large to i64), i64 87960930222080) to ptr), i8 0, i64 33, i1 false)
272 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@large{{.*}}, i64 33)
273 ; CHECK: call i32 asm "", "=r,=*m,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(%struct.large) @large, ptr elementtype(%struct.large) @large)
275 ; A simple asm goto construct to check that callbr is handled correctly:
276 ;  int asm_goto(int n) {
277 ;    int v = 1;
278 ;    asm goto("cmp %0, %1; jnz %l2;" :: "r"(n), "r"(v)::skip_label);
279 ;    return 0;
280 ;  skip_label:
281 ;    return 1;
282 ;  }
283 ; asm goto statements can't have outputs, so just make sure we check the input
284 ; and the compiler doesn't crash.
285 define dso_local i32 @asm_goto(i32 %n) sanitize_memory {
286 entry:
287   callbr void asm sideeffect "cmp $0, $1; jnz ${2:l}", "r,r,!i,~{dirflag},~{fpsr},~{flags}"(i32 %n, i32 1)
288           to label %cleanup [label %skip_label]
290 skip_label:                                       ; preds = %entry
291   br label %cleanup
293 cleanup:                                          ; preds = %entry, %skip_label
294   %retval.0 = phi i32 [ 2, %skip_label ], [ 1, %entry ]
295   ret i32 %retval.0
298 ; CHECK-LABEL: @asm_goto
299 ; KMSAN: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
300 ; KMSAN: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
301 ; KMSAN: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
302 ; KMSAN: [[LABEL]]:
303 ; KMSAN-NEXT: call void @__msan_warning