about summary refs log tree commit diff
path: root/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs
blob: f48466e8ce10cd3f899ec14491eb357218048c69 (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
89
90
91
//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows

// This test check for correct handling of atomic read-modify-write operations for all integer sizes.
// Atomic max and min should return the previous value, and store the result in the atomic.
// Atomic addition and subtraction should have wrapping semantics.
// `and`, `nand`, `or`, `xor` should behave like their non-atomic counterparts.

// FIXME(genmc): add 128 bit atomics for platforms that support it, once GenMC gets 128 bit atomic support

#![no_main]

use std::sync::atomic::*;

const ORD: Ordering = Ordering::SeqCst;

fn assert_eq<T: Eq>(x: T, y: T) {
    if x != y {
        std::process::abort();
    }
}

macro_rules! test_rmw_edge_cases {
    ($int:ty, $atomic:ty) => {{
        let x = <$atomic>::new(123);
        // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
        x.store(123, ORD);

        // MAX, ADD
        assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value
        assert_eq(123, x.fetch_max(<$int>::MAX, ORD)); // `max` stores the new value
        assert_eq(<$int>::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping
        assert_eq(<$int>::MAX.wrapping_add(10), x.load(ORD));

        // MIN, SUB
        x.store(42, ORD);
        assert_eq(42, x.fetch_min(<$int>::MAX, ORD)); // `min` keeps existing value
        assert_eq(42, x.fetch_min(<$int>::MIN, ORD)); // `min` stores the new value
        assert_eq(<$int>::MIN, x.fetch_sub(10, ORD)); // `fetch_sub` should be wrapping
        assert_eq(<$int>::MIN.wrapping_sub(10), x.load(ORD));

        // Small enough pattern to work for all integer sizes.
        let pattern = 0b01010101;

        // AND
        x.store(!0, ORD);
        assert_eq(!0, x.fetch_and(pattern, ORD));
        assert_eq(!0 & pattern, x.load(ORD));

        // NAND
        x.store(!0, ORD);
        assert_eq(!0, x.fetch_nand(pattern, ORD));
        assert_eq(!(!0 & pattern), x.load(ORD));

        // OR
        x.store(!0, ORD);
        assert_eq(!0, x.fetch_or(pattern, ORD));
        assert_eq(!0 | pattern, x.load(ORD));

        // XOR
        x.store(!0, ORD);
        assert_eq(!0, x.fetch_xor(pattern, ORD));
        assert_eq(!0 ^ pattern, x.load(ORD));

        // SWAP
        x.store(!0, ORD);
        assert_eq(!0, x.swap(pattern, ORD));
        assert_eq(pattern, x.load(ORD));

        // Check correct behavior of atomic min/max combined with overflowing add/sub.
        x.store(10, ORD);
        assert_eq(10, x.fetch_add(<$int>::MAX, ORD)); // definitely overflows, so new value of x is smaller than 10
        assert_eq(<$int>::MAX.wrapping_add(10), x.fetch_max(10, ORD)); // new value of x should be 10
        // assert_eq(10, x.load(ORD)); // FIXME(genmc,#4572): enable this check once GenMC correctly handles min/max truncation.
    }};
}

#[unsafe(no_mangle)]
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
    test_rmw_edge_cases!(u8, AtomicU8);
    test_rmw_edge_cases!(u16, AtomicU16);
    test_rmw_edge_cases!(u32, AtomicU32);
    test_rmw_edge_cases!(u64, AtomicU64);
    test_rmw_edge_cases!(usize, AtomicUsize);
    test_rmw_edge_cases!(i8, AtomicI8);
    test_rmw_edge_cases!(i16, AtomicI16);
    test_rmw_edge_cases!(i32, AtomicI32);
    test_rmw_edge_cases!(i64, AtomicI64);
    test_rmw_edge_cases!(isize, AtomicIsize);

    0
}