lib/std/arch/rv64/asm/emit.rad 8.2 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 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
}