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