82 changes: 82 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/Mips32/vararg-mipsel.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s

target datalayout = "e-m:m-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64el--linux"

define i32 @foo(i32 %guard, ...) {
%vl = alloca ptr, align 8
call void @llvm.lifetime.start.p0(i64 32, ptr %vl)
call void @llvm.va_start(ptr %vl)
call void @llvm.va_end(ptr %vl)
call void @llvm.lifetime.end.p0(i64 32, ptr %vl)
ret i32 0
}

; First, check allocation of the save area.

; CHECK-LABEL: @foo
; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls
; CHECK: [[B:%.*]] = add i64 0, [[A]]
; CHECK: [[C:%.*]] = alloca {{.*}} [[B]]

; CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 [[B]], i1 false)

; CHECK: [[D:%.*]] = call i64 @llvm.umin.i64(i64 [[B]], i64 800)
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C]], ptr align 8 @__msan_va_arg_tls, i64 [[D]], i1 false)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
declare void @llvm.va_start(ptr) #2
declare void @llvm.va_end(ptr) #2
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

define i32 @bar() {
%1 = call i32 (i32, ...) @foo(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; Save the incoming shadow value from the arguments in the __msan_va_arg_tls
; array.
; CHECK-LABEL: @bar
; CHECK: store i32 0, ptr @__msan_va_arg_tls, align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 16) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check multiple fixed arguments.
declare i32 @foo2(i32 %g1, i32 %g2, ...)
define i32 @bar2() {
%1 = call i32 (i32, i32, ...) @foo2(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; CHECK-LABEL: @bar2
; CHECK: store i64 0, ptr @__msan_va_arg_tls, align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.
define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)
149 changes: 149 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/PowerPC32/kernel-ppcle.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
; RUN: opt < %s -S -msan-kernel=1 -passes=msan 2>&1 | FileCheck %s

target datalayout = "e-m:e-i64:64-n32:64"
target triple = "powerpc64le--linux"

define void @Store1(ptr %p, i8 %x) sanitize_memory {
entry:
store i8 %x, ptr %p
ret void
}

; CHECK-LABEL: define {{[^@]+}}@Store1(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_store_1(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: store i8 {{.+}}, ptr [[SHADOW]]
; CHECK: ret void

define void @Store2(ptr %p, i16 %x) sanitize_memory {
entry:
store i16 %x, ptr %p
ret void
}

; CHECK-LABEL: define {{[^@]+}}@Store2(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_store_2(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: store i16 {{.+}}, ptr [[SHADOW]]
; CHECK: ret void

define void @Store4(ptr %p, i32 %x) sanitize_memory {
entry:
store i32 %x, ptr %p
ret void
}

; CHECK-LABEL: define {{[^@]+}}@Store4(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_store_4(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: store i32 {{.+}}, ptr [[SHADOW]]
; CHECK: ret void

define void @Store8(ptr %p, i64 %x) sanitize_memory {
entry:
store i64 %x, ptr %p
ret void
}

; CHECK-LABEL: define {{[^@]+}}@Store8(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_store_8(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: store i64 {{.+}}, ptr [[SHADOW]]
; CHECK: ret void

define void @Store16(ptr %p, i128 %x) sanitize_memory {
entry:
store i128 %x, ptr %p
ret void
}

; CHECK-LABEL: define {{[^@]+}}@Store16(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_store_n(ptr %p, i64 16)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: store i128 {{.+}}, ptr [[SHADOW]]
; CHECK: ret void

define i8 @Load1(ptr %p) sanitize_memory {
entry:
%0 = load i8, ptr %p
ret i8 %0
}

; CHECK-LABEL: define {{[^@]+}}@Load1(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_load_1(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: [[SHADOW_VAL:%[a-z0-9_]+]] = load i8, ptr [[SHADOW]]
; CHECK: [[ORIGIN_VAL:%[a-z0-9_]+]] = load i32, ptr [[ORIGIN]]
; CHECK: store i8 [[SHADOW_VAL]], ptr %retval_shadow
; CHECK: store i32 [[ORIGIN_VAL]], ptr %retval_origin
; CHECK: ret i8 {{.+}}

define i16 @Load2(ptr %p) sanitize_memory {
entry:
%0 = load i16, ptr %p
ret i16 %0
}

; CHECK-LABEL: define {{[^@]+}}@Load2(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_load_2(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: [[SHADOW_VAL:%[a-z0-9_]+]] = load i16, ptr [[SHADOW]]
; CHECK: [[ORIGIN_VAL:%[a-z0-9_]+]] = load i32, ptr [[ORIGIN]]
; CHECK: store i16 [[SHADOW_VAL]], ptr %retval_shadow
; CHECK: store i32 [[ORIGIN_VAL]], ptr %retval_origin
; CHECK: ret i16 {{.+}}

define i32 @Load4(ptr %p) sanitize_memory {
entry:
%0 = load i32, ptr %p
ret i32 %0
}

; CHECK-LABEL: define {{[^@]+}}@Load4(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_load_4(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: [[SHADOW_VAL:%[a-z0-9_]+]] = load i32, ptr [[SHADOW]]
; CHECK: [[ORIGIN_VAL:%[a-z0-9_]+]] = load i32, ptr [[ORIGIN]]
; CHECK: store i32 [[SHADOW_VAL]], ptr %retval_shadow
; CHECK: store i32 [[ORIGIN_VAL]], ptr %retval_origin
; CHECK: ret i32 {{.+}}

define i64 @Load8(ptr %p) sanitize_memory {
entry:
%0 = load i64, ptr %p
ret i64 %0
}

; CHECK-LABEL: define {{[^@]+}}@Load8(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_load_8(ptr %p)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: [[SHADOW_VAL:%[a-z0-9_]+]] = load i64, ptr [[SHADOW]]
; CHECK: [[ORIGIN_VAL:%[a-z0-9_]+]] = load i32, ptr [[ORIGIN]]
; CHECK: store i64 [[SHADOW_VAL]], ptr %retval_shadow
; CHECK: store i32 [[ORIGIN_VAL]], ptr %retval_origin
; CHECK: ret i64 {{.+}}

define i128 @Load16(ptr %p) sanitize_memory {
entry:
%0 = load i128, ptr %p
ret i128 %0
}

; CHECK-LABEL: define {{[^@]+}}@Load16(
; CHECK: [[META:%[a-z0-9_]+]] = call { ptr, ptr } @__msan_metadata_ptr_for_load_n(ptr %p, i64 16)
; CHECK: [[SHADOW:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 0
; CHECK: [[ORIGIN:%[a-z0-9_]+]] = extractvalue { ptr, ptr } [[META]], 1
; CHECK: [[SHADOW_VAL:%[a-z0-9_]+]] = load i128, ptr [[SHADOW]]
; CHECK: [[ORIGIN_VAL:%[a-z0-9_]+]] = load i32, ptr [[ORIGIN]]
; CHECK: store i128 [[SHADOW_VAL]], ptr %retval_shadow
; CHECK: store i32 [[ORIGIN_VAL]], ptr %retval_origin
; CHECK: ret i128 {{.+}}
125 changes: 125 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/PowerPC32/vararg-ppc.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s

target datalayout = "E-m:e-i64:64-n32:64"
target triple = "powerpc64--linux"

define i32 @foo(i32 %guard, ...) {
%vl = alloca ptr, align 8
call void @llvm.lifetime.start.p0(i64 32, ptr %vl)
call void @llvm.va_start(ptr %vl)
call void @llvm.va_end(ptr %vl)
call void @llvm.lifetime.end.p0(i64 32, ptr %vl)
ret i32 0
}

; First, check allocation of the save area.

; CHECK-LABEL: @foo
; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls
; CHECK: [[B:%.*]] = add i64 0, [[A]]
; CHECK: [[C:%.*]] = alloca {{.*}} [[B]]

; CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 [[B]], i1 false)

; CHECK: [[D:%.*]] = call i64 @llvm.umin.i64(i64 [[B]], i64 800)
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C]], ptr align 8 @__msan_va_arg_tls, i64 [[D]], i1 false)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
declare void @llvm.va_start(ptr) #2
declare void @llvm.va_end(ptr) #2
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

define i32 @bar() {
%1 = call i32 (i32, ...) @foo(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; Save the incoming shadow value from the arguments in the __msan_va_arg_tls
; array. The first argument is stored at position 4, since it's right
; justified.
; CHECK-LABEL: @bar
; CHECK: store i32 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 4) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 16) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check vector argument.
define i32 @bar2() {
%1 = call i32 (i32, ...) @foo(i32 0, <2 x i64> <i64 1, i64 2>)
ret i32 %1
}

; The vector is at offset 16 of parameter save area, but __msan_va_arg_tls
; corresponds to offset 8+ of parameter save area - so the offset from
; __msan_va_arg_tls is actually misaligned.
; CHECK-LABEL: @bar2
; CHECK: store <2 x i64> zeroinitializer, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check i64 array.
define i32 @bar4() {
%1 = call i32 (i32, ...) @foo(i32 0, [2 x i64] [i64 1, i64 2])
ret i32 %1
}

; CHECK-LABEL: @bar4
; CHECK: store [2 x i64] zeroinitializer, ptr @__msan_va_arg_tls, align 8
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Check i128 array.
define i32 @bar5() {
%1 = call i32 (i32, ...) @foo(i32 0, [2 x i128] [i128 1, i128 2])
ret i32 %1
}

; CHECK-LABEL: @bar5
; CHECK: store [2 x i128] zeroinitializer, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 40, {{.*}} @__msan_va_arg_overflow_size_tls

; Check 8-aligned byval.
define i32 @bar6(ptr %arg) {
%1 = call i32 (i32, ...) @foo(i32 0, ptr byval([2 x i64]) align 8 %arg)
ret i32 %1
}

; CHECK-LABEL: @bar6
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 @__msan_va_arg_tls, ptr align 8 {{.*}}, i64 16, i1 false)
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Check 16-aligned byval.
define i32 @bar7(ptr %arg) {
%1 = call i32 (i32, ...) @foo(i32 0, ptr byval([4 x i64]) align 16 %arg)
ret i32 %1
}

; CHECK-LABEL: @bar7
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), ptr align 8 {{.*}}, i64 32, i1 false)
; CHECK: store {{.*}} 40, {{.*}} @__msan_va_arg_overflow_size_tls


; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.
define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)
123 changes: 123 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/PowerPC32/vararg-ppcle.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s

target datalayout = "e-m:e-i64:64-n32:64"
target triple = "powerpc64le--linux"

define i32 @foo(i32 %guard, ...) {
%vl = alloca ptr, align 8
call void @llvm.lifetime.start.p0(i64 32, ptr %vl)
call void @llvm.va_start(ptr %vl)
call void @llvm.va_end(ptr %vl)
call void @llvm.lifetime.end.p0(i64 32, ptr %vl)
ret i32 0
}

; First, check allocation of the save area.

; CHECK-LABEL: @foo
; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls
; CHECK: [[B:%.*]] = add i64 0, [[A]]
; CHECK: [[C:%.*]] = alloca {{.*}} [[B]]

; CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 [[B]], i1 false)

; CHECK: [[D:%.*]] = call i64 @llvm.umin.i64(i64 [[B]], i64 800)
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C]], ptr align 8 @__msan_va_arg_tls, i64 [[D]], i1 false)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
declare void @llvm.va_start(ptr) #2
declare void @llvm.va_end(ptr) #2
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

define i32 @bar() {
%1 = call i32 (i32, ...) @foo(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; Save the incoming shadow value from the arguments in the __msan_va_arg_tls
; array.
; CHECK-LABEL: @bar
; CHECK: store i32 0, ptr @__msan_va_arg_tls, align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 16) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check vector argument.
define i32 @bar2() {
%1 = call i32 (i32, ...) @foo(i32 0, <2 x i64> <i64 1, i64 2>)
ret i32 %1
}

; The vector is at offset 16 of parameter save area, but __msan_va_arg_tls
; corresponds to offset 8+ of parameter save area - so the offset from
; __msan_va_arg_tls is actually misaligned.
; CHECK-LABEL: @bar2
; CHECK: store <2 x i64> zeroinitializer, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check i64 array.
define i32 @bar4() {
%1 = call i32 (i32, ...) @foo(i32 0, [2 x i64] [i64 1, i64 2])
ret i32 %1
}

; CHECK-LABEL: @bar4
; CHECK: store [2 x i64] zeroinitializer, ptr @__msan_va_arg_tls, align 8
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Check i128 array.
define i32 @bar5() {
%1 = call i32 (i32, ...) @foo(i32 0, [2 x i128] [i128 1, i128 2])
ret i32 %1
}

; CHECK-LABEL: @bar5
; CHECK: store [2 x i128] zeroinitializer, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 40, {{.*}} @__msan_va_arg_overflow_size_tls

; Check 8-aligned byval.
define i32 @bar6(ptr %arg) {
%1 = call i32 (i32, ...) @foo(i32 0, ptr byval([2 x i64]) align 8 %arg)
ret i32 %1
}

; CHECK-LABEL: @bar6
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 @__msan_va_arg_tls, ptr align 8 {{.*}}, i64 16, i1 false)
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Check 16-aligned byval.
define i32 @bar7(ptr %arg) {
%1 = call i32 (i32, ...) @foo(i32 0, ptr byval([4 x i64]) align 16 %arg)
ret i32 %1
}

; CHECK-LABEL: @bar7
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), ptr align 8 {{.*}}, i64 32, i1 false)
; CHECK: store {{.*}} 40, {{.*}} @__msan_va_arg_overflow_size_tls

; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.
define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s

target datalayout = "E-m:m-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64--linux"

define i32 @foo(i32 %guard, ...) {
%vl = alloca ptr, align 8
call void @llvm.lifetime.start.p0(i64 32, ptr %vl)
call void @llvm.va_start(ptr %vl)
call void @llvm.va_end(ptr %vl)
call void @llvm.lifetime.end.p0(i64 32, ptr %vl)
ret i32 0
}

; First, check allocation of the save area.

; CHECK-LABEL: @foo
; CHECK: [[A:%.*]] = load {{.*}} @__msan_va_arg_overflow_size_tls
; CHECK: [[B:%.*]] = add i64 0, [[A]]
; CHECK: [[C:%.*]] = alloca {{.*}} [[B]]

; CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[C]], i8 0, i64 [[B]], i1 false)

; CHECK: [[D:%.*]] = call i64 @llvm.umin.i64(i64 [[B]], i64 800)
; CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[C]], ptr align 8 @__msan_va_arg_tls, i64 [[D]], i1 false)

declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1
declare void @llvm.va_start(ptr) #2
declare void @llvm.va_end(ptr) #2
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

define i32 @bar() {
%1 = call i32 (i32, ...) @foo(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; Save the incoming shadow value from the arguments in the __msan_va_arg_tls
; array. The first argument is stored at position 4, since it's right
; justified.
; CHECK-LABEL: @bar
; CHECK: store i32 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 4) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 16) to ptr), align 8
; CHECK: store {{.*}} 24, {{.*}} @__msan_va_arg_overflow_size_tls

; Check multiple fixed arguments.
declare i32 @foo2(i32 %g1, i32 %g2, ...)
define i32 @bar2() {
%1 = call i32 (i32, i32, ...) @foo2(i32 0, i32 1, i64 2, double 3.000000e+00)
ret i32 %1
}

; CHECK-LABEL: @bar2
; CHECK: store i64 0, ptr @__msan_va_arg_tls, align 8
; CHECK: store i64 0, ptr inttoptr (i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 8) to ptr), align 8
; CHECK: store {{.*}} 16, {{.*}} @__msan_va_arg_overflow_size_tls

; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.
define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)

