lib/std/arch/rv64/asm.rad 24.5 KiB raw
1
//! Minimal RV64 assembler.
2
//!
3
//! This module assembles `.ras` source files into RV64 text words plus a raw
4
//! data prefix that can be linked into a compiler-generated program. It exists
5
//! so the Radiance driver can mix hand-written RV64 assembly with generated IL
6
//! output without invoking an external assembler or linker.
7
//!
8
//! Assembly is intentionally direct and buffer-oriented. The caller provides a
9
//! text buffer, a data buffer, an arena, and the runtime base address where the
10
//! data buffer will be loaded. The parser writes encoded instructions into the
11
//! text buffer as it reads them and writes directive bytes into the data buffer
12
//! while in `.data`. The returned [`Program`] only contains slices into those
13
//! caller-provided buffers, so no ownership transfer or late copy is needed.
14
//!
15
//! The scanner is assembly-specific. It produces tokens for registers (`%a0`),
16
//! labels (`@name`), directives, strings, characters, numbers, and
17
//! punctuation. The parser consumes those tokens as a small line-oriented
18
//! language: *directives* declare sections or emit data, *labels* define
19
//! symbols at the current section offset, and *instructions* are validated
20
//! against RV64 operand forms before being encoded.
21
//!
22
//! Labels are defined at the current text instruction index or data byte
23
//! offset. The parser is single-pass because it keeps assembly cheap and lets
24
//! instructions and data be emitted immediately, but forward references mean
25
//! some operands cannot be encoded when first seen. Branches, jumps,
26
//! load-address operands, and data directives that reference labels therefore
27
//! record fixups. After parsing reaches EOF, the emitter resolves the final
28
//! symbol table and patches every recorded use with the correct PC-relative
29
//! offset, absolute data address, or encoded data value.
30
//!
31
//! Data labels are resolved relative to the data base address. The compiler
32
//! driver accumulates all assembly data in a RO data prefix, passes
33
//! [`RO_DATA_BASE`] + `currentPrefixLen` for each input, then appends the
34
//! input's emitted data to that prefix. Global text symbols are exported for
35
//! call resolution when the assembled text is appended to the RV64 generator,
36
//! shifted by the generator's current code length so disassembly/debug output
37
//! can name those instruction addresses correctly. Non-global text labels
38
//! remain local to their assembly fragment.
39
use std::lang::alloc;
40
use std::lang::strings;
41
use std::lang::gen;
42
use std::collections::dict;
43
use std::arch::rv64::encode;
44
use std::arch::rv64;
45
46
/// Assembler scanner module.
47
export mod scanner;
48
/// Assembler parser module.
49
export mod parser;
50
/// Assembler emission and fixup module.
51
export mod emit;
52
/// Tests.
53
@test mod tests;
54
55
/// In-memory result of assembling one RV64 assembly fragment.
56
///
57
/// [`Program`] is the boundary between the textual assembler and the rest of
58
/// the compiler. The assembler reads an assembly source file, encodes all
59
/// instructions, lays out all data bytes, resolves fixups that can be resolved
60
/// inside the fragment, and returns these three slices as the assembled
61
/// program.
62
///
63
/// The value is intentionally not a standalone object file or linked
64
/// executable. It carries only the sections and symbol table needed by the
65
/// compiler driver. The slices point at caller-owned storage: `text` and
66
/// `data` are backed by the buffers passed to [`assemble`], while symbol names
67
/// are interned in the assembler's string pool.
68
///
69
/// Symbol offsets are section-local byte offsets. Text symbols name positions
70
/// in `text`; data symbols name positions in `data`. When the compiler
71
/// consumes the program, [`rv64::addAssembly`] appends the text words to the
72
/// generated text stream and registers text labels at their relocated offsets.
73
/// The driver copies `data` into the final read-only data prefix; the data
74
/// base supplied to [`assemble`] lets the assembler resolve data addresses as
75
/// they will appear in that final layout.
76
export record Program: Copy {
77
    /// Encoded instructions in the text section.
78
    text: *[u32],
79
    /// Raw bytes in the data section.
80
    data: *[u8],
81
    /// Symbols defined by the source.
82
    symbols: *[Symbol],
83
    /// Text references resolved by the whole-program emitter.
84
    externalFixups: *[Fixup],
85
}
86
87
/// Errors reported while assembling source text.
88
export union Error: Copy {
89
    /// Invalid syntax or operand form at a source offset.
90
    Invalid { offset: u32, message: *[u8] },
91
    /// The source emitted more text words than the caller-provided buffer holds.
92
    TextOverflow,
93
    /// The source emitted more data bytes than the caller-provided buffer holds.
94
    DataOverflow,
95
}
96
97
/// Active output section.
98
export union Section: Copy {
99
    /// Instruction section.
100
    Text,
101
    /// Data byte section.
102
    Data,
103
}
104
105
/// Branch opcode that needs fixup.
106
export union BranchOp: Copy {
107
    /// Branch if equal.
108
    Beq,
109
    /// Branch if not equal.
110
    Bne,
111
    /// Branch if less than, signed.
112
    Blt,
113
    /// Branch if greater than or equal, signed.
114
    Bge,
115
    /// Branch if less than, unsigned.
116
    Bltu,
117
    /// Branch if greater than or equal, unsigned.
118
    Bgeu,
119
    /// Branch if less than or equal, signed pseudo-instruction.
120
    Ble,
121
    /// Branch if greater than, signed pseudo-instruction.
122
    Bgt,
123
}
124
125
/// Parser and encoder behavior for one instruction mnemonic.
126
export union InstructionEncoder: Copy {
127
    /// No-operand instruction encoded by a fixed encoder.
128
    NoOperand { enc: fn() -> u32 },
129
    /// Load-immediate pseudo-instruction.
130
    Li,
131
    /// Load-address pseudo-instruction.
132
    La,
133
    /// Two-register instruction or pseudo-instruction.
134
    RR { enc: fn(gen::Reg, gen::Reg) -> u32 },
135
    /// Three-register instruction.
136
    RRR { enc: fn(gen::Reg, gen::Reg, gen::Reg) -> u32 },
137
    /// Register, register, immediate instruction.
138
    RRI { enc: fn(gen::Reg, gen::Reg, i32) -> u32 },
139
    /// Shift-immediate instruction with RV64 shift bounds.
140
    Shift { enc: fn(gen::Reg, gen::Reg, i32) -> u32 },
141
    /// Shift-immediate instruction with RV64 W-mode shift bounds.
142
    WordShift { enc: fn(gen::Reg, gen::Reg, i32) -> u32 },
143
    /// Load instruction with memory operand syntax.
144
    Load { enc: fn(gen::Reg, gen::Reg, i32) -> u32 },
145
    /// Store instruction with memory operand syntax.
146
    Store { enc: fn(gen::Reg, gen::Reg, i32) -> u32 },
147
    /// Two-register branch instruction.
148
    Branch { op: BranchOp },
149
    /// One-register branch-to-zero pseudo-instruction.
150
    BranchZero { op: BranchOp },
151
    /// Memory fence with optional predecessor and successor masks.
152
    Fence,
153
    /// `jal` instruction with explicit destination register.
154
    Jal,
155
    /// Jump pseudo-instruction with fixed destination register.
156
    Jump { rd: gen::Reg },
157
    /// CSR read-style operand form.
158
    RdCsr { enc: fn(gen::Reg, u32) -> u32 },
159
    /// CSR write-style operand form.
160
    CsrRs1 { enc: fn(u32, gen::Reg) -> u32 },
161
    /// CSR read/write operand form.
162
    Csrrw,
163
    /// CSR immediate operand form.
164
    Csrsi,
165
    /// Upper-immediate operand form.
166
    Upper { enc: fn(gen::Reg, i32) -> u32 },
167
}
168
169
/// Classified directive name.
170
export union DirectiveKind: Copy {
171
    /// `.align` directive.
172
    Align,
173
    /// `.ascii` directive.
174
    Ascii,
175
    /// `.byte` directive.
176
    Byte,
177
    /// `.constant` directive.
178
    Constant,
179
    /// `.data` directive.
180
    Data,
181
    /// `.dword` directive.
182
    Dword,
183
    /// `.export` directive.
184
    Export,
185
    /// `.space` directive.
186
    Space,
187
    /// `.text` directive.
188
    Text,
189
    /// `.word` directive.
190
    Word,
191
}
192
193
/// Instruction descriptor table row.
194
record InstructionEntry: Copy {
195
    /// Assembly mnemonic text.
196
    name: *[u8],
197
    /// Operand parser and encoder behavior.
198
    encoder: InstructionEncoder,
199
}
200
201
/// Directive descriptor table row.
202
record DirectiveEntry: Copy {
203
    /// Directive name without the leading `.`.
204
    name: *[u8],
205
    /// Parser behavior for the directive.
206
    kind: DirectiveKind,
207
}
208
209
/// Register descriptor table row.
210
record RegisterEntry: Copy {
211
    /// Register alias text without the leading `%`.
212
    name: *[u8],
213
    /// Numeric register selected by the alias.
214
    reg: gen::Reg,
215
}
216
217
/// CSR descriptor table row.
218
record CsrEntry: Copy {
219
    /// CSR name text.
220
    name: *[u8],
221
    /// Numeric CSR address.
222
    csr: u32,
223
}
224
225
/// Width of an integer data directive.
226
export union DataWidth: Copy {
227
    /// 32-bit data value.
228
    Word,
229
    /// 64-bit data value.
230
    Dword,
231
}
232
233
/// Extra slot used when sizing source-derived symbol and fixup buffers.
234
export constant SOURCE_CAP_PADDING: u32 = 1;
235
/// Scale factor used to keep assembler hash tables sparse.
236
export constant TABLE_CAPACITY_SCALE: u32 = 4;
237
/// Minimum hash-table capacity used by the assembler.
238
export constant MIN_TABLE_CAPACITY: u32 = 8;
239
/// `@label` names exclude the leading sigil byte when interned.
240
export constant LABEL_SIGIL_LEN: u32 = 1;
241
/// `.directive` names exclude the leading sigil byte when matched.
242
export constant DIRECTIVE_SIGIL_LEN: u32 = 1;
243
/// String and character literals are delimited by one byte on each side.
244
export constant QUOTE_DELIM_LEN: u32 = 1;
245
/// Number of bits in one byte.
246
export constant BITS_PER_BYTE: u64 = 8;
247
/// Mask for extracting one encoded byte.
248
export constant BYTE_MASK: u64 = 0xFF;
249
/// Largest signed 32-bit assembler value.
250
export constant I32_MAX_VALUE: i64 = 2147483647;
251
/// Magnitude of the smallest signed 32-bit assembler value.
252
export constant I32_MIN_MAGNITUDE: i64 = 2147483648;
253
/// Largest unsigned 32-bit assembler value.
254
export constant U32_MAX_VALUE: i64 = 4294967295;
255
/// Largest unsigned 8-bit assembler value.
256
export constant U8_MAX_VALUE: i64 = 255;
257
/// Upper bound for CSR immediate operands.
258
export constant CSR_IMM_LIMIT: i64 = 32;
259
/// Upper bound for RV64 W-mode shift immediates.
260
export constant WORD_SHIFT_LIMIT: i32 = 32;
261
/// Upper bound for RV64 shift immediates.
262
export constant SHIFT_LIMIT: i32 = 64;
263
/// Largest `lui` or `auipc` immediate.
264
export constant UPPER_IMM_MAX_VALUE: i64 = 0xFFFFF;
265
266
/// Sorted instruction descriptor table used by the assembler parser.
267
export constant INSTRUCTIONS: [InstructionEntry; 89] = [
268
    { name: "add",    encoder: InstructionEncoder::RRR { enc: encode::add } },
269
    { name: "addi",   encoder: InstructionEncoder::RRI { enc: encode::addi } },
270
    { name: "addiw",  encoder: InstructionEncoder::RRI { enc: encode::addiw } },
271
    { name: "addw",   encoder: InstructionEncoder::RRR { enc: encode::addw } },
272
    { name: "and",    encoder: InstructionEncoder::RRR { enc: encode::and_ } },
273
    { name: "andi",   encoder: InstructionEncoder::RRI { enc: encode::andi } },
274
    { name: "auipc",  encoder: InstructionEncoder::Upper { enc: encode::auipc } },
275
    { name: "beq",    encoder: InstructionEncoder::Branch { op: BranchOp::Beq } },
276
    { name: "beqz",   encoder: InstructionEncoder::BranchZero { op: BranchOp::Beq } },
277
    { name: "bge",    encoder: InstructionEncoder::Branch { op: BranchOp::Bge } },
278
    { name: "bgeu",   encoder: InstructionEncoder::Branch { op: BranchOp::Bgeu } },
279
    { name: "bgt",    encoder: InstructionEncoder::Branch { op: BranchOp::Bgt } },
280
    { name: "ble",    encoder: InstructionEncoder::Branch { op: BranchOp::Ble } },
281
    { name: "blt",    encoder: InstructionEncoder::Branch { op: BranchOp::Blt } },
282
    { name: "bltu",   encoder: InstructionEncoder::Branch { op: BranchOp::Bltu } },
283
    { name: "bne",    encoder: InstructionEncoder::Branch { op: BranchOp::Bne } },
284
    { name: "bnez",   encoder: InstructionEncoder::BranchZero { op: BranchOp::Bne } },
285
    { name: "call",   encoder: InstructionEncoder::Jump { rd: rv64::RA } },
286
    { name: "csrc",   encoder: InstructionEncoder::CsrRs1 { enc: encode::csrc } },
287
    { name: "csrr",   encoder: InstructionEncoder::RdCsr { enc: encode::csrr } },
288
    { name: "csrrw",  encoder: InstructionEncoder::Csrrw },
289
    { name: "csrsi",  encoder: InstructionEncoder::Csrsi },
290
    { name: "csrw",   encoder: InstructionEncoder::CsrRs1 { enc: encode::csrw } },
291
    { name: "div",    encoder: InstructionEncoder::RRR { enc: encode::div } },
292
    { name: "divu",   encoder: InstructionEncoder::RRR { enc: encode::divu } },
293
    { name: "divuw",  encoder: InstructionEncoder::RRR { enc: encode::divuw } },
294
    { name: "divw",   encoder: InstructionEncoder::RRR { enc: encode::divw } },
295
    { name: "ebreak", encoder: InstructionEncoder::NoOperand { enc: encode::ebreak } },
296
    { name: "ecall",  encoder: InstructionEncoder::NoOperand { enc: encode::ecall } },
297
    { name: "fence",  encoder: InstructionEncoder::Fence },
298
    { name: "fence.i", encoder: InstructionEncoder::NoOperand { enc: encode::fenceI } },
299
    { name: "j",      encoder: InstructionEncoder::Jump { rd: rv64::ZERO } },
300
    { name: "jal",    encoder: InstructionEncoder::Jal },
301
    { name: "jalr",   encoder: InstructionEncoder::RRI { enc: encode::jalr } },
302
    { name: "la",     encoder: InstructionEncoder::La },
303
    { name: "lb",     encoder: InstructionEncoder::Load { enc: encode::lb } },
304
    { name: "lbu",    encoder: InstructionEncoder::Load { enc: encode::lbu } },
305
    { name: "ld",     encoder: InstructionEncoder::Load { enc: encode::ld } },
306
    { name: "lh",     encoder: InstructionEncoder::Load { enc: encode::lh } },
307
    { name: "lhu",    encoder: InstructionEncoder::Load { enc: encode::lhu } },
308
    { name: "li",     encoder: InstructionEncoder::Li },
309
    { name: "lui",    encoder: InstructionEncoder::Upper { enc: encode::lui } },
310
    { name: "lw",     encoder: InstructionEncoder::Load { enc: encode::lw } },
311
    { name: "lwu",    encoder: InstructionEncoder::Load { enc: encode::lwu } },
312
    { name: "mret",   encoder: InstructionEncoder::NoOperand { enc: encode::mret } },
313
    { name: "mul",    encoder: InstructionEncoder::RRR { enc: encode::mul } },
314
    { name: "mulh",   encoder: InstructionEncoder::RRR { enc: encode::mulh } },
315
    { name: "mulhsu", encoder: InstructionEncoder::RRR { enc: encode::mulhsu } },
316
    { name: "mulhu",  encoder: InstructionEncoder::RRR { enc: encode::mulhu } },
317
    { name: "mulw",   encoder: InstructionEncoder::RRR { enc: encode::mulw } },
318
    { name: "mv",     encoder: InstructionEncoder::RR { enc: encode::mv } },
319
    { name: "neg",    encoder: InstructionEncoder::RR { enc: encode::neg } },
320
    { name: "nop",    encoder: InstructionEncoder::NoOperand { enc: encode::nop } },
321
    { name: "not",    encoder: InstructionEncoder::RR { enc: encode::not_ } },
322
    { name: "or",     encoder: InstructionEncoder::RRR { enc: encode::or_ } },
323
    { name: "ori",    encoder: InstructionEncoder::RRI { enc: encode::ori } },
324
    { name: "rem",    encoder: InstructionEncoder::RRR { enc: encode::rem } },
325
    { name: "remu",   encoder: InstructionEncoder::RRR { enc: encode::remu } },
326
    { name: "remuw",  encoder: InstructionEncoder::RRR { enc: encode::remuw } },
327
    { name: "remw",   encoder: InstructionEncoder::RRR { enc: encode::remw } },
328
    { name: "ret",    encoder: InstructionEncoder::NoOperand { enc: encode::ret } },
329
    { name: "sb",     encoder: InstructionEncoder::Store { enc: encode::sb } },
330
    { name: "sd",     encoder: InstructionEncoder::Store { enc: encode::sd } },
331
    { name: "seqz",   encoder: InstructionEncoder::RR { enc: encode::seqz } },
332
    { name: "sh",     encoder: InstructionEncoder::Store { enc: encode::sh } },
333
    { name: "sll",    encoder: InstructionEncoder::RRR { enc: encode::sll } },
334
    { name: "slli",   encoder: InstructionEncoder::Shift { enc: encode::slli } },
335
    { name: "slliw",  encoder: InstructionEncoder::WordShift { enc: encode::slliw } },
336
    { name: "sllw",   encoder: InstructionEncoder::RRR { enc: encode::sllw } },
337
    { name: "slt",    encoder: InstructionEncoder::RRR { enc: encode::slt } },
338
    { name: "slti",   encoder: InstructionEncoder::RRI { enc: encode::slti } },
339
    { name: "sltiu",  encoder: InstructionEncoder::RRI { enc: encode::sltiu } },
340
    { name: "sltu",   encoder: InstructionEncoder::RRR { enc: encode::sltu } },
341
    { name: "snez",   encoder: InstructionEncoder::RR { enc: encode::snez } },
342
    { name: "sra",    encoder: InstructionEncoder::RRR { enc: encode::sra } },
343
    { name: "srai",   encoder: InstructionEncoder::Shift { enc: encode::srai } },
344
    { name: "sraiw",  encoder: InstructionEncoder::WordShift { enc: encode::sraiw } },
345
    { name: "sraw",   encoder: InstructionEncoder::RRR { enc: encode::sraw } },
346
    { name: "srl",    encoder: InstructionEncoder::RRR { enc: encode::srl } },
347
    { name: "srli",   encoder: InstructionEncoder::Shift { enc: encode::srli } },
348
    { name: "srliw",  encoder: InstructionEncoder::WordShift { enc: encode::srliw } },
349
    { name: "srlw",   encoder: InstructionEncoder::RRR { enc: encode::srlw } },
350
    { name: "sub",    encoder: InstructionEncoder::RRR { enc: encode::sub } },
351
    { name: "subw",   encoder: InstructionEncoder::RRR { enc: encode::subw } },
352
    { name: "sw",     encoder: InstructionEncoder::Store { enc: encode::sw } },
353
    { name: "tail",   encoder: InstructionEncoder::Jump { rd: rv64::ZERO } },
354
    { name: "wfi",    encoder: InstructionEncoder::NoOperand { enc: encode::wfi } },
355
    { name: "xor",    encoder: InstructionEncoder::RRR { enc: encode::xor } },
356
    { name: "xori",   encoder: InstructionEncoder::RRI { enc: encode::xori } },
357
];
358
359
/// Sorted directive lookup table used by the assembler parser.
360
export constant DIRECTIVES: [DirectiveEntry; 10] = [
361
    { name: "align",    kind: DirectiveKind::Align },
362
    { name: "ascii",    kind: DirectiveKind::Ascii },
363
    { name: "byte",     kind: DirectiveKind::Byte },
364
    { name: "constant", kind: DirectiveKind::Constant },
365
    { name: "data",     kind: DirectiveKind::Data },
366
    { name: "dword",    kind: DirectiveKind::Dword },
367
    { name: "export",   kind: DirectiveKind::Export },
368
    { name: "space",    kind: DirectiveKind::Space },
369
    { name: "text",     kind: DirectiveKind::Text },
370
    { name: "word",     kind: DirectiveKind::Word },
371
];
372
373
/// Sorted register-name lookup table used by the assembler parser.
374
export constant REGISTERS: [RegisterEntry; 33] = [
375
    { name: "a0",   reg: rv64::A0 },
376
    { name: "a1",   reg: rv64::A1 },
377
    { name: "a2",   reg: rv64::A2 },
378
    { name: "a3",   reg: rv64::A3 },
379
    { name: "a4",   reg: rv64::A4 },
380
    { name: "a5",   reg: rv64::A5 },
381
    { name: "a6",   reg: rv64::A6 },
382
    { name: "a7",   reg: rv64::A7 },
383
    { name: "fp",   reg: rv64::FP },
384
    { name: "gp",   reg: rv64::GP },
385
    { name: "ra",   reg: rv64::RA },
386
    { name: "s0",   reg: rv64::S0 },
387
    { name: "s1",   reg: rv64::S1 },
388
    { name: "s10",  reg: rv64::S10 },
389
    { name: "s11",  reg: rv64::S11 },
390
    { name: "s2",   reg: rv64::S2 },
391
    { name: "s3",   reg: rv64::S3 },
392
    { name: "s4",   reg: rv64::S4 },
393
    { name: "s5",   reg: rv64::S5 },
394
    { name: "s6",   reg: rv64::S6 },
395
    { name: "s7",   reg: rv64::S7 },
396
    { name: "s8",   reg: rv64::S8 },
397
    { name: "s9",   reg: rv64::S9 },
398
    { name: "sp",   reg: rv64::SP },
399
    { name: "t0",   reg: rv64::T0 },
400
    { name: "t1",   reg: rv64::T1 },
401
    { name: "t2",   reg: rv64::T2 },
402
    { name: "t3",   reg: rv64::T3 },
403
    { name: "t4",   reg: rv64::T4 },
404
    { name: "t5",   reg: rv64::T5 },
405
    { name: "t6",   reg: rv64::T6 },
406
    { name: "tp",   reg: rv64::TP },
407
    { name: "zero", reg: rv64::ZERO },
408
];
409
410
/// Sorted CSR-name lookup table used by the assembler parser.
411
export constant CSRS: [CsrEntry; 10] = [
412
    { name: "instret",  csr: 0xC02 },
413
    { name: "mcause",   csr: 0x342 },
414
    { name: "mepc",     csr: 0x341 },
415
    { name: "mhartid",  csr: 0xF14 },
416
    { name: "mie",      csr: 0x304 },
417
    { name: "mip",      csr: 0x344 },
418
    { name: "mscratch", csr: 0x340 },
419
    { name: "mstatus",  csr: 0x300 },
420
    { name: "mtval",    csr: 0x343 },
421
    { name: "mtvec",    csr: 0x305 },
422
];
423
424
/// Recorded symbol definition.
425
export record Symbol: Copy {
426
    /// Symbol name.
427
    name: *[u8],
428
    /// Section the symbol belongs to.
429
    section: Section,
430
    /// Byte offset within the section.
431
    offset: i32,
432
    /// Whether `.export` exported this symbol outside its assembly fragment.
433
    isExported: bool,
434
}
435
436
/// Information needed to resolve a pending symbol reference.
437
export union FixupInfo: Copy {
438
    /// Branch to a text label.
439
    Branch { op: BranchOp, rs1: gen::Reg, rs2: gen::Reg, index: u32 },
440
    /// JAL-like jump to a text label.
441
    Jal { rd: gen::Reg, index: u32 },
442
    /// Absolute address materialization into a register.
443
    Addr { rd: gen::Reg, index: u32 },
444
    /// A 32-bit data word referring to a symbol offset.
445
    Word { offset: u32 },
446
    /// A 64-bit data word referring to a symbol offset.
447
    Dword { offset: u32 },
448
}
449
450
/// Pending symbol reference.
451
export record Fixup: Copy {
452
    /// Referenced symbol.
453
    symbol: *[u8],
454
    /// Fixup payload.
455
    info: FixupInfo,
456
}
457
458
/// Parser and emission state.
459
export record Assembler {
460
    /// Allocation arena for temporary assembler state.
461
    arena: *unsafe mut alloc::Arena,
462
    /// Assembler lexical scanner.
463
    scan: scanner::Scanner,
464
    /// Output text buffer.
465
    text: *mut [u32],
466
    /// Number of emitted text words.
467
    textLen: u32,
468
    /// Output data buffer.
469
    data: *mut [u8],
470
    /// Number of emitted data bytes.
471
    dataLen: u32,
472
    /// Current output section.
473
    section: Section,
474
    /// Defined symbols.
475
    symbols: *mut [Symbol],
476
    /// Number of defined symbols.
477
    symbolsLen: u32,
478
    /// Name-to-symbol index map.
479
    symbolMap: dict::Dict,
480
    /// Name-to-integer map.
481
    constMap: dict::Dict,
482
    /// Names marked by `.export`.
483
    exportMap: dict::Dict,
484
    /// Pending fixups.
485
    fixups: *mut [Fixup],
486
    /// Number of pending fixups.
487
    fixupsLen: u32,
488
    /// Fixups that reference text outside this assembly fragment.
489
    externalFixups: *mut [Fixup],
490
    /// Number of external fixups.
491
    externalFixupsLen: u32,
492
    /// Absolute runtime address of data-section offset zero.
493
    dataBase: u32,
494
}
495
496
/// Assemble source using `dataBase` as the runtime address of the data-section.
497
export unsafe fn assemble(
498
    sourceKind: scanner::SourceKind,
499
    source: *[u8],
500
    textBuf: *mut [u32],
501
    dataBuf: *mut [u8],
502
    arena: &mut alloc::Arena,
503
    pool: *unsafe mut strings::Pool,
504
    dataBase: u32
505
) -> Program throws (Error) {
506
    let slotCap = tokenCapacity(sourceKind, source, pool);
507
    let tableCap = nextPowerOfTwo(slotCap * TABLE_CAPACITY_SCALE);
508
509
    let symbols = try! alloc::allocSlice(arena, @sizeOf(Symbol), @alignOf(Symbol), slotCap);
510
    let fixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap);
