1 ; RUN: opt < %s -msan -S | FileCheck %s 2 3 target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" 4 target triple = "aarch64-unknown-linux-gnu" 5 6 %struct.__va_list = type { i8*, i8*, i8*, i32, i32 } 7 8 define i32 @foo(i32 %guard, ...) { 9 %vl = alloca %struct.__va_list, align 8 10 %1 = bitcast %struct.__va_list* %vl to i8* 11 call void @llvm.lifetime.start(i64 32, i8* %1) 12 call void @llvm.va_start(i8* %1) 13 call void @llvm.va_end(i8* %1) 14 call void @llvm.lifetime.end(i64 32, i8* %1) 15 ret i32 0 16 } 17 18 ; First check if the variadic shadow values are saved in stack with correct 19 ; size (192 is total of general purpose registers size, 56, rounded to 16 20 ; plus total of floating-point registers size, 128). 21 22 ; CHECK-LABEL: @foo 23 ; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls 24 ; CHECK: [[B:%.*]] = add i64 192, [[A]] 25 ; CHECK: alloca {{.*}} [[B]] 26 27 ; We expect three memcpy operations: one for the general purpose registers, 28 ; one for floating-point/SIMD ones, and one for thre remaining arguments. 29 30 ; Propagate the GR shadow values on for the va_list::__gp_top, adjust the 31 ; offset in the __msan_va_arg_tls based on va_list:__gp_off, and finally 32 ; issue the memcpy. 33 ; CHECK: [[GRP:%.*]] = getelementptr inbounds i8, i8* {{%.*}}, i64 {{%.*}} 34 ; CHECK: [[GRSIZE:%.*]] = sub i64 56, {{%.*}} 35 ; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{%.*}}, i8* [[GRP]], i64 [[GRSIZE]], i32 8, i1 false) 36 37 ; Propagate the VR shadow values on for the va_list::__vr_top, adjust the 38 ; offset in the __msan_va_arg_tls based on va_list:__vr_off, and finally 39 ; issue the memcpy. 40 ; CHECK: [[VRP:%.*]] = getelementptr inbounds i8, i8* {{%.*}}, i64 {{%.*}} 41 ; CHECK: [[VRSIZE:%.*]] = sub i64 128, {{%.*}} 42 ; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{%.*}}, i8* [[VRP]], i64 [[VRSIZE]], i32 8, i1 false) 43 44 ; Copy the remaining shadow values on the va_list::__stack position (it is 45 ; on the constant offset of 192 from __msan_va_arg_tls). 46 ; CHECK: [[STACK:%.*]] = getelementptr inbounds i8, i8* {{%.*}}, i32 192 47 ; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{%.*}}, i8* [[STACK]], i64 {{%.*}}, i32 16, i1 false) 48 49 declare void @llvm.lifetime.start(i64, i8* nocapture) #1 50 declare void @llvm.va_start(i8*) #2 51 declare void @llvm.va_end(i8*) #2 52 declare void @llvm.lifetime.end(i64, i8* nocapture) #1 53 54 define i32 @bar() { 55 %1 = call i32 (i32, ...) @foo(i32 0, i32 1, i32 2, double 3.000000e+00, 56 double 4.000000e+00, i32 5, i32 6, 57 double 7.000000e+00, i32 8, i32 9, i32 10, i32 11) 58 ret i32 %1 59 } 60 61 ; Save the incoming shadow value from the arguments in the __msan_va_arg_tls 62 ; array. General purpose registers are saved at positions from 0 to 56, Floating 63 ; point and SIMD are saved from 64 to 192, and the remaining from 192. 64 ; CHECK-LABEL: @bar 65 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 8 66 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 64 67 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 80 68 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 16 69 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 24 70 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 96 71 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 32 72 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 40 73 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 48 74 ; CHECK: store {{.*}} @__msan_va_arg_tls {{.*}} 192 75 ; CHECK: store {{.*}} 8, {{.*}} @__msan_va_arg_overflow_size_tls 76