lib/std/arch/rv64/atomicTests.rad 5.0 KiB raw
1
//! RV64 A-extension and fence encoding checks.
2
3
use std::testing;
4
use std::lang::alloc;
5
use std::lang::strings;
6
use super::atomics;
7
use super::encode;
8
use super::decode;
9
use super::asm;
10
11
/// Assembler workspace for individual atomic instruction fixtures.
12
static MEMORY: [u8; 65536] = [0; 65536];
13
/// Interned fixture names.
14
unsafe static STRINGS: strings::Pool = strings::Pool { table: undefined, count: 0 };
15
16
/// Output storage for one assembled instruction.
17
static WORDS: [u32; 1] = [0; 1];
18
19
/// Exact unordered word encodings for a0, a1, and a2 operands.
20
@test fn encodings() throws (testing::TestError) {
21
    let operations: [u32; 11] = [0, 1, 2, 3, 4, 8, 12, 16, 20, 24, 28];
22
    let words: [u32; 11] = [
23
        0x00c5a52f, 0x08c5a52f, 0x1005a52f, 0x18c5a52f, 0x20c5a52f,
24
        0x40c5a52f, 0x60c5a52f, 0x80c5a52f, 0xa0c5a52f, 0xc0c5a52f, 0xe0c5a52f,
25
    ];
26
    for operation, i in &operations[..] {
27
        for wide in 0..2 {
28
            for order in 0..4 {
29
                let item = atomics::Instruction {
30
                    format: atomics::Format { operation, width: 2 + wide, order },
31
                    rd: super::A0, rs1: super::A1, rs2: super::ZERO if operation == 2 else super::A2,
32
                };
33
                let word = words[i] | (wide << 12) | (order << 25);
34
                try testing::expect(atomics::encode(item) == word);
35
                let decoded = atomics::decode(word) else {
36
                    throw testing::TestError::Failed;
37
                };
38
                try testing::expect(decoded == item);
39
                let case decode::Instr::Atomic(instruction) = decode::decode(word) else {
40
                    throw testing::TestError::Failed;
41
                };
42
                try testing::expect(instruction == item);
43
            }
44
        }
45
    }
46
}
47
48
/// Reject reserved opcodes, widths, LR sources, and malformed suffixes.
49
@test fn invalid() throws (testing::TestError) {
50
    for word in &[0x2805a52f, 0x00c5852f, 0x10c5a52f, 0x00c5a513] {
51
        try testing::expect(atomics::decode(word) == nil);
52
    }
53
    for name in &["lr", "lr.q", "lr.w.aq.rl", "sc.d.rl.aq", "amoswap.d.bad", "amoadd.wextra"] {
54
        try testing::expect(atomics::parse(name) == nil);
55
    }
56
    let format = atomics::parse("amoswap.d.aqrl") else {
57
        throw testing::TestError::Failed;
58
    };
59
    try testing::expect(format.operation == 1 and format.width == 3 and format.order == 3);
60
}
61
62
/// Assemble one instruction and return its exact word.
63
unsafe fn assemble(source: *[u8]) -> u32 throws (testing::TestError) {
64
    let mut arena = alloc::new(&mut MEMORY[..]);
65
    let words = &mut WORDS[..];
66
    let data: *mut [u8] = &mut [];
67
    let result = try asm::assemble(asm::scanner::SourceKind::String, source,
68
        &mut words[..], &mut data[..], &mut arena, &mut STRINGS, 0)
69
        catch {
70
            throw testing::TestError::Failed;
71
        };
72
    try testing::expect(result.text.len == 1);
73
    return result.text[0];
74
}
75
76
/// Check instruction suffix scanning and assembler operand order.
77
@test unsafe fn assembly() throws (testing::TestError) {
78
    try testing::expect((try assemble("amoadd.d.aqrl %a0 %a2 (%a1);")) == 0x06c5b52f);
79
    try testing::expect((try assemble("amoswap.w.aq %a0 %a2 0(%a1);")) == 0x0cc5a52f);
80
    try testing::expect((try assemble("lr.d.aq %a0 (%a1);")) == 0x1405b52f);
81
    try testing::expect((try assemble("sc.w.rl %a0 %a2 (%a1);")) == 0x1ac5a52f);
82
    try testing::expect((try assemble("fence.i;")) == 0x0000100f);
83
    try testing::expect((try assemble("fence iorw iorw;")) == 0x0ff0000f);
84
    try testing::expect((try assemble("fence r rw;")) == 0x0230000f);
85
    try testing::expect((try assemble("fence rw w;")) == 0x0310000f);
86
    try testing::expect((try assemble("fence 0 0;")) == 0x0000000f);
87
}
88
89
/// Decode fence classes and local instruction synchronization.
90
@test fn fences() throws (testing::TestError) {
91
    try testing::expect(encode::fenceI() == 0x0000100f);
92
    try testing::expect(decode::decode(encode::fenceI()) == decode::Instr::FenceI);
93
    let case decode::Instr::Fence { predecessor, successor } = decode::decode(encode::fenceOrder(15, 15))
94
        else {
95
            throw testing::TestError::Failed;
96
        };
97
    try testing::expect(predecessor == 15 and successor == 15);
98
    let case decode::Instr::Unknown { .. } = decode::decode(0x0000200f)
99
        else {
100
            throw testing::TestError::Failed;
101
        };
102
}
103
104
/// Reject invalid atomic operands and fence access classes.
105
@test unsafe fn invalidAssembly() throws (testing::TestError) {
106
    for source in &[
107
        "lr.w %a0 %a2 (%a1);", "sc.d %a0 %a2 8(%a1);",
108
        "amoadd.d.aq.aq %a0 %a2 (%a1);", "fence rr rw;", "fence rx rw;",
109
        "fence 1 rw;", "fence.i %a0;",
110
    ] {
111
        let mut arena = alloc::new(&mut MEMORY[..]);
112
        let words = &mut WORDS[..];
113
        let data: *mut [u8] = &mut [];
114
        let mut rejected = false;
115
        try asm::assemble(asm::scanner::SourceKind::String, source,
116
            &mut words[..], &mut data[..], &mut arena, &mut STRINGS, 0) catch {
117
            set rejected = true;
118
        };
119
        try testing::expect(rejected);
120
    }
121
}