511
    let externalFixups = try! alloc::allocSlice(arena, @sizeOf(Fixup), @alignOf(Fixup), slotCap);
512
    let entries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
513
    let constEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
514
    let exportEntries = try! alloc::allocSlice(arena, @sizeOf(dict::Entry), @alignOf(dict::Entry), tableCap);
515
516
    let symbolBuf = symbols as *mut [Symbol];
517
    let fixupBuf = fixups as *mut [Fixup];
518
    let externalBuf = externalFixups as *mut [Fixup];
519
    let mut a = Assembler {
520
        arena: arena as *unsafe mut alloc::Arena,
521
        scan: scanner::scanner(sourceKind, source, pool),
522
        text: textBuf,
523
        textLen: 0,
524
        data: dataBuf,
525
        dataLen: 0,
526
        section: Section::Text,
527
        symbols: symbolBuf,
528
        symbolsLen: 0,
529
        symbolMap: dict::init(entries as *mut [dict::Entry]),
530
        constMap: dict::init(constEntries as *mut [dict::Entry]),
531
        exportMap: dict::init(exportEntries as *mut [dict::Entry]),
532
        fixups: fixupBuf,
533
        fixupsLen: 0,
534
        externalFixups: externalBuf,
535
        externalFixupsLen: 0,
536
        dataBase,
537
    };