; CHECK: declare void @__msan_maybe_warning_1(i8 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_1(i8 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_2(i16 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_2(i16 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_4(i32 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_4(i32 signext, ptr, i32 signext)
; CHECK: declare void @__msan_maybe_warning_8(i64 signext, i32 signext)
; CHECK: declare void @__msan_maybe_store_origin_8(i64 signext, ptr, i32 signext)
1,457 changes: 1,457 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/avx-intrinsics-x86.ll

Large diffs are not rendered by default.

2,154 changes: 2,154 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/avx2-intrinsics-x86.ll

Large diffs are not rendered by default.

3,626 changes: 3,626 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/mmx-intrinsics.ll

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/msan_x86_bts_asm.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
; Test for the conservative assembly handling mode used by KMSAN.
; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
; RUN: -msan-handle-asm-conservative=0 -S -passes=msan 2>&1 | FileCheck \
; RUN: "-check-prefix=CHECK" %s
; RUN: opt < %s -msan-kernel=1 -msan-check-access-address=0 \
; RUN: -msan-handle-asm-conservative=1 -S -passes=msan 2>&1 | FileCheck \
; RUN: "-check-prefixes=CHECK,CHECK-CONS" %s

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; The IR below was generated from the following source:
; int main() {
; bool bit;
; unsigned long value = 2;
; long nr = 0;
; unsigned long *addr = &value;
; asm("btsq %2, %1; setc %0" : "=qm" (bit), "=m" (addr): "Ir" (nr));
; if (bit)
; return 0;
; else
; return 1;
; }
;
; In the regular instrumentation mode MSan is unable to understand that |bit|
; is initialized by the asm() call, and therefore reports a false positive on
; the if-statement.
; The conservative assembly handling mode initializes every memory location
; passed by pointer into an asm() call. This prevents false positive reports,
; but may introduce false negatives.
;
; This test makes sure that the conservative mode unpoisons the shadow of |bit|
; by writing 0 to it.

define dso_local i32 @main() sanitize_memory {
entry:
%retval = alloca i32, align 4
%bit = alloca i8, align 1
%value = alloca i64, align 8
%nr = alloca i64, align 8
%addr = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store i64 2, ptr %value, align 8
store i64 0, ptr %nr, align 8
store ptr %value, ptr %addr, align 8
%0 = load i64, ptr %nr, align 8
call void asm "btsq $2, $1; setc $0", "=*qm,=*m,Ir,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i8) %bit, ptr elementtype(ptr) %addr, i64 %0)
%1 = load i8, ptr %bit, align 1
%tobool = trunc i8 %1 to i1
br i1 %tobool, label %if.then, label %if.else

if.then: ; preds = %entry
ret i32 0

if.else: ; preds = %entry
ret i32 1
}

; %nr is first poisoned, then unpoisoned (written to). Need to optimize this in the future.
; CHECK: call void @__msan_poison_alloca(ptr %nr{{.*}})
; CHECK: call { ptr, ptr } @__msan_metadata_ptr_for_store_8(ptr %nr)

; Hooks for inputs usually go before the assembly statement. But here we have none,
; because %nr is passed by value. However we check %nr for being initialized.
; CHECK-CONS: call { ptr, ptr } @__msan_metadata_ptr_for_load_8(ptr %nr)

; In the conservative mode, call the store hooks for %bit and %addr:
; CHECK-CONS: call void @__msan_instrument_asm_store(ptr %bit, i64 1)
; CHECK-CONS: call void @__msan_instrument_asm_store(ptr %addr, i64 8)

; Landing pad for the %nr check above.
; CHECK-CONS: call void @__msan_warning

; CHECK: call void asm "btsq $2, $1; setc $0"

; CHECK: [[META:%.*]] = call {{.*}} @__msan_metadata_ptr_for_load_1(ptr %bit)
; CHECK: [[SHADOW:%.*]] = extractvalue { ptr, ptr } [[META]], 0

; Now load the shadow value for the boolean.
; CHECK: [[MSLD:%.*]] = load {{.*}} [[SHADOW]]
; CHECK: [[MSPROP:%.*]] = trunc i8 [[MSLD]] to i1

; Is the shadow poisoned?
; CHECK: br i1 [[MSPROP]], label %[[IFTRUE:.*]], label {{.*}}

; If yes, raise a warning.
; CHECK: [[IFTRUE]]:
; CHECK: call void @__msan_warning

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
; RUN: opt < %s -msan-check-access-address=0 -S -passes=msan 2>&1 | FileCheck \
; RUN: %s
; RUN: opt < %s -msan-check-access-address=0 -msan-track-origins=1 -S \
; RUN: -passes=msan 2>&1 | FileCheck -check-prefix=CHECK \
; RUN: -check-prefix=CHECK-ORIGINS %s
; REQUIRES: x86-registered-target

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Store intrinsic.

define void @StoreIntrinsic(ptr %p, <4 x float> %x) nounwind uwtable sanitize_memory {
call void @llvm.x86.sse.storeu.ps(ptr %p, <4 x float> %x)
ret void
}

declare void @llvm.x86.sse.storeu.ps(ptr, <4 x float>) nounwind

; CHECK-LABEL: @StoreIntrinsic
; CHECK-NOT: br
; CHECK-NOT: = or
; CHECK: store <4 x i32> {{.*}} align 1
; CHECK: store <4 x float> %{{.*}}, ptr %{{.*}}, align 1{{$}}
; CHECK: ret void


; Load intrinsic.

define <16 x i8> @LoadIntrinsic(ptr %p) nounwind uwtable sanitize_memory {
%call = call <16 x i8> @llvm.x86.sse3.ldu.dq(ptr %p)
ret <16 x i8> %call
}

declare <16 x i8> @llvm.x86.sse3.ldu.dq(ptr %p) nounwind

; CHECK-LABEL: @LoadIntrinsic
; CHECK: load <16 x i8>, ptr {{.*}} align 1
; CHECK-ORIGINS: [[ORIGIN:%[01-9a-z]+]] = load i32, ptr {{.*}}
; CHECK-NOT: br
; CHECK-NOT: = or
; CHECK: call <16 x i8> @llvm.x86.sse3.ldu.dq
; CHECK: store <16 x i8> {{.*}} @__msan_retval_tls
; CHECK-ORIGINS: store i32 {{.*}}[[ORIGIN]], ptr @__msan_retval_origin_tls
; CHECK: ret <16 x i8>


; Simple NoMem intrinsic
; Check that shadow is OR'ed, and origin is Select'ed
; And no shadow checks!

define <8 x i16> @Pmulhuw128(<8 x i16> %a, <8 x i16> %b) nounwind uwtable sanitize_memory {
%call = call <8 x i16> @llvm.x86.sse2.pmulhu.w(<8 x i16> %a, <8 x i16> %b)
ret <8 x i16> %call
}

declare <8 x i16> @llvm.x86.sse2.pmulhu.w(<8 x i16> %a, <8 x i16> %b) nounwind

; CHECK-LABEL: @Pmulhuw128
; CHECK-NEXT: load <8 x i16>, ptr @__msan_param_tls
; CHECK-ORIGINS: load i32, ptr @__msan_param_origin_tls
; CHECK-NEXT: load <8 x i16>, ptr {{.*}} @__msan_param_tls
; CHECK-ORIGINS: load i32, ptr {{.*}} @__msan_param_origin_tls
; CHECK-NEXT: call void @llvm.donothing
; CHECK-NEXT: = or <8 x i16>
; CHECK-ORIGINS: = bitcast <8 x i16> {{.*}} to i128
; CHECK-ORIGINS-NEXT: = icmp ne i128 {{.*}}, 0
; CHECK-ORIGINS-NEXT: = select i1 {{.*}}, i32 {{.*}}, i32
; CHECK-NEXT: call <8 x i16> @llvm.x86.sse2.pmulhu.w
; CHECK-NEXT: store <8 x i16> {{.*}} @__msan_retval_tls
; CHECK-ORIGINS: store i32 {{.*}} @__msan_retval_origin_tls
; CHECK-NEXT: ret <8 x i16>
519 changes: 519 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/sse-intrinsics-x86.ll

Large diffs are not rendered by default.

1,381 changes: 1,381 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/sse2-intrinsics-x86.ll

Large diffs are not rendered by default.

431 changes: 431 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/sse41-intrinsics-x86.ll

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/vararg-too-large.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
; RUN: opt < %s -msan-check-access-address=0 -S 2>&1 -passes=msan | FileCheck \
; RUN: %s

; Test that MSan doesn't generate code overflowing __msan_va_arg_tls when too many arguments are
; passed to a variadic function.

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define dso_local i64 @many_args() {
entry:
%ret = call i64 (i64, ...) @sum(i64 120,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1,
i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1, i64 1
)
ret i64 %ret
}

; If the size of __msan_va_arg_tls changes the second argument of `add` must also be changed.
; CHECK-LABEL: @many_args
; CHECK: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 792)
; CHECK-NOT: i64 add (i64 ptrtoint (ptr @__msan_va_arg_tls to i64), i64 800)
declare i64 @sum(i64 %n, ...)
File renamed without changes.
117 changes: 117 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/vararg_call.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
; RUN: opt < %s -msan-check-access-address=0 -S -passes=msan 2>&1 | FileCheck \
; RUN: %s
; RUN: opt < %s -msan-check-access-address=0 -msan-track-origins=1 -S \
; RUN: -passes=msan 2>&1 | FileCheck %s "--check-prefixes=CHECK,CHECK-ORIGIN"
; RUN: opt < %s -msan-check-access-address=0 -S \
; RUN: -passes="msan<track-origins=1>" 2>&1 | FileCheck %s "--check-prefixes=CHECK,CHECK-ORIGIN"
; RUN: opt < %s -msan-check-access-address=0 -msan-track-origins=2 -S \
; RUN: -passes=msan 2>&1 | FileCheck %s "--check-prefixes=CHECK,CHECK-ORIGIN"

; Test that shadow and origin are stored for variadic function params.

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

%struct.__va_list_tag = type { i32, i32, ptr, ptr }

define dso_local i32 @test(i32 %a, i32 %b, i32 %c) local_unnamed_addr {
entry:
%call = tail call i32 (i32, ...) @sum(i32 3, i32 %a, i32 %b, i32 %c)
ret i32 %call
}

; CHECK: store i32 0, {{.*}} @__msan_param_tls {{.*}} i64 8
; CHECK: store i32 0, {{.*}} @__msan_param_tls {{.*}} i64 16
; CHECK: store i32 0, {{.*}} @__msan_param_tls {{.*}} i64 24
; CHECK: store i32 0, {{.*}} @__msan_va_arg_tls {{.*}} i64 8
; CHECK-ORIGIN: store i32 0, {{.*}} @__msan_va_arg_origin_tls {{.*}} i64 8
; CHECK: store i32 0, {{.*}} @__msan_va_arg_tls {{.*}} i64 16
; CHECK-ORIGIN: store i32 0, {{.*}} @__msan_va_arg_origin_tls {{.*}} i64 16
; CHECK: store i32 0, {{.*}} @__msan_va_arg_tls {{.*}} i64 24
; CHECK-ORIGIN: store i32 0, {{.*}} @__msan_va_arg_origin_tls {{.*}} i64 24

define dso_local i32 @sum(i32 %n, ...) local_unnamed_addr #0 {
entry:
%args = alloca [1 x %struct.__va_list_tag], align 16
call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %args) #2
call void @llvm.va_start(ptr nonnull %args)
%cmp9 = icmp sgt i32 %n, 0
br i1 %cmp9, label %for.body.lr.ph, label %for.end

; CHECK: call void @llvm.memcpy.{{.*}} [[SHADOW_COPY:%[_0-9a-z]+]], {{.*}} @__msan_va_arg_tls
; CHECK-ORIGIN: call void @llvm.memcpy{{.*}} [[ORIGIN_COPY:%[_0-9a-z]+]], {{.*}} @__msan_va_arg_origin_tls

; CHECK: call void @llvm.va_start
; CHECK: call void @llvm.memcpy.{{.*}}, {{.*}} [[SHADOW_COPY]], i{{.*}} [[REGSAVE:[0-9]+]]
; CHECK-ORIGIN: call void @llvm.memcpy.{{.*}}, {{.*}} [[ORIGIN_COPY]], i{{.*}} [[REGSAVE]]

; CHECK: [[OVERFLOW_SHADOW:%[_0-9a-z]+]] = getelementptr i8, ptr [[SHADOW_COPY]], i{{.*}} [[REGSAVE]]
; CHECK: call void @llvm.memcpy.{{.*}}[[OVERFLOW_SHADOW]]
; CHECK-ORIGIN: [[OVERFLOW_ORIGIN:%[_0-9a-z]+]] = getelementptr i8, ptr [[ORIGIN_COPY]], i{{.*}} [[REGSAVE]]
; CHECK-ORIGIN: call void @llvm.memcpy.{{.*}}[[OVERFLOW_ORIGIN]]

for.body.lr.ph: ; preds = %entry
%0 = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %args, i64 0, i64 0, i32 3
%overflow_arg_area_p = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %args, i64 0, i64 0, i32 2
%gp_offset.pre = load i32, ptr %args, align 16
br label %for.body

for.body: ; preds = %vaarg.end, %for.body.lr.ph
%gp_offset = phi i32 [ %gp_offset.pre, %for.body.lr.ph ], [ %gp_offset12, %vaarg.end ]
%sum.011 = phi i32 [ 0, %for.body.lr.ph ], [ %add, %vaarg.end ]
%i.010 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %vaarg.end ]
%fits_in_gp = icmp ult i32 %gp_offset, 41
br i1 %fits_in_gp, label %vaarg.in_reg, label %vaarg.in_mem

vaarg.in_reg: ; preds = %for.body
%reg_save_area = load ptr, ptr %0, align 16
%1 = sext i32 %gp_offset to i64
%2 = getelementptr i8, ptr %reg_save_area, i64 %1
%3 = add i32 %gp_offset, 8
store i32 %3, ptr %args, align 16
br label %vaarg.end

vaarg.in_mem: ; preds = %for.body
%overflow_arg_area = load ptr, ptr %overflow_arg_area_p, align 8
%overflow_arg_area.next = getelementptr i8, ptr %overflow_arg_area, i64 8
store ptr %overflow_arg_area.next, ptr %overflow_arg_area_p, align 8
br label %vaarg.end

vaarg.end: ; preds = %vaarg.in_mem, %vaarg.in_reg
%gp_offset12 = phi i32 [ %3, %vaarg.in_reg ], [ %gp_offset, %vaarg.in_mem ]
%vaarg.addr.in = phi ptr [ %2, %vaarg.in_reg ], [ %overflow_arg_area, %vaarg.in_mem ]
%4 = load i32, ptr %vaarg.addr.in, align 4
%add = add nsw i32 %4, %sum.011
%inc = add nuw nsw i32 %i.010, 1
%exitcond = icmp eq i32 %inc, %n
br i1 %exitcond, label %for.end, label %for.body

for.end: ; preds = %vaarg.end, %entry
%sum.0.lcssa = phi i32 [ 0, %entry ], [ %add, %vaarg.end ]
call void @llvm.va_end(ptr nonnull %args)
call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %args) #2
ret i32 %sum.0.lcssa
}


; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #1

; Function Attrs: nounwind
declare void @llvm.va_start(ptr) #2

; Function Attrs: nounwind
declare void @llvm.va_end(ptr) #2

; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #1

declare dso_local i80 @sum_i80(i32, ...) local_unnamed_addr

; Unaligned types like i80 should also work.
define dso_local i80 @test_i80(i80 %a, i80 %b, i80 %c) local_unnamed_addr {
entry:
%call = tail call i80 (i32, ...) @sum_i80(i32 3, i80 %a, i80 %b, i80 %c)
ret i80 %call
}

1,315 changes: 1,315 additions & 0 deletions llvm/test/Instrumentation/MemorySanitizer/i386/vararg_shadow.ll

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
EXPECT_TRUE(N0->memPreds().empty());
EXPECT_THAT(N1->memPreds(), testing::ElementsAre(N0));
EXPECT_TRUE(N2->preds(DAG).empty());

// Check UnscheduledSuccs.
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u); // N1
EXPECT_EQ(N1->getNumUnscheduledSuccs(), 0u);
EXPECT_EQ(N2->getNumUnscheduledSuccs(), 0u);
}

TEST_F(DependencyGraphTest, Preds) {
Expand Down Expand Up @@ -286,6 +291,14 @@ define i8 @foo(i8 %v0, i8 %v1) {
EXPECT_THAT(StN->preds(DAG),
testing::UnorderedElementsAre(CallN, CallN, AddN2));
EXPECT_THAT(RetN->preds(DAG), testing::ElementsAre(AddN2));

// Check UnscheduledSuccs.
EXPECT_EQ(AddN0->getNumUnscheduledSuccs(), 1u); // AddN2
EXPECT_EQ(AddN1->getNumUnscheduledSuccs(), 2u); // AddN2, CallN
EXPECT_EQ(AddN2->getNumUnscheduledSuccs(), 2u); // StN, RetN
EXPECT_EQ(CallN->getNumUnscheduledSuccs(), 2u); // StN, StN
EXPECT_EQ(StN->getNumUnscheduledSuccs(), 0u);
EXPECT_EQ(RetN->getNumUnscheduledSuccs(), 0u);
}

TEST_F(DependencyGraphTest, MemDGNode_getPrevNode_getNextNode) {
Expand Down Expand Up @@ -711,6 +724,8 @@ define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %v4, i8 %v5) {
EXPECT_EQ(DAG.getInterval().top(), S3);
EXPECT_EQ(DAG.getInterval().bottom(), S3);
[[maybe_unused]] auto *S3N = cast<sandboxir::MemDGNode>(DAG.getNode(S3));
// Check UnscheduledSuccs.
EXPECT_EQ(S3N->getNumUnscheduledSuccs(), 0u);
}
{
// Scenario 2: Extend below
Expand All @@ -722,6 +737,10 @@ define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %v4, i8 %v5) {
EXPECT_TRUE(S4N->hasMemPred(S3N));
EXPECT_TRUE(S5N->hasMemPred(S4N));
EXPECT_TRUE(S5N->hasMemPred(S3N));
// Check UnscheduledSuccs.
EXPECT_EQ(S3N->getNumUnscheduledSuccs(), 2u); // S4N, S5N
EXPECT_EQ(S4N->getNumUnscheduledSuccs(), 1u); // S5N
EXPECT_EQ(S5N->getNumUnscheduledSuccs(), 0u);
}
{
// Scenario 3: Extend above
Expand All @@ -746,5 +765,12 @@ define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %v4, i8 %v5) {
EXPECT_TRUE(S5N->hasMemPred(S3N));
EXPECT_TRUE(S5N->hasMemPred(S2N));
EXPECT_TRUE(S5N->hasMemPred(S1N));

// Check UnscheduledSuccs.
EXPECT_EQ(S1N->getNumUnscheduledSuccs(), 4u); // S2N, S3N, S4N, S5N
EXPECT_EQ(S2N->getNumUnscheduledSuccs(), 3u); // S3N, S4N, S5N
EXPECT_EQ(S3N->getNumUnscheduledSuccs(), 2u); // S4N, S5N
EXPECT_EQ(S4N->getNumUnscheduledSuccs(), 1u); // S5N
EXPECT_EQ(S5N->getNumUnscheduledSuccs(), 0u);
}
}