summaryrefslogtreecommitdiffstats
path: root/tests/assembly
diff options
context:
space:
mode:
Diffstat (limited to 'tests/assembly')
-rw-r--r--tests/assembly/asm/arm-types.rs1
-rw-r--r--tests/assembly/asm/loongarch-type.rs3
-rw-r--r--tests/assembly/asm/mips-types.rs12
-rw-r--r--tests/assembly/closure-inherit-target-feature.rs60
-rw-r--r--tests/assembly/dwarf4.rs24
-rw-r--r--tests/assembly/dwarf5.rs3
-rw-r--r--tests/assembly/is_aligned.rs1
-rw-r--r--tests/assembly/libs/issue-115339-zip-arrays.rs25
-rw-r--r--tests/assembly/slice-is_ascii.rs1
-rw-r--r--tests/assembly/stack-probes.rs (renamed from tests/assembly/x86-stack-probes.rs)13
-rw-r--r--tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs406
-rw-r--r--tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs414
-rw-r--r--tests/assembly/stack-protector/stack-protector-heuristics-effect.rs4
-rw-r--r--tests/assembly/stack-protector/stack-protector-target-support.rs31
-rw-r--r--tests/assembly/strict_provenance.rs1
-rw-r--r--tests/assembly/thin-lto.rs8
-rw-r--r--tests/assembly/wasm_exceptions.rs60
-rw-r--r--tests/assembly/x86_64-array-pair-load-store-merge.rs20
-rw-r--r--tests/assembly/x86_64-function-return.rs30
19 files changed, 1086 insertions, 31 deletions
diff --git a/tests/assembly/asm/arm-types.rs b/tests/assembly/asm/arm-types.rs
index b22a26ce3..9520f9327 100644
--- a/tests/assembly/asm/arm-types.rs
+++ b/tests/assembly/asm/arm-types.rs
@@ -1,6 +1,7 @@
// assembly-output: emit-asm
// compile-flags: --target armv7-unknown-linux-gnueabihf
// compile-flags: -C target-feature=+neon
+// compile-flags: -C opt-level=0
// needs-llvm-components: arm
#![feature(no_core, lang_items, rustc_attrs, repr_simd)]
diff --git a/tests/assembly/asm/loongarch-type.rs b/tests/assembly/asm/loongarch-type.rs
index 4e296f3ad..4aeecf92d 100644
--- a/tests/assembly/asm/loongarch-type.rs
+++ b/tests/assembly/asm/loongarch-type.rs
@@ -1,4 +1,3 @@
-// min-llvm-version: 16.0
// assembly-output: emit-asm
// compile-flags: --target loongarch64-unknown-linux-gnu
// needs-llvm-components: loongarch
@@ -42,7 +41,7 @@ extern "C" {
// Hack to avoid function merging
extern "Rust" {
- fn dont_merge(s: &str);
+ fn dont_merge(s: &str);
}
// CHECK-LABEL: sym_fn:
diff --git a/tests/assembly/asm/mips-types.rs b/tests/assembly/asm/mips-types.rs
index 6aa28b062..27469b229 100644
--- a/tests/assembly/asm/mips-types.rs
+++ b/tests/assembly/asm/mips-types.rs
@@ -72,7 +72,7 @@ macro_rules! check_reg { ($func:ident, $ty:ty, $reg:tt, $mov:literal) => {
// mips32-LABEL: sym_static_32:
// mips32: #APP
-// mips32: lw $3, %got(extern_static)
+// mips32: lw $3, %got(extern_static)($gp)
// mips32: #NO_APP
#[cfg(mips32)]
#[no_mangle]
@@ -82,7 +82,7 @@ pub unsafe fn sym_static_32() {
// mips32-LABEL: sym_fn_32:
// mips32: #APP
-// mips32: lw $3, %got(extern_func)
+// mips32: lw $3, %got(extern_func)($gp)
// mips32: #NO_APP
#[cfg(mips32)]
#[no_mangle]
@@ -92,7 +92,9 @@ pub unsafe fn sym_fn_32() {
// mips64-LABEL: sym_static_64:
// mips64: #APP
-// mips64: ld $3, %got_disp(extern_static)
+// mips64: lui $3, %got_hi(extern_static)
+// mips64: daddu $3, $3, $gp
+// mips64: ld $3, %got_lo(extern_static)($3)
// mips64: #NO_APP
#[cfg(mips64)]
#[no_mangle]
@@ -102,7 +104,9 @@ pub unsafe fn sym_static_64() {
// mips64-LABEL: sym_fn_64:
// mips64: #APP
-// mips64: ld $3, %got_disp(extern_func)
+// mips64: lui $3, %got_hi(extern_func)
+// mips64: daddu $3, $3, $gp
+// mips64: ld $3, %got_lo(extern_func)($3)
// mips64: #NO_APP
#[cfg(mips64)]
#[no_mangle]
diff --git a/tests/assembly/closure-inherit-target-feature.rs b/tests/assembly/closure-inherit-target-feature.rs
new file mode 100644
index 000000000..7acda76e2
--- /dev/null
+++ b/tests/assembly/closure-inherit-target-feature.rs
@@ -0,0 +1,60 @@
+// only-x86_64
+// ignore-sgx Tests incompatible with LVI mitigations
+// assembly-output: emit-asm
+// make sure the feature is not enabled at compile-time
+// compile-flags: -C opt-level=3 -C target-feature=-sse4.1 -C llvm-args=-x86-asm-syntax=intel
+
+#![feature(target_feature_11)]
+#![crate_type = "rlib"]
+
+use std::arch::x86_64::{__m128, _mm_blend_ps};
+
+#[no_mangle]
+pub unsafe fn sse41_blend_nofeature(x: __m128, y: __m128) -> __m128 {
+ let f = {
+ // check that _mm_blend_ps is not being inlined into the closure
+ // CHECK-LABEL: {{sse41_blend_nofeature.*closure.*:}}
+ // CHECK-NOT: blendps
+ // CHECK: {{call .*_mm_blend_ps.*}}
+ // CHECK-NOT: blendps
+ // CHECK: ret
+ #[inline(never)] |x, y| _mm_blend_ps(x, y, 0b0101)
+ };
+ f(x, y)
+}
+
+#[no_mangle]
+#[target_feature(enable = "sse4.1")]
+pub fn sse41_blend_noinline(x: __m128, y: __m128) -> __m128 {
+ let f = {
+ // check that _mm_blend_ps is being inlined into the closure
+ // CHECK-LABEL: {{sse41_blend_noinline.*closure.*:}}
+ // CHECK-NOT: _mm_blend_ps
+ // CHECK: blendps
+ // CHECK-NOT: _mm_blend_ps
+ // CHECK: ret
+ #[inline(never)] |x, y| unsafe {
+ _mm_blend_ps(x, y, 0b0101)
+ }
+ };
+ f(x, y)
+}
+
+#[no_mangle]
+#[target_feature(enable = "sse4.1")]
+pub fn sse41_blend_doinline(x: __m128, y: __m128) -> __m128 {
+ // check that the closure and _mm_blend_ps are being inlined into the function
+ // CHECK-LABEL: sse41_blend_doinline:
+ // CHECK-NOT: {{sse41_blend_doinline.*closure.*}}
+ // CHECK-NOT: _mm_blend_ps
+ // CHECK: blendps
+ // CHECK-NOT: {{sse41_blend_doinline.*closure.*}}
+ // CHECK-NOT: _mm_blend_ps
+ // CHECK: ret
+ let f = {
+ #[inline] |x, y| unsafe {
+ _mm_blend_ps(x, y, 0b0101)
+ }
+ };
+ f(x, y)
+}
diff --git a/tests/assembly/dwarf4.rs b/tests/assembly/dwarf4.rs
new file mode 100644
index 000000000..6e1584458
--- /dev/null
+++ b/tests/assembly/dwarf4.rs
@@ -0,0 +1,24 @@
+// Makes sure that `-Z dwarf-version=4` causes `rustc` to emit DWARF version 4.
+// assembly-output: emit-asm
+// compile-flags: -g --target x86_64-unknown-linux-gnu -Z dwarf-version=4 -Copt-level=0
+// needs-llvm-components: x86
+
+#![feature(no_core, lang_items)]
+#![crate_type = "rlib"]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+pub fn wibble() {}
+
+pub struct X;
+
+// CHECK: .section .debug_info
+// CHECK-NOT: .short 2
+// CHECK-NOT: .short 5
+// CHECK: .short 4
+// CHECK-NOT: .section .debug_pubnames
+// CHECK-NOT: .section .debug_pubtypes
diff --git a/tests/assembly/dwarf5.rs b/tests/assembly/dwarf5.rs
index f41e6bd55..46d4e84b4 100644
--- a/tests/assembly/dwarf5.rs
+++ b/tests/assembly/dwarf5.rs
@@ -1,6 +1,6 @@
// Makes sure that `-Z dwarf-version=5` causes `rustc` to emit DWARF version 5.
// assembly-output: emit-asm
-// compile-flags: -g --target x86_64-unknown-linux-gnu -Z dwarf-version=5
+// compile-flags: -g --target x86_64-unknown-linux-gnu -Z dwarf-version=5 -Copt-level=0
// needs-llvm-components: x86
#![feature(no_core, lang_items)]
@@ -18,3 +18,4 @@ pub fn wibble() {}
// CHECK-NOT: .short 2
// CHECK-NOT: .short 4
// CHECK: .short 5
+// CHECK: .section .debug_names
diff --git a/tests/assembly/is_aligned.rs b/tests/assembly/is_aligned.rs
index 148d11ee4..d152d200a 100644
--- a/tests/assembly/is_aligned.rs
+++ b/tests/assembly/is_aligned.rs
@@ -1,5 +1,4 @@
// assembly-output: emit-asm
-// min-llvm-version: 15.0
// only-x86_64
// ignore-sgx
// revisions: opt-speed opt-size
diff --git a/tests/assembly/libs/issue-115339-zip-arrays.rs b/tests/assembly/libs/issue-115339-zip-arrays.rs
new file mode 100644
index 000000000..26b7b9770
--- /dev/null
+++ b/tests/assembly/libs/issue-115339-zip-arrays.rs
@@ -0,0 +1,25 @@
+// assembly-output: emit-asm
+// # zen3 previously exhibited odd vectorization
+// compile-flags: --crate-type=lib -Ctarget-cpu=znver3 -O
+// only-x86_64
+// ignore-sgx
+
+use std::iter;
+
+// previously this produced a long chain of
+// 56: vpextrb $6, %xmm0, %ecx
+// 57: orb %cl, 22(%rsi)
+// 58: vpextrb $7, %xmm0, %ecx
+// 59: orb %cl, 23(%rsi)
+// [...]
+
+// CHECK-LABEL: zip_arrays:
+#[no_mangle]
+pub fn zip_arrays(mut a: [u8; 32], b: [u8; 32]) -> [u8; 32] {
+ // CHECK-NOT: vpextrb
+ // CHECK-NOT: orb %cl
+ // CHECK: vorps
+ iter::zip(&mut a, b).for_each(|(a, b)| *a |= b);
+ // CHECK: retq
+ a
+}
diff --git a/tests/assembly/slice-is_ascii.rs b/tests/assembly/slice-is_ascii.rs
index b3e1fee15..124121164 100644
--- a/tests/assembly/slice-is_ascii.rs
+++ b/tests/assembly/slice-is_ascii.rs
@@ -3,7 +3,6 @@
// [LIN] only-linux
// assembly-output: emit-asm
// compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel
-// min-llvm-version: 14
// only-x86_64
// ignore-sgx
// ignore-debug
diff --git a/tests/assembly/x86-stack-probes.rs b/tests/assembly/stack-probes.rs
index c7141fb20..6466df3ff 100644
--- a/tests/assembly/x86-stack-probes.rs
+++ b/tests/assembly/stack-probes.rs
@@ -1,11 +1,12 @@
-// min-llvm-version: 16
-// revisions: x86_64 i686
+// revisions: x86_64 i686 aarch64
// assembly-output: emit-asm
-//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
+//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
//[x86_64] needs-llvm-components: x86
-//[i686] compile-flags: --target i686-unknown-linux-gnu
+//[i686] compile-flags: --target i686-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
//[i686] needs-llvm-components: x86
-// compile-flags: -C llvm-args=-x86-asm-syntax=intel
+//[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
+//[aarch64] needs-llvm-components: aarch64
+//[aarch64] min-llvm-version: 18
#![feature(no_core, lang_items)]
#![crate_type = "lib"]
@@ -29,6 +30,7 @@ pub fn small_stack_probe(x: u8, f: fn(&mut [u8; 8192])) {
// CHECK-NOT: __rust_probestack
// x86_64: sub rsp, 4096
// i686: sub esp, 4096
+ // aarch64: sub sp, sp, #1, lsl #12
f(&mut [x; 8192]);
}
@@ -38,5 +40,6 @@ pub fn big_stack_probe(x: u8, f: fn(&[u8; 65536])) {
// CHECK-NOT: __rust_probestack
// x86_64: sub rsp, 4096
// i686: sub esp, 4096
+ // aarch64: sub sp, sp, #1, lsl #12
f(&mut [x; 65536]);
}
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs
new file mode 100644
index 000000000..fca2c85d5
--- /dev/null
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs
@@ -0,0 +1,406 @@
+// revisions: all strong basic none missing
+// assembly-output: emit-asm
+// only-windows
+// only-msvc
+// ignore-64bit 64-bit table based SEH has slightly different behaviors than classic SEH
+// [all] compile-flags: -Z stack-protector=all
+// [strong] compile-flags: -Z stack-protector=strong
+// [basic] compile-flags: -Z stack-protector=basic
+// [none] compile-flags: -Z stack-protector=none
+// compile-flags: -C opt-level=2 -Z merge-functions=disabled
+
+#![crate_type = "lib"]
+
+#![allow(incomplete_features)]
+
+#![feature(unsized_locals, unsized_fn_params)]
+
+
+// CHECK-LABEL: emptyfn:
+#[no_mangle]
+pub fn emptyfn() {
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_char
+#[no_mangle]
+pub fn array_char(f: fn(*const char)) {
+ let a = ['c'; 1];
+ let b = ['d'; 3];
+ let c = ['e'; 15];
+
+ f(&a as *const _);
+ f(&b as *const _);
+ f(&c as *const _);
+
+ // Any type of local array variable leads to stack protection with the
+ // "strong" heuristic. The 'basic' heuristic only adds stack protection to
+ // functions with local array variables of a byte-sized type, however. Since
+ // 'char' is 4 bytes in Rust, this function is not protected by the 'basic'
+ // heuristic
+ //
+ // (This test *also* takes the address of the local stack variables. We
+ // cannot know that this isn't what triggers the `strong` heuristic.
+ // However, the test strategy of passing the address of a stack array to an
+ // external function is sufficient to trigger the `basic` heuristic (see
+ // test `array_u8_large()`). Since the `basic` heuristic only checks for the
+ // presence of stack-local array variables, we can be confident that this
+ // test also captures this part of the `strong` heuristic specification.)
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_u8_1
+#[no_mangle]
+pub fn array_u8_1(f: fn(*const u8)) {
+ let a = [0u8; 1];
+ f(&a as *const _);
+
+ // The 'strong' heuristic adds stack protection to functions with local
+ // array variables regardless of their size.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_u8_small:
+#[no_mangle]
+pub fn array_u8_small(f: fn(*const u8)) {
+ let a = [0u8; 2];
+ let b = [0u8; 7];
+ f(&a as *const _);
+ f(&b as *const _);
+
+ // Small arrays do not lead to stack protection by the 'basic' heuristic.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_u8_large:
+#[no_mangle]
+pub fn array_u8_large(f: fn(*const u8)) {
+ let a = [0u8; 9];
+ f(&a as *const _);
+
+ // Since `a` is a byte array with size greater than 8, the basic heuristic
+ // will also protect this function.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+#[derive(Copy, Clone)]
+pub struct ByteSizedNewtype(u8);
+
+// CHECK-LABEL: array_bytesizednewtype_9:
+#[no_mangle]
+pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
+ let a = [ByteSizedNewtype(0); 9];
+ f(&a as *const _);
+
+ // Since `a` is a byte array in the LLVM output, the basic heuristic will
+ // also protect this function.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: local_var_addr_used_indirectly
+#[no_mangle]
+pub fn local_var_addr_used_indirectly(f: fn(bool)) {
+ let a = 5;
+ let a_addr = &a as *const _ as usize;
+ f(a_addr & 0x10 == 0);
+
+ // This function takes the address of a local variable taken. Although this
+ // address is never used as a way to refer to stack memory, the `strong`
+ // heuristic adds stack smash protection. This is also the case in C++:
+ // ```
+ // cat << EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+ // #include <cstdint>
+ // void f(void (*g)(bool)) {
+ // int32_t x;
+ // g((reinterpret_cast<uintptr_t>(&x) & 0x10U) == 0);
+ // }
+ // EOF
+ // ```
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+
+// CHECK-LABEL: local_string_addr_taken
+#[no_mangle]
+pub fn local_string_addr_taken(f: fn(&String)) {
+ let x = String::new();
+ f(&x);
+
+ // Taking the address of the local variable `x` leads to stack smash
+ // protection with the `strong` heuristic, but not with the `basic`
+ // heuristic. It does not matter that the reference is not mut.
+ //
+ // An interesting note is that a similar function in C++ *would* be
+ // protected by the `basic` heuristic, because `std::string` has a char
+ // array internally as a small object optimization:
+ // ```
+ // cat <<EOF | clang++ -O2 -fstack-protector -S -x c++ - -o - | grep stack_chk
+ // #include <string>
+ // void f(void (*g)(const std::string&)) {
+ // std::string x;
+ // g(x);
+ // }
+ // EOF
+ // ```
+ //
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+pub trait SelfByRef {
+ fn f(&self) -> i32;
+}
+
+impl SelfByRef for i32 {
+ fn f(&self) -> i32 {
+ return self + 1;
+ }
+}
+
+// CHECK-LABEL: local_var_addr_taken_used_locally_only
+#[no_mangle]
+pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) {
+ let x = factory();
+ let g = x.f();
+ sink(g);
+
+ // Even though the local variable conceptually has its address taken, as
+ // it's passed by reference to the trait function, the use of the reference
+ // is easily inlined. There is therefore no stack smash protection even with
+ // the `strong` heuristic.
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+pub struct Gigastruct {
+ does: u64,
+ not: u64,
+ have: u64,
+ array: u64,
+ members: u64
+}
+
+// CHECK-LABEL: local_large_var_moved
+#[no_mangle]
+pub fn local_large_var_moved(f: fn(Gigastruct)) {
+ let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 };
+ f(x);
+
+ // Even though the local variable conceptually doesn't have its address
+ // taken, it's so large that the "move" is implemented with a reference to a
+ // stack-local variable in the ABI. Consequently, this function *is*
+ // protected by the `strong` heuristic. This is also the case for
+ // rvalue-references in C++, regardless of struct size:
+ // ```
+ // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+ // #include <cstdint>
+ // #include <utility>
+ // void f(void (*g)(uint64_t&&)) {
+ // uint64_t x;
+ // g(std::move(x));
+ // }
+ // EOF
+ // ```
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: local_large_var_cloned
+#[no_mangle]
+pub fn local_large_var_cloned(f: fn(Gigastruct)) {
+ f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 });
+
+ // A new instance of `Gigastruct` is passed to `f()`, without any apparent
+ // connection to this stack frame. Still, since instances of `Gigastruct`
+ // are sufficiently large, it is allocated in the caller stack frame and
+ // passed as a pointer. As such, this function is *also* protected by the
+ // `strong` heuristic, just like `local_large_var_moved`. This is also the
+ // case for pass-by-value of sufficiently large structs in C++:
+ // ```
+ // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+ // #include <cstdint>
+ // #include <utility>
+ // struct Gigastruct { uint64_t a, b, c, d, e; };
+ // void f(void (*g)(Gigastruct)) {
+ // g(Gigastruct{});
+ // }
+ // EOF
+ // ```
+
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+
+extern "C" {
+ // A call to an external `alloca` function is *not* recognized as an
+ // `alloca(3)` operation. This function is a compiler built-in, as the
+ // man page explains. Clang translates it to an LLVM `alloca`
+ // instruction with a count argument, which is also what the LLVM stack
+ // protector heuristics looks for. The man page for `alloca(3)` details
+ // a way to avoid using the compiler built-in: pass a -std=c11
+ // argument, *and* don't include <alloca.h>. Though this leads to an
+ // external alloca() function being called, it doesn't lead to stack
+ // protection being included. It even fails with a linker error
+ // "undefined reference to `alloca'". Example:
+ // ```
+ // cat<<EOF | clang -fstack-protector-strong -x c -std=c11 - -o /dev/null
+ // #include <stdlib.h>
+ // void * alloca(size_t);
+ // void f(void (*g)(void*)) {
+ // void * p = alloca(10);
+ // g(p);
+ // }
+ // int main() { return 0; }
+ // EOF
+ // ```
+ // The following tests demonstrate that calls to an external `alloca`
+ // function in Rust also doesn't trigger stack protection.
+
+ fn alloca(size: usize) -> *mut ();
+}
+
+// CHECK-LABEL: alloca_small_compile_time_constant_arg
+#[no_mangle]
+pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
+ f(unsafe { alloca(8) });
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: alloca_large_compile_time_constant_arg
+#[no_mangle]
+pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
+ f(unsafe { alloca(9) });
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+
+// CHECK-LABEL: alloca_dynamic_arg
+#[no_mangle]
+pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
+ f(unsafe { alloca(n) });
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// The question then is: in what ways can Rust code generate array-`alloca`
+// LLVM instructions? This appears to only be generated by
+// rustc_codegen_ssa::traits::Builder::array_alloca() through
+// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT
+// this is support for the "unsized locals" unstable feature:
+// https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html.
+
+
+// CHECK-LABEL: unsized_fn_param
+#[no_mangle]
+pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
+ let n = if l { 1 } else { 2 };
+ f(*Box::<[u8]>::from(&s[0..n])); // slice-copy with Box::from
+
+ // Even though slices are conceptually passed by-value both into this
+ // function and into `f()`, this is implemented with pass-by-reference
+ // using a suitably constructed fat-pointer (as if the functions
+ // accepted &[u8]). This function therefore doesn't need dynamic array
+ // alloca, and is therefore not protected by the `strong` or `basic`
+ // heuristics.
+
+
+ // We should have a __security_check_cookie call in `all` and `strong` modes but
+ // LLVM does not support generating stack protectors in functions with funclet
+ // based EH personalities.
+ // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
+ // all-NOT: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: unsized_local
+#[no_mangle]
+pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
+ let n = if l { 1 } else { 2 };
+ let mut a: [u8] = *Box::<[u8]>::from(&s[0..n]); // slice-copy with Box::from
+ f(&mut a);
+
+ // This function allocates a slice as a local variable in its stack
+ // frame. Since the size is not a compile-time constant, an array
+ // alloca is required, and the function is protected by both the
+ // `strong` and `basic` heuristic.
+
+ // We should have a __security_check_cookie call in `all`, `strong` and `basic` modes but
+ // LLVM does not support generating stack protectors in functions with funclet
+ // based EH personalities.
+ // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
+ // all-NOT: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs
new file mode 100644
index 000000000..d9abf554a
--- /dev/null
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs
@@ -0,0 +1,414 @@
+// revisions: all strong basic none missing
+// assembly-output: emit-asm
+// only-windows
+// only-msvc
+// ignore-32bit 64-bit table based SEH has slightly different behaviors than classic SEH
+// [all] compile-flags: -Z stack-protector=all
+// [strong] compile-flags: -Z stack-protector=strong
+// [basic] compile-flags: -Z stack-protector=basic
+// [none] compile-flags: -Z stack-protector=none
+// compile-flags: -C opt-level=2 -Z merge-functions=disabled
+
+#![crate_type = "lib"]
+
+#![allow(incomplete_features)]
+
+#![feature(unsized_locals, unsized_fn_params)]
+
+
+// CHECK-LABEL: emptyfn:
+#[no_mangle]
+pub fn emptyfn() {
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_char
+#[no_mangle]
+pub fn array_char(f: fn(*const char)) {
+ let a = ['c'; 1];
+ let b = ['d'; 3];
+ let c = ['e'; 15];
+
+ f(&a as *const _);
+ f(&b as *const _);
+ f(&c as *const _);
+
+ // Any type of local array variable leads to stack protection with the
+ // "strong" heuristic. The 'basic' heuristic only adds stack protection to
+ // functions with local array variables of a byte-sized type, however. Since
+ // 'char' is 4 bytes in Rust, this function is not protected by the 'basic'
+ // heuristic
+ //
+ // (This test *also* takes the address of the local stack variables. We
+ // cannot know that this isn't what triggers the `strong` heuristic.
+ // However, the test strategy of passing the address of a stack array to an
+ // external function is sufficient to trigger the `basic` heuristic (see
+ // test `array_u8_large()`). Since the `basic` heuristic only checks for the
+ // presence of stack-local array variables, we can be confident that this
+ // test also captures this part of the `strong` heuristic specification.)
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_u8_1
+#[no_mangle]
+pub fn array_u8_1(f: fn(*const u8)) {
+ let a = [0u8; 1];
+ f(&a as *const _);
+
+ // The 'strong' heuristic adds stack protection to functions with local
+ // array variables regardless of their size.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_u8_small:
+#[no_mangle]
+pub fn array_u8_small(f: fn(*const u8)) {
+ let a = [0u8; 2];
+ let b = [0u8; 7];
+ f(&a as *const _);
+ f(&b as *const _);
+
+ // Small arrays do not lead to stack protection by the 'basic' heuristic.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: array_u8_large:
+#[no_mangle]
+pub fn array_u8_large(f: fn(*const u8)) {
+ let a = [0u8; 9];
+ f(&a as *const _);
+
+ // Since `a` is a byte array with size greater than 8, the basic heuristic
+ // will also protect this function.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+#[derive(Copy, Clone)]
+pub struct ByteSizedNewtype(u8);
+
+// CHECK-LABEL: array_bytesizednewtype_9:
+#[no_mangle]
+pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
+ let a = [ByteSizedNewtype(0); 9];
+ f(&a as *const _);
+
+ // Since `a` is a byte array in the LLVM output, the basic heuristic will
+ // also protect this function.
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: local_var_addr_used_indirectly
+#[no_mangle]
+pub fn local_var_addr_used_indirectly(f: fn(bool)) {
+ let a = 5;
+ let a_addr = &a as *const _ as usize;
+ f(a_addr & 0x10 == 0);
+
+ // This function takes the address of a local variable taken. Although this
+ // address is never used as a way to refer to stack memory, the `strong`
+ // heuristic adds stack smash protection. This is also the case in C++:
+ // ```
+ // cat << EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+ // #include <cstdint>
+ // void f(void (*g)(bool)) {
+ // int32_t x;
+ // g((reinterpret_cast<uintptr_t>(&x) & 0x10U) == 0);
+ // }
+ // EOF
+ // ```
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+
+// CHECK-LABEL: local_string_addr_taken
+#[no_mangle]
+pub fn local_string_addr_taken(f: fn(&String)) {
+ // CHECK-DAG: .seh_endprologue
+ let x = String::new();
+ f(&x);
+
+ // Taking the address of the local variable `x` leads to stack smash
+ // protection with the `strong` heuristic, but not with the `basic`
+ // heuristic. It does not matter that the reference is not mut.
+ //
+ // An interesting note is that a similar function in C++ *would* be
+ // protected by the `basic` heuristic, because `std::string` has a char
+ // array internally as a small object optimization:
+ // ```
+ // cat <<EOF | clang++ -O2 -fstack-protector -S -x c++ - -o - | grep stack_chk
+ // #include <string>
+ // void f(void (*g)(const std::string&)) {
+ // std::string x;
+ // g(x);
+ // }
+ // EOF
+ // ```
+ //
+
+ // We should have a __security_check_cookie call in `all` and `strong` modes but
+ // LLVM does not support generating stack protectors in functions with funclet
+ // based EH personalities.
+ // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
+ // all-NOT: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+
+ // CHECK-DAG: .seh_endproc
+}
+
+pub trait SelfByRef {
+ fn f(&self) -> i32;
+}
+
+impl SelfByRef for i32 {
+ fn f(&self) -> i32 {
+ return self + 1;
+ }
+}
+
+// CHECK-LABEL: local_var_addr_taken_used_locally_only
+#[no_mangle]
+pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) {
+ let x = factory();
+ let g = x.f();
+ sink(g);
+
+ // Even though the local variable conceptually has its address taken, as
+ // it's passed by reference to the trait function, the use of the reference
+ // is easily inlined. There is therefore no stack smash protection even with
+ // the `strong` heuristic.
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+pub struct Gigastruct {
+ does: u64,
+ not: u64,
+ have: u64,
+ array: u64,
+ members: u64
+}
+
+// CHECK-LABEL: local_large_var_moved
+#[no_mangle]
+pub fn local_large_var_moved(f: fn(Gigastruct)) {
+ let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 };
+ f(x);
+
+ // Even though the local variable conceptually doesn't have its address
+ // taken, it's so large that the "move" is implemented with a reference to a
+ // stack-local variable in the ABI. Consequently, this function *is*
+ // protected by the `strong` heuristic. This is also the case for
+ // rvalue-references in C++, regardless of struct size:
+ // ```
+ // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+ // #include <cstdint>
+ // #include <utility>
+ // void f(void (*g)(uint64_t&&)) {
+ // uint64_t x;
+ // g(std::move(x));
+ // }
+ // EOF
+ // ```
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: local_large_var_cloned
+#[no_mangle]
+pub fn local_large_var_cloned(f: fn(Gigastruct)) {
+ f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 });
+
+ // A new instance of `Gigastruct` is passed to `f()`, without any apparent
+ // connection to this stack frame. Still, since instances of `Gigastruct`
+ // are sufficiently large, it is allocated in the caller stack frame and
+ // passed as a pointer. As such, this function is *also* protected by the
+ // `strong` heuristic, just like `local_large_var_moved`. This is also the
+ // case for pass-by-value of sufficiently large structs in C++:
+ // ```
+ // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
+ // #include <cstdint>
+ // #include <utility>
+ // struct Gigastruct { uint64_t a, b, c, d, e; };
+ // void f(void (*g)(Gigastruct)) {
+ // g(Gigastruct{});
+ // }
+ // EOF
+ // ```
+
+
+ // all: __security_check_cookie
+ // strong: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+
+extern "C" {
+ // A call to an external `alloca` function is *not* recognized as an
+ // `alloca(3)` operation. This function is a compiler built-in, as the
+ // man page explains. Clang translates it to an LLVM `alloca`
+ // instruction with a count argument, which is also what the LLVM stack
+ // protector heuristics looks for. The man page for `alloca(3)` details
+ // a way to avoid using the compiler built-in: pass a -std=c11
+ // argument, *and* don't include <alloca.h>. Though this leads to an
+ // external alloca() function being called, it doesn't lead to stack
+ // protection being included. It even fails with a linker error
+ // "undefined reference to `alloca'". Example:
+ // ```
+ // cat<<EOF | clang -fstack-protector-strong -x c -std=c11 - -o /dev/null
+ // #include <stdlib.h>
+ // void * alloca(size_t);
+ // void f(void (*g)(void*)) {
+ // void * p = alloca(10);
+ // g(p);
+ // }
+ // int main() { return 0; }
+ // EOF
+ // ```
+ // The following tests demonstrate that calls to an external `alloca`
+ // function in Rust also doesn't trigger stack protection.
+
+ fn alloca(size: usize) -> *mut ();
+}
+
+// CHECK-LABEL: alloca_small_compile_time_constant_arg
+#[no_mangle]
+pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
+ f(unsafe { alloca(8) });
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: alloca_large_compile_time_constant_arg
+#[no_mangle]
+pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
+ f(unsafe { alloca(9) });
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+
+// CHECK-LABEL: alloca_dynamic_arg
+#[no_mangle]
+pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
+ f(unsafe { alloca(n) });
+
+ // all: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// The question then is: in what ways can Rust code generate array-`alloca`
+// LLVM instructions? This appears to only be generated by
+// rustc_codegen_ssa::traits::Builder::array_alloca() through
+// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT
+// this is support for the "unsized locals" unstable feature:
+// https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html.
+
+
+// CHECK-LABEL: unsized_fn_param
+#[no_mangle]
+pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
+ let n = if l { 1 } else { 2 };
+ f(*Box::<[u8]>::from(&s[0..n])); // slice-copy with Box::from
+
+ // Even though slices are conceptually passed by-value both into this
+ // function and into `f()`, this is implemented with pass-by-reference
+ // using a suitably constructed fat-pointer (as if the functions
+ // accepted &[u8]). This function therefore doesn't need dynamic array
+ // alloca, and is therefore not protected by the `strong` or `basic`
+ // heuristics.
+
+
+ // We should have a __security_check_cookie call in `all` and `strong` modes but
+ // LLVM does not support generating stack protectors in functions with funclet
+ // based EH personalities.
+ // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
+ // all-NOT: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+
+ // basic-NOT: __security_check_cookie
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
+
+// CHECK-LABEL: unsized_local
+#[no_mangle]
+pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
+ let n = if l { 1 } else { 2 };
+ let mut a: [u8] = *Box::<[u8]>::from(&s[0..n]); // slice-copy with Box::from
+ f(&mut a);
+
+ // This function allocates a slice as a local variable in its stack
+ // frame. Since the size is not a compile-time constant, an array
+ // alloca is required, and the function is protected by both the
+ // `strong` and `basic` heuristic.
+
+ // We should have a __security_check_cookie call in `all`, `strong` and `basic` modes but
+ // LLVM does not support generating stack protectors in functions with funclet
+ // based EH personalities.
+ // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
+ // all-NOT: __security_check_cookie
+ // strong-NOT: __security_check_cookie
+ // basic-NOT: __security_check_cookie
+
+ // none-NOT: __security_check_cookie
+ // missing-NOT: __security_check_cookie
+}
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
index 7c2b60550..ca566b6e4 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
@@ -1,13 +1,15 @@
// revisions: all strong basic none missing
// assembly-output: emit-asm
// ignore-macos slightly different policy on stack protection of arrays
-// ignore-windows stack check code uses different function names
+// ignore-msvc stack check code uses different function names
// ignore-nvptx64 stack protector is not supported
+// ignore-wasm32-bare
// [all] compile-flags: -Z stack-protector=all
// [strong] compile-flags: -Z stack-protector=strong
// [basic] compile-flags: -Z stack-protector=basic
// [none] compile-flags: -Z stack-protector=none
// compile-flags: -C opt-level=2 -Z merge-functions=disabled
+// min-llvm-version: 17.0.2
#![crate_type = "lib"]
diff --git a/tests/assembly/stack-protector/stack-protector-target-support.rs b/tests/assembly/stack-protector/stack-protector-target-support.rs
index d5b48105e..6d87fd191 100644
--- a/tests/assembly/stack-protector/stack-protector-target-support.rs
+++ b/tests/assembly/stack-protector/stack-protector-target-support.rs
@@ -2,7 +2,7 @@
// targets, with the exception of nvptx64-nvidia-cuda
//
// revisions: r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23
-// revisions: r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r34 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44
+// revisions: r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44
// revisions: r45 r46 r47 r48 r49 r50 r51 r52 r53 r54 r55 r56 r57 r58 r59 r60 r61 r62 r63 r64 r65
// revisions: r66 r67 r68 r69 r70 r71 r72 r73 r74 r75 r76 r77 r78 r79 r80 r81 r82 r83 r84
// assembly-output: emit-asm
@@ -72,8 +72,7 @@
// [r32] needs-llvm-components: arm
// [r33] compile-flags: --target armv7-unknown-linux-musleabihf
// [r33] needs-llvm-components: arm
-// [r34] compile-flags: --target asmjs-unknown-emscripten
-// [r34] needs-llvm-components: webassembly
+
// [r35] compile-flags: --target i586-pc-windows-msvc
// [r35] needs-llvm-components: x86
// [r36] compile-flags: --target i586-unknown-linux-gnu
@@ -152,28 +151,30 @@
// [r72] needs-llvm-components: webassembly
// [r73] compile-flags:--target wasm32-wasi
// [r73] needs-llvm-components: webassembly
-// [r74] compile-flags:--target x86_64-apple-ios
-// [r74] needs-llvm-components: x86
-// [r75] compile-flags:--target x86_64-fortanix-unknown-sgx
+// [r74] compile-flags:--target wasm32-wasi-preview1-threads
+// [r74] needs-llvm-components: webassembly
+// [r75] compile-flags:--target x86_64-apple-ios
// [r75] needs-llvm-components: x86
-// [r76] compile-flags:--target x86_64-unknown-fuchsia
+// [r76] compile-flags:--target x86_64-fortanix-unknown-sgx
// [r76] needs-llvm-components: x86
-// [r77] compile-flags:--target x86_64-linux-android
+// [r77] compile-flags:--target x86_64-unknown-fuchsia
// [r77] needs-llvm-components: x86
-// [r78] compile-flags:--target x86_64-sun-solaris
+// [r78] compile-flags:--target x86_64-linux-android
// [r78] needs-llvm-components: x86
-// [r79] compile-flags:--target x86_64-unknown-freebsd
+// [r79] compile-flags:--target x86_64-pc-solaris
// [r79] needs-llvm-components: x86
-// [r80] compile-flags:--target x86_64-unknown-illumos
+// [r80] compile-flags:--target x86_64-unknown-freebsd
// [r80] needs-llvm-components: x86
-// [r81] compile-flags:--target x86_64-unknown-linux-gnux32
+// [r81] compile-flags:--target x86_64-unknown-illumos
// [r81] needs-llvm-components: x86
-// [r82] compile-flags:--target x86_64-unknown-linux-musl
+// [r82] compile-flags:--target x86_64-unknown-linux-gnux32
// [r82] needs-llvm-components: x86
-// [r83] compile-flags:--target x86_64-unknown-netbsd
+// [r83] compile-flags:--target x86_64-unknown-linux-musl
// [r83] needs-llvm-components: x86
-// [r84] compile-flags: --target x86_64-unknown-redox
+// [r84] compile-flags:--target x86_64-unknown-netbsd
// [r84] needs-llvm-components: x86
+// [r85] compile-flags: --target x86_64-unknown-redox
+// [r85] needs-llvm-components: x86
// compile-flags: -Z stack-protector=all
// compile-flags: -C opt-level=2
diff --git a/tests/assembly/strict_provenance.rs b/tests/assembly/strict_provenance.rs
index 24a7c6b5b..ef8566a93 100644
--- a/tests/assembly/strict_provenance.rs
+++ b/tests/assembly/strict_provenance.rs
@@ -2,7 +2,6 @@
// compile-flags: -Copt-level=1
// only-x86_64
// ignore-sgx
-// min-llvm-version: 15.0
#![crate_type = "rlib"]
// CHECK-LABEL: old_style
diff --git a/tests/assembly/thin-lto.rs b/tests/assembly/thin-lto.rs
new file mode 100644
index 000000000..deb8fd21d
--- /dev/null
+++ b/tests/assembly/thin-lto.rs
@@ -0,0 +1,8 @@
+// compile-flags: -O -C lto=thin -C prefer-dynamic=no
+// only-x86_64-unknown-linux-gnu
+// assembly-output: emit-asm
+
+// CHECK: main
+
+pub fn main() {
+}
diff --git a/tests/assembly/wasm_exceptions.rs b/tests/assembly/wasm_exceptions.rs
new file mode 100644
index 000000000..b7d20881b
--- /dev/null
+++ b/tests/assembly/wasm_exceptions.rs
@@ -0,0 +1,60 @@
+// only-wasm32-bare
+// assembly-output: emit-asm
+// compile-flags: -C target-feature=+exception-handling
+// compile-flags: -C panic=unwind
+// compile-flags: -C llvm-args=-wasm-enable-eh
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+#![feature(rustc_attrs)]
+
+extern {
+ fn may_panic();
+
+ #[rustc_nounwind]
+ fn log_number(number: usize);
+}
+
+struct LogOnDrop;
+
+impl Drop for LogOnDrop {
+ fn drop(&mut self) {
+ unsafe { log_number(0); }
+ }
+}
+
+// CHECK-LABEL: test_cleanup:
+#[no_mangle]
+pub fn test_cleanup() {
+ let _log_on_drop = LogOnDrop;
+ unsafe { may_panic(); }
+
+ // CHECK-NOT: call
+ // CHECK: try
+ // CHECK: call may_panic
+ // CHECK: catch_all
+ // CHECK: rethrow
+ // CHECK: end_try
+}
+
+// CHECK-LABEL: test_rtry:
+#[no_mangle]
+pub fn test_rtry() {
+ unsafe {
+ core::intrinsics::r#try(|_| {
+ may_panic();
+ }, core::ptr::null_mut(), |data, exception| {
+ log_number(data as usize);
+ log_number(exception as usize);
+ });
+ }
+
+ // CHECK-NOT: call
+ // CHECK: try
+ // CHECK: call may_panic
+ // CHECK: catch
+ // CHECK: call log_number
+ // CHECK: call log_number
+ // CHECK-NOT: rethrow
+ // CHECK: end_try
+}
diff --git a/tests/assembly/x86_64-array-pair-load-store-merge.rs b/tests/assembly/x86_64-array-pair-load-store-merge.rs
new file mode 100644
index 000000000..55e317e91
--- /dev/null
+++ b/tests/assembly/x86_64-array-pair-load-store-merge.rs
@@ -0,0 +1,20 @@
+// assembly-output: emit-asm
+// compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel
+// only-x86_64
+// ignore-sgx
+// ignore-macos (manipulates rsp too)
+
+// Depending on various codegen choices, this might end up copying
+// a `<2 x i8>`, an `i16`, or two `i8`s.
+// Regardless of those choices, make sure the instructions use (2-byte) words.
+
+// CHECK-LABEL: array_copy_2_elements:
+#[no_mangle]
+pub fn array_copy_2_elements(a: &[u8; 2], p: &mut [u8; 2]) {
+ // CHECK-NOT: byte
+ // CHECK-NOT: mov
+ // CHECK: mov{{.+}}, word ptr
+ // CHECK-NEXT: mov word ptr
+ // CHECK-NEXT: ret
+ *p = *a;
+}
diff --git a/tests/assembly/x86_64-function-return.rs b/tests/assembly/x86_64-function-return.rs
new file mode 100644
index 000000000..0fcaca2d4
--- /dev/null
+++ b/tests/assembly/x86_64-function-return.rs
@@ -0,0 +1,30 @@
+// Test that the function return is (not) converted into a jump to the thunk
+// when the `-Zfunction-return={keep,thunk-extern}` flag is (not) set.
+
+// revisions: unset keep thunk-extern keep-thunk-extern thunk-extern-keep
+// assembly-output: emit-asm
+// compile-flags: -O
+// [keep] compile-flags: -Zfunction-return=keep
+// [thunk-extern] compile-flags: -Zfunction-return=thunk-extern
+// [keep-thunk-extern] compile-flags: -Zfunction-return=keep -Zfunction-return=thunk-extern
+// [thunk-extern-keep] compile-flags: -Zfunction-return=thunk-extern -Zfunction-return=keep
+// only-x86_64
+// ignore-x86_64-apple-darwin Symbol is called `___x86_return_thunk` (Darwin's extra underscore)
+// ignore-sgx Tests incompatible with LVI mitigations
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: foo:
+#[no_mangle]
+pub unsafe fn foo() {
+ // unset: ret
+ // unset-NOT: jmp __x86_return_thunk
+ // keep: ret
+ // keep-NOT: jmp __x86_return_thunk
+ // thunk-extern: jmp __x86_return_thunk
+ // thunk-extern-NOT: ret
+ // keep-thunk-extern: jmp __x86_return_thunk
+ // keep-thunk-extern-NOT: ret
+ // thunk-extern-keep: ret
+ // thunk-extern-keep-NOT: jmp __x86_return_thunk
+}