compiler/
kernel/
lib/
examples/
std/
arch/
rv64/
asm/
image/
shared/
asm.rad
24.5 KiB
atomicTests.rad
5.0 KiB
atomics.rad
3.6 KiB
bounds.rad
8.7 KiB
decode.rad
15.2 KiB
emit.rad
33.2 KiB
encode.rad
22.0 KiB
image.rad
4.5 KiB
isel.rad
51.0 KiB
printer.rad
14.2 KiB
shared.rad
16.8 KiB
tests.rad
17.2 KiB
rv64.rad
17.1 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
9.2 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/arch/rv64/asm.rad
raw
| 1 | //! Minimal RV64 assembler. |
| 2 | //! |
| 3 | //! This module assembles `.ras` source files into RV64 text words plus a raw |
| 4 | //! data prefix that can be linked into a compiler-generated program. It exists |
| 5 | //! so the Radiance driver can mix hand-written RV64 assembly with generated IL |
| 6 | //! output without invoking an external assembler or linker. |
| 7 | //! |
| 8 | //! Assembly is intentionally direct and buffer-oriented. The caller provides a |
| 9 | //! text buffer, a data buffer, an arena, and the runtime base address where the |
| 10 | //! data buffer will be loaded. The parser writes encoded instructions into the |
| 11 | //! text buffer as it reads them and writes directive bytes into the data buffer |
| 12 | //! while in `.data`. The returned [`Program`] only contains slices into those |
| 13 | //! caller-provided buffers, so no ownership transfer or late copy is needed. |
| 14 | //! |
| 15 | //! The scanner is assembly-specific. It produces tokens for registers (`%a0`), |
| 16 | //! labels (`@name`), directives, strings, characters, numbers, and |
| 17 | //! punctuation. The parser consumes those tokens as a small line-oriented |
| 18 | //! language: *directives* declare sections or emit data, *labels* define |
| 19 | //! symbols at the current section offset, and *instructions* are validated |
| 20 | //! against RV64 operand forms before being encoded. |
| 21 | //! |
| 22 | //! Labels are defined at the current text instruction index or data byte |
| 23 | //! offset. The parser is single-pass because it keeps assembly cheap and lets |
| 24 | //! instructions and data be emitted immediately, but forward references mean |
| 25 | //! some operands cannot be encoded when first seen. Branches, jumps, |
| 26 | //! load-address operands, and data directives that reference labels therefore |
| 27 | //! record fixups. After parsing reaches EOF, the emitter resolves the final |
| 28 | //! symbol table and patches every recorded use with the correct PC-relative |
| 29 | //! offset, absolute data address, or encoded data value. |
| 30 | //! |
| 31 | //! Data labels are resolved relative to the data base address. The compiler |
| 32 | //! driver accumulates all assembly data in a RO data prefix, passes |
| 33 | //! [`RO_DATA_BASE`] + `currentPrefixLen` for each input, then appends the |
| 34 | //! input's emitted data to that prefix. Global text symbols are exported for |
| 35 | //! call resolution when the assembled text is appended to the RV64 generator, |
| 36 | //! shifted by the generator's current code length so disassembly/debug output |
| 37 | //! can name those instruction addresses correctly. Non-global text labels |
| 38 | //! remain local to their assembly fragment. |
| 39 | use std::lang::alloc; |
| 40 | use std::lang::strings; |
| 41 | use std::lang::gen; |
| 42 | use std::collections::dict; |
| 43 | use std::arch::rv64::encode; |
| 44 | use std::arch::rv64; |
| 45 | |
| 46 | /// Assembler scanner module. |
| 47 | export mod scanner; |
| 48 | /// Assembler parser module. |
| 49 | export mod parser; |
| 50 | /// Assembler emission and fixup module. |
| 51 | export mod emit; |
| 52 | /// Tests. |
| 53 | @test mod tests; |
| 54 | |
| 55 | /// In-memory result of assembling one RV64 assembly fragment. |
| 56 | /// |
| 57 | /// [`Program`] is the boundary between the textual assembler and the rest of |
| 58 | /// the compiler. The assembler reads an assembly source file, encodes all |
| 59 | /// instructions, lays out all data bytes, resolves fixups that can be resolved |
| 60 | /// inside the fragment, and returns these three slices as the assembled |
| 61 | /// program. |
| 62 | /// |
| 63 | /// The value is intentionally not a standalone object file or linked |
| 64 | /// executable. It carries only the sections and symbol table needed by the |
| 65 | /// compiler driver. The slices point at caller-owned storage: `text` and |
| 66 | /// `data` are backed by the buffers passed to [`assemble`], while symbol names |
| 67 | /// are interned in the assembler's string pool. |
| 68 | /// |
| 69 | /// Symbol offsets are section-local byte offsets. Text symbols name positions |
| 70 | /// in `text`; data symbols name positions in `data`. When the compiler |
| 71 | /// consumes the program, [`rv64::addAssembly`] appends the text words to the |
| 72 | /// generated text stream and registers text labels at their relocated offsets. |
| 73 | /// The driver copies `data` into the final read-only data prefix; the data |
| 74 | /// base supplied to [`assemble`] lets the assembler resolve data addresses as |
| 75 | /// they will appear in that final layout. |
| 76 | export record Program: Copy { |
| 77 | /// Encoded instructions in the text section. |
| 78 | text: *[u32], |
| 79 | /// Raw bytes in the data section. |
| 80 | data: *[u8], |
| 81 | /// Symbols defined by the source. |
| 82 | symbols: *[Symbol], |
| 83 | /// Text references resolved by the whole-program emitter. |
| 84 | externalFixups: *[Fixup], |
| 85 | } |
| 86 | |
| 87 | /// Errors reported while assembling source text. |
| 88 | export union Error: Copy { |
| 89 | /// Invalid syntax or operand form at a source offset. |
| 90 | Invalid { offset: u32, message: *[u8] }, |
| 91 | /// The source emitted more text words than the caller-provided buffer holds. |
| 92 | TextOverflow, |
| 93 | /// The source emitted more data bytes than the caller-provided buffer holds. |
| 94 | DataOverflow, |
| 95 | } |
| 96 | |
| 97 | /// Active output section. |
| 98 | export union Section: Copy { |
| 99 | /// Instruction section. |
| 100 | Text, |
| 101 | /// Data byte section. |
| 102 | Data, |
| 103 | } |
| 104 | |
| 105 | /// Branch opcode that needs fixup. |
| 106 | export union BranchOp: Copy { |
| 107 | /// Branch if equal. |
| 108 | Beq, |
| 109 | /// Branch if not equal. |
| 110 | Bne, |
| 111 | /// Branch if less than, signed. |
| 112 | Blt, |
| 113 | /// Branch if greater than or equal, signed. |
| 114 | Bge, |
| 115 | /// Branch if less than, unsigned. |
| 116 | Bltu, |
| 117 | /// Branch if greater than or equal, unsigned. |
| 118 | Bgeu, |
| 119 | /// Branch if less than or equal, signed pseudo-instruction. |
| 120 | Ble, |
| 121 | /// Branch if greater than, signed pseudo-instruction. |
| 122 | Bgt, |
| 123 | } |
| 124 | |
| 125 | /// Parser and encoder behavior for one instruction mnemonic. |
| 126 | export union InstructionEncoder: Copy { |
| 127 | /// No-operand instruction encoded by a fixed encoder. |
| 128 | NoOperand { enc: fn() -> u32 }, |
| 129 | /// Load-immediate pseudo-instruction. |
| 130 | Li, |
| 131 | /// Load-address pseudo-instruction. |
| 132 | La, |
| 133 | /// Two-register instruction or pseudo-instruction. |
| 134 | RR { enc: fn(gen::Reg, gen::Reg) -> u32 }, |
| 135 | /// Three-register instruction. |
| 136 | RRR { enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32 }, |
| 137 | /// Register, register, immediate instruction. |
| 138 | RRI { enc: fn(gen::Reg, gen::Reg, i32) -> u32 }, |
| 139 | /// Shift-immediate instruction with RV64 shift bounds. |
| 140 | Shift { enc: fn(gen::Reg, gen::Reg, i32) -> u32 }, |
| 141 | /// Shift-immediate instruction with RV64 W-mode shift bounds. |
| 142 | WordShift { enc: fn(gen::Reg, gen::Reg, i32) -> u32 }, |
| 143 | /// Load instruction with memory operand syntax. |
| 144 | Load { enc: fn(gen::Reg, gen::Reg, i32) -> u32 }, |
| 145 | /// Store instruction with memory operand syntax. |
| 146 | Store { enc: fn(gen::Reg, gen::Reg, i32) -> u32 }, |
| 147 | /// Two-register branch instruction. |
| 148 | Branch { op: BranchOp }, |
| 149 | /// One-register branch-to-zero pseudo-instruction. |
| 150 | BranchZero { op: BranchOp }, |
| 151 | /// Memory fence with optional predecessor and successor masks. |
| 152 | Fence, |
| 153 | /// `jal` instruction with explicit destination register. |
| 154 | Jal, |
| 155 | /// Jump pseudo-instruction with fixed destination register. |
| 156 | Jump { rd: gen::Reg }, |
| 157 | /// CSR read-style operand form. |
| 158 | RdCsr { enc: fn(gen::Reg, u32) -> u32 }, |
| 159 | /// CSR write-style operand form. |
| 160 | CsrRs1 { enc: fn(u32, gen::Reg) -> u32 }, |
| 161 | /// CSR read/write operand form. |
| 162 | Csrrw, |
| 163 | /// CSR immediate operand form. |
| 164 | Csrsi, |
| 165 | /// Upper-immediate operand form. |
| 166 | Upper { enc: fn(gen::Reg, i32) -> u32 }, |
| 167 | } |
| 168 | |
| 169 | /// Classified directive name. |
| 170 | export union DirectiveKind: Copy { |
| 171 | /// `.align` directive. |
| 172 | Align, |
| 173 | /// `.ascii` directive. |
| 174 | Ascii, |
| 175 | /// `.byte` directive. |
| 176 | Byte, |
| 177 | /// `.constant` directive. |
| 178 | Constant, |
| 179 | /// `.data` directive. |
| 180 | Data, |
| 181 | /// `.dword` directive. |
| 182 | Dword, |
| 183 | /// `.export` directive. |
| 184 | Export, |
| 185 | /// `.space` directive. |
| 186 | Space, |
| 187 | /// `.text` directive. |
| 188 | Text, |
| 189 | /// `.word` directive. |
| 190 | Word, |
| 191 | } |
| 192 | |
| 193 | /// Instruction descriptor table row. |
| 194 | record InstructionEntry: Copy { |
| 195 | /// Assembly mnemonic text. |
| 196 | name: *[u8], |
| 197 | /// Operand parser and encoder behavior. |
| 198 | encoder: InstructionEncoder, |
| 199 | } |
| 200 | |
| 201 | /// Directive descriptor table row. |
| 202 | record DirectiveEntry: Copy { |
| 203 | /// Directive name without the leading `.`. |
| 204 | name: *[u8], |
| 205 | /// Parser behavior for the directive. |
| 206 | kind: DirectiveKind, |
| 207 | } |
| 208 | |
| 209 | /// Register descriptor table row. |
| 210 | record RegisterEntry: Copy { |
| 211 | /// Register alias text without the leading `%`. |
| 212 | name: *[u8], |
| 213 | /// Numeric register selected by the alias. |
| 214 | reg: gen::Reg, |
| 215 | } |
| 216 | |
| 217 | /// CSR descriptor table row. |
| 218 | record CsrEntry: Copy { |
| 219 | /// CSR name text. |
| 220 | name: *[u8], |
| 221 | /// Numeric CSR address. |
| 222 | csr: u32, |
| 223 | } |
| 224 | |
| 225 | /// Width of an integer data directive. |
| 226 | export union DataWidth: Copy { |
| 227 | /// 32-bit data value. |
| 228 | Word, |
| 229 | /// 64-bit data value. |
| 230 | Dword, |
| 231 | } |
| 232 | |
| 233 | /// Extra slot used when sizing source-derived symbol and fixup buffers. |
| 234 | export constant SOURCE_CAP_PADDING: u32 = 1; |
| 235 | /// Scale factor used to keep assembler hash tables sparse. |
| 236 | export constant TABLE_CAPACITY_SCALE: u32 = 4; |
| 237 | /// Minimum hash-table capacity used by the assembler. |
| 238 | export constant MIN_TABLE_CAPACITY: u32 = 8; |
| 239 | /// `@label` names exclude the leading sigil byte when interned. |
| 240 | export constant LABEL_SIGIL_LEN: u32 = 1; |
| 241 | /// `.directive` names exclude the leading sigil byte when matched. |
| 242 | export constant DIRECTIVE_SIGIL_LEN: u32 = 1; |
| 243 | /// String and character literals are delimited by one byte on each side. |
| 244 | export constant QUOTE_DELIM_LEN: u32 = 1; |
| 245 | /// Number of bits in one byte. |
| 246 | export constant BITS_PER_BYTE: u64 = 8; |
| 247 | /// Mask for extracting one encoded byte. |
| 248 | export constant BYTE_MASK: u64 = 0xFF; |
| 249 | /// Largest signed 32-bit assembler value. |
| 250 | export constant I32_MAX_VALUE: i64 = 2147483647; |
| 251 | /// Magnitude of the smallest signed 32-bit assembler value. |
| 252 | export constant I32_MIN_MAGNITUDE: i64 = 2147483648; |
| 253 | /// Largest unsigned 32-bit assembler value. |
| 254 | export constant U32_MAX_VALUE: i64 = 4294967295; |
| 255 | /// Largest unsigned 8-bit assembler value. |
| 256 | export constant U8_MAX_VALUE: i64 = 255; |
| 257 | /// Upper bound for CSR immediate operands. |
| 258 | export constant CSR_IMM_LIMIT: i64 = 32; |
| 259 | /// Upper bound for RV64 W-mode shift immediates. |
| 260 | export constant WORD_SHIFT_LIMIT: i32 = 32; |
| 261 | /// Upper bound for RV64 shift immediates. |
| 262 | export constant SHIFT_LIMIT: i32 = 64; |
| 263 | /// Largest `lui` or `auipc` immediate. |
| 264 | export constant UPPER_IMM_MAX_VALUE: i64 = 0xFFFFF; |
| 265 | |
| 266 | /// Sorted instruction descriptor table used by the assembler parser. |
| 267 | export constant INSTRUCTIONS: [InstructionEntry; 89] = [ |
| 268 | { name: "add", encoder: InstructionEncoder::RRR { enc: encode::add } }, |
| 269 | { name: "addi", encoder: InstructionEncoder::RRI { enc: encode::addi } }, |
| 270 | { name: "addiw", encoder: InstructionEncoder::RRI { enc: encode::addiw } }, |
| 271 | { name: "addw", encoder: InstructionEncoder::RRR { enc: encode::addw } }, |
| 272 | { name: "and", encoder: InstructionEncoder::RRR { enc: encode::and_ } }, |
| 273 | { name: "andi", encoder: InstructionEncoder::RRI { enc: encode::andi } }, |
| 274 | { name: "auipc", encoder: InstructionEncoder::Upper { enc: encode::auipc } }, |
| 275 | { name: "beq", encoder: InstructionEncoder::Branch { op: BranchOp::Beq } }, |
| 276 | { name: "beqz", encoder: InstructionEncoder::BranchZero { op: BranchOp::Beq } }, |
| 277 | { name: "bge", encoder: InstructionEncoder::Branch { op: BranchOp::Bge } }, |
| 278 | { name: "bgeu", encoder: InstructionEncoder::Branch { op: BranchOp::Bgeu } }, |
| 279 | { name: "bgt", encoder: InstructionEncoder::Branch { op: BranchOp::Bgt } }, |
| 280 | { name: "ble", encoder: InstructionEncoder::Branch { op: BranchOp::Ble } }, |
| 281 | { name: "blt", encoder: InstructionEncoder::Branch { op: BranchOp::Blt } }, |
| 282 | { name: "bltu", encoder: InstructionEncoder::Branch { op: BranchOp::Bltu } }, |
| 283 | { name: "bne", encoder: InstructionEncoder::Branch { op: BranchOp::Bne } }, |
| 284 | { name: "bnez", encoder: InstructionEncoder::BranchZero { op: BranchOp::Bne } }, |
| 285 | { name: "call", encoder: InstructionEncoder::Jump { rd: rv64::RA } }, |
| 286 | { name: "csrc", encoder: InstructionEncoder::CsrRs1 { enc: encode::csrc } }, |
| 287 | { name: "csrr", encoder: InstructionEncoder::RdCsr { enc: encode::csrr } }, |
| 288 | { name: "csrrw", encoder: InstructionEncoder::Csrrw }, |
| 289 | { name: "csrsi", encoder: InstructionEncoder::Csrsi }, |
| 290 | { name: "csrw", encoder: InstructionEncoder::CsrRs1 { enc: encode::csrw } }, |
| 291 | { name: "div", encoder: InstructionEncoder::RRR { enc: encode::div } }, |
| 292 | { name: "divu", encoder: InstructionEncoder::RRR { enc: encode::divu } }, |
| 293 | { name: "divuw", encoder: InstructionEncoder::RRR { enc: encode::divuw } }, |
| 294 | { name: "divw", encoder: InstructionEncoder::RRR { enc: encode::divw } }, |
| 295 | { name: "ebreak", encoder: InstructionEncoder::NoOperand { enc: encode::ebreak } }, |
| 296 | { name: "ecall", encoder: InstructionEncoder::NoOperand { enc: encode::ecall } }, |
| 297 | { name: "fence", encoder: InstructionEncoder::Fence }, |
| 298 | { name: "fence.i", encoder: InstructionEncoder::NoOperand { enc: encode::fenceI } }, |
| 299 | { name: "j", encoder: InstructionEncoder::Jump { rd: rv64::ZERO } }, |
| 300 | { name: "jal", encoder: InstructionEncoder::Jal }, |
| 301 | { name: "jalr", encoder: InstructionEncoder::RRI { enc: encode::jalr } }, |
| 302 | { name: "la", encoder: InstructionEncoder::La }, |
| 303 | { name: "lb", encoder: InstructionEncoder::Load { enc: encode::lb } }, |
| 304 | { name: "lbu", encoder: InstructionEncoder::Load { enc: encode::lbu } }, |
| 305 | { name: "ld", encoder: InstructionEncoder::Load { enc: encode::ld } }, |
| 306 | { name: "lh", encoder: InstructionEncoder::Load { enc: encode::lh } }, |
| 307 | { name: "lhu", encoder: InstructionEncoder::Load { enc: encode::lhu } }, |
| 308 | { name: "li", encoder: InstructionEncoder::Li }, |
| 309 | { name: "lui", encoder: InstructionEncoder::Upper { enc: encode::lui } }, |
| 310 | { name: "lw", encoder: InstructionEncoder::Load { enc: encode::lw } }, |
| 311 | { name: "lwu", encoder: InstructionEncoder::Load { enc: encode::lwu } }, |
| 312 | { name: "mret", encoder: InstructionEncoder::NoOperand { enc: encode::mret } }, |
| 313 | { name: "mul", encoder: InstructionEncoder::RRR { enc: encode::mul } }, |
| 314 | { name: "mulh", encoder: InstructionEncoder::RRR { enc: encode::mulh } }, |
| 315 | { name: "mulhsu", encoder: InstructionEncoder::RRR { enc: encode::mulhsu } }, |
| 316 | { name: "mulhu", encoder: InstructionEncoder::RRR { enc: encode::mulhu } }, |
| 317 | { name: "mulw", encoder: InstructionEncoder::RRR { enc: encode::mulw } }, |
| 318 | { name: "mv", encoder: InstructionEncoder::RR { enc: encode::mv } }, |
| 319 | { name: "neg", encoder: InstructionEncoder::RR { enc: encode::neg } }, |
| 320 | { name: "nop", encoder: InstructionEncoder::NoOperand { enc: encode::nop } }, |
| 321 | { name: "not", encoder: InstructionEncoder::RR { enc: encode::not_ } }, |
| 322 | { name: "or", encoder: InstructionEncoder::RRR { enc: encode::or_ } }, |
| 323 | { name: "ori", encoder: InstructionEncoder::RRI { enc: encode::ori } }, |
| 324 | { name: "rem", encoder: InstructionEncoder::RRR { enc: encode::rem } }, |
| 325 | { name: "remu", encoder: InstructionEncoder::RRR { enc: encode::remu } }, |
| 326 | { name: "remuw", encoder: InstructionEncoder::RRR { enc: encode::remuw } }, |
| 327 | { name: "remw", encoder: InstructionEncoder::RRR { enc: encode::remw } }, |
| 328 | { name: "ret", encoder: InstructionEncoder::NoOperand { enc: encode::ret } }, |
| 329 | { name: "sb", encoder: InstructionEncoder::Store { enc: encode::sb } }, |
| 330 | { name: "sd", encoder: InstructionEncoder::Store { enc: encode::sd } }, |
| 331 | { name: "seqz", encoder: InstructionEncoder::RR { enc: encode::seqz } }, |
| 332 | { name: "sh", encoder: InstructionEncoder::Store { enc: encode::sh } }, |
| 333 | { name: "sll", encoder: InstructionEncoder::RRR { enc: encode::sll } }, |
| 334 | { name: "slli", encoder: InstructionEncoder::Shift { enc: encode::slli } }, |
| 335 | { name: "slliw", encoder: InstructionEncoder::WordShift { enc: encode::slliw } }, |
| 336 | { name: "sllw", encoder: InstructionEncoder::RRR { enc: encode::sllw } }, |
| 337 | { name: "slt", encoder: InstructionEncoder::RRR { enc: encode::slt } }, |
| 338 | { name: "slti", encoder: InstructionEncoder::RRI { enc: encode::slti } }, |
| 339 | { name: "sltiu", encoder: InstructionEncoder::RRI { enc: encode::sltiu } }, |
| 340 | { name: "sltu", encoder: InstructionEncoder::RRR { enc: encode::sltu } }, |
| 341 | { name: "snez", encoder: InstructionEncoder::RR { enc: encode::snez } }, |
| 342 | { name: "sra", encoder: InstructionEncoder::RRR { enc: encode::sra } }, |
| 343 | { name: "srai", encoder: InstructionEncoder::Shift { enc: encode::srai } }, |
| 344 | { name: "sraiw", encoder: InstructionEncoder::WordShift { enc: encode::sraiw } }, |
| 345 | { name: "sraw", encoder: InstructionEncoder::RRR { enc: encode::sraw } }, |
| 346 | { name: "srl", encoder: InstructionEncoder::RRR { enc: encode::srl } }, |
| 347 | { name: "srli", encoder: InstructionEncoder::Shift { enc: encode::srli } }, |
| 348 | { name: "srliw", encoder: InstructionEncoder::WordShift { enc: encode::srliw } }, |
| 349 | { name: "srlw", encoder: InstructionEncoder::RRR { enc: encode::srlw } }, |
| 350 | { name: "sub", encoder: InstructionEncoder::RRR { enc: encode::sub } }, |
| 351 | { name: "subw", encoder: InstructionEncoder::RRR { enc: encode::subw } }, |
| 352 | { name: "sw", encoder: InstructionEncoder::Store { enc: encode::sw } }, |
| 353 | { name: "tail", encoder: InstructionEncoder::Jump { rd: rv64::ZERO } }, |
| 354 | { name: "wfi", encoder: InstructionEncoder::NoOperand { enc: encode::wfi } }, |
| 355 | { name: "xor", encoder: InstructionEncoder::RRR { enc: encode::xor } }, |
| 356 | { name: "xori", encoder: InstructionEncoder::RRI { enc: encode::xori } }, |
| 357 | ]; |
| 358 | |
| 359 | /// Sorted directive lookup table used by the assembler parser. |
| 360 | export constant DIRECTIVES: [DirectiveEntry; 10] = [ |
| 361 | { name: "align", kind: DirectiveKind::Align }, |
| 362 | { name: "ascii", kind: DirectiveKind::Ascii }, |
| 363 | { name: "byte", kind: DirectiveKind::Byte }, |
| 364 | { name: "constant", kind: DirectiveKind::Constant }, |
| 365 | { name: "data", kind: DirectiveKind::Data }, |
| 366 | { name: "dword", kind: DirectiveKind::Dword }, |
| 367 | { name: "export", kind: DirectiveKind::Export }, |
| 368 | { name: "space", kind: DirectiveKind::Space }, |
| 369 | { name: "text", kind: DirectiveKind::Text }, |
| 370 | { name: "word", kind: DirectiveKind::Word }, |
| 371 | ]; |
| 372 | |
| 373 | /// Sorted register-name lookup table used by the assembler parser. |
| 374 | export constant REGISTERS: [RegisterEntry; 33] = [ |
| 375 | { name: "a0", reg: rv64::A0 }, |
| 376 | { name: "a1", reg: rv64::A1 }, |
| 377 | { name: "a2", reg: rv64::A2 }, |
| 378 | { name: "a3", reg: rv64::A3 }, |
| 379 | { name: "a4", reg: rv64::A4 }, |
| 380 | { name: "a5", reg: rv64::A5 }, |
| 381 | { name: "a6", reg: rv64::A6 }, |
| 382 | { name: "a7", reg: rv64::A7 }, |
| 383 | { name: "fp", reg: rv64::FP }, |
| 384 | { name: "gp", reg: rv64::GP }, |
| 385 | { name: "ra", reg: rv64::RA }, |
| 386 | { name: "s0", reg: rv64::S0 }, |
| 387 | { name: "s1", reg: rv64::S1 }, |
| 388 | { name: "s10", reg: rv64::S10 }, |
| 389 | { name: "s11", reg: rv64::S11 }, |
| 390 | { name: "s2", reg: rv64::S2 }, |
| 391 | { name: "s3", reg: rv64::S3 }, |
| 392 | { name: "s4", reg: rv64::S4 }, |
| 393 | { name: "s5", reg: rv64::S5 }, |
| 394 | { name: "s6", reg: rv64::S6 }, |
| 395 | { name: "s7", reg: rv64::S7 }, |
| 396 | { name: "s8", reg: rv64::S8 }, |
| 397 | { name: "s9", reg: rv64::S9 }, |
| 398 | { name: "sp", reg: rv64::SP }, |
| 399 | { name: "t0", reg: rv64::T0 }, |
| 400 | { name: "t1", reg: rv64::T1 }, |
| 401 | { name: "t2", reg: rv64::T2 }, |
| 402 | { name: "t3", reg: rv64::T3 }, |
| 403 | { name: "t4", reg: rv64::T4 }, |
| 404 | { name: "t5", reg: rv64::T5 }, |
| 405 | { name: "t6", reg: rv64::T6 }, |
| 406 | { name: "tp", reg: rv64::TP }, |
| 407 | { name: "zero", reg: rv64::ZERO }, |
| 408 | ]; |
| 409 | |
| 410 | /// Sorted CSR-name lookup table used by the assembler parser. |
| 411 | export constant CSRS: [CsrEntry; 10] = [ |
| 412 | { name: "instret", csr: 0xC02 }, |
| 413 | { name: "mcause", csr: 0x342 }, |
| 414 | { name: "mepc", csr: 0x341 }, |
| 415 | { name: "mhartid", csr: 0xF14 }, |
| 416 | { name: "mie", csr: 0x304 }, |
| 417 | { name: "mip", csr: 0x344 }, |
| 418 | { name: "mscratch", csr: 0x340 }, |
| 419 | { name: "mstatus", csr: 0x300 }, |
| 420 | { name: "mtval", csr: 0x343 }, |
| 421 | { name: "mtvec", csr: 0x305 }, |
| 422 | ]; |
| 423 | |
| 424 | /// Recorded symbol definition. |
| 425 | export record Symbol: Copy { |
| 426 | /// Symbol name. |
| 427 | name: *[u8], |
| 428 | /// Section the symbol belongs to. |
| 429 | section: Section, |
| 430 | /// Byte offset within the section. |
| 431 | offset: i32, |
| 432 | /// Whether `.export` exported this symbol outside its assembly fragment. |
| 433 | isExported: bool, |
| 434 | } |
| 435 | |
| 436 | /// Information needed to resolve a pending symbol reference. |
| 437 | export union FixupInfo: Copy { |
| 438 | /// Branch to a text label. |
| 439 | Branch { op: BranchOp, rs1: gen::Reg, rs2: gen::Reg, index: u32 }, |
| 440 | /// JAL-like jump to a text label. |
| 441 | Jal { rd: gen::Reg, index: u32 }, |
| 442 | /// Absolute address materialization into a register. |
| 443 | Addr { rd: gen::Reg, index: u32 }, |
| 444 | /// A 32-bit data word referring to a symbol offset. |
| 445 | Word { offset: u32 }, |
| 446 | /// A 64-bit data word referring to a symbol offset. |
| 447 | Dword { offset: u32 }, |
| 448 | } |
| 449 | |
| 450 | /// Pending symbol reference. |
| 451 | export record Fixup: Copy { |
| 452 | /// Referenced symbol. |
| 453 | symbol: *[u8], |
| 454 | /// Fixup payload. |
| 455 | info: FixupInfo, |
| 456 | } |
| 457 | |
| 458 | /// Parser and emission state. |
| 459 | export record Assembler { |
| 460 | /// Allocation arena for temporary assembler state. |
| 461 | arena: *unsafe mut alloc::Arena, |
| 462 | /// Assembler lexical scanner. |
| 463 | scan: scanner::Scanner, |
| 464 | /// Output text buffer. |
| 465 | text: *mut [u32], |
| 466 | /// Number of emitted text words. |
| 467 | textLen: u32, |
| 468 | /// Output data buffer. |
| 469 | data: *mut [u8], |
| 470 | /// Number of emitted data bytes. |
| 471 | dataLen: u32, |
| 472 | /// Current output section. |
| 473 | section: Section, |
| 474 | /// Defined symbols. |
| 475 | symbols: *mut [Symbol], |
| 476 | /// Number of defined symbols. |
| 477 | symbolsLen: u32, |
| 478 | /// Name-to-symbol index map. |
| 479 | symbolMap: dict::Dict, |
| 480 | /// Name-to-integer map. |
| 481 | constMap: dict::Dict, |
| 482 | /// Names marked by `.export`. |
| 483 | exportMap: dict::Dict, |
| 484 | /// Pending fixups. |
| 485 | fixups: *mut [Fixup], |
| 486 | /// Number of pending fixups. |
| 487 | fixupsLen: u32, |
| 488 | /// Fixups that reference text outside this assembly fragment. |
| 489 | externalFixups: *mut [Fixup], |
| 490 | /// Number of external fixups. |
| 491 | externalFixupsLen: u32, |
| 492 | /// Absolute runtime address of data-section offset zero. |
| 493 | dataBase: u32, |
| 494 | } |
| 495 | |
| 496 | /// Assemble source using `dataBase` as the runtime address of the data-section. |
| 497 | export unsafe fn assemble( |
| 498 | sourceKind: scanner::SourceKind, |
| 499 | source: *[u8], |
| 500 | textBuf: *mut [u32], |
| 501 | dataBuf: *mut [u8], |
| 502 | arena: &mut alloc::Arena, |
| 503 | pool: *unsafe mut strings::Pool, |
| 504 | dataBase: u32 |
| 505 | ) -> Program throws (Error) { |
| 506 | let slotCap = tokenCapacity(sourceKind, source, pool); |
| 507 | let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE); |
| 508 | |
| 509 | let symbols = try! alloc::allocSlice(arena, @sizeOf(Symbol), @alignOf(Symbol), slotCap); |
| 510 | let fixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap); |
| 511 | let externalFixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap); |
| 512 | let entries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
| 513 | let constEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
| 514 | let exportEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap); |
| 515 | |
| 516 | let symbolBuf = symbols as *mut [Symbol]; |
| 517 | let fixupBuf = fixups as *mut [Fixup]; |
| 518 | let externalBuf = externalFixups as *mut [Fixup]; |
| 519 | let mut a = Assembler { |
| 520 | arena: arena as *unsafe mut alloc::Arena, |
| 521 | scan: scanner::scanner(sourceKind, source, pool), |
| 522 | text: textBuf, |
| 523 | textLen: 0, |
| 524 | data: dataBuf, |
| 525 | dataLen: 0, |
| 526 | section: Section::Text, |
| 527 | symbols: symbolBuf, |
| 528 | symbolsLen: 0, |
| 529 | symbolMap: dict::init(entries as *mut [dict::Entry]), |
| 530 | constMap: dict::init(constEntries as *mut [dict::Entry]), |
| 531 | exportMap: dict::init(exportEntries as *mut [dict::Entry]), |
| 532 | fixups: fixupBuf, |
| 533 | fixupsLen: 0, |
| 534 | externalFixups: externalBuf, |
| 535 | externalFixupsLen: 0, |
| 536 | dataBase, |
| 537 | }; |
| 538 | // Parse assembly source and emit instructions. |
| 539 | try parser::parseProgram(&mut a); |
| 540 | // Resolve fixups and finalize program. |
| 541 | try emit::finishProgram(&mut a); |
| 542 | |
| 543 | let case Assembler { |
| 544 | text, textLen, data, dataLen, symbols: definedSymbols, symbolsLen, |
| 545 | externalFixups: pendingFixups, externalFixupsLen, .. |
| 546 | } = a |
| 547 | else panic "expected assembler state"; |
| 548 | return Program { |
| 549 | text: &text[..textLen], |
| 550 | data: &data[..dataLen], |
| 551 | symbols: &definedSymbols[..symbolsLen], |
| 552 | externalFixups: &pendingFixups[..externalFixupsLen], |
| 553 | }; |
| 554 | } |
| 555 | |
| 556 | /// Bound symbol and fixup counts by lexical tokens, including one spare slot. |
| 557 | unsafe fn tokenCapacity(kind: scanner::SourceKind, source: *[u8], pool: *unsafe mut strings::Pool) -> u32 { |
| 558 | let mut scan = scanner::scanner(kind, source, pool); |
| 559 | let mut count = SOURCE_CAP_PADDING; |
| 560 | while true { |
| 561 | let token = scanner::next(&mut scan); |
| 562 | if token.kind == scanner::TokenKind::Eof { |
| 563 | break; |
| 564 | } |
| 565 | set count += 1; |
| 566 | if token.kind == scanner::TokenKind::Invalid { |
| 567 | break; |
| 568 | } |
| 569 | } |
| 570 | return count; |
| 571 | } |
| 572 | |
| 573 | /// Return the next power of two at least as large as `value`. |
| 574 | fn nextPowerOfTwo(value: u32) -> u32 { |
| 575 | let mut n: u32 = MIN_TABLE_CAPACITY; |
| 576 | while n < value { |
| 577 | set n <<= 1; |
| 578 | } |
| 579 | return n; |
| 580 | } |