lib/std/arch/rv64/asm/tests.rad 11.7 KiB raw
1
//! RV64 assembler tests.
2
3
use std::testing;
4
use std::mem;
5
use std::lang::alloc;
6
use std::lang::sexpr;
7
use std::lang::strings;
8
use std::arch::rv64;
9
use std::arch::rv64::encode;
10
use std::arch::rv64::printer;
11
12
use super::scanner;
13
14
static ASM_ARENA_STORAGE: [u8; 65536] = undefined;
15
static ASM_TEXT_STORAGE: [u32; 256] = undefined;
16
static ASM_DATA_STORAGE: [u8; 1024] = undefined;
17
static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 };
18
static PRINT_ARENA_STORAGE: [u8; 1024] = undefined;
19
static PRINT_BUFFER: [u8; 128] = undefined;
20
21
fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) {
22
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
23
    return try super::assemble(
24
        scanner::SourceKind::String,
25
        source,
26
        &mut ASM_TEXT_STORAGE[..],
27
        &mut ASM_DATA_STORAGE[..],
28
        &mut arena,
29
        &mut ASM_STRING_POOL,
30
        rv64::RO_DATA_BASE
31
    ) catch {
32
        throw testing::TestError::Failed;
33
    };
34
}
35
36
fn expectAssembleFail(source: *[u8]) throws (testing::TestError) {
37
    let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]);
38
    try super::assemble(
39
        scanner::SourceKind::String,
40
        source,
41
        &mut ASM_TEXT_STORAGE[..],
42
        &mut ASM_DATA_STORAGE[..],
43
        &mut arena,
44
        &mut ASM_STRING_POOL,
45
        rv64::RO_DATA_BASE
46
    ) catch {
47
        return;
48
    };
49
    throw testing::TestError::Failed;
50
}
51
52
fn printInstrText(instr: u32) -> *[u8] {
53
    let mut arena = alloc::new(&mut PRINT_ARENA_STORAGE[..]);
54
    let mut pos: u32 = 0;
55
    let mut out = sexpr::Output::Buffer { buf: &mut PRINT_BUFFER[..], pos: &mut pos };
56
    printer::printInstr(&mut out, &mut arena, instr);
57
    return &PRINT_BUFFER[..pos];
58
}
59
60
@test fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) {
61
    let program = try assembleSource(
62
        ".text;\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n"
63
    );
64
    try testing::expect(program.text.len == 2);
65
    try testing::expect(program.text[0] == encode::addi(rv64::A0, rv64::ZERO, 42));
66
    try testing::expect(program.text[1] == encode::sd(rv64::A0, rv64::SP, 8));
67
}
68
69
@test fn testAssembleDataAddressUsesRoDataBase() throws (testing::TestError) {
70
    let program = try assembleSource(
71
        ".text;\nla %t0 @value;\n.data;\n.byte 0;\n@value\n.byte 1;\n"
72
    );
73
    try testing::expect(program.text.len == 2);
74
    try testing::expect(program.text[0] == encode::lui(rv64::T0, 0x10));
75
    try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 1));
76
}
77
78
@test fn testAssembleTextAddressUsesPcRelative() throws (testing::TestError) {
79
    let program = try assembleSource(
80
        ".text;\nla %t0 @target;\n@target\nret;\n"
81
    );
82
    try testing::expect(program.text.len == 3);
83
    try testing::expect(program.text[0] == encode::auipc(rv64::T0, 0));
84
    try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 8));
