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 | \
4 ; RUN: opt < %s -msan-check-access-address=0 -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | \
5 ; RUN: FileCheck --check-prefixes=CHECK,USER-CONS %s
6 ; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
7 ; RUN: -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck \
8 ; RUN: --check-prefixes=CHECK,KMSAN %s
9 ; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
10 ; RUN: -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck \
11 ; RUN: --check-prefixes=CHECK,KMSAN,CHECK-CONS %s
13 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
14 target triple = "x86_64-unknown-linux-gnu"
16 %struct.pair = type { i32, i32 }
18 @id1 = common dso_local global i32 0, align 4
19 @is1 = common dso_local global i32 0, align 4
20 @id2 = common dso_local global i32 0, align 4
21 @is2 = common dso_local global i32 0, align 4
22 @id3 = common dso_local global i32 0, align 4
23 @pair2 = common dso_local global %struct.pair zeroinitializer, align 4
24 @pair1 = common dso_local global %struct.pair zeroinitializer, align 4
25 @c2 = common dso_local global i8 0, align 1
26 @c1 = common dso_local global i8 0, align 1
27 @memcpy_d1 = common dso_local global ptr null, align 8
28 @memcpy_d2 = common dso_local global ptr null, align 8
29 @memcpy_s1 = common dso_local global ptr null, align 8
30 @memcpy_s2 = common dso_local global ptr null, align 8
32 ; The functions below were generated from a C source that contains declarations like follows:
34 ; asm("" : "=r" (id1) : "r" (is1));
36 ; with corresponding input/output constraints.
37 ; Note that the assembly statement is always empty, as MSan doesn't look at it anyway.
39 ; One input register, one output register:
40 ; asm("" : "=r" (id1) : "r" (is1));
41 define dso_local void @f_1i_1o_reg() sanitize_memory {
43 %0 = load i32, ptr @is1, align 4
44 %1 = call i32 asm "", "=r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0)
45 store i32 %1, ptr @id1, align 4
49 ; CHECK-LABEL: @f_1i_1o_reg
50 ; CHECK: [[IS1_F1:%.*]] = load i32, ptr @is1, align 4
51 ; CHECK: call void @__msan_warning
52 ; CHECK: call i32 asm "",{{.*}}(i32 [[IS1_F1]])
53 ; KMSAN: [[PACK1_F1:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
54 ; KMSAN: [[EXT1_F1:%.*]] = extractvalue { ptr, ptr } [[PACK1_F1]], 0
55 ; KMSAN: store i32 0, ptr [[EXT1_F1]]
58 ; Two input registers, two output registers:
59 ; asm("" : "=r" (id1), "=r" (id2) : "r" (is1), "r"(is2));
60 define dso_local void @f_2i_2o_reg() sanitize_memory {
62 %0 = load i32, ptr @is1, align 4
63 %1 = load i32, ptr @is2, align 4
64 %2 = call { i32, i32 } asm "", "=r,=r,r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1)
65 %asmresult = extractvalue { i32, i32 } %2, 0
66 %asmresult1 = extractvalue { i32, i32 } %2, 1
67 store i32 %asmresult, ptr @id1, align 4
68 store i32 %asmresult1, ptr @id2, align 4
72 ; CHECK-LABEL: @f_2i_2o_reg
73 ; CHECK: [[IS1_F2:%.*]] = load i32, ptr @is1, align 4
74 ; CHECK: [[IS2_F2:%.*]] = load i32, ptr @is2, align 4
75 ; CHECK: call void @__msan_warning
76 ; KMSAN: call void @__msan_warning
77 ; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[IS1_F2]], i32 [[IS2_F2]])
78 ; KMSAN: [[PACK1_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
79 ; KMSAN: [[EXT1_F2:%.*]] = extractvalue { ptr, ptr } [[PACK1_F2]], 0
80 ; KMSAN: store i32 0, ptr [[EXT1_F2]]
81 ; KMSAN: [[PACK2_F2:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
82 ; KMSAN: [[EXT2_F2:%.*]] = extractvalue { ptr, ptr } [[PACK2_F2]], 0
83 ; KMSAN: store i32 0, ptr [[EXT2_F2]]
85 ; Input same as output, used twice:
86 ; asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r" (id2));
87 define dso_local void @f_2i_2o_reuse2_reg() sanitize_memory {
89 %0 = load i32, ptr @id1, align 4
90 %1 = load i32, ptr @id2, align 4
91 %2 = call { i32, i32 } asm "", "=r,=r,r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1)
92 %asmresult = extractvalue { i32, i32 } %2, 0
93 %asmresult1 = extractvalue { i32, i32 } %2, 1
94 store i32 %asmresult, ptr @id1, align 4
95 store i32 %asmresult1, ptr @id2, align 4
99 ; CHECK-LABEL: @f_2i_2o_reuse2_reg
100 ; CHECK: [[ID1_F3:%.*]] = load i32, ptr @id1, align 4
101 ; CHECK: [[ID2_F3:%.*]] = load i32, ptr @id2, align 4
102 ; CHECK: call void @__msan_warning
103 ; KMSAN: call void @__msan_warning
104 ; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F3]], i32 [[ID2_F3]])
105 ; KMSAN: [[PACK1_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
106 ; KMSAN: [[EXT1_F3:%.*]] = extractvalue { ptr, ptr } [[PACK1_F3]], 0
107 ; KMSAN: store i32 0, ptr [[EXT1_F3]]
108 ; KMSAN: [[PACK2_F3:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
109 ; KMSAN: [[EXT2_F3:%.*]] = extractvalue { ptr, ptr } [[PACK2_F3]], 0
110 ; KMSAN: store i32 0, ptr [[EXT2_F3]]
113 ; One of the input registers is also an output:
114 ; asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r"(is1));
115 define dso_local void @f_2i_2o_reuse1_reg() sanitize_memory {
117 %0 = load i32, ptr @id1, align 4
118 %1 = load i32, ptr @is1, align 4
119 %2 = call { i32, i32 } asm "", "=r,=r,r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0, i32 %1)
120 %asmresult = extractvalue { i32, i32 } %2, 0
121 %asmresult1 = extractvalue { i32, i32 } %2, 1
122 store i32 %asmresult, ptr @id1, align 4
123 store i32 %asmresult1, ptr @id2, align 4
127 ; CHECK-LABEL: @f_2i_2o_reuse1_reg
128 ; CHECK: [[ID1_F4:%.*]] = load i32, ptr @id1, align 4
129 ; CHECK: [[IS1_F4:%.*]] = load i32, ptr @is1, align 4
130 ; CHECK: call void @__msan_warning
131 ; KMSAN: call void @__msan_warning
132 ; CHECK: call { i32, i32 } asm "",{{.*}}(i32 [[ID1_F4]], i32 [[IS1_F4]])
133 ; KMSAN: [[PACK1_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
134 ; KMSAN: [[EXT1_F4:%.*]] = extractvalue { ptr, ptr } [[PACK1_F4]], 0
135 ; KMSAN: store i32 0, ptr [[EXT1_F4]]
136 ; KMSAN: [[PACK2_F4:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
137 ; KMSAN: [[EXT2_F4:%.*]] = extractvalue { ptr, ptr } [[PACK2_F4]], 0
138 ; KMSAN: store i32 0, ptr [[EXT2_F4]]
141 ; One input register, three output registers:
142 ; asm("" : "=r" (id1), "=r" (id2), "=r" (id3) : "r" (is1));
143 define dso_local void @f_1i_3o_reg() sanitize_memory {
145 %0 = load i32, ptr @is1, align 4
146 %1 = call { i32, i32, i32 } asm "", "=r,=r,=r,r,~{dirflag},~{fpsr},~{flags}"(i32 %0)
147 %asmresult = extractvalue { i32, i32, i32 } %1, 0
148 %asmresult1 = extractvalue { i32, i32, i32 } %1, 1
149 %asmresult2 = extractvalue { i32, i32, i32 } %1, 2
150 store i32 %asmresult, ptr @id1, align 4
151 store i32 %asmresult1, ptr @id2, align 4
152 store i32 %asmresult2, ptr @id3, align 4
156 ; CHECK-LABEL: @f_1i_3o_reg
157 ; CHECK: [[IS1_F5:%.*]] = load i32, ptr @is1, align 4
158 ; CHECK: call void @__msan_warning
159 ; CHECK: call { i32, i32, i32 } asm "",{{.*}}(i32 [[IS1_F5]])
160 ; KMSAN: [[PACK1_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id1{{.*}})
161 ; KMSAN: [[EXT1_F5:%.*]] = extractvalue { ptr, ptr } [[PACK1_F5]], 0
162 ; KMSAN: store i32 0, ptr [[EXT1_F5]]
163 ; KMSAN: [[PACK2_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id2{{.*}})
164 ; KMSAN: [[EXT2_F5:%.*]] = extractvalue { ptr, ptr } [[PACK2_F5]], 0
165 ; KMSAN: store i32 0, ptr [[EXT2_F5]]
166 ; KMSAN: [[PACK3_F5:%.*]] = call {{.*}} @__msan_metadata_ptr_for_store_4({{.*}}@id3{{.*}})
167 ; KMSAN: [[EXT3_F5:%.*]] = extractvalue { ptr, ptr } [[PACK3_F5]], 0
168 ; KMSAN: store i32 0, ptr [[EXT3_F5]]
171 ; 2 input memory args, 2 output memory args:
172 ; asm("" : "=m" (id1), "=m" (id2) : "m" (is1), "m"(is2))
173 define dso_local void @f_2i_2o_mem() sanitize_memory {
175 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)
179 ; CHECK-LABEL: @f_2i_2o_mem
180 ; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
181 ; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
182 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
183 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id2{{.*}}, i64 4)
184 ; 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)
187 ; Same input and output passed as both memory and register:
188 ; asm("" : "=r" (id1), "=m"(id1) : "r"(is1), "m"(is1));
189 define dso_local void @f_1i_1o_memreg() sanitize_memory {
191 %0 = load i32, ptr @is1, align 4
192 %1 = call i32 asm "", "=r,=*m,r,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, i32 %0, ptr elementtype(i32) @is1)
193 store i32 %1, ptr @id1, align 4
197 ; CHECK-LABEL: @f_1i_1o_memreg
198 ; CHECK: [[IS1_F7:%.*]] = load i32, ptr @is1, align 4
199 ; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id1 to i64), i64 87960930222080) to ptr), align 1
200 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@id1{{.*}}, i64 4)
201 ; CHECK: call void @__msan_warning
202 ; CHECK: call i32 asm "", "=r,=*m,r,*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id1, i32 [[IS1_F7]], ptr elementtype(i32) @is1)
205 ; Three outputs, first and last returned via regs, second via mem:
206 ; asm("" : "=r" (id1), "=m"(id2), "=r" (id3):);
207 define dso_local void @f_3o_reg_mem_reg() sanitize_memory {
209 %0 = call { i32, i32 } asm "", "=r,=*m,=r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id2)
210 %asmresult = extractvalue { i32, i32 } %0, 0
211 %asmresult1 = extractvalue { i32, i32 } %0, 1
212 store i32 %asmresult, ptr @id1, align 4
213 store i32 %asmresult1, ptr @id3, align 4
217 ; CHECK-LABEL: @f_3o_reg_mem_reg
218 ; USER-CONS: store i32 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @id2 to i64), i64 87960930222080) to ptr), align 1
219 ; CHECK-CONS: call void @__msan_instrument_asm_store(ptr @id2, i64 4)
220 ; CHECK: call { i32, i32 } asm "", "=r,=*m,=r,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) @id2)
223 ; Three inputs and three outputs of different types: a pair, a char, a function pointer.
224 ; Everything is meant to be passed in registers, but LLVM chooses to return the integer pair by pointer:
225 ; asm("" : "=r" (pair2), "=r" (c2), "=r" (memcpy_d1) : "r"(pair1), "r"(c1), "r"(memcpy_s1));
226 define dso_local void @f_3i_3o_complex_reg() sanitize_memory {
228 %0 = load i64, ptr @pair1, align 4
229 %1 = load i8, ptr @c1, align 1
230 %2 = load ptr, ptr @memcpy_s1, align 8
231 %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)
232 %asmresult = extractvalue { i8, ptr } %3, 0
233 %asmresult1 = extractvalue { i8, ptr } %3, 1
234 store i8 %asmresult, ptr @c2, align 1
235 store ptr %asmresult1, ptr @memcpy_d1, align 8
239 ; CHECK-LABEL: @f_3i_3o_complex_reg
240 ; CHECK: [[PAIR1_F9:%.*]] = load {{.*}} @pair1
241 ; CHECK: [[C1_F9:%.*]] = load {{.*}} @c1
242 ; CHECK: [[MEMCPY_S1_F9:%.*]] = load {{.*}} @memcpy_s1
243 ; USER-CONS: store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
244 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
245 ; CHECK: call void @__msan_warning
246 ; KMSAN: call void @__msan_warning
247 ; KMSAN: call void @__msan_warning
248 ; 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]])
250 ; Three inputs and three outputs of different types: a pair, a char, a function pointer.
251 ; Everything is passed in memory:
252 ; asm("" : "=m" (pair2), "=m" (c2), "=m" (memcpy_d1) : "m"(pair1), "m"(c1), "m"(memcpy_s1));
253 define dso_local void @f_3i_3o_complex_mem() sanitize_memory {
255 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)
259 ; CHECK-LABEL: @f_3i_3o_complex_mem
260 ; USER-CONS: store { i32, i32 } zeroinitializer, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @pair2 to i64), i64 87960930222080) to ptr), align 1
261 ; USER-CONS-NEXT: store i8 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @c2 to i64), i64 87960930222080) to ptr), align 1
262 ; USER-CONS-NEXT: store i64 0, ptr inttoptr (i64 xor (i64 ptrtoint (ptr @memcpy_d1 to i64), i64 87960930222080) to ptr), align 1
263 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@pair2{{.*}}, i64 8)
264 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@c2{{.*}}, i64 1)
265 ; CHECK-CONS: call void @__msan_instrument_asm_store({{.*}}@memcpy_d1{{.*}}, i64 8)
266 ; 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)
269 ; A simple asm goto construct to check that callbr is handled correctly:
270 ; int asm_goto(int n) {
272 ; asm goto("cmp %0, %1; jnz %l2;" :: "r"(n), "r"(v)::skip_label);
277 ; asm goto statements can't have outputs, so just make sure we check the input
278 ; and the compiler doesn't crash.
279 define dso_local i32 @asm_goto(i32 %n) sanitize_memory {
281 callbr void asm sideeffect "cmp $0, $1; jnz ${2:l}", "r,r,!i,~{dirflag},~{fpsr},~{flags}"(i32 %n, i32 1)
282 to label %cleanup [label %skip_label]
284 skip_label: ; preds = %entry
287 cleanup: ; preds = %entry, %skip_label
288 %retval.0 = phi i32 [ 2, %skip_label ], [ 1, %entry ]
292 ; CHECK-LABEL: @asm_goto
293 ; KMSAN: [[LOAD_ARG:%.*]] = load {{.*}} %_msarg
294 ; KMSAN: [[CMP:%.*]] = icmp ne {{.*}} [[LOAD_ARG]], 0
295 ; KMSAN: br {{.*}} [[CMP]], label %[[LABEL:.*]], label
297 ; KMSAN-NEXT: call void @__msan_warning