compiler/
lib/
examples/
std/
arch/
rv64/
asm/
scanner/
emit.rad
8.2 KiB
parser.rad
30.3 KiB
scanner.rad
9.2 KiB
tests.rad
7.3 KiB
asm.rad
23.3 KiB
decode.rad
14.3 KiB
emit.rad
31.6 KiB
encode.rad
21.6 KiB
isel.rad
51.6 KiB
printer.rad
12.8 KiB
tests.rad
17.2 KiB
rv64.rad
14.4 KiB
char/
collections/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.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
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
4.1 KiB
README
2.5 KiB
STYLE
2.6 KiB
std.lib
1.2 KiB
std.lib.test
347 B
lib/std/arch/rv64/asm/parser.rad
raw
| 1 | //! Assembler parser pass. |
| 2 | use std::mem; |
| 3 | use std::fmt; |
| 4 | use std::lang::alloc; |
| 5 | use std::lang::strings; |
| 6 | use std::lang::parser; |
| 7 | use std::lang::gen; |
| 8 | use std::collections::dict; |
| 9 | use std::arch::rv64::encode; |
| 10 | use std::arch::rv64; |
| 11 | |
| 12 | use super::emit; |
| 13 | use super::scanner; |
| 14 | |
| 15 | /// Parsed memory operand with base register and signed byte offset. |
| 16 | record MemOperand: Copy { |
| 17 | /// Base register inside the memory operand parentheses. |
| 18 | base: gen::Reg, |
| 19 | /// Signed byte offset preceding the base register. |
| 20 | offset: i32, |
| 21 | } |
| 22 | |
| 23 | /// Parse assembler source into the supplied assembler state. |
| 24 | export unsafe fn parseProgram(a: &mut super::Assembler) throws (super::Error) { |
| 25 | advance(a); |
| 26 | |
| 27 | while a.scan.current.kind <> scanner::TokenKind::Eof { |
| 28 | try parseItem(a); |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | /// Align `value` upward to `alignment`, returning nil on u32 overflow. |
| 33 | fn checkedAlignUp(value: u32, alignment: u32) -> ?u32 { |
| 34 | let padding = alignment - 1; |
| 35 | if value > parser::U32_MAX - padding { |
| 36 | return nil; |
| 37 | } |
| 38 | return mem::alignUp(value, alignment); |
| 39 | } |
| 40 | |
| 41 | /// Advance the parser by one token, preserving the previous token. |
| 42 | unsafe fn advance(a: &mut super::Assembler) { |
| 43 | set a.scan.previous = a.scan.current; |
| 44 | set a.scan.current = scanner::next(&mut a.scan); |
| 45 | } |
| 46 | |
| 47 | /// Consume the current token when it has `kind`. |
| 48 | unsafe fn consume(a: &mut super::Assembler, kind: scanner::TokenKind) -> bool { |
| 49 | if a.scan.current.kind == kind { |
| 50 | advance(a); |
| 51 | return true; |
| 52 | } |
| 53 | return false; |
| 54 | } |
| 55 | |
| 56 | /// Create an error at the current token. |
| 57 | fn fail(a: &super::Assembler, message: *[u8]) -> super::Error { |
| 58 | return super::Error::Invalid { offset: a.scan.current.offset, message }; |
| 59 | } |
| 60 | |
| 61 | /// Create an error at `tok`. |
| 62 | fn failOnToken(tok: scanner::Token, message: *[u8]) -> super::Error { |
| 63 | return super::Error::Invalid { offset: tok.offset, message }; |
| 64 | } |
| 65 | |
| 66 | /// Require that a data directive appears while assembling the data section. |
| 67 | fn expectDataSection(a: &super::Assembler, tok: scanner::Token) throws (super::Error) { |
| 68 | if a.section <> super::Section::Data { |
| 69 | throw failOnToken(tok, "data directive is only valid in the data section"); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /// Consume `kind` or throw `message` at the current token. |
| 74 | unsafe fn expect(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) throws (super::Error) { |
| 75 | if not consume(a, kind) { |
| 76 | throw fail(a, message); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /// Consume `kind` and return the consumed token. |
| 81 | unsafe fn expectToken(a: &mut super::Assembler, kind: scanner::TokenKind, message: *[u8]) -> scanner::Token throws (super::Error) { |
| 82 | try expect(a, kind, message); |
| 83 | return a.scan.previous; |
| 84 | } |
| 85 | |
| 86 | /// Require that the current item has reached its semicolon terminator. |
| 87 | fn expectTerminator(a: &super::Assembler, message: *[u8]) throws (super::Error) { |
| 88 | if a.scan.current.kind <> scanner::TokenKind::Semicolon { |
| 89 | throw fail(a, message); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | /// Require that `value` fits in i32. |
| 94 | fn expectI32Value(a: &super::Assembler, value: i64, message: *[u8]) -> i32 throws (super::Error) { |
| 95 | if value < -super::I32_MIN_MAGNITUDE or value > super::I32_MAX_VALUE { |
| 96 | throw fail(a, message); |
| 97 | } |
| 98 | return value as i32; |
| 99 | } |
| 100 | |
| 101 | /// Require that `value` fits in a signed 12-bit immediate field. |
| 102 | fn expectSmallImmValue(a: &super::Assembler, value: i64) -> i32 throws (super::Error) { |
| 103 | if not encode::isSmallImm64(value) { |
| 104 | throw fail(a, "immediate out of range"); |
| 105 | } |
| 106 | return value as i32; |
| 107 | } |
| 108 | |
| 109 | /// Define a label at the current text or data offset. |
| 110 | unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
| 111 | if dict::get(&a.symbolMap, name) <> nil { |
| 112 | throw failOnToken(tok, "duplicate label"); |
| 113 | } |
| 114 | emit::defineSymbol(a, name); |
| 115 | } |
| 116 | |
| 117 | /// Emit a parsed integer data value after applying source-level range checks. |
| 118 | unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) { |
| 119 | match width { |
| 120 | case super::DataWidth::Word => |
| 121 | try emit::emitDataValue(a, (try expectI32Value(a, value, "word literal out of range")) as i64, width), |
| 122 | case super::DataWidth::Dword => |
| 123 | try emit::emitDataValue(a, value, width), |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | /// Parse a possibly scoped name from one or more `::`-separated segments. |
| 128 | unsafe fn parseScopedName( |
| 129 | a: &mut super::Assembler, |
| 130 | kind: scanner::TokenKind, |
| 131 | message: *[u8], |
| 132 | trimPrefix: u32 |
| 133 | ) -> *[u8] throws (super::Error) { |
| 134 | let first = try expectToken(a, kind, message); |
| 135 | let start = first.offset + trimPrefix; |
| 136 | let mut end = first.offset + first.source.len; |
| 137 | |
| 138 | while consume(a, scanner::TokenKind::ColonColon) { |
| 139 | let segment = try expectToken(a, scanner::TokenKind::Ident, "expected identifier after `::`"); |
| 140 | set end = segment.offset + segment.source.len; |
| 141 | } |
| 142 | let source = &a.scan.source[start..end]; |
| 143 | return strings::intern(a.scan.pool, source); |
| 144 | } |
| 145 | |
| 146 | /// Parse a bare symbol name. |
| 147 | unsafe fn parseSymbolName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
| 148 | return try parseScopedName(a, scanner::TokenKind::Ident, "expected symbol name", 0); |
| 149 | } |
| 150 | |
| 151 | /// Return `true` when [`tok`] is any label token form. |
| 152 | fn isLabel(tok: scanner::TokenKind) -> bool { |
| 153 | return tok == scanner::TokenKind::Label or tok == scanner::TokenKind::QuotedLabel; |
| 154 | } |
| 155 | |
| 156 | /// Parse the contents of a quoted label token, decoding escapes as needed. |
| 157 | unsafe fn parseQuotedLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
| 158 | let tok = try expectToken(a, scanner::TokenKind::QuotedLabel, "expected label name"); |
| 159 | let rawStart = super::LABEL_SIGIL_LEN + super::QUOTE_DELIM_LEN; |
| 160 | let raw = &tok.source[rawStart..tok.source.len - super::QUOTE_DELIM_LEN]; |
| 161 | let storage = try alloc::allocSlice(a.arena, 1, 1, raw.len) catch { |
| 162 | panic "asm: out of memory allocating quoted label"; |
| 163 | } as *mut [u8]; |
| 164 | let len = fmt::unescapeString(raw, storage); |
| 165 | |
| 166 | return strings::intern(a.scan.pool, &storage[..len]); |
| 167 | } |
| 168 | |
| 169 | /// Parse a label reference or definition name. |
| 170 | unsafe fn parseLabelName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
| 171 | if a.scan.current.kind == scanner::TokenKind::QuotedLabel { |
| 172 | return try parseQuotedLabelName(a); |
| 173 | } |
| 174 | return try parseScopedName(a, scanner::TokenKind::Label, "expected label name", super::LABEL_SIGIL_LEN); |
| 175 | } |
| 176 | |
| 177 | /// Parse a directive name without its leading `.`. |
| 178 | unsafe fn parseDirectiveName(a: &mut super::Assembler) -> *[u8] throws (super::Error) { |
| 179 | let name = try expectToken(a, scanner::TokenKind::Directive, "expected directive name"); |
| 180 | return &name.source[super::DIRECTIVE_SIGIL_LEN..]; |
| 181 | } |
| 182 | |
| 183 | /// Parse one top-level assembler item. |
| 184 | unsafe fn parseItem(a: &mut super::Assembler) throws (super::Error) { |
| 185 | match a.scan.current.kind { |
| 186 | case scanner::TokenKind::Ident => { |
| 187 | let tok = a.scan.current; |
| 188 | let name = try parseSymbolName(a); |
| 189 | try parseInstruction(a, name, tok); |
| 190 | try expect(a, scanner::TokenKind::Semicolon, "expected `;` after instruction"); |
| 191 | } |
| 192 | case scanner::TokenKind::Number => { |
| 193 | let tok = a.scan.current; |
| 194 | advance(a); |
| 195 | throw failOnToken(tok, "unexpected number at top level"); |
| 196 | } |
| 197 | case scanner::TokenKind::Label, scanner::TokenKind::QuotedLabel => { |
| 198 | let tok = a.scan.current; |
| 199 | let name = try parseLabelName(a); |
| 200 | try defineSymbol(a, name, tok); |
| 201 | } |
| 202 | case scanner::TokenKind::Directive => { |
| 203 | let tok = a.scan.current; |
| 204 | let name = try parseDirectiveName(a); |
| 205 | try parseDirective(a, name, tok); |
| 206 | try expect(a, scanner::TokenKind::Semicolon, "expected `;` after directive"); |
| 207 | } |
| 208 | else => throw fail(a, "expected label, instruction, or directive"), |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | /// Find `name` in a sorted descriptor table. |
| 213 | fn findSortedNameIndex(name: *[u8], len: u32, getName: fn(u32) -> *[u8]) -> ?u32 { |
| 214 | let mut left: u32 = 0; |
| 215 | let mut right: u32 = len; |
| 216 | |
| 217 | while left < right { |
| 218 | let mid = left + ((right - left) / 2); |
| 219 | let cmp = mem::cmp(name, getName(mid)); |
| 220 | |
| 221 | match cmp { |
| 222 | case -1 => set right = mid, |
| 223 | case 1 => set left = mid + 1, |
| 224 | else => return mid, |
| 225 | } |
| 226 | } |
| 227 | return nil; |
| 228 | } |
| 229 | |
| 230 | /// Adapter used by [`findSortedNameIndex`] to read an instruction mnemonic. |
| 231 | fn instructionNameAt(index: u32) -> *[u8] { |
| 232 | return super::INSTRUCTIONS[index].name; |
| 233 | } |
| 234 | |
| 235 | /// Adapter used by [`findSortedNameIndex`] to read a directive name. |
| 236 | fn directiveNameAt(index: u32) -> *[u8] { |
| 237 | return super::DIRECTIVES[index].name; |
| 238 | } |
| 239 | |
| 240 | /// Adapter used by [`findSortedNameIndex`] to read a register name. |
| 241 | fn registerNameAt(index: u32) -> *[u8] { |
| 242 | return super::REGISTERS[index].name; |
| 243 | } |
| 244 | |
| 245 | /// Adapter used by [`findSortedNameIndex`] to read a CSR name. |
| 246 | fn csrNameAt(index: u32) -> *[u8] { |
| 247 | return super::CSRS[index].name; |
| 248 | } |
| 249 | |
| 250 | /// Look up the operand parser and encoder for an instruction mnemonic. |
| 251 | fn lookupInstruction(name: *[u8]) -> ?super::InstructionEncoder { |
| 252 | let index = findSortedNameIndex(name, super::INSTRUCTIONS.len, instructionNameAt) else { |
| 253 | return nil; |
| 254 | }; |
| 255 | return super::INSTRUCTIONS[index].encoder; |
| 256 | } |
| 257 | |
| 258 | /// Classify a directive name. |
| 259 | fn classifyDirective(name: *[u8]) -> ?super::DirectiveKind { |
| 260 | let index = findSortedNameIndex(name, super::DIRECTIVES.len, directiveNameAt) else { |
| 261 | return nil; |
| 262 | }; |
| 263 | return super::DIRECTIVES[index].kind; |
| 264 | } |
| 265 | |
| 266 | /// Look up a percent-prefixed register name after the `%` has been removed. |
| 267 | fn lookupRegister(name: *[u8]) -> ?gen::Reg { |
| 268 | let index = findSortedNameIndex(name, super::REGISTERS.len, registerNameAt) else { |
| 269 | return nil; |
| 270 | }; |
| 271 | return super::REGISTERS[index].reg; |
| 272 | } |
| 273 | |
| 274 | /// Look up a CSR name. |
| 275 | fn lookupCsr(name: *[u8]) -> ?u32 { |
| 276 | let index = findSortedNameIndex(name, super::CSRS.len, csrNameAt) else { |
| 277 | return nil; |
| 278 | }; |
| 279 | return super::CSRS[index].csr; |
| 280 | } |
| 281 | |
| 282 | /// Parse an instruction after its mnemonic has already been consumed. |
| 283 | unsafe fn parseInstruction(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
| 284 | if a.section <> super::Section::Text { |
| 285 | throw failOnToken(tok, "instructions are only valid in the text section"); |
| 286 | } |
| 287 | let form = lookupInstruction(name) else { |
| 288 | throw failOnToken(tok, "unknown instruction"); |
| 289 | }; |
| 290 | match form { |
| 291 | case super::InstructionEncoder::NoOperand { enc } => { |
| 292 | if a.scan.current.kind <> scanner::TokenKind::Semicolon { |
| 293 | throw fail(a, "unexpected operand"); |
| 294 | } |
| 295 | try emit::emitText(a, enc()); |
| 296 | return; |
| 297 | } |
| 298 | case super::InstructionEncoder::Li => return try parseLi(a), |
| 299 | case super::InstructionEncoder::La => return try parseLa(a), |
| 300 | case super::InstructionEncoder::RR { enc } => return try parseRR(a, enc), |
| 301 | case super::InstructionEncoder::RRR { enc } => return try parseRRR(a, enc), |
| 302 | case super::InstructionEncoder::RRI { enc } => return try parseRRI(a, enc), |
| 303 | case super::InstructionEncoder::Shift { enc } => |
| 304 | return try parseShift(a, enc, super::SHIFT_LIMIT, "shift amount out of range"), |
| 305 | case super::InstructionEncoder::WordShift { enc } => |
| 306 | return try parseShift(a, enc, super::WORD_SHIFT_LIMIT, "word shift amount out of range"), |
| 307 | case super::InstructionEncoder::Load { enc } => return try parseLoad(a, enc), |
| 308 | case super::InstructionEncoder::Store { enc } => return try parseStore(a, enc), |
| 309 | case super::InstructionEncoder::Branch { op } => return try parseBranch(a, op), |
| 310 | case super::InstructionEncoder::BranchZero { op } => return try parseBranchZero(a, op), |
| 311 | case super::InstructionEncoder::Jal => return try parseJal(a), |
| 312 | case super::InstructionEncoder::Jump { rd } => return try parseJ(a, rd), |
| 313 | case super::InstructionEncoder::RdCsr { enc } => return try parseRdCsr(a, enc), |
| 314 | case super::InstructionEncoder::CsrRs1 { enc } => return try parseCsrRs1(a, enc), |
| 315 | case super::InstructionEncoder::Csrrw => return try parseCsrrw(a), |
| 316 | case super::InstructionEncoder::Csrsi => return try parseCsrsi(a), |
| 317 | case super::InstructionEncoder::Upper { enc } => return try parseUpper(a, enc), |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | /// Parse the `li` pseudo-instruction. |
| 322 | unsafe fn parseLi(a: &mut super::Assembler) throws (super::Error) { |
| 323 | let rd = try parseRegister(a); |
| 324 | let value = try parseValue(a); |
| 325 | if encode::isSmallImm64(value) { |
| 326 | try emit::emitText(a, encode::addi(rd, rv64::ZERO, value as i32)); |
| 327 | return; |
| 328 | } |
| 329 | let imm = try expectI32Value(a, value, "li immediate out of range"); |
| 330 | let split = rv64::emit::splitImm(imm); |
| 331 | |
| 332 | try emit::emitText(a, encode::lui(rd, split.hi)); |
| 333 | try emit::emitText(a, encode::addi(rd, rd, split.lo)); |
| 334 | } |
| 335 | |
| 336 | /// Parse the `la` pseudo-instruction. |
| 337 | unsafe fn parseLa(a: &mut super::Assembler) throws (super::Error) { |
| 338 | let rd = try parseRegister(a); |
| 339 | let target = try parseLabelName(a); |
| 340 | let index = a.text.len; |
| 341 | |
| 342 | try emit::recordTextFixup(a, target, super::FixupInfo::Addr { rd, index }, 2); |
| 343 | } |
| 344 | |
| 345 | /// Parse a CSR read-like instruction with destination register then CSR. |
| 346 | unsafe fn parseRdCsr(a: &mut super::Assembler, enc: fn(gen::Reg, u32) -> u32) throws (super::Error) { |
| 347 | let rd = try parseRegister(a); |
| 348 | let csr = try parseCsr(a); |
| 349 | |
| 350 | try emit::emitText(a, enc(rd, csr)); |
| 351 | } |
| 352 | |
| 353 | /// Parse a CSR write-like instruction with CSR then source register. |
| 354 | unsafe fn parseCsrRs1(a: &mut super::Assembler, enc: fn(u32, gen::Reg) -> u32) throws (super::Error) { |
| 355 | let csr = try parseCsr(a); |
| 356 | let rs1 = try parseRegister(a); |
| 357 | |
| 358 | try emit::emitText(a, enc(csr, rs1)); |
| 359 | } |
| 360 | |
| 361 | /// Parse `csrrw`. |
| 362 | unsafe fn parseCsrrw(a: &mut super::Assembler) throws (super::Error) { |
| 363 | let rd = try parseRegister(a); |
| 364 | let csr = try parseCsr(a); |
| 365 | let rs1 = try parseRegister(a); |
| 366 | |
| 367 | try emit::emitText(a, encode::csrrw(rd, csr, rs1)); |
| 368 | } |
| 369 | |
| 370 | /// Parse a CSR immediate instruction. |
| 371 | unsafe fn parseCsrsi(a: &mut super::Assembler) throws (super::Error) { |
| 372 | let csr = try parseCsr(a); |
| 373 | let imm = try parseValue(a); |
| 374 | if imm < 0 or imm >= super::CSR_IMM_LIMIT { |
| 375 | throw fail(a, "CSR immediate out of range"); |
| 376 | } |
| 377 | try emit::emitText(a, encode::csrsi(csr, imm as u32)); |
| 378 | } |
| 379 | |
| 380 | /// Parse a two-register instruction. |
| 381 | unsafe fn parseRR(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg) -> u32) throws (super::Error) { |
| 382 | let rd = try parseRegister(a); |
| 383 | let rs = try parseRegister(a); |
| 384 | |
| 385 | try emit::emitText(a, enc(rd, rs)); |
| 386 | } |
| 387 | |
| 388 | /// Parse a three-register instruction. |
| 389 | unsafe fn parseRRR(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32) throws (super::Error) { |
| 390 | let rd = try parseRegister(a); |
| 391 | let rs1 = try parseRegister(a); |
| 392 | let rs2 = try parseRegister(a); |
| 393 | |
| 394 | try emit::emitText(a, enc(rd, rs1, rs2)); |
| 395 | } |
| 396 | |
| 397 | /// Parse a register-register-immediate instruction. |
| 398 | unsafe fn parseRRI(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
| 399 | let rd = try parseRegister(a); |
| 400 | let rs1 = try parseRegister(a); |
| 401 | let imm = try parseSmallImm(a); |
| 402 | |
| 403 | try emit::emitText(a, enc(rd, rs1, imm)); |
| 404 | } |
| 405 | |
| 406 | /// Parse a shift-immediate instruction and enforce its RV64 shift bound. |
| 407 | unsafe fn parseShift( |
| 408 | a: &mut super::Assembler, |
| 409 | enc: fn(gen::Reg, gen::Reg, i32) -> u32, |
| 410 | limit: i32, |
| 411 | message: *[u8] |
| 412 | ) throws (super::Error) { |
| 413 | let rd = try parseRegister(a); |
| 414 | let rs1 = try parseRegister(a); |
| 415 | let shamt64 = try parseValue(a); |
| 416 | |
| 417 | if shamt64 < 0 { |
| 418 | throw fail(a, "shift amount must be non-negative"); |
| 419 | } |
| 420 | if shamt64 >= limit as i64 { |
| 421 | throw fail(a, message); |
| 422 | } |
| 423 | let shamt = shamt64 as i32; |
| 424 | |
| 425 | try emit::emitText(a, enc(rd, rs1, shamt)); |
| 426 | } |
| 427 | |
| 428 | /// Parse a load instruction with a memory operand. |
| 429 | unsafe fn parseLoad(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
| 430 | let rd = try parseRegister(a); |
| 431 | let memop = try parseMemory(a); |
| 432 | |
| 433 | try emit::emitText(a, enc(rd, memop.base, memop.offset)); |
| 434 | } |
| 435 | |
| 436 | /// Parse a store instruction with a memory operand. |
| 437 | unsafe fn parseStore(a: &mut super::Assembler, enc: fn(gen::Reg, gen::Reg, i32) -> u32) throws (super::Error) { |
| 438 | let rs2 = try parseRegister(a); |
| 439 | let memop = try parseMemory(a); |
| 440 | |
| 441 | try emit::emitText(a, enc(rs2, memop.base, memop.offset)); |
| 442 | } |
| 443 | |
| 444 | /// Parse a two-register branch instruction. |
| 445 | unsafe fn parseBranch(a: &mut super::Assembler, op: super::BranchOp) throws (super::Error) { |
| 446 | let rs1 = try parseRegister(a); |
| 447 | let rs2 = try parseRegister(a); |
| 448 | |
| 449 | try parseBranchLabel(a, op, rs1, rs2); |
| 450 | } |
| 451 | |
| 452 | /// Parse an optional label operand. |
| 453 | unsafe fn parseOptionalLabel(a: &mut super::Assembler) -> ?*[u8] throws (super::Error) { |
| 454 | if not isLabel(a.scan.current.kind) { |
| 455 | return nil; |
| 456 | } |
| 457 | return try parseLabelName(a); |
| 458 | } |
| 459 | |
| 460 | /// Parse a branch target as either a label fixup or immediate offset. |
| 461 | unsafe fn parseBranchLabel(a: &mut super::Assembler, op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg) throws (super::Error) { |
| 462 | let index = a.text.len; |
| 463 | if let target = try parseOptionalLabel(a) { |
| 464 | try emit::recordTextFixup(a, target, super::FixupInfo::Branch { op, rs1, rs2, index }, 1); |
| 465 | return; |
| 466 | } |
| 467 | let imm = try parseBranchImm(a); |
| 468 | try emit::emitText(a, emit::encodeBranch(op, rs1, rs2, imm)); |
| 469 | } |
| 470 | |
| 471 | /// Parse a branch-to-zero pseudo-instruction. |
| 472 | unsafe fn parseBranchZero(a: &mut super::Assembler, op: super::BranchOp) throws (super::Error) { |
| 473 | let rs = try parseRegister(a); |
| 474 | try parseBranchLabel(a, op, rs, rv64::ZERO); |
| 475 | } |
| 476 | |
| 477 | /// Parse `jal` with an explicit destination register. |
| 478 | unsafe fn parseJal(a: &mut super::Assembler) throws (super::Error) { |
| 479 | let rd = try parseRegister(a); |
| 480 | try parseJ(a, rd); |
| 481 | } |
| 482 | |
| 483 | /// Parse a jump target for `jal` or a jump pseudo-instruction. |
| 484 | unsafe fn parseJ(a: &mut super::Assembler, rd: gen::Reg) throws (super::Error) { |
| 485 | let index = a.text.len; |
| 486 | if let target = try parseOptionalLabel(a) { |
| 487 | try emit::recordTextFixup(a, target, super::FixupInfo::Jal { rd, index }, 1); |
| 488 | return; |
| 489 | } |
| 490 | let imm = try parseJumpImm(a); |
| 491 | try emit::emitText(a, encode::jal(rd, imm)); |
| 492 | } |
| 493 | |
| 494 | /// Parse an upper-immediate instruction. |
| 495 | unsafe fn parseUpper(a: &mut super::Assembler, enc: fn(gen::Reg, i32) -> u32) throws (super::Error) { |
| 496 | let rd = try parseRegister(a); |
| 497 | let imm64 = try parseValue(a); |
| 498 | if imm64 < 0 or imm64 > super::UPPER_IMM_MAX_VALUE { |
| 499 | throw fail(a, "upper immediate out of range"); |
| 500 | } |
| 501 | try emit::emitText(a, enc(rd, imm64 as i32)); |
| 502 | } |
| 503 | |
| 504 | /// Parse a directive after its name has already been consumed. |
| 505 | unsafe fn parseDirective(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) { |
| 506 | let directive = classifyDirective(name) else { |
| 507 | throw failOnToken(tok, "unknown directive"); |
| 508 | }; |
| 509 | match directive { |
| 510 | case super::DirectiveKind::Text => { |
| 511 | try expectTerminator(a, "unexpected operand"); |
| 512 | set a.section = super::Section::Text; |
| 513 | return; |
| 514 | } |
| 515 | case super::DirectiveKind::Data => { |
| 516 | try expectTerminator(a, "unexpected operand"); |
| 517 | set a.section = super::Section::Data; |
| 518 | return; |
| 519 | } |
| 520 | case super::DirectiveKind::Align => |
| 521 | return try parseAlignDirective(a), |
| 522 | case super::DirectiveKind::Ascii => { |
| 523 | try expectDataSection(a, tok); |
| 524 | return try parseStringDirective(a); |
| 525 | } |
| 526 | case super::DirectiveKind::Byte => { |
| 527 | try expectDataSection(a, tok); |
| 528 | return try parseByteDirective(a); |
| 529 | } |
| 530 | case super::DirectiveKind::Constant => |
| 531 | return try parseConstantDirective(a), |
| 532 | case super::DirectiveKind::Dword => { |
| 533 | try expectDataSection(a, tok); |
| 534 | return try parseIntDirective(a, super::DataWidth::Dword); |
| 535 | } |
| 536 | case super::DirectiveKind::Export => |
| 537 | return try parseExportDirective(a), |
| 538 | case super::DirectiveKind::Space => { |
| 539 | try expectDataSection(a, tok); |
| 540 | return try parseSpaceDirective(a); |
| 541 | } |
| 542 | case super::DirectiveKind::Word => { |
| 543 | try expectDataSection(a, tok); |
| 544 | return try parseIntDirective(a, super::DataWidth::Word); |
| 545 | } |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | /// Parse a `.constant` directive. |
| 550 | unsafe fn parseConstantDirective(a: &mut super::Assembler) throws (super::Error) { |
| 551 | let name = try parseSymbolName(a); |
| 552 | let value = try expectI32Value(a, try parseExpr(a), "constant out of range"); |
| 553 | |
| 554 | dict::insert(&mut a.constMap, name, value); |
| 555 | } |
| 556 | |
| 557 | /// Parse a `.export` directive. |
| 558 | unsafe fn parseExportDirective(a: &mut super::Assembler) throws (super::Error) { |
| 559 | let name = try parseLabelName(a); |
| 560 | dict::insert(&mut a.exportMap, name, 1); |
| 561 | if let idx = dict::get(&a.symbolMap, name) { |
| 562 | set a.symbols[idx as u32].isExported = true; |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | /// Parse a `.space` directive. |
| 567 | unsafe fn parseSpaceDirective(a: &mut super::Assembler) throws (super::Error) { |
| 568 | let count = try parseValue(a); |
| 569 | if count < 0 { |
| 570 | throw fail(a, "space size must be non-negative"); |
| 571 | } |
| 572 | // The data section grows on demand; only reject sizes that cannot be |
| 573 | // represented as a section offset. |
| 574 | if count > super::U32_MAX_VALUE - a.data.len as i64 { |
| 575 | throw super::Error::DataOverflow; |
| 576 | } |
| 577 | for _ in 0..count as u32 { |
| 578 | try emit::emitByte(a, 0); |
| 579 | } |
| 580 | } |
| 581 | |
| 582 | /// Parse an `.align` directive for the current section. |
| 583 | unsafe fn parseAlignDirective(a: &mut super::Assembler) throws (super::Error) { |
| 584 | let amount64 = try parseValue(a); |
| 585 | if amount64 <= 0 { |
| 586 | throw fail(a, "alignment must be positive"); |
| 587 | } |
| 588 | if amount64 > super::U32_MAX_VALUE { |
| 589 | throw fail(a, "alignment out of range"); |
| 590 | } |
| 591 | let amount = amount64 as u32; |
| 592 | if (amount & (amount - 1)) <> 0 { |
| 593 | throw fail(a, "alignment must be a power of two"); |
| 594 | } |
| 595 | match a.section { |
| 596 | case super::Section::Text => { |
| 597 | if amount % rv64::INSTR_SIZE as u32 <> 0 { |
| 598 | throw fail(a, "text alignment must be a multiple of 4"); |
| 599 | } |
| 600 | let bytes = a.text.len * rv64::INSTR_SIZE as u32; |
| 601 | let aligned = checkedAlignUp(bytes, amount) else { |
| 602 | throw super::Error::TextOverflow; |
| 603 | }; |
| 604 | let words = (aligned - bytes) / rv64::INSTR_SIZE as u32; |
| 605 | try emit::emitTextPadding(a, words); |
| 606 | } |
| 607 | case super::Section::Data => { |
| 608 | let aligned = checkedAlignUp(a.data.len, amount) else { |
| 609 | throw super::Error::DataOverflow; |
| 610 | }; |
| 611 | for _ in a.data.len..aligned { |
| 612 | try emit::emitByte(a, 0); |
| 613 | } |
| 614 | } |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | /// Parse a `.byte` directive. |
| 619 | unsafe fn parseByteDirective(a: &mut super::Assembler) throws (super::Error) { |
| 620 | loop { |
| 621 | if a.scan.current.kind == scanner::TokenKind::Char { |
| 622 | let ch = parseCharLiteral(a.scan.current) else { |
| 623 | throw fail(a, "invalid char literal"); |
| 624 | }; |
| 625 | try emit::emitByte(a, ch); |
| 626 | advance(a); |
| 627 | } else { |
| 628 | let value = try parseValue(a); |
| 629 | if value < 0 or value > super::U8_MAX_VALUE { |
| 630 | throw fail(a, "byte literal out of range"); |
| 631 | } |
| 632 | try emit::emitByte(a, value as u8); |
| 633 | } |
| 634 | if not consume(a, scanner::TokenKind::Comma) { |
| 635 | return; |
| 636 | } |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | /// Parse a fixed-width integer data directive. |
| 641 | unsafe fn parseIntDirective(a: &mut super::Assembler, width: super::DataWidth) throws (super::Error) { |
| 642 | loop { |
| 643 | if isLabel(a.scan.current.kind) { |
| 644 | let target = try parseLabelName(a); |
| 645 | try emit::recordDataFixup(a, target, width); |
| 646 | } else if a.scan.current.kind == scanner::TokenKind::Char { |
| 647 | let ch = parseCharLiteral(a.scan.current) else { |
| 648 | throw fail(a, "invalid char literal"); |
| 649 | }; |
| 650 | advance(a); |
| 651 | try emitDataValue(a, ch as i64, width); |
| 652 | } else { |
| 653 | try emitDataValue(a, try parseValue(a), width); |
| 654 | } |
| 655 | if not consume(a, scanner::TokenKind::Comma) { |
| 656 | return; |
| 657 | } |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | /// Parse a `.ascii` string literal list. |
| 662 | unsafe fn parseStringDirective(a: &mut super::Assembler) throws (super::Error) { |
| 663 | loop { |
| 664 | let literal = try expectToken(a, scanner::TokenKind::String, "expected string literal"); |
| 665 | try emit::emitDecodedString(a, literal.source); |
| 666 | if not consume(a, scanner::TokenKind::Comma) { |
| 667 | return; |
| 668 | } |
| 669 | } |
| 670 | } |
| 671 | |
| 672 | /// Parse and resolve a register operand. |
| 673 | unsafe fn parseRegister(a: &mut super::Assembler) -> gen::Reg throws (super::Error) { |
| 674 | let tok = try expectToken(a, scanner::TokenKind::Register, "expected register"); |
| 675 | let reg = lookupRegister(&tok.source[1..]) else { |
| 676 | throw super::Error::Invalid { offset: tok.offset, message: "unknown register" }; |
| 677 | }; |
| 678 | return reg; |
| 679 | } |
| 680 | |
| 681 | /// Parse a simple signed immediate or constant value. |
| 682 | unsafe fn parseValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 683 | if consume(a, scanner::TokenKind::Minus) { |
| 684 | return -(try parseValuePrimary(a)); |
| 685 | } |
| 686 | return try parseValuePrimary(a); |
| 687 | } |
| 688 | |
| 689 | /// Parse the primary form used by simple immediate values. |
| 690 | unsafe fn parseValuePrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 691 | if a.scan.current.kind == scanner::TokenKind::Number { |
| 692 | return try parseInteger(a); |
| 693 | } |
| 694 | if a.scan.current.kind == scanner::TokenKind::Ident { |
| 695 | return try parseConstantValue(a); |
| 696 | } |
| 697 | throw fail(a, "expected number or constant"); |
| 698 | } |
| 699 | |
| 700 | /// Parse an additive constant expression. |
| 701 | unsafe fn parseExpr(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 702 | let mut value = try parseExprMul(a); |
| 703 | |
| 704 | while a.scan.current.kind == scanner::TokenKind::Plus or a.scan.current.kind == scanner::TokenKind::Minus { |
| 705 | let op = a.scan.current.kind; |
| 706 | advance(a); |
| 707 | |
| 708 | let rhs = try parseExprMul(a); |
| 709 | if op == scanner::TokenKind::Plus { |
| 710 | set value += rhs; |
| 711 | } else { |
| 712 | set value -= rhs; |
| 713 | } |
| 714 | } |
| 715 | return value; |
| 716 | } |
| 717 | |
| 718 | /// Parse multiplicative expression operators. |
| 719 | unsafe fn parseExprMul(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 720 | let mut value = try parseExprUnary(a); |
| 721 | |
| 722 | while a.scan.current.kind == scanner::TokenKind::Star or a.scan.current.kind == scanner::TokenKind::Slash { |
| 723 | let op = a.scan.current.kind; |
| 724 | advance(a); |
| 725 | |
| 726 | let rhs = try parseExprUnary(a); |
| 727 | if op == scanner::TokenKind::Star { |
| 728 | set value *= rhs; |
| 729 | } else { |
| 730 | if rhs == 0 { |
| 731 | throw fail(a, "division by zero"); |
| 732 | } |
| 733 | set value /= rhs; |
| 734 | } |
| 735 | } |
| 736 | return value; |
| 737 | } |
| 738 | |
| 739 | /// Parse unary expression operators. |
| 740 | unsafe fn parseExprUnary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 741 | if consume(a, scanner::TokenKind::Minus) { |
| 742 | return -(try parseExprUnary(a)); |
| 743 | } |
| 744 | if consume(a, scanner::TokenKind::Plus) { |
| 745 | return try parseExprUnary(a); |
| 746 | } |
| 747 | return try parseExprPrimary(a); |
| 748 | } |
| 749 | |
| 750 | /// Parse expression atoms. |
| 751 | unsafe fn parseExprPrimary(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 752 | if consume(a, scanner::TokenKind::LParen) { |
| 753 | let value = try parseExpr(a); |
| 754 | try expect(a, scanner::TokenKind::RParen, "expected `)`"); |
| 755 | return value; |
| 756 | } |
| 757 | if a.scan.current.kind == scanner::TokenKind::Number { |
| 758 | return try parseInteger(a); |
| 759 | } |
| 760 | if a.scan.current.kind == scanner::TokenKind::Ident { |
| 761 | return try parseConstantValue(a); |
| 762 | } |
| 763 | throw fail(a, "expected expression"); |
| 764 | } |
| 765 | |
| 766 | /// Parse and resolve a named assembler constant. |
| 767 | unsafe fn parseConstantValue(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 768 | let name = try parseSymbolName(a); |
| 769 | let value = dict::get(&a.constMap, name) else { |
| 770 | throw super::Error::Invalid { offset: a.scan.previous.offset, message: "undefined constant" }; |
| 771 | }; |
| 772 | return value as i64; |
| 773 | } |
| 774 | |
| 775 | /// Parse and resolve a CSR operand. |
| 776 | unsafe fn parseCsr(a: &mut super::Assembler) -> u32 throws (super::Error) { |
| 777 | let name = try parseSymbolName(a); |
| 778 | let csr = lookupCsr(name) else { |
| 779 | throw super::Error::Invalid { offset: a.scan.previous.offset, message: "unknown CSR" }; |
| 780 | }; |
| 781 | return csr; |
| 782 | } |
| 783 | |
| 784 | /// Parse an offset(base) memory operand. |
| 785 | unsafe fn parseMemory(a: &mut super::Assembler) -> MemOperand throws (super::Error) { |
| 786 | let mut offset: i32 = 0; |
| 787 | if a.scan.current.kind <> scanner::TokenKind::LParen { |
| 788 | set offset = try expectSmallImmValue(a, try parseValue(a)); |
| 789 | } |
| 790 | try expect(a, scanner::TokenKind::LParen, "expected `(`"); |
| 791 | let base = try parseRegister(a); |
| 792 | try expect(a, scanner::TokenKind::RParen, "expected `)`"); |
| 793 | |
| 794 | return MemOperand { base, offset }; |
| 795 | } |
| 796 | |
| 797 | /// Parse an immediate value that fits in a signed 12-bit field. |
| 798 | unsafe fn parseSmallImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
| 799 | return try expectSmallImmValue(a, try parseValue(a)); |
| 800 | } |
| 801 | |
| 802 | /// Parse and validate a branch immediate. |
| 803 | unsafe fn parseBranchImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
| 804 | let value = try expectI32Value(a, try parseValue(a), "branch immediate out of range"); |
| 805 | if not encode::isBranchImm(value) { |
| 806 | throw fail(a, "branch immediate out of range"); |
| 807 | } |
| 808 | return value; |
| 809 | } |
| 810 | |
| 811 | /// Parse and validate a jump immediate. |
| 812 | unsafe fn parseJumpImm(a: &mut super::Assembler) -> i32 throws (super::Error) { |
| 813 | let value = try expectI32Value(a, try parseValue(a), "jump immediate out of range"); |
| 814 | if not encode::isJumpImm(value) { |
| 815 | throw fail(a, "jump immediate out of range"); |
| 816 | } |
| 817 | return value; |
| 818 | } |
| 819 | |
| 820 | /// Parse an integer token as an i64. |
| 821 | unsafe fn parseInteger(a: &mut super::Assembler) -> i64 throws (super::Error) { |
| 822 | let tok = try expectToken(a, scanner::TokenKind::Number, "expected number"); |
| 823 | let value = parseIntegerText(tok.source) else { |
| 824 | throw failOnToken(tok, "invalid integer literal"); |
| 825 | }; |
| 826 | return value; |
| 827 | } |
| 828 | |
| 829 | /// Parse integer literal text as an i64. |
| 830 | fn parseIntegerText(text: *[u8]) -> ?i64 { |
| 831 | if text.len == 0 { |
| 832 | return nil; |
| 833 | } |
| 834 | let negative = text[0] == '-'; |
| 835 | let magnitudeText = &text[1..] if negative or text[0] == '+' else text; |
| 836 | let literal = try fmt::parseInt(magnitudeText) catch { |
| 837 | return nil; |
| 838 | }; |
| 839 | if negative { |
| 840 | if literal.magnitude > parser::I64_MIN_MAGNITUDE { |
| 841 | return nil; |
| 842 | } |
| 843 | if literal.magnitude == parser::I64_MIN_MAGNITUDE { |
| 844 | return parser::I64_MIN; |
| 845 | } |
| 846 | return -(literal.magnitude as i64); |
| 847 | } |
| 848 | if literal.magnitude > parser::I64_MAX_MAGNITUDE { |
| 849 | return nil; |
| 850 | } |
| 851 | return literal.magnitude as i64; |
| 852 | } |
| 853 | |
| 854 | /// Parse a character literal token as one byte. |
| 855 | fn parseCharLiteral(tok: scanner::Token) -> ?u8 { |
| 856 | return try fmt::parseChar(tok.source) catch { |
| 857 | return nil; |
| 858 | }; |
| 859 | } |