85
}
86
87
@test fn testAssembleQuotedLabelNames() throws (testing::TestError) {
88
    let program = try assembleSource(
89
        ".text;\nj @\"foo.bar.baz\";\n@\"foo.bar.baz\"\nret;\n"
90
    );
91
    try testing::expect(program.text.len == 2);
92
    try testing::expect(program.text[0] == encode::jal(rv64::ZERO, 4));
93
    try testing::expect(program.text[1] == encode::jalr(rv64::ZERO, rv64::RA, 0));
94
}
95
96
/// Verbose source must fit an arena sized for its small assembled program.
97
@test fn testAssembleLongCommentWithBoundedSymbolStorage() throws (testing::TestError) {
98
    let mut source: [u8; 8192] = ['x'; 8192];
99
    set source[0] = '/';
100
    set source[1] = '/';
101
    let body = "\n.text; j @\"local::destination\"; @\"local::destination\" ret;";
102
    let _copied = try! mem::copy(&mut source[8192 - body.len..], body);
103
    let program = try assembleSource(&source[..]);
104
    try testing::expect(program.text.len == 2);
105
    try testing::expect(program.text[0] == encode::jal(rv64::ZERO, 4));
106
    try testing::expect(program.text[1] == encode::jalr(rv64::ZERO, rv64::RA, 0));
107
}
108
109
/// Fence encodings must order device I/O and synchronize instruction fetches.
110
@test fn testAssembleMachineFences() throws (testing::TestError) {
111
    let program = try assembleSource(".text; fence; fence.i;");
112
    try testing::expect(program.text.len == 2);
113
    try testing::expect(program.text[0] == 0x0ff0000f);
114
    try testing::expect(program.text[1] == 0x0000100f);
115
    try expectAssembleFail(".text; fence.i %a0;");
116
    try expectAssembleFail(".text; fence.x;");
117
}
118
119
@test fn testAssembleGlobalMarksOnlyDeclaredSymbols() throws (testing::TestError) {
120
    let program = try assembleSource(
121
        ".text;\n.export @exported;\n@local\nret;\n@exported\nret;\n@late\n.export @late;\nret;\n"
122
    );
123
    try testing::expect(program.symbols.len == 3);
124
    try testing::expect(not program.symbols[0].isExported);
125
    try testing::expect(program.symbols[1].isExported);
126
    try testing::expect(program.symbols[2].isExported);
127
}
128
129
@test fn testAssembleExternalTextFixups() throws (testing::TestError) {
130
    let program = try assembleSource(
131
        ".text;\ntail @\"::default\";\nla %t0 @\"::default\";\n"
132
    );
133
    try testing::expect(program.externalFixups.len == 2);
134
135
    let case super::FixupInfo::Jal { rd, index } = program.externalFixups[0].info else {
136
        throw testing::TestError::Failed;
137
    };
138
    try testing::expect(mem::eq(program.externalFixups[0].symbol, "::default"));
139
    try testing::expect(rd == rv64::ZERO);
140
    try testing::expect(index == 0);
141
142
    let case super::FixupInfo::Addr { rd: addrRd, index: addrIndex } = program.externalFixups[1].info else {
143
        throw testing::TestError::Failed;
144
    };
145
    try testing::expect(mem::eq(program.externalFixups[1].symbol, "::default"));
146
    try testing::expect(addrRd == rv64::T0);
147
    try testing::expect(addrIndex == 1);
148
}
149
150
@test fn testAssembleInvalidOperandsFail() throws (testing::TestError) {
151
    try expectAssembleFail(
152
        ".text;\nbeq %a0 %a1 @missing;\n"
153
    );
154
    try expectAssembleFail(
155
        ".text;\naddi a0 zero 1;\n"
156
    );
157
    try expectAssembleFail(
158
        ".text;\naddi % a0 %zero 1;\n"
159
    );
160
    try expectAssembleFail(
161
        ".text;\nli %a0 UNKNOWN;\n"
162
    );
163
    try expectAssembleFail(
164
        ".text;\n@start\nj start;\n"
165
    );
166
    try expectAssembleFail(
167
        ".data;\n.dword @missing;\n"
168
    );
169
}
170
171
@test fn testAssembleInvalidSyntaxFails() throws (testing::TestError) {
172
    try expectAssembleFail(
173
        ".text;\n@dup\n@dup\nret;\n"
174
    );
175
    try expectAssembleFail(
176
        ".text;\naddi %a0, %zero, 1\n"
177
    );
178
    try expectAssembleFail(
179
        ".constant PAGE, 4096;\n"
180
    );
181
    try expectAssembleFail(
182
        ".text;\naddi %a0, %zero, 1;\n"
183
    );
184
    try expectAssembleFail(
185
        ".export @kernel::main, @data::sym;\n"
186
    );
187
}
188
189
@test fn testAssembleInvalidSectionsFail() throws (testing::TestError) {
190
    try expectAssembleFail(
191
        ".data;\n.dword @target;\n.text;\n@target\nret;\n"
192
    );
193
    try expectAssembleFail(
194
        ".data;\naddi %a0 %zero 1;\n"
195
    );
196
    try expectAssembleFail(
197
        ".text;\n.byte 1;\n"
198
    );
199
    try expectAssembleFail(
200
        ".text;\n.word 1;\n"
201
    );
202
    try expectAssembleFail(
203
        ".text;\n.dword 1;\n"
204
    );
205
    try expectAssembleFail(
206
        ".text;\n.ascii \"x\";\n"
207
    );
208
    try expectAssembleFail(
209
        ".data;\n@value\n.byte 1;\n.text;\nj @value;\n"
210
    );
211
}
212
213
@test fn testAssembleInvalidDirectivesFail() throws (testing::TestError) {
214
    try expectAssembleFail(
215
        ".data;\n.ascii 'x';\n"
216
    );
217
    try expectAssembleFail(
218
        ".data;\n.byte 1 + 2;\n"
219
    );
220
    try expectAssembleFail(
221
        ".data;\n.byte 256;\n"
222
    );
223
    try expectAssembleFail(
224
        ".data;\n.word 2147483648;\n"
225
    );
226
    try expectAssembleFail(
227
        ".data;\n.space 4294967296;\n"
228
    );
229
    try expectAssembleFail(
230
        ".data;\n.align 3;\n"
231
    );
232
    try expectAssembleFail(
233
        ".text;\n.align 12;\n"
234
    );
235
    try expectAssembleFail(
236
        ".data;\n.align 4294967296;\n"
237
    );
238
}
239
240
@test fn testAssembleInvalidImmediateRangesFail() throws (testing::TestError) {
241
    try expectAssembleFail(
242
        ".text;\nslli %a0 %a1 64;\n"
243
    );
244
    try expectAssembleFail(
245
        ".text;\nslli %a0 %a1 4294967296;\n"
246
    );
247
    try expectAssembleFail(
248
        ".text;\nslliw %a0 %a1 2147483648;\n"
249
    );
250
    try expectAssembleFail(
251
        ".text;\ncsrsi mstatus 32;\n"
252
    );
253
}
254
255
@test fn testAssembleAtomicOperationsAndWidths() throws (testing::TestError) {
256
    let sources: [*[u8]; 22] = [
257
        "lr.w %t0 0(%t2);",
258
        "lr.d %t0 0(%t2);",
259
        "sc.w %t0 %t1 0(%t2);",
260
        "sc.d %t0 %t1 0(%t2);",
261
        "amoswap.w %t0 %t1 0(%t2);",
262
        "amoswap.d %t0 %t1 0(%t2);",
263
        "amoadd.w %t0 %t1 0(%t2);",
264
        "amoadd.d %t0 %t1 0(%t2);",
265
        "amoxor.w %t0 %t1 0(%t2);",
266
        "amoxor.d %t0 %t1 0(%t2);",
267
        "amoand.w %t0 %t1 0(%t2);",
268
        "amoand.d %t0 %t1 0(%t2);",
269
        "amoor.w %t0 %t1 0(%t2);",
270
        "amoor.d %t0 %t1 0(%t2);",
271
        "amomin.w %t0 %t1 0(%t2);",
272
        "amomin.d %t0 %t1 0(%t2);",
273
        "amomax.w %t0 %t1 0(%t2);",
274
        "amomax.d %t0 %t1 0(%t2);",
275
        "amominu.w %t0 %t1 0(%t2);",
276
        "amominu.d %t0 %t1 0(%t2);",
277
        "amomaxu.w %t0 %t1 0(%t2);",
278
        "amomaxu.d %t0 %t1 0(%t2);",
279
    ];
280
    // Fixed ISA words keep encoder bugs visible instead of sharing its formula.
281
    let expected: [u32; 22] = [
282
        0x1003a2af, 0x1003b2af,
283
        0x1863a2af, 0x1863b2af,
284
        0x0863a2af, 0x0863b2af,
285
        0x0063a2af, 0x0063b2af,
286
        0x2063a2af, 0x2063b2af,
287
        0x6063a2af, 0x6063b2af,
288
        0x4063a2af, 0x4063b2af,
289
        0x8063a2af, 0x8063b2af,
290
        0xa063a2af, 0xa063b2af,
291
        0xc063a2af, 0xc063b2af,
292
        0xe063a2af, 0xe063b2af,
293
    ];
294
    for i in 0..sources.len {
295
        let program = try assembleSource(sources[i]);
296
        try testing::expect(program.text.len == 1);
297
        try testing::expect(program.text[0] == expected[i]);
298
    }
299
}
300
301
@test fn testAssembleAtomicOrderingAndRegisterFields() throws (testing::TestError) {
302
    let sources: [*[u8]; 5] = [
303
        "amoswap.w.aq %t0 %t1 0(%t2);",
304
        "amoswap.d.rl %t0 %t1 0(%t2);",
305
        "amoswap.d.aqrl %t0 %t1 0(%t2);",
306
        "lr.w.aqrl %t6 (%ra);",
307
        "sc.d.aq %zero %t6 (%t5);",
308
    ];
309
    let expected: [u32; 5] = [
310
        0x0c63a2af, 0x0a63b2af, 0x0e63b2af, 0x1600afaf, 0x1dff302f,
311
    ];
312
    for i in 0..sources.len {
313
        let program = try assembleSource(sources[i]);
314
        try testing::expect(program.text.len == 1);
315
        try testing::expect(program.text[0] == expected[i]);
316
    }
317
}
318
319
@test fn testAssembleInvalidAtomicOperandsFail() throws (testing::TestError) {
320
    try expectAssembleFail("lr.w %t0 %t1 0(%t2);");
321
    try expectAssembleFail("lr.d %t0;");
322
    try expectAssembleFail("sc.w %t0 0(%t2);");
323
    try expectAssembleFail("sc.d %t0 %t1 0(%t2) %t3;");
324
    try expectAssembleFail("amoadd.d %t0 %t1;");
325
    try expectAssembleFail("amoswap.w %t0 %t1 %t2;");
326
    try expectAssembleFail("lr.w %t0 1(%t2);");
327
    try expectAssembleFail("sc.d %t0 %t1 -1(%t2);");
328
    try expectAssembleFail("amoadd.w %t0 %t1 4294967296(%t2);");
329
}
330
331
@test fn testAssembleInvalidAtomicSuffixesFail() throws (testing::TestError) {
332
    try expectAssembleFail("lr %t0 0(%t2);");
333
    try expectAssembleFail("lr.b %t0 0(%t2);");
334
    try expectAssembleFail("sc.q %t0 %t1 0(%t2);");
335
    try expectAssembleFail("amoadd.wu %t0 %t1 0(%t2);");
336
    try expectAssembleFail("amoswap.aq.w %t0 %t1 0(%t2);");
337
    try expectAssembleFail("amoswap.w.rlaq %t0 %t1 0(%t2);");
338
    try expectAssembleFail("amoswap.w.aq.rl %t0 %t1 0(%t2);");
339
    try expectAssembleFail("amoswap.w.aq.aq %t0 %t1 0(%t2);");
340
    try expectAssembleFail("amoswap .w %t0 %t1 0(%t2);");
341
    try expectAssembleFail("amoswap.w .aq %t0 %t1 0(%t2);");
342
    try expectAssembleFail("add.w.aq %t0 %t1 %t2;");
343
}
344
345
@test fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) {
346
    let text = printInstrText(encode::addi(rv64::A0, rv64::SP, 42));
347
    try testing::expect(mem::eq(text, "addi    %a0, %sp, 42"));
348
}