compiler/
lib/
examples/
std/
arch/
rv64/
asm/
scanner/
emit.rad
8.8 KiB
parser.rad
32.9 KiB
scanner.rad
9.1 KiB
tests.rad
10.0 KiB
image/
shared/
asm.rad
24.8 KiB
atomicTests.rad
5.0 KiB
atomics.rad
3.6 KiB
bounds.rad
33.4 KiB
decode.rad
15.2 KiB
emit.rad
33.2 KiB
encode.rad
22.0 KiB
image.rad
4.5 KiB
isel.rad
52.6 KiB
printer.rad
13.9 KiB
shared.rad
16.8 KiB
tests.rad
17.2 KiB
rv64.rad
17.5 KiB
char/
collections/
graph/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/arch/rv64/asm/tests.rad
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] = [0; 65536]; |
| 15 | static ASM_TEXT_STORAGE: [u32; 256] = [0; 256]; |
| 16 | static ASM_DATA_STORAGE: [u8; 1024] = [0; 1024]; |
| 17 | unsafe static ASM_STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
| 18 | |
| 19 | /// The retired-instruction CSR assembles to its exact architectural encoding. |
| 20 | @test unsafe fn retiredCounter() throws (testing::TestError) { |
| 21 | let program = try assembleSource(".text;\ncsrr %a0 instret;\n"); |
| 22 | assert program.text.len == 1 and program.text[0] == 0xc0202573; |
| 23 | } |
| 24 | |
| 25 | /// Source buffer for comment capacity tests. |
| 26 | static SOURCE: [u8; 4096] = [0; 4096]; |
| 27 | |
| 28 | /// Long comments consume source space while leaving symbol and fixup demand small. |
| 29 | @test unsafe fn commentStorage() throws (testing::TestError) { |
| 30 | let source = &mut SOURCE[..]; |
| 31 | for i in 0..source.len { |
| 32 | set source[i] = 32; |
| 33 | } |
| 34 | set source[0] = '/'; set source[1] = '/'; |
| 35 | let text = "\n.text;\nret;\n"; |
| 36 | for byte, i in text { |
| 37 | set source[source.len - text.len + i] = byte; |
| 38 | } |
| 39 | let program = try assembleSource(&SOURCE[..]); |
| 40 | try testing::expect(program.text.len == 1 and program.text[0] == encode::ret()); |
| 41 | } |
| 42 | |
| 43 | unsafe fn assembleSource(source: *[u8]) -> super::Program throws (testing::TestError) { |
| 44 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
| 45 | return try super::assemble( |
| 46 | scanner::SourceKind::String, |
| 47 | source, |
| 48 | &mut ASM_TEXT_STORAGE[..], |
| 49 | &mut ASM_DATA_STORAGE[..], |
| 50 | &mut arena, |
| 51 | &mut ASM_STRING_POOL, |
| 52 | rv64::RO_DATA_BASE |
| 53 | ) catch { |
| 54 | throw testing::TestError::Failed; |
| 55 | }; |
| 56 | } |
| 57 | |
| 58 | unsafe fn expectAssembleFail(source: *[u8]) throws (testing::TestError) { |
| 59 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
| 60 | try super::assemble( |
| 61 | scanner::SourceKind::String, |
| 62 | source, |
| 63 | &mut ASM_TEXT_STORAGE[..], |
| 64 | &mut ASM_DATA_STORAGE[..], |
| 65 | &mut arena, |
| 66 | &mut ASM_STRING_POOL, |
| 67 | rv64::RO_DATA_BASE |
| 68 | ) catch { |
| 69 | return; |
| 70 | }; |
| 71 | throw testing::TestError::Failed; |
| 72 | } |
| 73 | |
| 74 | /// Compare the instruction text with the expected bytes. |
| 75 | fn expectInstrText(instr: u32, expected: &[u8]) throws (testing::TestError) { |
| 76 | let mut buffer: [u8; 128] = [0; 128]; |
| 77 | let bytes: 'output = &mut buffer[..] in { |
| 78 | let mut out = sexpr::Buffer 'output { buf: bytes, pos: 0 }; |
| 79 | printer::printInstr(&mut out, instr); |
| 80 | try testing::expectBytesEq(&out.buf[..out.pos], expected); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | @test unsafe fn testAssemblePercentPrefixedRegisters() throws (testing::TestError) { |
| 85 | let program = try assembleSource( |
| 86 | ".text;\naddi %a0 %zero 42;\nsd %a0 8(%sp);\n" |
| 87 | ); |
| 88 | try testing::expect(program.text.len == 2); |
| 89 | try testing::expect(program.text[0] == encode::addi(rv64::A0, rv64::ZERO, 42)); |
| 90 | try testing::expect(program.text[1] == encode::sd(rv64::A0, rv64::SP, 8)); |
| 91 | } |
| 92 | |
| 93 | @test unsafe fn testAssembleDataAddressUsesRoDataBase() throws (testing::TestError) { |
| 94 | let program = try assembleSource( |
| 95 | ".text;\nla %t0 @value;\n.data;\n.byte 0;\n@value\n.byte 1;\n" |
| 96 | ); |
| 97 | try testing::expect(program.text.len == 2); |
| 98 | try testing::expect(program.text[0] == encode::lui(rv64::T0, 0x10)); |
| 99 | try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 1)); |
| 100 | } |
| 101 | |
| 102 | @test unsafe fn testAssembleTextAddressUsesPcRelative() throws (testing::TestError) { |
| 103 | let program = try assembleSource( |
| 104 | ".text;\nla %t0 @target;\n@target\nret;\n" |
| 105 | ); |
| 106 | try testing::expect(program.text.len == 3); |
| 107 | try testing::expect(program.text[0] == encode::auipc(rv64::T0, 0)); |
| 108 | try testing::expect(program.text[1] == encode::addi(rv64::T0, rv64::T0, 8)); |
| 109 | } |
| 110 | |
| 111 | @test unsafe fn testAssembleQuotedLabelNames() throws (testing::TestError) { |
| 112 | let program = try assembleSource( |
| 113 | ".text;\nj @\"foo.bar.baz\";\n@\"foo.bar.baz\"\nret;\n" |
| 114 | ); |
| 115 | try testing::expect(program.text.len == 2); |
| 116 | try testing::expect(program.text[0] == encode::jal(rv64::ZERO, 4)); |
| 117 | try testing::expect(program.text[1] == encode::jalr(rv64::ZERO, rv64::RA, 0)); |
| 118 | } |
| 119 | |
| 120 | @test unsafe fn testAssembleGlobalMarksOnlyDeclaredSymbols() throws (testing::TestError) { |
| 121 | let program = try assembleSource( |
| 122 | ".text;\n.export @exported;\n@local\nret;\n@exported\nret;\n@late\n.export @late;\nret;\n" |
| 123 | ); |
| 124 | try testing::expect(program.symbols.len == 3); |
| 125 | try testing::expect(not program.symbols[0].isExported); |
| 126 | try testing::expect(program.symbols[1].isExported); |
| 127 | try testing::expect(program.symbols[2].isExported); |
| 128 | } |
| 129 | |
| 130 | @test unsafe fn testAssembleExternalTextFixups() throws (testing::TestError) { |
| 131 | let program = try assembleSource( |
| 132 | ".text;\ntail @\"::default\";\nla %t0 @\"::default\";\n" |
| 133 | ); |
| 134 | try testing::expect(program.externalFixups.len == 2); |
| 135 | |
| 136 | let case super::FixupInfo::Jal { rd, index } = program.externalFixups[0].info else { |
| 137 | throw testing::TestError::Failed; |
| 138 | }; |
| 139 | try testing::expect(mem::eq(program.externalFixups[0].symbol, "::default")); |
| 140 | try testing::expect(rd == rv64::ZERO); |
| 141 | try testing::expect(index == 0); |
| 142 | |
| 143 | let case super::FixupInfo::Addr { rd: addrRd, index: addrIndex } = program.externalFixups[1].info else { |
| 144 | throw testing::TestError::Failed; |
| 145 | }; |
| 146 | try testing::expect(mem::eq(program.externalFixups[1].symbol, "::default")); |
| 147 | try testing::expect(addrRd == rv64::T0); |
| 148 | try testing::expect(addrIndex == 1); |
| 149 | } |
| 150 | |
| 151 | @test unsafe fn testAssembleTextOverflow() throws (testing::TestError) { |
| 152 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
| 153 | try super::assemble( |
| 154 | scanner::SourceKind::String, |
| 155 | ".text;\nret;\nret;\n", |
| 156 | &mut ASM_TEXT_STORAGE[..1], |
| 157 | &mut ASM_DATA_STORAGE[..1], |
| 158 | &mut arena, |
| 159 | &mut ASM_STRING_POOL, |
| 160 | rv64::RO_DATA_BASE |
| 161 | ) catch err { |
| 162 | match err { |
| 163 | case super::Error::TextOverflow => return, |
| 164 | else => throw testing::TestError::Failed, |
| 165 | } |
| 166 | }; |
| 167 | throw testing::TestError::Failed; |
| 168 | } |
| 169 | |
| 170 | @test unsafe fn testAssembleDataOverflow() throws (testing::TestError) { |
| 171 | let mut arena = alloc::new(&mut ASM_ARENA_STORAGE[..]); |
| 172 | try super::assemble( |
| 173 | scanner::SourceKind::String, |
| 174 | ".data;\n.byte 1, 2;\n", |
| 175 | &mut ASM_TEXT_STORAGE[..1], |
| 176 | &mut ASM_DATA_STORAGE[..1], |
| 177 | &mut arena, |
| 178 | &mut ASM_STRING_POOL, |
| 179 | rv64::RO_DATA_BASE |
| 180 | ) catch err { |
| 181 | match err { |
| 182 | case super::Error::DataOverflow => return, |
| 183 | else => throw testing::TestError::Failed, |
| 184 | } |
| 185 | }; |
| 186 | throw testing::TestError::Failed; |
| 187 | } |
| 188 | |
| 189 | @test unsafe fn testAssembleInvalidOperandsFail() throws (testing::TestError) { |
| 190 | try expectAssembleFail( |
| 191 | ".text;\nbeq %a0 %a1 @missing;\n" |
| 192 | ); |
| 193 | try expectAssembleFail( |
| 194 | ".text;\naddi a0 zero 1;\n" |
| 195 | ); |
| 196 | try expectAssembleFail( |
| 197 | ".text;\naddi % a0 %zero 1;\n" |
| 198 | ); |
| 199 | try expectAssembleFail( |
| 200 | ".text;\nli %a0 UNKNOWN;\n" |
| 201 | ); |
| 202 | try expectAssembleFail( |
| 203 | ".text;\n@start\nj start;\n" |
| 204 | ); |
| 205 | try expectAssembleFail( |
| 206 | ".data;\n.dword @missing;\n" |
| 207 | ); |
| 208 | } |
| 209 | |
| 210 | @test unsafe fn testAssembleInvalidSyntaxFails() throws (testing::TestError) { |
| 211 | try expectAssembleFail( |
| 212 | ".text;\n@dup\n@dup\nret;\n" |
| 213 | ); |
| 214 | try expectAssembleFail( |
| 215 | ".text;\naddi %a0, %zero, 1\n" |
| 216 | ); |
| 217 | try expectAssembleFail( |
| 218 | ".constant PAGE, 4096;\n" |
| 219 | ); |
| 220 | try expectAssembleFail( |
| 221 | ".text;\naddi %a0, %zero, 1;\n" |
| 222 | ); |
| 223 | try expectAssembleFail( |
| 224 | ".export @kernel::main, @data::sym;\n" |
| 225 | ); |
| 226 | } |
| 227 | |
| 228 | @test unsafe fn testAssembleInvalidSectionsFail() throws (testing::TestError) { |
| 229 | try expectAssembleFail( |
| 230 | ".data;\n.dword @target;\n.text;\n@target\nret;\n" |
| 231 | ); |
| 232 | try expectAssembleFail( |
| 233 | ".data;\naddi %a0 %zero 1;\n" |
| 234 | ); |
| 235 | try expectAssembleFail( |
| 236 | ".text;\n.byte 1;\n" |
| 237 | ); |
| 238 | try expectAssembleFail( |
| 239 | ".text;\n.word 1;\n" |
| 240 | ); |
| 241 | try expectAssembleFail( |
| 242 | ".text;\n.dword 1;\n" |
| 243 | ); |
| 244 | try expectAssembleFail( |
| 245 | ".text;\n.ascii \"x\";\n" |
| 246 | ); |
| 247 | try expectAssembleFail( |
| 248 | ".data;\n@value\n.byte 1;\n.text;\nj @value;\n" |
| 249 | ); |
| 250 | } |
| 251 | |
| 252 | @test unsafe fn testAssembleInvalidDirectivesFail() throws (testing::TestError) { |
| 253 | try expectAssembleFail( |
| 254 | ".data;\n.ascii 'x';\n" |
| 255 | ); |
| 256 | try expectAssembleFail( |
| 257 | ".data;\n.byte 1 + 2;\n" |
| 258 | ); |
| 259 | try expectAssembleFail( |
| 260 | ".data;\n.byte 256;\n" |
| 261 | ); |
| 262 | try expectAssembleFail( |
| 263 | ".data;\n.word 2147483648;\n" |
| 264 | ); |
| 265 | try expectAssembleFail( |
| 266 | ".data;\n.space 4294967296;\n" |
| 267 | ); |
| 268 | try expectAssembleFail( |
| 269 | ".data;\n.align 3;\n" |
| 270 | ); |
| 271 | try expectAssembleFail( |
| 272 | ".text;\n.align 12;\n" |
| 273 | ); |
| 274 | try expectAssembleFail( |
| 275 | ".data;\n.align 4294967296;\n" |
| 276 | ); |
| 277 | } |
| 278 | |
| 279 | @test unsafe fn testAssembleInvalidImmediateRangesFail() throws (testing::TestError) { |
| 280 | try expectAssembleFail( |
| 281 | ".text;\nslli %a0 %a1 64;\n" |
| 282 | ); |
| 283 | try expectAssembleFail( |
| 284 | ".text;\nslli %a0 %a1 4294967296;\n" |
| 285 | ); |
| 286 | try expectAssembleFail( |
| 287 | ".text;\nslliw %a0 %a1 2147483648;\n" |
| 288 | ); |
| 289 | try expectAssembleFail( |
| 290 | ".text;\ncsrsi mstatus 32;\n" |
| 291 | ); |
| 292 | } |
| 293 | |
| 294 | @test fn testPrintInstrUsesPercentPrefixedRegisters() throws (testing::TestError) { |
| 295 | try expectInstrText(encode::addi(rv64::A0, rv64::SP, 42), "addi %a0, %sp, 42"); |
| 296 | } |
| 297 | |
| 298 | /// Immediate operands preserve signs, separators, and address syntax. |
| 299 | @test fn testSafePrintImmediates() throws (testing::TestError) { |
| 300 | try expectInstrText(encode::addi(rv64::A0, rv64::ZERO, -2048), "li %a0, -2048"); |
| 301 | try expectInstrText(encode::ld(rv64::A0, rv64::SP, -8), "ld %a0, -8(%sp)"); |
| 302 | try expectInstrText(encode::sd(rv64::A0, rv64::SP, 16), "sd %a0, 16(%sp)"); |
| 303 | try expectInstrText(encode::beq(rv64::A0, rv64::SP, -4), "beq %a0, %sp, -4"); |
| 304 | try expectInstrText(encode::bne(rv64::A0, rv64::ZERO, 4), "bnez %a0, 4"); |
| 305 | try expectInstrText(encode::jal(rv64::ZERO, -4), "j -4"); |
| 306 | try expectInstrText(0xFFFFFFFF, "unknown(4294967295)"); |
| 307 | } |