lib/std/arch/rv64/asm/emit.rad 8.6 KiB 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 unsafe fn defineSymbol(a: &mut super::Assembler, name: *[u8]) {
12
    let idx = a.symbolsLen;
13
    let offset: i32 = a.dataLen as i32
14
        if a.section == super::Section::Data
15
        else a.textLen as i32 * rv64::INSTR_SIZE;
16
17
    assert a.symbolsLen < a.symbols.len, "defineSymbol: symbol buffer full";
18
    set a.symbols[a.symbolsLen] = super::Symbol {
19
        name,
20
        section: a.section,
21
        offset,
22
        isExported: dict::get(&a.exportMap, name) <> nil,
23
    };
24
    set a.symbolsLen += 1;
25
    dict::insert(&mut a.symbolMap, name, idx as i32);
26
}
27
28
/// Append one encoded instruction word to the text section.
29
export unsafe fn emitText(a: &mut super::Assembler, word: u32) throws (super::Error) {
30
    if a.textLen >= a.text.len {
31
        throw super::Error::TextOverflow;
32
    }
33
    set a.text[a.textLen] = word;
34
    set a.textLen += 1;
35
}
36
37
/// Append `words` no-op instructions to the text section.
38
export unsafe fn emitTextPadding(a: &mut super::Assembler, words: u32) throws (super::Error) {
39
    for _ in 0..words {
40
        try emitText(a, encode::nop());
41
    }
42
}
43
44
/// Append one byte to the data section.
45
export unsafe fn emitByte(a: &mut super::Assembler, byte: u8) throws (super::Error) {
46
    if a.dataLen >= a.data.len {
47
        throw super::Error::DataOverflow;
48
    }
49
    set a.data[a.dataLen] = byte;
50
    set a.dataLen += 1;
51
}
52
53
/// Emit a little-endian integer with `bytes` bytes.
54
unsafe fn emitDataInt(a: &mut super::Assembler, bits: u64, bytes: u32) throws (super::Error) {
55
    for i in 0..bytes {
56
        try emitByte(a, ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8);
57
    }
58
}
59
60
/// Patch a little-endian integer with `bytes` bytes.
61
fn patchDataInt(a: &mut super::Assembler, offset: u32, bits: u64, bytes: u32) {
62
    for i in 0..bytes {
63
        set a.data[offset + i] = ((bits >> ((i as u64) * super::BITS_PER_BYTE)) & super::BYTE_MASK) as u8;
64
    }
65
}
66
67
/// Emit an integer data directive value.
68
export unsafe fn emitDataValue(a: &mut super::Assembler, value: i64, width: super::DataWidth) throws (super::Error) {
69
    match width {
70
        case super::DataWidth::Word => try emitDataInt(a, value as u64, rv64::WORD_SIZE as u32),
71
        case super::DataWidth::Dword => try emitDataInt(a, value as u64, rv64::DWORD_SIZE as u32),
72
    }
73
}
74
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) {
77
    let offset = a.dataLen;
78
    match width {
79
        case super::DataWidth::Word => {
80
            recordFixup(a, target, super::FixupInfo::Word { offset });
81
            try emitDataInt(a, 0, rv64::WORD_SIZE as u32);
82
        }
83
        case super::DataWidth::Dword => {
84
            recordFixup(a, target, super::FixupInfo::Dword { offset });
85
            try emitDataInt(a, 0, rv64::DWORD_SIZE as u32);
86
        }
87
    }
88
}
89
90
/// Record a pending symbol fixup.
91
unsafe fn recordFixup(a: &mut super::Assembler, symbol: *[u8], info: super::FixupInfo) {
92
    assert a.fixupsLen < a.fixups.len, "recordFixup: fixup buffer full";
93
    set a.fixups[a.fixupsLen] = super::Fixup { symbol, info };
94
    set a.fixupsLen += 1;
95
}
96
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) {
99
    assert a.externalFixupsLen < a.externalFixups.len, "recordExternalFixup: fixup buffer full";
100
    set a.externalFixups[a.externalFixupsLen] = fixup;
101
    set a.externalFixupsLen += 1;
102
}
103
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) {
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 unsafe fn finishProgram(a: &mut super::Assembler) throws (super::Error) {
127
    for i in 0..a.fixupsLen {
128
        let fixup = a.fixups[i];
129
        let symbol = findSymbol(a, fixup.symbol) else {
130
            match fixup.info {
131
                case super::FixupInfo::Jal { .. }, super::FixupInfo::Addr { .. } => {
132
                    recordExternalFixup(a, fixup);
133
                    continue;
134
                }
135
                else => throw super::Error::Invalid { offset: 0, message: "undefined symbol" },
136
            }
137
        };
138
        match fixup.info {
139
            case super::FixupInfo::Branch { op, rs1, rs2, index } => {
140
                if symbol.section <> super::Section::Text {
141
                    throw super::Error::Invalid { offset: 0, message: "branch target must be in text section" };
142
                }
143
                let srcOffset = index as i32 * rv64::INSTR_SIZE;
144
                let rel = symbol.offset - srcOffset;
145
146
                if not encode::isBranchImm(rel) {
147
                    throw super::Error::Invalid { offset: 0, message: "branch target out of range" };
148
                }
149
                let word = encodeBranch(op, rs1, rs2, rel);
150
151
                set a.text[index] = word;
152
            }
153
            case super::FixupInfo::Jal { rd, index } => {
154
                if symbol.section <> super::Section::Text {
155
                    throw super::Error::Invalid { offset: 0, message: "jump target must be in text section" };
156
                }
157
                let srcOffset = index as i32 * rv64::INSTR_SIZE;
158
                let rel = symbol.offset - srcOffset;
159
160
                if not encode::isJumpImm(rel) {
161
                    throw super::Error::Invalid { offset: 0, message: "jump target out of range" };
162
                }
163
                set a.text[index] = encode::jal(rd, rel);
164
            }
165
            case super::FixupInfo::Addr { rd, index } => {
166
                let mut addr = symbol.offset - (index as i32 * rv64::INSTR_SIZE);
167
                if symbol.section == super::Section::Data {
168
                    set addr = symbol.offset + (a.dataBase as i32);
169
                }
170
                let split = emit::splitImm(addr);
171
                set a.text[index] = encode::lui(rd, split.hi)
172
                    if symbol.section == super::Section::Data
173
                    else encode::auipc(rd, split.hi);
174
                set a.text[index + 1] = encode::addi(rd, rd, split.lo);
175
            }
176
            case super::FixupInfo::Word { offset } => {
177
                let addr = try dataSymbolAddr(a, symbol);
178
                patchDataInt(a, offset, addr as u64, rv64::WORD_SIZE as u32);
179
            }
180
            case super::FixupInfo::Dword { offset } => {
181
                let addr = try dataSymbolAddr(a, symbol);
182
                patchDataInt(a, offset, addr as u64, rv64::DWORD_SIZE as u32);
183
            }
184
        }
185
    }
186
}
187
188
/// Encode a concrete branch operation.
189
export fn encodeBranch(op: super::BranchOp, rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
190
    match op {
191
        case super::BranchOp::Beq  => return encode::beq(rs1, rs2, imm),
192
        case super::BranchOp::Bne  => return encode::bne(rs1, rs2, imm),
193
        case super::BranchOp::Blt  => return encode::blt(rs1, rs2, imm),
194
        case super::BranchOp::Bge  => return encode::bge(rs1, rs2, imm),
195
        case super::BranchOp::Bltu => return encode::bltu(rs1, rs2, imm),
196
        case super::BranchOp::Bgeu => return encode::bgeu(rs1, rs2, imm),
197
        case super::BranchOp::Ble  => return encode::ble(rs1, rs2, imm),
198
        case super::BranchOp::Bgt  => return encode::bgt(rs1, rs2, imm),
199
    }
200
}
201
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) {
204
    let raw = &literal[super::QUOTE_DELIM_LEN..literal.len - super::QUOTE_DELIM_LEN];
205
    let mut i: u32 = 0;
206
207
    while i < raw.len {
208
        if raw[i] == '\\' and i + 1 < raw.len {
209
            try emitByte(a, fmt::decodeAsciiEscape(raw[i + 1]));
210
            set i += 2;
211
        } else {
212
            try emitByte(a, raw[i]);
213
            set i += 1;
214
        }
215
    }
216
}