538
    // Parse assembly source and emit instructions.
539
    try parser::parseProgram(&mut a);
540
    // Resolve fixups and finalize program.
541
    try emit::finishProgram(&mut a);
542
543
    let case Assembler {
544
        text, textLen, data, dataLen, symbols: definedSymbols, symbolsLen,
545
        externalFixups: pendingFixups, externalFixupsLen, ..
546
    } = a
547
        else panic "expected assembler state";
548
    return Program {
549
        text: &text[..textLen],
550
        data: &data[..dataLen],
551
        symbols: &definedSymbols[..symbolsLen],
552
        externalFixups: &pendingFixups[..externalFixupsLen],
553
    };
554
}
555
556
/// Bound symbol and fixup counts by lexical tokens, including one spare slot.
557
unsafe fn tokenCapacity(kind: scanner::SourceKind, source: *[u8], pool: *unsafe mut strings::Pool) -> u32 {
558
    let mut scan = scanner::scanner(kind, source, pool);
559
    let mut count = SOURCE_CAP_PADDING;
560
    while true {
561
        let token = scanner::next(&mut scan);
562
        if token.kind == scanner::TokenKind::Eof {
563
            break;
564
        }
565
        set count += 1;
566
        if token.kind == scanner::TokenKind::Invalid {
567
            break;
568
        }
569
    }
570
    return count;
571
}
572
573
/// Return the next power of two at least as large as `value`.
574
fn nextPowerOfTwo(value: u32) -> u32 {
575
    let mut n: u32 = MIN_TABLE_CAPACITY;
576
    while n < value {
577
        set n <<= 1;
578
    }
579
    return n;
580
}