compiler/
lib/
examples/
std/
arch/
rv64/
asm/
scanner/
emit.rad
8.2 KiB
parser.rad
29.8 KiB
scanner.rad
9.0 KiB
tests.rad
6.3 KiB
asm.rad
22.6 KiB
decode.rad
14.3 KiB
emit.rad
26.0 KiB
encode.rad
21.5 KiB
isel.rad
47.0 KiB
printer.rad
12.6 KiB
tests.rad
17.1 KiB
rv64.rad
11.9 KiB
char/
collections/
lang/
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.5 KiB
intrinsics.rad
391 B
io.rad
1.3 KiB
lang.rad
258 B
mem.rad
2.2 KiB
sys.rad
173 B
testing.rad
2.4 KiB
tests.rad
14.8 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
366 B
.gitsigners
112 B
LICENSE
1.1 KiB
Makefile
3.6 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.2 KiB
std.lib.test
347 B
lib/std/arch/rv64/asm/emit.rad
raw
| 1 | //! Assembler emission and fixup helpers. |
| 2 | use std::arch::rv64::emit; |
| 3 | use std::arch::rv64::encode; |
| 4 | use std::arch::rv64; |
| 5 | use std::fmt; |
| 6 | |
| 7 | use std::collections::dict; |
| 8 | use std::lang::gen; |
| 9 | |
| 10 | /// Define a symbol at the current text or data offset. |
| 11 | export fn defineSymbol(a: *mut super::Assembler, name: *[u8]) { |
| 12 | if a.symbols.len >= a.symbols.cap { |
| 13 | panic "asm: symbol buffer full"; |
| 14 | } |
| 15 | let idx = a.symbols.len; |
| 16 | let offset: i32 = a.data.len as i32 |
| 17 | if a.section == super::Section::Data |
| 18 | else a.text.len as i32 * rv64::INSTR_SIZE; |
| 19 | |
| 20 | set a.symbols = @sliceOf(a.symbols.ptr, idx + 1, a.symbols.cap); |
| 21 | set a.symbols[idx] = super::Symbol { |
| 22 | name, |
| 23 | section: a.section, |
| 24 | offset, |
| 25 | isExported: dict::get(&a.exportMap, name) <> nil, |
| 26 | }; |
| 27 | dict::insert(&mut a.symbolMap, name, idx as i32); |
| 28 | } |
| 29 | |
| 30 | /// Append one encoded instruction word to the text section. |
| 31 | export fn emitText(a: *mut super::Assembler, word: u32) throws (super::Error) { |
| 32 | if a.text.len >= a.text.cap { |
| 33 | throw super::Error::TextOverflow; |
| 34 | } |
| 35 | let idx = a.text.len; |
| 36 | set a.text = @sliceOf(a.text.ptr, idx + 1, a.text.cap); |
| 37 | set a.text[idx] = word; |
| 38 | } |
| 39 | |
| 40 | /// Append `words` no-op instructions to the text section. |
| 41 | export fn emitTextPadding(a: *mut super::Assembler, words: u32) throws (super::Error) { |
| 42 | for _ in 0..words { |
| 43 | try emitText(a, encode::nop()); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | /// Append one byte to the data section. |
| 48 | export fn emitByte(a: *mut super::Assembler, byte: u8) throws (super::Error) { |
| 49 | if a.data.len >= a.data.cap { |
| 50 | throw super::Error::DataOverflow; |
| 51 | } |
| 52 | let idx = a.data.len; |
| 53 | set a.data = @sliceOf(a.data.ptr, idx + 1, a.data.cap); |
| 54 | set a.data[idx] = byte; |
| 55 | } |
| 56 | |
| 57 | /// Emit a little-endian integer with `bytes` bytes. |
| 58 | fn emitDataInt(a: *mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) { |
| 59 | for i in 0..bytes { |
| 60 | try emitByte(a, ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /// Patch a little-endian integer with `bytes` bytes. |
| 65 | fn patchDataInt(a: *mut super::Assembler, offset: u32, bits: u64, bytes: u32) { |
| 66 | for i in 0..bytes { |
| 67 | set a.data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /// Emit an integer data directive value. |
| 72 | export fn emitDataValue(a: *mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) { |
| 73 | match width { |
| 74 | case super::DataWidth::Word => try emitDataInt(a, value as u64, rv64::WORD_SIZE as u32), |
| 75 | case super::DataWidth::Dword => try emitDataInt(a, value as u64, rv64::DWORD_SIZE as u32), |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | /// Record a data-section symbol fixup and reserve its bytes. |
| 80 | export fn recordDataFixup(a: *mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) { |
| 81 | let offset = a.data.len; |
| 82 | match width { |
| 83 | case super::DataWidth::Word => { |
| 84 | recordFixup(a, target, super::FixupInfo::Word { offset }); |
| 85 | try emitDataInt(a, 0, rv64::WORD_SIZE as u32); |
| 86 | } |
| 87 | case super::DataWidth::Dword => { |
| 88 | recordFixup(a, target, super::FixupInfo::Dword { offset }); |
| 89 | try emitDataInt(a, 0, rv64::DWORD_SIZE as u32); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | /// Record a pending symbol fixup. |
| 95 | fn recordFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo) { |
| 96 | if a.fixups.len >= a.fixups.cap { |
| 97 | panic "asm: fixup buffer full"; |
| 98 | } |
| 99 | let idx = a.fixups.len as u32; |
| 100 | set a.fixups = @sliceOf(a.fixups.ptr, idx + 1, a.fixups.cap); |
| 101 | set a.fixups[idx] = super::Fixup { symbol, info }; |
| 102 | } |
| 103 | |
| 104 | /// Record a text-section symbol fixup and reserve its instruction words. |
| 105 | export fn recordTextFixup(a: *mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) { |
| 106 | recordFixup(a, symbol, info); |
| 107 | try emitTextPadding(a, words); |
| 108 | } |
| 109 | |
| 110 | /// Find a previously defined symbol by name. |
| 111 | fn findSymbol(a: *super::Assembler, name: *[u8]) -> ?super::Symbol { |
| 112 | let idx = dict::get(&a.symbolMap, name) |
| 113 | else return nil; |
| 114 | return a.symbols[idx as u32]; |
| 115 | } |
| 116 | |
| 117 | /// Return the final address for a data symbol. |
| 118 | fn dataSymbolAddr(a: *super::Assembler, symbol: super::Symbol) -> i32 throws (super::Error) { |
| 119 | if symbol.section <> super::Section::Data { |
| 120 | throw super::Error::Invalid { offset: 0, message: "data address target must be in data section" }; |
| 121 | } |
| 122 | return symbol.offset + (a.dataBase as i32); |
| 123 | } |
| 124 | |
| 125 | /// Resolve final symbol references and patch all delayed output. |
| 126 | export fn finishProgram(a: *mut super::Assembler) throws (super::Error) { |
| 127 | for i in 0..a.fixups.len { |
| 128 | let fixup = a.fixups[i]; |
| 129 | let symbol = findSymbol(a, fixup.symbol) else { |
| 130 | throw super::Error::Invalid { offset: 0, message: "undefined symbol" }; |
| 131 | }; |
| 132 | match fixup.info { |
| 133 | case super::FixupInfo::Branch { op, rs1, rs2, index } => { |
| 134 | if symbol.section <> super::Section::Text { |
| 135 | throw super::Error::Invalid { offset: 0, message: "branch target must be in text section" }; |
| 136 | } |
| 137 | let srcOffset = index as i32 * rv64::INSTR_SIZE; |
| 138 | let rel = symbol.offset - srcOffset; |
| 139 | |
| 140 | if not encode::isBranchImm(rel) { |
| 141 | throw super::Error::Invalid { offset: 0, message: "branch target out of range" }; |
| 142 | } |
| 143 | let word = encodeBranch(op, rs1, rs2, rel); |
| 144 | |
| 145 | set a.text[index] = word; |
| 146 | } |
| 147 | case super::FixupInfo::Jal { rd, index } => { |
| 148 | if symbol.section <> super::Section::Text { |
| 149 | throw super::Error::Invalid { offset: 0, message: "jump target must be in text section" }; |
| 150 | } |
| 151 | let srcOffset = index as i32 * rv64::INSTR_SIZE; |
| 152 | let rel = symbol.offset - srcOffset; |
| 153 | |
| 154 | if not encode::isJumpImm(rel) { |
| 155 | throw super::Error::Invalid { offset: 0, message: "jump target out of range" }; |
| 156 | } |
| 157 | set a.text[index] = encode::jal(rd, rel); |
| 158 | } |
| 159 | case super::FixupInfo::Addr { rd, index } => { |
| 160 | let mut addr = symbol.offset - (index as i32 * rv64::INSTR_SIZE); |
| 161 | if symbol.section == super::Section::Data { |
| 162 | set addr = symbol.offset + (a.dataBase as i32); |
| 163 | } |
| 164 | let split = emit::splitImm(addr); |
| 165 | set a.text[index] = encode::lui(rd, split.hi) |
| 166 | if symbol.section == super::Section::Data |
| 167 | else encode::auipc(rd, split.hi); |
| 168 | set a.text[index + 1] = encode::addi(rd, rd, split.lo); |
| 169 | } |
| 170 | case super::FixupInfo::Word { offset } => { |
| 171 | let addr = try dataSymbolAddr(a, symbol); |
| 172 | patchDataInt(a, offset, addr as u64, rv64::WORD_SIZE as u32); |
| 173 | } |
| 174 | case super::FixupInfo::Dword { offset } => { |
| 175 | let addr = try dataSymbolAddr(a, symbol); |
| 176 | patchDataInt(a, offset, addr as u64, rv64::DWORD_SIZE as u32); |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | /// Encode a concrete branch operation. |
| 183 | export fn encodeBranch(op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
| 184 | match op { |
| 185 | case super::BranchOp::Beq => return encode::beq(rs1, rs2, imm), |
| 186 | case super::BranchOp::Bne => return encode::bne(rs1, rs2, imm), |
| 187 | case super::BranchOp::Blt => return encode::blt(rs1, rs2, imm), |
| 188 | case super::BranchOp::Bge => return encode::bge(rs1, rs2, imm), |
| 189 | case super::BranchOp::Bltu => return encode::bltu(rs1, rs2, imm), |
| 190 | case super::BranchOp::Bgeu => return encode::bgeu(rs1, rs2, imm), |
| 191 | case super::BranchOp::Ble => return encode::ble(rs1, rs2, imm), |
| 192 | case super::BranchOp::Bgt => return encode::bgt(rs1, rs2, imm), |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | /// Decode string literal escapes and emit the resulting data bytes. |
| 197 | export fn emitDecodedString(a: *mut super::Assembler, literal: *[u8]) throws (super::Error) { |
| 198 | let raw = &literal[super::QUOTE_DELIM_LEN..literal.len - super::QUOTE_DELIM_LEN]; |
| 199 | let mut i: u32 = 0; |
| 200 | |
| 201 | while i < raw.len { |
| 202 | if raw[i] == '\\' and i + 1 < raw.len { |
| 203 | try emitByte(a, fmt::decodeAsciiEscape(raw[i + 1])); |
| 204 | set i += 2; |
| 205 | } else { |
| 206 | try emitByte(a, raw[i]); |
| 207 | set i += 1; |
| 208 | } |
| 209 | } |
| 210 | } |