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 6 7target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 8target triple = "x86_64-unknown-linux-gnu" 9 10%struct.pair = type { i32, i32 } 11%struct.large = type { [33 x i8] } 12 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 27 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. 34 35; One input register, one output register: 36; asm("" : "=r" (id1) : "r" (is1)); 37define dso_local void @f_1i_1o_reg() sanitize_memory { 38entry: 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 43} 44 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]] 52 53 54; Two input registers, two output registers: 55; asm("" : "=r" (id1), "=r" (id2) : "r" (is1), "r"(is2)); 56define dso_local void @f_2i_2o_reg() sanitize_memory { 57entry: 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 66} 67 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]] 80 81; Input same as output, used twice: 82; asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r" (id2)); 83define dso_local void @f_2i_2o_reuse2_reg() sanitize_memory { 84entry: 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 93} 94 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]] 107 108 109; One of the input registers is also an output: 110; asm("" : "=r" (id1), "=r" (id2) : "r" (id1), "r"(is1)); 111define dso_local void @f_2i_2o_reuse1_reg() sanitize_memory { 112entry: 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 121} 122 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]] 135 136 137; One input register, three output registers: 138; asm("" : "=r" (id1), "=r" (id2), "=r" (id3) : "r" (is1)); 139define dso_local void @f_1i_3o_reg() sanitize_memory { 140entry: 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 150} 151 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]] 165 166 167; 2 input memory args, 2 output memory args: 168; asm("" : "=m" (id1), "=m" (id2) : "m" (is1), "m"(is2)) 169define dso_local void @f_2i_2o_mem() sanitize_memory { 170entry: 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 173} 174 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) 181 182 183; Same input and output passed as both memory and register: 184; asm("" : "=r" (id1), "=m"(id1) : "r"(is1), "m"(is1)); 185define dso_local void @f_1i_1o_memreg() sanitize_memory { 186entry: 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 191} 192 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) 199 200; Three outputs, first and last returned via regs, second via mem: 201; asm("" : "=r" (id1), "=m"(id2), "=r" (id3):); 202define dso_local void @f_3o_reg_mem_reg() sanitize_memory { 203entry: 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 210} 211 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) 216 217 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)); 221define dso_local void @f_3i_3o_complex_reg() sanitize_memory { 222entry: 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 232} 233 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]]) 244 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)); 248define dso_local void @f_3i_3o_complex_mem() sanitize_memory { 249entry: 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 252} 253 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) 262 263; Use memset when the size is larger. 264define dso_local void @f_1i_1o_mem_large() sanitize_memory { 265entry: 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 269} 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) 274 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. 285define dso_local i32 @asm_goto(i32 %n) sanitize_memory { 286entry: 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] 289 290skip_label: ; preds = %entry 291 br label %cleanup 292 293cleanup: ; preds = %entry, %skip_label 294 %retval.0 = phi i32 [ 2, %skip_label ], [ 1, %entry ] 295 ret i32 %retval.0 296} 297 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 304