about summary refs log tree commit diff
path: root/tests/ui/abi/custom.rs
blob: 0f6ff77f580844793d70fa500f666d03af229632 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Test that `extern "custom"` functions can be called from assembly, and defined using a naked
// function, and `global_asm!` with an `extern "custom"` block.
//
//@ run-pass
//@ only-x86_64
#![feature(abi_custom)]

use std::arch::{asm, global_asm, naked_asm};

#[unsafe(naked)]
unsafe extern "custom" fn double() {
    naked_asm!("add rax, rax", "ret");
}

global_asm!(
    // work around macOS prefixing symbols with _
    "    .globl  {0}",
    "{0}:",
    "    add     rax, 1",
    "    ret",
    sym increment,
);

unsafe extern "custom" {
    fn increment();
}

#[repr(transparent)]
struct Thing(u64);

impl Thing {
    #[unsafe(naked)]
    unsafe extern "custom" fn is_even() {
        naked_asm!("test al, 1", "sete al", "ret");
    }
}

trait BitwiseNot {
    #[unsafe(naked)]
    unsafe extern "custom" fn bitwise_not() {
        naked_asm!("not rax", "ret");
    }
}

impl BitwiseNot for Thing {}

#[unsafe(naked)]
unsafe extern "C" fn const_generic<const N: u64>() {
    naked_asm!(
        "mov rax, {}",
        "ret",
        const N,
    );
}

pub fn main() {
    let mut x: u64 = 21;
    unsafe { asm!("call {}", sym double, inout("rax") x) };
    assert_eq!(x, 42);

    let mut x: u64 = 41;
    unsafe { asm!("call {}", sym increment, inout("rax") x) };
    assert_eq!(x, 42);

    let mut x: u8;
    unsafe { asm!("call {}", sym Thing::is_even, inout("al") 42u8 => x) };
    assert!(x != 0);

    let mut x: u64 = 42;
    unsafe { asm!("call {}", sym Thing::bitwise_not, inout("rax") x) };
    assert_eq!(x, !42);

    // Create and call in `asm!` an `extern "custom"` function pointer.
    fn caller(f: unsafe extern "custom" fn(), mut x: u64) -> u64 {
        unsafe { asm!("call {}", in(reg) f, inout("rax") x) };
        x
    }

    assert_eq!(caller(double, 2), 4);

    let x: u64;
    unsafe { asm!("call {}", sym const_generic::<42>, out("rax") x) };
    assert_eq!(x, 42);

    let x: u64;
    unsafe { asm!("call {}", sym const_generic::<84>, out("rax") x) };
    assert_eq!(x, 84);
}