compiler: Emit and patch assembly through safe helpers

eee99ebe5262843d2fbf5f0ee4349f50cefecafa0cf57fb3004383607e83dd67
Alexis Sellier committed ago 1 parent e4ef0fa5
lib/std/arch/rv64/asm/emit.rad +12 -12
6 6
7 7
use std::collections::dict;
8 8
use std::lang::gen;
9 9
10 10
/// Define a symbol at the current text or data offset.
11 -
export unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8]) {
11 +
export fn defineSymbol(a: &mut super::Assembler, name: *[u8]) {
12 12
    let idx = a.symbolsLen;
13 13
    let offset: i32 = a.dataLen as i32
14 14
        if a.section == super::Section::Data
15 15
        else a.textLen as i32 * rv64::INSTR_SIZE;
16 16
24 24
    set a.symbolsLen += 1;
25 25
    dict::insert(&mut a.symbolMap, name, idx as i32);
26 26
}
27 27
28 28
/// Append one encoded instruction word to the text section.
29 -
export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
29 +
export fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
30 30
    if a.textLen >= a.text.len {
31 31
        throw super::Error::TextOverflow;
32 32
    }
33 33
    set a.text[a.textLen] = word;
34 34
    set a.textLen += 1;
35 35
}
36 36
37 37
/// Append `words` no-op instructions to the text section.
38 -
export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
38 +
export fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
39 39
    for _ in 0..words {
40 40
        try emitText(a, encode::nop());
41 41
    }
42 42
}
43 43
44 44
/// Append one byte to the data section.
45 -
export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
45 +
export fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
46 46
    if a.dataLen >= a.data.len {
47 47
        throw super::Error::DataOverflow;
48 48
    }
49 49
    set a.data[a.dataLen] = byte;
50 50
    set a.dataLen += 1;
51 51
}
52 52
53 53
/// Emit a little-endian integer with `bytes` bytes.
54 -
unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
54 +
fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
55 55
    for i in 0..bytes {
56 56
        try emitByte(a, ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8);
57 57
    }
58 58
}
59 59
63 63
        set a.data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8;
64 64
    }
65 65
}
66 66
67 67
/// Emit an integer data directive value.
68 -
export unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
68 +
export fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
69 69
    match width {
70 70
        case super::DataWidth::Word => try emitDataInt(a, value as u64, rv64::WORD_SIZE as u32),
71 71
        case super::DataWidth::Dword => try emitDataInt(a, value as u64, rv64::DWORD_SIZE as u32),
72 72
    }
73 73
}
74 74
75 75
/// Record a data-section symbol fixup and reserve its bytes.
76 -
export unsafe fn recordDataFixup(a: &mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) {
76 +
export fn recordDataFixup(a: &mut super::Assembler, target: *[u8], width: super::DataWidth) throws (super::Error) {
77 77
    let offset = a.dataLen;
78 78
    match width {
79 79
        case super::DataWidth::Word => {
80 80
            recordFixup(a, target, super::FixupInfo::Word { offset });
81 81
            try emitDataInt(a, 0, rv64::WORD_SIZE as u32);
86 86
        }
87 87
    }
88 88
}
89 89
90 90
/// Record a pending symbol fixup.
91 -
unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
91 +
fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
92 92
    assert a.fixupsLen < a.fixups.len, "recordFixup: fixup buffer full";
93 93
    set a.fixups[a.fixupsLen] = super::Fixup { symbol, info };
94 94
    set a.fixupsLen += 1;
95 95
}
96 96
97 97
/// Record a text fixup that must be resolved after all program text is known.
98 -
unsafe fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) {
98 +
fn recordExternalFixup(a: &mut super::Assembler, fixup: super::Fixup) {
99 99
    assert a.externalFixupsLen < a.externalFixups.len, "recordExternalFixup: fixup buffer full";
100 100
    set a.externalFixups[a.externalFixupsLen] = fixup;
101 101
    set a.externalFixupsLen += 1;
102 102
}
103 103
104 104
/// Record a text-section symbol fixup and reserve its instruction words.
105 -
export unsafe fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
105 +
export fn recordTextFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo, words: u32) throws (super::Error) {
106 106
    recordFixup(a, symbol, info);
107 107
    try emitTextPadding(a, words);
108 108
}
109 109
110 110
/// Find a previously defined symbol by name.
121 121
    }
122 122
    return symbol.offset + (a.dataBase as i32);
123 123
}
124 124
125 125
/// Resolve final symbol references and patch all delayed output.
126 -
export unsafe fn finishProgram(a: &mut super::Assembler) throws (super::Error) {
126 +
export fn finishProgram(a: &mut super::Assembler) throws (super::Error) {
127 127
    for i in 0..a.fixupsLen {
128 128
        let fixup = a.fixups[i];
129 129
        let symbol = findSymbol(a, fixup.symbol) else {
130 130
            match fixup.info {
131 131
                case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => {
198 198
        case super::BranchOp::Bgt  => return encode::bgt(rs1, rs2, imm),
199 199
    }
200 200
}
201 201
202 202
/// Decode string literal escapes and emit the resulting data bytes.
203 -
export unsafe fn emitDecodedString(a: &mut super::Assembler, literal: *[u8]) throws (super::Error) {
203 +
export fn emitDecodedString(a: &mut super::Assembler, literal: *[u8]) throws (super::Error) {
204 204
    let raw = &literal[super::QUOTE_DELIM_LEN..literal.len - super::QUOTE_DELIM_LEN];
205 205
    let mut i: u32 = 0;
206 206
207 207
    while i < raw.len {
208 208
        if raw[i] == '\\' and i + 1 < raw.len {
lib/std/arch/rv64/asm/parser.rad +2 -2
106 106
    }
107 107
    return value as i32;
108 108
}
109 109
110 110
/// Define a label at the current text or data offset.
111 -
unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
111 +
fn defineSymbol(a: &mut super::Assembler, name: *[u8], tok: scanner::Token) throws (super::Error) {
112 112
    if dict::get(&a.symbolMap, name) <> nil {
113 113
        throw failOnToken(tok, "duplicate label");
114 114
    }
115 115
    emit::defineSymbol(a, name);
116 116
}
117 117
118 118
/// Emit a parsed integer data value after applying source-level range checks.
119 -
unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
119 +
fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
120 120
    match width {
121 121
        case super::DataWidth::Word =>
122 122
            try emit::emitDataValue(a, (try expectI32Value(a, value, "word literal out of range")) as i64, width),
123 123
        case super::DataWidth::Dword =>
124 124
            try emit::emitDataValue(a, value, width),