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::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
}