1; RUN: opt < %s -msan -S | FileCheck %s 2 3target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" 4target triple = "aarch64-unknown-linux-gnu" 5 6%struct.__va_list = type { i8*, i8*, i8*, i32, i32 } 7 8define 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 49declare void @llvm.lifetime.start(i64, i8* nocapture) #1 50declare void @llvm.va_start(i8*) #2 51declare void @llvm.va_end(i8*) #2 52declare void @llvm.lifetime.end(i64, i8* nocapture) #1 53 54define 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