lib/std/arch/rv64.rad 8.8 KiB raw
1
//! RV64 code generation backend.
2
//!
3
//! Generates RISC-V 64-bit machine code from IL (intermediate language).
4
//!
5
//! # Submodules
6
//!
7
//! * encode: Instruction encoding functions
8
//! * decode: Instruction decoding (for disassembly/printing)
9
//! * emit: Binary emission context and branch patching
10
//! * isel: Instruction selection (IL to RV64 instructions)
11
//! * printer: Assembly text output
12
13
pub mod encode;
14
pub mod decode;
15
pub mod emit;
16
pub mod isel;
17
pub mod printer;
18
19
@test mod tests;
20
21
use std::mem;
22
use std::collections::dict;
23
use std::lang::il;
24
use std::lang::alloc;
25
use std::lang::gen;
26
use std::lang::gen::labels;
27
use std::lang::gen::regalloc;
28
use std::lang::gen::data;
29
use std::lang::gen::types;
30
31
////////////////
32
// Registers  //
33
////////////////
34
35
pub const ZERO: gen::Reg = gen::Reg(0);   /// Hard-wired zero.
36
pub const RA:   gen::Reg = gen::Reg(1);   /// Return address.
37
pub const SP:   gen::Reg = gen::Reg(2);   /// Stack pointer.
38
pub const GP:   gen::Reg = gen::Reg(3);   /// Global pointer.
39
pub const TP:   gen::Reg = gen::Reg(4);   /// Thread pointer.
40
pub const T0:   gen::Reg = gen::Reg(5);   /// Temporary/alternate link register.
41
pub const T1:   gen::Reg = gen::Reg(6);   /// Temporary.
42
pub const T2:   gen::Reg = gen::Reg(7);   /// Temporary.
43
pub const S0:   gen::Reg = gen::Reg(8);   /// Saved register/frame pointer.
44
pub const FP:   gen::Reg = gen::Reg(8);   /// Frame pointer (alias for S0).
45
pub const S1:   gen::Reg = gen::Reg(9);   /// Saved register.
46
pub const A0:   gen::Reg = gen::Reg(10);  /// Function argument/return.
47
pub const A1:   gen::Reg = gen::Reg(11);  /// Function argument/return.
48
pub const A2:   gen::Reg = gen::Reg(12);  /// Function argument.
49
pub const A3:   gen::Reg = gen::Reg(13);  /// Function argument.
50
pub const A4:   gen::Reg = gen::Reg(14);  /// Function argument.
51
pub const A5:   gen::Reg = gen::Reg(15);  /// Function argument.
52
pub const A6:   gen::Reg = gen::Reg(16);  /// Function argument.
53
pub const A7:   gen::Reg = gen::Reg(17);  /// Function argument.
54
pub const S2:   gen::Reg = gen::Reg(18);  /// Saved register.
55
pub const S3:   gen::Reg = gen::Reg(19);  /// Saved register.
56
pub const S4:   gen::Reg = gen::Reg(20);  /// Saved register.
57
pub const S5:   gen::Reg = gen::Reg(21);  /// Saved register.
58
pub const S6:   gen::Reg = gen::Reg(22);  /// Saved register.
59
pub const S7:   gen::Reg = gen::Reg(23);  /// Saved register.
60
pub const S8:   gen::Reg = gen::Reg(24);  /// Saved register.
61
pub const S9:   gen::Reg = gen::Reg(25);  /// Saved register.
62
pub const S10:  gen::Reg = gen::Reg(26);  /// Saved register.
63
pub const S11:  gen::Reg = gen::Reg(27);  /// Saved register.
64
pub const T3:   gen::Reg = gen::Reg(28);  /// Temporary.
65
pub const T4:   gen::Reg = gen::Reg(29);  /// Temporary.
66
pub const T5:   gen::Reg = gen::Reg(30);  /// Temporary.
67
pub const T6:   gen::Reg = gen::Reg(31);  /// Temporary.
68
69
/// Create a register from a number. Panics if `n > 31`.
70
pub fn reg(n: u8) -> gen::Reg {
71
    assert n < 32;
72
    return gen::Reg(n);
73
}
74
75
////////////////////////////
76
// Architecture constants //
77
////////////////////////////
78
79
/// Total number of general-purpose registers.
80
pub const NUM_REGISTERS: u8 = 32;
81
/// Number of saved registers.
82
pub const NUM_SAVED_REGISTERS: u8 = 11;
83
/// Word size in bytes (32-bit).
84
pub const WORD_SIZE: i32 = 4;
85
/// Doubleword size in bytes (64-bit).
86
pub const DWORD_SIZE: i32 = 8;
87
/// Instruction size in bytes.
88
pub const INSTR_SIZE: i32 = 4;
89
/// Stack alignment requirement in bytes.
90
pub const STACK_ALIGNMENT: i32 = 16;
91
92
/// Minimum blit size (in bytes) to use a loop instead of inline copy.
93
/// Blits below this threshold are fully unrolled as LD/SD pairs.
94
pub const BLIT_LOOP_THRESHOLD: i32 = 256;
95
96
/////////////////////////
97
// Codegen Allocation  //
98
/////////////////////////
99
100
/// Argument registers for function calls.
101
pub const ARG_REGS: [gen::Reg; 8] = [A0, A1, A2, A3, A4, A5, A6, A7];
102
103
/// Scratch register for code gen. Never allocated to user values.
104
pub const SCRATCH1: gen::Reg = T5;
105
106
/// Second scratch register for operations needing two temporaries.
107
pub const SCRATCH2: gen::Reg = T6;
108
109
/// Dedicated scratch for address offset adjustment. Never allocated to user
110
/// values and never used for operand materialization, so it can never
111
/// conflict with `rd`, `rs`, or `base` in load/store helpers.
112
pub const ADDR_SCRATCH: gen::Reg = T4;
113
114
/// Callee-saved registers that need save/restore if used.
115
pub const CALLEE_SAVED: [gen::Reg; NUM_SAVED_REGISTERS] = [S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11];
116
117
/// Maximum 12-bit signed immediate value.
118
pub const MAX_IMM: i32 = 2047;
119
120
/// Minimum 12-bit signed immediate value.
121
pub const MIN_IMM: i32 = -2048;
122
123
/// Allocatable registers for register allocation.
124
const ALLOCATABLE_REGS: [gen::Reg; 23] = [
125
    T0, T1, T2, T3,                             // Temporaries
126
    A0, A1, A2, A3, A4, A5, A6, A7,             // Arguments
127
    S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, // Saved
128
];
129
130
/// Get target configuration for register allocation.
131
// TODO: This should be a constant variable.
132
pub fn targetConfig() -> regalloc::TargetConfig {
133
    return regalloc::TargetConfig {
134
        allocatable: &ALLOCATABLE_REGS[..],
135
        argRegs: &ARG_REGS[..],
136
        calleeSaved: &CALLEE_SAVED[..],
137
        slotSize: DWORD_SIZE,
138
    };
139
}
140
141
///////////////////////
142
// Codegen Constants //
143
///////////////////////
144
145
/// Base address where read-only data is loaded.
146
pub const RO_DATA_BASE: u32 = 0x10000;
147
148
/// Base address where read-write data is loaded.
149
pub const RW_DATA_BASE: u32 = 0xFFFFF0;
150
151
/// Storage buffers passed from driver for code generation.
152
pub record Storage {
153
    /// Buffer for data symbols.
154
    dataSyms: *mut [data::DataSym],
155
    /// Hash table entries for data symbol lookup.
156
    dataSymEntries: *mut [dict::Entry],
157
}
158
159
/// Result of code generation.
160
pub record Program {
161
    /// Slice of emitted code.
162
    code: *[u32],
163
    /// Slice of function addresses (name + start index).
164
    funcs: *[types::FuncAddr],
165
    /// Number of read-only data bytes emitted.
166
    roDataSize: u32,
167
    /// Number of read-write data bytes emitted.
168
    rwDataSize: u32,
169
    /// Debug entries mapping PCs to source locations. Empty when debug is off.
170
    debugEntries: *[types::DebugEntry],
171
}
172
173
/// Generate code for an IL program.
174
pub fn generate(
175
    program: *il::Program,
176
    storage: Storage,
177
    roDataBuf: *mut [u8],
178
    rwDataBuf: *mut [u8],
179
    arena: *mut alloc::Arena,
180
    debug: bool
181
) -> Program {
182
    let mut e = try! emit::emitter(arena, debug);
183
    let config = targetConfig();
184
185
    // Build data map.
186
    let mut dataSymCount: u32 = 0;
187
188
    let roLayoutSize = data::layoutSection(program, storage.dataSyms, &mut dataSymCount, RO_DATA_BASE, true);
189
    data::layoutSection(program, storage.dataSyms, &mut dataSymCount, RW_DATA_BASE, false);
190
191
    // Build hash-indexed data symbol map for O(1) lookups.
192
    let dataSyms = &storage.dataSyms[..dataSymCount];
193
    let mut dataSymMap = data::buildMap(dataSyms, storage.dataSymEntries);
194
195
    // Code base address: code follows read-only data, aligned to dword boundary.
196
    let codeBase = mem::alignUp(RO_DATA_BASE + roLayoutSize, DWORD_SIZE as u32);
197
198
    // Emit placeholder entry jump to default function if there is one.
199
    // We'll patch this at the end once we know where the function is.
200
    let mut defaultName: ?*[u8] = nil;
201
    if let defIdx = program.defaultFnIdx {
202
        defaultName = program.fns[defIdx].name;
203
        emit::emit(&mut e, encode::nop()); // Placeholder for two-instruction jump.
204
        emit::emit(&mut e, encode::nop()); //
205
    }
206
207
    // Generate code for all functions.
208
    for i in 0..program.fns.len {
209
        let func = program.fns[i];
210
        if not func.isExtern {
211
            let checkpoint = alloc::save(arena);
212
            let ralloc = try! regalloc::allocate(func, &config, arena);
213
            isel::selectFn(&mut e, &dataSymMap, &ralloc, func);
214
215
            // Reclaim unused memory after instruction selection.
216
            alloc::restore(arena, checkpoint);
217
        }
218
    }
219
    // Patch entry jump now that we know where the default function is.
220
    // Nb. we use a two-instruction jump even if it isn't always needed.
221
    if let target = defaultName {
222
        let offset = emit::branchOffsetToFunc(&e, 0, target);
223
        let s = emit::splitImm(offset);
224
225
        emit::patch(&mut e, 0, encode::auipc(SCRATCH1, s.hi));
226
        emit::patch(&mut e, 1, encode::jalr(ZERO, SCRATCH1, s.lo));
227
    }
228
    // Patch function calls and address loads now that all functions are emitted.
229
    emit::patchCalls(&mut e);
230
    emit::patchAddrLoads(&mut e);
231
232
    // Emit data sections.
233
    let roDataSize = data::emitSection(program, &dataSymMap, &e.labels, codeBase, roDataBuf, true);
234
    let rwDataSize = data::emitSection(program, &dataSymMap, &e.labels, codeBase, rwDataBuf, false);
235
236
    return Program {
237
        code: emit::getCode(&e),
238
        funcs: emit::getFuncs(&e),
239
        roDataSize,
240
        rwDataSize,
241
        debugEntries: emit::getDebugEntries(&e),
242
    };
243
}