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