Use `export` instead of `pub`
52b9cccca36dd131b1b8033ae21973ebed025d1b72ab5bff40cde3bf8d39672e
1 parent
bfa128ab
lib/std.rad
+11 -11
| 1 | 1 | //! The Radiance Standard Library. |
|
| 2 | 2 | ||
| 3 | - | pub mod io; |
|
| 4 | - | pub mod collections; |
|
| 5 | - | pub mod lang; |
|
| 6 | - | pub mod sys; |
|
| 7 | - | pub mod arch; |
|
| 8 | - | pub mod fmt; |
|
| 9 | - | pub mod mem; |
|
| 10 | - | pub mod vec; |
|
| 11 | - | pub mod intrinsics; |
|
| 3 | + | export mod io; |
|
| 4 | + | export mod collections; |
|
| 5 | + | export mod lang; |
|
| 6 | + | export mod sys; |
|
| 7 | + | export mod arch; |
|
| 8 | + | export mod fmt; |
|
| 9 | + | export mod mem; |
|
| 10 | + | export mod vec; |
|
| 11 | + | export mod intrinsics; |
|
| 12 | 12 | ||
| 13 | 13 | // Test modules. |
|
| 14 | - | @test pub mod testing; |
|
| 15 | - | @test pub mod tests; |
|
| 14 | + | @test export mod testing; |
|
| 15 | + | @test export mod tests; |
lib/std/arch.rad
+1 -1
| 1 | 1 | //! RISC-V instruction set architecture encodings. |
|
| 2 | - | pub mod rv64; |
|
| 2 | + | export mod rv64; |
lib/std/arch/rv64.rad
+59 -59
| 8 | 8 | //! * decode: Instruction decoding (for disassembly/printing) |
|
| 9 | 9 | //! * emit: Binary emission context and branch patching |
|
| 10 | 10 | //! * isel: Instruction selection (IL to RV64 instructions) |
|
| 11 | 11 | //! * printer: Assembly text output |
|
| 12 | 12 | ||
| 13 | - | pub mod encode; |
|
| 14 | - | pub mod decode; |
|
| 15 | - | pub mod emit; |
|
| 16 | - | pub mod isel; |
|
| 17 | - | pub mod printer; |
|
| 13 | + | export mod encode; |
|
| 14 | + | export mod decode; |
|
| 15 | + | export mod emit; |
|
| 16 | + | export mod isel; |
|
| 17 | + | export mod printer; |
|
| 18 | 18 | ||
| 19 | 19 | @test mod tests; |
|
| 20 | 20 | ||
| 21 | 21 | use std::mem; |
|
| 22 | 22 | use std::collections::dict; |
| 30 | 30 | ||
| 31 | 31 | //////////////// |
|
| 32 | 32 | // Registers // |
|
| 33 | 33 | //////////////// |
|
| 34 | 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. |
|
| 35 | + | export const ZERO: gen::Reg = gen::Reg(0); /// Hard-wired zero. |
|
| 36 | + | export const RA: gen::Reg = gen::Reg(1); /// Return address. |
|
| 37 | + | export const SP: gen::Reg = gen::Reg(2); /// Stack pointer. |
|
| 38 | + | export const GP: gen::Reg = gen::Reg(3); /// Global pointer. |
|
| 39 | + | export const TP: gen::Reg = gen::Reg(4); /// Thread pointer. |
|
| 40 | + | export const T0: gen::Reg = gen::Reg(5); /// Temporary/alternate link register. |
|
| 41 | + | export const T1: gen::Reg = gen::Reg(6); /// Temporary. |
|
| 42 | + | export const T2: gen::Reg = gen::Reg(7); /// Temporary. |
|
| 43 | + | export const S0: gen::Reg = gen::Reg(8); /// Saved register/frame pointer. |
|
| 44 | + | export const FP: gen::Reg = gen::Reg(8); /// Frame pointer (alias for S0). |
|
| 45 | + | export const S1: gen::Reg = gen::Reg(9); /// Saved register. |
|
| 46 | + | export const A0: gen::Reg = gen::Reg(10); /// Function argument/return. |
|
| 47 | + | export const A1: gen::Reg = gen::Reg(11); /// Function argument/return. |
|
| 48 | + | export const A2: gen::Reg = gen::Reg(12); /// Function argument. |
|
| 49 | + | export const A3: gen::Reg = gen::Reg(13); /// Function argument. |
|
| 50 | + | export const A4: gen::Reg = gen::Reg(14); /// Function argument. |
|
| 51 | + | export const A5: gen::Reg = gen::Reg(15); /// Function argument. |
|
| 52 | + | export const A6: gen::Reg = gen::Reg(16); /// Function argument. |
|
| 53 | + | export const A7: gen::Reg = gen::Reg(17); /// Function argument. |
|
| 54 | + | export const S2: gen::Reg = gen::Reg(18); /// Saved register. |
|
| 55 | + | export const S3: gen::Reg = gen::Reg(19); /// Saved register. |
|
| 56 | + | export const S4: gen::Reg = gen::Reg(20); /// Saved register. |
|
| 57 | + | export const S5: gen::Reg = gen::Reg(21); /// Saved register. |
|
| 58 | + | export const S6: gen::Reg = gen::Reg(22); /// Saved register. |
|
| 59 | + | export const S7: gen::Reg = gen::Reg(23); /// Saved register. |
|
| 60 | + | export const S8: gen::Reg = gen::Reg(24); /// Saved register. |
|
| 61 | + | export const S9: gen::Reg = gen::Reg(25); /// Saved register. |
|
| 62 | + | export const S10: gen::Reg = gen::Reg(26); /// Saved register. |
|
| 63 | + | export const S11: gen::Reg = gen::Reg(27); /// Saved register. |
|
| 64 | + | export const T3: gen::Reg = gen::Reg(28); /// Temporary. |
|
| 65 | + | export const T4: gen::Reg = gen::Reg(29); /// Temporary. |
|
| 66 | + | export const T5: gen::Reg = gen::Reg(30); /// Temporary. |
|
| 67 | + | export const T6: gen::Reg = gen::Reg(31); /// Temporary. |
|
| 68 | 68 | ||
| 69 | 69 | /// Create a register from a number. Panics if `n > 31`. |
|
| 70 | - | pub fn reg(n: u8) -> gen::Reg { |
|
| 70 | + | export fn reg(n: u8) -> gen::Reg { |
|
| 71 | 71 | assert n < 32; |
|
| 72 | 72 | return gen::Reg(n); |
|
| 73 | 73 | } |
|
| 74 | 74 | ||
| 75 | 75 | //////////////////////////// |
|
| 76 | 76 | // Architecture constants // |
|
| 77 | 77 | //////////////////////////// |
|
| 78 | 78 | ||
| 79 | 79 | /// Total number of general-purpose registers. |
|
| 80 | - | pub const NUM_REGISTERS: u8 = 32; |
|
| 80 | + | export const NUM_REGISTERS: u8 = 32; |
|
| 81 | 81 | /// Number of saved registers. |
|
| 82 | - | pub const NUM_SAVED_REGISTERS: u8 = 11; |
|
| 82 | + | export const NUM_SAVED_REGISTERS: u8 = 11; |
|
| 83 | 83 | /// Word size in bytes (32-bit). |
|
| 84 | - | pub const WORD_SIZE: i32 = 4; |
|
| 84 | + | export const WORD_SIZE: i32 = 4; |
|
| 85 | 85 | /// Doubleword size in bytes (64-bit). |
|
| 86 | - | pub const DWORD_SIZE: i32 = 8; |
|
| 86 | + | export const DWORD_SIZE: i32 = 8; |
|
| 87 | 87 | /// Instruction size in bytes. |
|
| 88 | - | pub const INSTR_SIZE: i32 = 4; |
|
| 88 | + | export const INSTR_SIZE: i32 = 4; |
|
| 89 | 89 | /// Stack alignment requirement in bytes. |
|
| 90 | - | pub const STACK_ALIGNMENT: i32 = 16; |
|
| 90 | + | export const STACK_ALIGNMENT: i32 = 16; |
|
| 91 | 91 | ||
| 92 | 92 | /// Minimum blit size (in bytes) to use a loop instead of inline copy. |
|
| 93 | 93 | /// Blits below this threshold are fully unrolled as LD/SD pairs. |
|
| 94 | - | pub const BLIT_LOOP_THRESHOLD: i32 = 256; |
|
| 94 | + | export const BLIT_LOOP_THRESHOLD: i32 = 256; |
|
| 95 | 95 | ||
| 96 | 96 | ///////////////////////// |
|
| 97 | 97 | // Codegen Allocation // |
|
| 98 | 98 | ///////////////////////// |
|
| 99 | 99 | ||
| 100 | 100 | /// Argument registers for function calls. |
|
| 101 | - | pub const ARG_REGS: [gen::Reg; 8] = [A0, A1, A2, A3, A4, A5, A6, A7]; |
|
| 101 | + | export const ARG_REGS: [gen::Reg; 8] = [A0, A1, A2, A3, A4, A5, A6, A7]; |
|
| 102 | 102 | ||
| 103 | 103 | /// Scratch register for code gen. Never allocated to user values. |
|
| 104 | - | pub const SCRATCH1: gen::Reg = T5; |
|
| 104 | + | export const SCRATCH1: gen::Reg = T5; |
|
| 105 | 105 | ||
| 106 | 106 | /// Second scratch register for operations needing two temporaries. |
|
| 107 | - | pub const SCRATCH2: gen::Reg = T6; |
|
| 107 | + | export const SCRATCH2: gen::Reg = T6; |
|
| 108 | 108 | ||
| 109 | 109 | /// Dedicated scratch for address offset adjustment. Never allocated to user |
|
| 110 | 110 | /// values and never used for operand materialization, so it can never |
|
| 111 | 111 | /// conflict with `rd`, `rs`, or `base` in load/store helpers. |
|
| 112 | - | pub const ADDR_SCRATCH: gen::Reg = T4; |
|
| 112 | + | export const ADDR_SCRATCH: gen::Reg = T4; |
|
| 113 | 113 | ||
| 114 | 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]; |
|
| 115 | + | export const CALLEE_SAVED: [gen::Reg; NUM_SAVED_REGISTERS] = [S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11]; |
|
| 116 | 116 | ||
| 117 | 117 | /// Maximum 12-bit signed immediate value. |
|
| 118 | - | pub const MAX_IMM: i32 = 2047; |
|
| 118 | + | export const MAX_IMM: i32 = 2047; |
|
| 119 | 119 | ||
| 120 | 120 | /// Minimum 12-bit signed immediate value. |
|
| 121 | - | pub const MIN_IMM: i32 = -2048; |
|
| 121 | + | export const MIN_IMM: i32 = -2048; |
|
| 122 | 122 | ||
| 123 | 123 | /// Allocatable registers for register allocation. |
|
| 124 | 124 | const ALLOCATABLE_REGS: [gen::Reg; 23] = [ |
|
| 125 | 125 | T0, T1, T2, T3, // Temporaries |
|
| 126 | 126 | A0, A1, A2, A3, A4, A5, A6, A7, // Arguments |
|
| 127 | 127 | S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, // Saved |
|
| 128 | 128 | ]; |
|
| 129 | 129 | ||
| 130 | 130 | /// Get target configuration for register allocation. |
|
| 131 | 131 | // TODO: This should be a constant variable. |
|
| 132 | - | pub fn targetConfig() -> regalloc::TargetConfig { |
|
| 132 | + | export fn targetConfig() -> regalloc::TargetConfig { |
|
| 133 | 133 | return regalloc::TargetConfig { |
|
| 134 | 134 | allocatable: &ALLOCATABLE_REGS[..], |
|
| 135 | 135 | argRegs: &ARG_REGS[..], |
|
| 136 | 136 | calleeSaved: &CALLEE_SAVED[..], |
|
| 137 | 137 | slotSize: DWORD_SIZE, |
| 141 | 141 | /////////////////////// |
|
| 142 | 142 | // Codegen Constants // |
|
| 143 | 143 | /////////////////////// |
|
| 144 | 144 | ||
| 145 | 145 | /// Base address where read-only data is loaded. |
|
| 146 | - | pub const RO_DATA_BASE: u32 = 0x10000; |
|
| 146 | + | export const RO_DATA_BASE: u32 = 0x10000; |
|
| 147 | 147 | ||
| 148 | 148 | /// Base address where read-write data is loaded. |
|
| 149 | - | pub const RW_DATA_BASE: u32 = 0xFFFFF0; |
|
| 149 | + | export const RW_DATA_BASE: u32 = 0xFFFFF0; |
|
| 150 | 150 | ||
| 151 | 151 | /// Storage buffers passed from driver for code generation. |
|
| 152 | - | pub record Storage { |
|
| 152 | + | export record Storage { |
|
| 153 | 153 | /// Buffer for data symbols. |
|
| 154 | 154 | dataSyms: *mut [data::DataSym], |
|
| 155 | 155 | /// Hash table entries for data symbol lookup. |
|
| 156 | 156 | dataSymEntries: *mut [dict::Entry], |
|
| 157 | 157 | } |
|
| 158 | 158 | ||
| 159 | 159 | /// Result of code generation. |
|
| 160 | - | pub record Program { |
|
| 160 | + | export record Program { |
|
| 161 | 161 | /// Slice of emitted code. |
|
| 162 | 162 | code: *[u32], |
|
| 163 | 163 | /// Slice of function addresses (name + start index). |
|
| 164 | 164 | funcs: *[types::FuncAddr], |
|
| 165 | 165 | /// Number of read-only data bytes emitted. |
| 169 | 169 | /// Debug entries mapping PCs to source locations. Empty when debug is off. |
|
| 170 | 170 | debugEntries: *[types::DebugEntry], |
|
| 171 | 171 | } |
|
| 172 | 172 | ||
| 173 | 173 | /// Generate code for an IL program. |
|
| 174 | - | pub fn generate( |
|
| 174 | + | export fn generate( |
|
| 175 | 175 | program: *il::Program, |
|
| 176 | 176 | storage: Storage, |
|
| 177 | 177 | roDataBuf: *mut [u8], |
|
| 178 | 178 | rwDataBuf: *mut [u8], |
|
| 179 | 179 | arena: *mut alloc::Arena, |
lib/std/arch/rv64/decode.rad
+13 -13
| 8 | 8 | /////////////////////// |
|
| 9 | 9 | // Field Extraction // |
|
| 10 | 10 | /////////////////////// |
|
| 11 | 11 | ||
| 12 | 12 | /// Extract opcode (bits 6:0). |
|
| 13 | - | pub fn opcode(instr: u32) -> u32 { |
|
| 13 | + | export fn opcode(instr: u32) -> u32 { |
|
| 14 | 14 | return instr & 0x7F; |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Extract rd (bits 11:7). |
|
| 18 | - | pub fn rd(instr: u32) -> u8 { |
|
| 18 | + | export fn rd(instr: u32) -> u8 { |
|
| 19 | 19 | return ((instr >> 7) & 0x1F) as u8; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Extract funct3 (bits 14:12). |
|
| 23 | - | pub fn funct3(instr: u32) -> u32 { |
|
| 23 | + | export fn funct3(instr: u32) -> u32 { |
|
| 24 | 24 | return (instr >> 12) & 0x07; |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Extract rs1 (bits 19:15). |
|
| 28 | - | pub fn rs1(instr: u32) -> u8 { |
|
| 28 | + | export fn rs1(instr: u32) -> u8 { |
|
| 29 | 29 | return ((instr >> 15) & 0x1F) as u8; |
|
| 30 | 30 | } |
|
| 31 | 31 | ||
| 32 | 32 | /// Extract rs2 (bits 24:20). |
|
| 33 | - | pub fn rs2(instr: u32) -> u8 { |
|
| 33 | + | export fn rs2(instr: u32) -> u8 { |
|
| 34 | 34 | return ((instr >> 20) & 0x1F) as u8; |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Extract funct7 (bits 31:25). |
|
| 38 | - | pub fn funct7(instr: u32) -> u32 { |
|
| 38 | + | export fn funct7(instr: u32) -> u32 { |
|
| 39 | 39 | return instr >> 25; |
|
| 40 | 40 | } |
|
| 41 | 41 | ||
| 42 | 42 | ////////////////////////// |
|
| 43 | 43 | // Immediate Extraction // |
|
| 44 | 44 | ////////////////////////// |
|
| 45 | 45 | ||
| 46 | 46 | /// Extract I-type immediate (sign-extended). |
|
| 47 | - | pub fn immI(instr: u32) -> i32 { |
|
| 47 | + | export fn immI(instr: u32) -> i32 { |
|
| 48 | 48 | let imm = instr >> 20; |
|
| 49 | 49 | if (imm & 0x800) != 0 { |
|
| 50 | 50 | return (imm | 0xFFFFF000) as i32; |
|
| 51 | 51 | } |
|
| 52 | 52 | return imm as i32; |
|
| 53 | 53 | } |
|
| 54 | 54 | ||
| 55 | 55 | /// Extract S-type immediate (sign-extended). |
|
| 56 | - | pub fn immS(instr: u32) -> i32 { |
|
| 56 | + | export fn immS(instr: u32) -> i32 { |
|
| 57 | 57 | let lo = (instr >> 7) & 0x1F; |
|
| 58 | 58 | let hi = (instr >> 25) & 0x7F; |
|
| 59 | 59 | let imm = (hi << 5) | lo; |
|
| 60 | 60 | if (imm & 0x800) != 0 { |
|
| 61 | 61 | return (imm | 0xFFFFF000) as i32; |
|
| 62 | 62 | } |
|
| 63 | 63 | return imm as i32; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Extract B-type immediate (sign-extended). |
|
| 67 | - | pub fn immB(instr: u32) -> i32 { |
|
| 67 | + | export fn immB(instr: u32) -> i32 { |
|
| 68 | 68 | let imm11 = (instr >> 7) & 0x1; |
|
| 69 | 69 | let imm4_1 = (instr >> 8) & 0xF; |
|
| 70 | 70 | let imm10_5 = (instr >> 25) & 0x3F; |
|
| 71 | 71 | let imm12 = (instr >> 31) & 0x1; |
|
| 72 | 72 | let imm = (imm12 << 12) | (imm11 << 11) | (imm10_5 << 5) | (imm4_1 << 1); |
| 75 | 75 | } |
|
| 76 | 76 | return imm as i32; |
|
| 77 | 77 | } |
|
| 78 | 78 | ||
| 79 | 79 | /// Extract J-type immediate (sign-extended). |
|
| 80 | - | pub fn immJ(instr: u32) -> i32 { |
|
| 80 | + | export fn immJ(instr: u32) -> i32 { |
|
| 81 | 81 | let imm19_12 = (instr >> 12) & 0xFF; |
|
| 82 | 82 | let imm11 = (instr >> 20) & 0x1; |
|
| 83 | 83 | let imm10_1 = (instr >> 21) & 0x3FF; |
|
| 84 | 84 | let imm20 = (instr >> 31) & 0x1; |
|
| 85 | 85 | let imm = (imm20 << 20) | (imm19_12 << 12) | (imm11 << 11) | (imm10_1 << 1); |
| 88 | 88 | } |
|
| 89 | 89 | return imm as i32; |
|
| 90 | 90 | } |
|
| 91 | 91 | ||
| 92 | 92 | /// Extract U-type immediate (upper 20 bits, sign-extended). |
|
| 93 | - | pub fn immU(instr: u32) -> i32 { |
|
| 93 | + | export fn immU(instr: u32) -> i32 { |
|
| 94 | 94 | let imm = instr >> 12; |
|
| 95 | 95 | if (imm & 0x80000) != 0 { |
|
| 96 | 96 | return (imm | 0xFFF00000) as i32; |
|
| 97 | 97 | } |
|
| 98 | 98 | return imm as i32; |
| 101 | 101 | ///////////////////////// |
|
| 102 | 102 | // Decoded Instruction // |
|
| 103 | 103 | ///////////////////////// |
|
| 104 | 104 | ||
| 105 | 105 | /// Decoded RV64 instruction. |
|
| 106 | - | pub union Instr { |
|
| 106 | + | export union Instr { |
|
| 107 | 107 | // R-type ALU. |
|
| 108 | 108 | Add { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 109 | 109 | Sub { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 110 | 110 | Sll { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 111 | 111 | Slt { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg }, |
| 193 | 193 | // Unknown/invalid instruction. |
|
| 194 | 194 | Unknown { bits: u32 }, |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Decode a 32-bit instruction word into an [`Instr`]. |
|
| 198 | - | pub fn decode(instr: u32) -> Instr { |
|
| 198 | + | export fn decode(instr: u32) -> Instr { |
|
| 199 | 199 | let op = opcode(instr); |
|
| 200 | 200 | let f3 = funct3(instr); |
|
| 201 | 201 | let f7 = funct7(instr); |
|
| 202 | 202 | let rd = super::reg(rd(instr)); |
|
| 203 | 203 | let rs1 = super::reg(rs1(instr)); |
lib/std/arch/rv64/emit.rad
+42 -42
| 26 | 26 | ////////////////////// |
|
| 27 | 27 | // Emission Context // |
|
| 28 | 28 | ////////////////////// |
|
| 29 | 29 | ||
| 30 | 30 | /// Branch/jump that needs offset patching after all blocks are emitted. |
|
| 31 | - | pub record PendingBranch { |
|
| 31 | + | export record PendingBranch { |
|
| 32 | 32 | /// Index into code buffer where the branch instruction is. |
|
| 33 | 33 | index: u32, |
|
| 34 | 34 | /// Target block index. |
|
| 35 | 35 | target: u32, |
|
| 36 | 36 | /// Type of branch for re-encoding. |
|
| 37 | 37 | kind: BranchKind, |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Type of branch instruction. |
|
| 41 | - | pub union BranchKind { |
|
| 41 | + | export union BranchKind { |
|
| 42 | 42 | /// Conditional branch (B-type encoding). |
|
| 43 | 43 | Cond { op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 44 | 44 | /// Inverted conditional branch (B-type encoding with negated condition). |
|
| 45 | 45 | InvertedCond { op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg }, |
|
| 46 | 46 | /// Unconditional jump (J-type encoding). |
|
| 47 | 47 | Jump, |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// Function call that needs offset patching. |
|
| 51 | - | pub record PendingCall { |
|
| 51 | + | export record PendingCall { |
|
| 52 | 52 | /// Index in code buffer where the call was emitted. |
|
| 53 | 53 | index: u32, |
|
| 54 | 54 | /// Target function name. |
|
| 55 | 55 | target: *[u8], |
|
| 56 | 56 | } |
|
| 57 | 57 | ||
| 58 | 58 | /// Function address load that needs offset patching. |
|
| 59 | 59 | /// Used when taking a function's address as a value. |
|
| 60 | - | pub record PendingAddrLoad { |
|
| 60 | + | export record PendingAddrLoad { |
|
| 61 | 61 | /// Index in code buffer where the load was emitted. |
|
| 62 | 62 | index: u32, |
|
| 63 | 63 | /// Target function name. |
|
| 64 | 64 | target: *[u8], |
|
| 65 | 65 | /// Destination register. |
|
| 66 | 66 | rd: gen::Reg, |
|
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Adjusted base register and offset for addressing. |
|
| 70 | - | pub record AdjustedOffset { |
|
| 70 | + | export record AdjustedOffset { |
|
| 71 | 71 | /// Base register. |
|
| 72 | 72 | base: gen::Reg, |
|
| 73 | 73 | /// Byte offset from register. |
|
| 74 | 74 | offset: i32, |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | 77 | /// Callee-saved register with its stack offset. |
|
| 78 | - | pub record SavedReg { |
|
| 78 | + | export record SavedReg { |
|
| 79 | 79 | /// Register to save/restore. |
|
| 80 | 80 | reg: gen::Reg, |
|
| 81 | 81 | /// Offset from SP. |
|
| 82 | 82 | offset: i32, |
|
| 83 | 83 | } |
|
| 84 | 84 | ||
| 85 | 85 | /// Emission context. Tracks state during code generation. |
|
| 86 | - | pub record Emitter { |
|
| 86 | + | export record Emitter { |
|
| 87 | 87 | /// Emitted instructions storage. |
|
| 88 | 88 | code: *mut [u32], |
|
| 89 | 89 | /// Current number of emitted instructions. |
|
| 90 | 90 | codeLen: u32, |
|
| 91 | 91 | /// Local branches needing offset patching. |
| 111 | 111 | /// Number of debug entries recorded. |
|
| 112 | 112 | debugEntriesLen: u32, |
|
| 113 | 113 | } |
|
| 114 | 114 | ||
| 115 | 115 | /// Computed stack frame layout for a function. |
|
| 116 | - | pub record Frame { |
|
| 116 | + | export record Frame { |
|
| 117 | 117 | /// Total frame size in bytes (aligned). |
|
| 118 | 118 | totalSize: i32, |
|
| 119 | 119 | /// Callee-saved registers and their offsets. |
|
| 120 | 120 | // TODO: Use constant length when language supports it. |
|
| 121 | 121 | savedRegs: [SavedReg; super::NUM_SAVED_REGISTERS], |
| 130 | 130 | /// When false, SP never changes after the prologue. |
|
| 131 | 131 | isDynamic: bool, |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | 134 | /// Compute frame layout from local size and used callee-saved registers. |
|
| 135 | - | pub fn computeFrame(localSize: i32, usedCalleeSaved: u32, epilogueBlock: u32, isLeaf: bool, isDynamic: bool) -> Frame { |
|
| 135 | + | export fn computeFrame(localSize: i32, usedCalleeSaved: u32, epilogueBlock: u32, isLeaf: bool, isDynamic: bool) -> Frame { |
|
| 136 | 136 | let mut frame = Frame { |
|
| 137 | 137 | totalSize: 0, |
|
| 138 | 138 | savedRegs: undefined, |
|
| 139 | 139 | savedRegsLen: 0, |
|
| 140 | 140 | epilogueBlock, |
| 170 | 170 | } |
|
| 171 | 171 | return frame; |
|
| 172 | 172 | } |
|
| 173 | 173 | ||
| 174 | 174 | /// Create a new emitter. |
|
| 175 | - | pub fn emitter(arena: *mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) { |
|
| 175 | + | export fn emitter(arena: *mut alloc::Arena, debug: bool) -> Emitter throws (alloc::AllocError) { |
|
| 176 | 176 | let code = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_INSTRS); |
|
| 177 | 177 | let pendingBranches = try alloc::allocSlice(arena, @sizeOf(PendingBranch), @alignOf(PendingBranch), MAX_PENDING); |
|
| 178 | 178 | let pendingCalls = try alloc::allocSlice(arena, @sizeOf(PendingCall), @alignOf(PendingCall), MAX_PENDING); |
|
| 179 | 179 | let pendingAddrLoads = try alloc::allocSlice(arena, @sizeOf(PendingAddrLoad), @alignOf(PendingAddrLoad), MAX_PENDING); |
|
| 180 | 180 | let blockOffsets = try alloc::allocSlice(arena, @sizeOf(i32), @alignOf(i32), labels::MAX_BLOCKS_PER_FN); |
| 207 | 207 | /////////////////////// |
|
| 208 | 208 | // Emission Helpers // |
|
| 209 | 209 | /////////////////////// |
|
| 210 | 210 | ||
| 211 | 211 | /// Emit a single instruction. |
|
| 212 | - | pub fn emit(e: *mut Emitter, instr: u32) { |
|
| 212 | + | export fn emit(e: *mut Emitter, instr: u32) { |
|
| 213 | 213 | assert e.codeLen < e.code.len, "emit: code buffer full"; |
|
| 214 | 214 | e.code[e.codeLen] = instr; |
|
| 215 | 215 | e.codeLen += 1; |
|
| 216 | 216 | } |
|
| 217 | 217 | ||
| 218 | 218 | /// Compute branch offset to a function by name. |
|
| 219 | - | pub fn branchOffsetToFunc(e: *Emitter, srcIndex: u32, name: *[u8]) -> i32 { |
|
| 219 | + | export fn branchOffsetToFunc(e: *Emitter, srcIndex: u32, name: *[u8]) -> i32 { |
|
| 220 | 220 | return labels::branchToFunc(&e.labels, srcIndex, name, super::INSTR_SIZE); |
|
| 221 | 221 | } |
|
| 222 | 222 | ||
| 223 | 223 | /// Patch an instruction at a given index. |
|
| 224 | - | pub fn patch(e: *mut Emitter, index: u32, instr: u32) { |
|
| 224 | + | export fn patch(e: *mut Emitter, index: u32, instr: u32) { |
|
| 225 | 225 | e.code[index] = instr; |
|
| 226 | 226 | } |
|
| 227 | 227 | ||
| 228 | 228 | /// Record a block's address for branch resolution. |
|
| 229 | - | pub fn recordBlock(e: *mut Emitter, blockIdx: u32) { |
|
| 229 | + | export fn recordBlock(e: *mut Emitter, blockIdx: u32) { |
|
| 230 | 230 | assert e.codeLen <= MAX_CODE_LEN; |
|
| 231 | 231 | labels::recordBlock(&mut e.labels, blockIdx, e.codeLen as i32 * super::INSTR_SIZE); |
|
| 232 | 232 | } |
|
| 233 | 233 | ||
| 234 | 234 | /// Record a function's code offset for call resolution. |
|
| 235 | - | pub fn recordFuncOffset(e: *mut Emitter, name: *[u8]) { |
|
| 235 | + | export fn recordFuncOffset(e: *mut Emitter, name: *[u8]) { |
|
| 236 | 236 | assert e.codeLen <= MAX_CODE_LEN; |
|
| 237 | 237 | dict::insert(&mut e.labels.funcs, name, e.codeLen as i32 * super::INSTR_SIZE); |
|
| 238 | 238 | } |
|
| 239 | 239 | ||
| 240 | 240 | /// Record a function's start position for printing. |
|
| 241 | - | pub fn recordFunc(e: *mut Emitter, name: *[u8]) { |
|
| 241 | + | export fn recordFunc(e: *mut Emitter, name: *[u8]) { |
|
| 242 | 242 | assert e.funcsLen < e.funcs.len, "recordFunc: funcs buffer full"; |
|
| 243 | 243 | e.funcs[e.funcsLen] = types::FuncAddr { name, index: e.codeLen }; |
|
| 244 | 244 | e.funcsLen += 1; |
|
| 245 | 245 | } |
|
| 246 | 246 | ||
| 247 | 247 | /// Record a local branch needing later patching. |
|
| 248 | 248 | /// Unconditional jumps use a single slot (J-type, +-1MB range). |
|
| 249 | 249 | /// Conditional branches use two slots (B-type has only +-4KB range, |
|
| 250 | 250 | /// so large functions may need the inverted-branch + JAL fallback). |
|
| 251 | - | pub fn recordBranch(e: *mut Emitter, targetBlock: u32, kind: BranchKind) { |
|
| 251 | + | export fn recordBranch(e: *mut Emitter, targetBlock: u32, kind: BranchKind) { |
|
| 252 | 252 | assert e.pendingBranchesLen < e.pendingBranches.len, "recordBranch: buffer full"; |
|
| 253 | 253 | e.pendingBranches[e.pendingBranchesLen] = PendingBranch { |
|
| 254 | 254 | index: e.codeLen, |
|
| 255 | 255 | target: targetBlock, |
|
| 256 | 256 | kind: kind, |
| 266 | 266 | } |
|
| 267 | 267 | ||
| 268 | 268 | /// Record a function call needing later patching. |
|
| 269 | 269 | /// Emits placeholder instructions that will be patched later. |
|
| 270 | 270 | /// Uses two slots to support long-distance calls. |
|
| 271 | - | pub fn recordCall(e: *mut Emitter, target: *[u8]) { |
|
| 271 | + | export fn recordCall(e: *mut Emitter, target: *[u8]) { |
|
| 272 | 272 | assert e.pendingCallsLen < e.pendingCalls.len, "recordCall: buffer full"; |
|
| 273 | 273 | e.pendingCalls[e.pendingCallsLen] = PendingCall { |
|
| 274 | 274 | index: e.codeLen, |
|
| 275 | 275 | target, |
|
| 276 | 276 | }; |
| 281 | 281 | } |
|
| 282 | 282 | ||
| 283 | 283 | /// Record a function address load needing later patching. |
|
| 284 | 284 | /// Emits placeholder instructions that will be patched to load the function's address. |
|
| 285 | 285 | /// Uses two slots to compute long-distance addresses. |
|
| 286 | - | pub fn recordAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 286 | + | export fn recordAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) { |
|
| 287 | 287 | assert e.pendingAddrLoadsLen < e.pendingAddrLoads.len, "recordAddrLoad: buffer full"; |
|
| 288 | 288 | e.pendingAddrLoads[e.pendingAddrLoadsLen] = PendingAddrLoad { |
|
| 289 | 289 | index: e.codeLen, |
|
| 290 | 290 | target, |
|
| 291 | 291 | rd: rd, |
| 300 | 300 | /// |
|
| 301 | 301 | /// Called after each function. |
|
| 302 | 302 | /// |
|
| 303 | 303 | /// Uses two-instruction sequences: short branches use `branch` and `nop`, |
|
| 304 | 304 | /// long branches use inverted branch and `jal` or `auipc` and `jalr`. |
|
| 305 | - | pub fn patchLocalBranches(e: *mut Emitter) { |
|
| 305 | + | export fn patchLocalBranches(e: *mut Emitter) { |
|
| 306 | 306 | for i in 0..e.pendingBranchesLen { |
|
| 307 | 307 | let p = e.pendingBranches[i]; |
|
| 308 | 308 | let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE); |
|
| 309 | 309 | match p.kind { |
|
| 310 | 310 | case BranchKind::Cond { op, rs1, rs2 } => { |
| 357 | 357 | } |
|
| 358 | 358 | } |
|
| 359 | 359 | ||
| 360 | 360 | /// Patch all pending function calls. |
|
| 361 | 361 | /// Called after all functions have been generated. |
|
| 362 | - | pub fn patchCalls(e: *mut Emitter) { |
|
| 362 | + | export fn patchCalls(e: *mut Emitter) { |
|
| 363 | 363 | for i in 0..e.pendingCallsLen { |
|
| 364 | 364 | let p = e.pendingCalls[i]; |
|
| 365 | 365 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 366 | 366 | let s = splitImm(offset); |
|
| 367 | 367 |
| 373 | 373 | } |
|
| 374 | 374 | ||
| 375 | 375 | /// Patch all pending function address loads. |
|
| 376 | 376 | /// Called after all functions have been generated. |
|
| 377 | 377 | /// Uses PC-relative addresses up to 2GB away. |
|
| 378 | - | pub fn patchAddrLoads(e: *mut Emitter) { |
|
| 378 | + | export fn patchAddrLoads(e: *mut Emitter) { |
|
| 379 | 379 | for i in 0..e.pendingAddrLoadsLen { |
|
| 380 | 380 | let p = e.pendingAddrLoads[i]; |
|
| 381 | 381 | let offset = branchOffsetToFunc(e, p.index, p.target); |
|
| 382 | 382 | let s = splitImm(offset); |
|
| 383 | 383 |
| 391 | 391 | ///////////////////////// |
|
| 392 | 392 | // Immediate Handling // |
|
| 393 | 393 | ///////////////////////// |
|
| 394 | 394 | ||
| 395 | 395 | /// Split immediate into `hi` and `lo` bits. |
|
| 396 | - | pub record SplitImm { |
|
| 396 | + | export record SplitImm { |
|
| 397 | 397 | /// Upper 20 bits. |
|
| 398 | 398 | hi: i32, |
|
| 399 | 399 | /// Lower 12 bits. |
|
| 400 | 400 | lo: i32, |
|
| 401 | 401 | } |
|
| 402 | 402 | ||
| 403 | 403 | /// Split a 32-bit immediate for `AUIPC, ADDI` / `JALR` sequences. |
|
| 404 | 404 | /// Handles sign extension: if *lo* is negative, increment *hi*. |
|
| 405 | - | pub fn splitImm(imm: i32) -> SplitImm { |
|
| 405 | + | export fn splitImm(imm: i32) -> SplitImm { |
|
| 406 | 406 | let lo = imm & 0xFFF; |
|
| 407 | 407 | let mut hi = (imm >> 12) & 0xFFFFF; |
|
| 408 | 408 | // If `lo`'s sign bit is set, it will be sign-extended to negative. |
|
| 409 | 409 | // Compensate by incrementing `hi`. |
|
| 410 | 410 | if (lo & 0x800) != 0 { |
| 433 | 433 | /// Load an immediate value into a register. |
|
| 434 | 434 | /// Handles the full range of 64-bit immediates. |
|
| 435 | 435 | /// For values fitting in 12 bits, uses a single `ADDI`. |
|
| 436 | 436 | /// For values fitting in 32 bits, uses `LUI` + `ADDIW`. |
|
| 437 | 437 | /// For wider values, loads upper and lower halves then combines with shift and add. |
|
| 438 | - | pub fn loadImm(e: *mut Emitter, rd: gen::Reg, imm: i64) { |
|
| 438 | + | export fn loadImm(e: *mut Emitter, rd: gen::Reg, imm: i64) { |
|
| 439 | 439 | let immMin = super::MIN_IMM as i64; |
|
| 440 | 440 | let immMax = super::MAX_IMM as i64; |
|
| 441 | 441 | ||
| 442 | 442 | if imm >= immMin and imm <= immMax { |
|
| 443 | 443 | emit(e, encode::addi(rd, super::ZERO, imm as i32)); |
| 472 | 472 | emit(e, encode::slli(rd, rd, 10)); |
|
| 473 | 473 | emit(e, encode::addi(rd, rd, lower & 0x3FF)); |
|
| 474 | 474 | } |
|
| 475 | 475 | ||
| 476 | 476 | /// Emit add-immediate, handling large immediates. |
|
| 477 | - | pub fn emitAddImm(e: *mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) { |
|
| 477 | + | export fn emitAddImm(e: *mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) { |
|
| 478 | 478 | if imm >= super::MIN_IMM and imm <= super::MAX_IMM { |
|
| 479 | 479 | emit(e, encode::addi(rd, rs, imm)); |
|
| 480 | 480 | } else { |
|
| 481 | 481 | loadImm(e, super::SCRATCH1, imm as i64); |
|
| 482 | 482 | emit(e, encode::add(rd, rs, super::SCRATCH1)); |
| 486 | 486 | //////////////////////// |
|
| 487 | 487 | // Load/Store Helpers // |
|
| 488 | 488 | //////////////////////// |
|
| 489 | 489 | ||
| 490 | 490 | /// Emit unsigned load with automatic offset adjustment. |
|
| 491 | - | pub fn emitLoad(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 491 | + | export fn emitLoad(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 492 | 492 | let adj = adjustOffset(e, base, offset); |
|
| 493 | 493 | match typ { |
|
| 494 | 494 | case il::Type::W8 => emit(e, encode::lbu(rd, adj.base, adj.offset)), |
|
| 495 | 495 | case il::Type::W16 => emit(e, encode::lhu(rd, adj.base, adj.offset)), |
|
| 496 | 496 | case il::Type::W32 => emit(e, encode::lwu(rd, adj.base, adj.offset)), |
|
| 497 | 497 | case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)), |
|
| 498 | 498 | } |
|
| 499 | 499 | } |
|
| 500 | 500 | ||
| 501 | 501 | /// Emit signed load with automatic offset adjustment. |
|
| 502 | - | pub fn emitSload(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 502 | + | export fn emitSload(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 503 | 503 | let adj = adjustOffset(e, base, offset); |
|
| 504 | 504 | match typ { |
|
| 505 | 505 | case il::Type::W8 => emit(e, encode::lb(rd, adj.base, adj.offset)), |
|
| 506 | 506 | case il::Type::W16 => emit(e, encode::lh(rd, adj.base, adj.offset)), |
|
| 507 | 507 | case il::Type::W32 => emit(e, encode::lw(rd, adj.base, adj.offset)), |
|
| 508 | 508 | case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)), |
|
| 509 | 509 | } |
|
| 510 | 510 | } |
|
| 511 | 511 | ||
| 512 | 512 | /// Emit store with automatic offset adjustment. |
|
| 513 | - | pub fn emitStore(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 513 | + | export fn emitStore(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) { |
|
| 514 | 514 | let adj = adjustOffset(e, base, offset); |
|
| 515 | 515 | match typ { |
|
| 516 | 516 | case il::Type::W8 => emit(e, encode::sb(rs, adj.base, adj.offset)), |
|
| 517 | 517 | case il::Type::W16 => emit(e, encode::sh(rs, adj.base, adj.offset)), |
|
| 518 | 518 | case il::Type::W32 => emit(e, encode::sw(rs, adj.base, adj.offset)), |
|
| 519 | 519 | case il::Type::W64 => emit(e, encode::sd(rs, adj.base, adj.offset)), |
|
| 520 | 520 | } |
|
| 521 | 521 | } |
|
| 522 | 522 | ||
| 523 | 523 | /// Emit 64-bit load with automatic offset adjustment. |
|
| 524 | - | pub fn emitLd(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 524 | + | export fn emitLd(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 525 | 525 | let adj = adjustOffset(e, base, offset); |
|
| 526 | 526 | emit(e, encode::ld(rd, adj.base, adj.offset)); |
|
| 527 | 527 | } |
|
| 528 | 528 | ||
| 529 | 529 | /// Emit 64-bit store with automatic offset adjustment. |
|
| 530 | - | pub fn emitSd(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 530 | + | export fn emitSd(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 531 | 531 | let adj = adjustOffset(e, base, offset); |
|
| 532 | 532 | emit(e, encode::sd(rs, adj.base, adj.offset)); |
|
| 533 | 533 | } |
|
| 534 | 534 | ||
| 535 | 535 | /// Emit 32-bit load with automatic offset adjustment. |
|
| 536 | - | pub fn emitLw(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 536 | + | export fn emitLw(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 537 | 537 | let adj = adjustOffset(e, base, offset); |
|
| 538 | 538 | emit(e, encode::lw(rd, adj.base, adj.offset)); |
|
| 539 | 539 | } |
|
| 540 | 540 | ||
| 541 | 541 | /// Emit 32-bit store with automatic offset adjustment. |
|
| 542 | - | pub fn emitSw(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 542 | + | export fn emitSw(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 543 | 543 | let adj = adjustOffset(e, base, offset); |
|
| 544 | 544 | emit(e, encode::sw(rs, adj.base, adj.offset)); |
|
| 545 | 545 | } |
|
| 546 | 546 | ||
| 547 | 547 | /// Emit 8-bit load with automatic offset adjustment. |
|
| 548 | - | pub fn emitLb(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 548 | + | export fn emitLb(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 549 | 549 | let adj = adjustOffset(e, base, offset); |
|
| 550 | 550 | emit(e, encode::lb(rd, adj.base, adj.offset)); |
|
| 551 | 551 | } |
|
| 552 | 552 | ||
| 553 | 553 | /// Emit 8-bit store with automatic offset adjustment. |
|
| 554 | - | pub fn emitSb(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 554 | + | export fn emitSb(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) { |
|
| 555 | 555 | let adj = adjustOffset(e, base, offset); |
|
| 556 | 556 | emit(e, encode::sb(rs, adj.base, adj.offset)); |
|
| 557 | 557 | } |
|
| 558 | 558 | ||
| 559 | 559 | ////////////////////////// |
|
| 560 | 560 | // Prologue / Epilogue // |
|
| 561 | 561 | ////////////////////////// |
|
| 562 | 562 | ||
| 563 | 563 | /// Emit function prologue. |
|
| 564 | 564 | /// Allocates stack frame, saves RA/FP, saves callee-saved registers. |
|
| 565 | - | pub fn emitPrologue(e: *mut Emitter, frame: *Frame) { |
|
| 565 | + | export fn emitPrologue(e: *mut Emitter, frame: *Frame) { |
|
| 566 | 566 | // Fast path: leaf function with no locals. |
|
| 567 | 567 | if frame.totalSize == 0 { |
|
| 568 | 568 | return; |
|
| 569 | 569 | } |
|
| 570 | 570 | let totalSize = frame.totalSize; |
| 594 | 594 | emitSd(e, sr.reg, super::SP, sr.offset); |
|
| 595 | 595 | } |
|
| 596 | 596 | } |
|
| 597 | 597 | ||
| 598 | 598 | /// Emit a return: jump to epilogue, or emit `ret` directly for leaf functions. |
|
| 599 | - | pub fn emitReturn(e: *mut Emitter, frame: *Frame) { |
|
| 599 | + | export fn emitReturn(e: *mut Emitter, frame: *Frame) { |
|
| 600 | 600 | if frame.totalSize == 0 { |
|
| 601 | 601 | // Leaf function: no frame to tear down, emit ret directly. |
|
| 602 | 602 | emit(e, encode::ret()); |
|
| 603 | 603 | return; |
|
| 604 | 604 | } |
|
| 605 | 605 | recordBranch(e, frame.epilogueBlock, BranchKind::Jump); |
|
| 606 | 606 | } |
|
| 607 | 607 | ||
| 608 | 608 | /// Emit function epilogue. |
|
| 609 | 609 | /// Restores callee-saved registers, `RA/FP`, deallocates frame, returns. |
|
| 610 | - | pub fn emitEpilogue(e: *mut Emitter, frame: *Frame) { |
|
| 610 | + | export fn emitEpilogue(e: *mut Emitter, frame: *Frame) { |
|
| 611 | 611 | // Record epilogue block address for return jumps. |
|
| 612 | 612 | recordBlock(e, frame.epilogueBlock); |
|
| 613 | 613 | ||
| 614 | 614 | // Fast path: leaf function with no locals. |
|
| 615 | 615 | if frame.totalSize == 0 { |
| 642 | 642 | ////////////////// |
|
| 643 | 643 | // Code Access // |
|
| 644 | 644 | ////////////////// |
|
| 645 | 645 | ||
| 646 | 646 | /// Get emitted code as a slice. |
|
| 647 | - | pub fn getCode(e: *Emitter) -> *[u32] { |
|
| 647 | + | export fn getCode(e: *Emitter) -> *[u32] { |
|
| 648 | 648 | return &e.code[..e.codeLen]; |
|
| 649 | 649 | } |
|
| 650 | 650 | ||
| 651 | 651 | /// Get function addresses for printing. |
|
| 652 | - | pub fn getFuncs(e: *Emitter) -> *[types::FuncAddr] { |
|
| 652 | + | export fn getFuncs(e: *Emitter) -> *[types::FuncAddr] { |
|
| 653 | 653 | return &e.funcs[..e.funcsLen]; |
|
| 654 | 654 | } |
|
| 655 | 655 | ||
| 656 | 656 | /// Record a debug entry mapping the current PC to a source location. |
|
| 657 | 657 | /// Deduplicates consecutive entries with the same location. |
|
| 658 | - | pub fn recordSrcLoc(e: *mut Emitter, loc: il::SrcLoc) { |
|
| 658 | + | export fn recordSrcLoc(e: *mut Emitter, loc: il::SrcLoc) { |
|
| 659 | 659 | let pc = e.codeLen * super::INSTR_SIZE as u32; |
|
| 660 | 660 | ||
| 661 | 661 | // Skip if this is the same location as the previous entry. |
|
| 662 | 662 | if e.debugEntriesLen > 0 { |
|
| 663 | 663 | let prev = &e.debugEntries[e.debugEntriesLen - 1]; |
| 673 | 673 | }; |
|
| 674 | 674 | e.debugEntriesLen += 1; |
|
| 675 | 675 | } |
|
| 676 | 676 | ||
| 677 | 677 | /// Get debug entries as a slice. |
|
| 678 | - | pub fn getDebugEntries(e: *Emitter) -> *[types::DebugEntry] { |
|
| 678 | + | export fn getDebugEntries(e: *Emitter) -> *[types::DebugEntry] { |
|
| 679 | 679 | return &e.debugEntries[..e.debugEntriesLen]; |
|
| 680 | 680 | } |
lib/std/arch/rv64/encode.rad
+118 -118
| 6 | 6 | ||
| 7 | 7 | ////////////////////// |
|
| 8 | 8 | // Opcode Constants // |
|
| 9 | 9 | ////////////////////// |
|
| 10 | 10 | ||
| 11 | - | pub const OP_LOAD: u32 = 0x03; |
|
| 12 | - | pub const OP_STORE: u32 = 0x23; |
|
| 13 | - | pub const OP_BRANCH: u32 = 0x63; |
|
| 14 | - | pub const OP_JALR: u32 = 0x67; |
|
| 15 | - | pub const OP_JAL: u32 = 0x6F; |
|
| 16 | - | pub const OP_OP: u32 = 0x33; |
|
| 17 | - | pub const OP_IMM: u32 = 0x13; |
|
| 18 | - | pub const OP_AUIPC: u32 = 0x17; |
|
| 19 | - | pub const OP_LUI: u32 = 0x37; |
|
| 20 | - | pub const OP_SYSTEM: u32 = 0x73; |
|
| 21 | - | pub const OP_OP32: u32 = 0x3B; // RV64: 32-bit operations |
|
| 22 | - | pub const OP_IMM32: u32 = 0x1B; // RV64: 32-bit immediate operations |
|
| 11 | + | export const OP_LOAD: u32 = 0x03; |
|
| 12 | + | export const OP_STORE: u32 = 0x23; |
|
| 13 | + | export const OP_BRANCH: u32 = 0x63; |
|
| 14 | + | export const OP_JALR: u32 = 0x67; |
|
| 15 | + | export const OP_JAL: u32 = 0x6F; |
|
| 16 | + | export const OP_OP: u32 = 0x33; |
|
| 17 | + | export const OP_IMM: u32 = 0x13; |
|
| 18 | + | export const OP_AUIPC: u32 = 0x17; |
|
| 19 | + | export const OP_LUI: u32 = 0x37; |
|
| 20 | + | export const OP_SYSTEM: u32 = 0x73; |
|
| 21 | + | export const OP_OP32: u32 = 0x3B; // RV64: 32-bit operations |
|
| 22 | + | export const OP_IMM32: u32 = 0x1B; // RV64: 32-bit immediate operations |
|
| 23 | 23 | ||
| 24 | 24 | ////////////////////// |
|
| 25 | 25 | // Funct3 Constants // |
|
| 26 | 26 | ////////////////////// |
|
| 27 | 27 | ||
| 28 | 28 | // Memory operations |
|
| 29 | 29 | ||
| 30 | - | pub const F3_BYTE: u32 = 0x0; // LB/SB |
|
| 31 | - | pub const F3_HALF: u32 = 0x1; // LH/SH |
|
| 32 | - | pub const F3_WORD: u32 = 0x2; // LW/SW |
|
| 33 | - | pub const F3_DWORD: u32 = 0x3; // LD/SD (RV64) |
|
| 34 | - | pub const F3_BYTE_U: u32 = 0x4; // LBU |
|
| 35 | - | pub const F3_HALF_U: u32 = 0x5; // LHU |
|
| 36 | - | pub const F3_WORD_U: u32 = 0x6; // LWU (RV64) |
|
| 30 | + | export const F3_BYTE: u32 = 0x0; // LB/SB |
|
| 31 | + | export const F3_HALF: u32 = 0x1; // LH/SH |
|
| 32 | + | export const F3_WORD: u32 = 0x2; // LW/SW |
|
| 33 | + | export const F3_DWORD: u32 = 0x3; // LD/SD (RV64) |
|
| 34 | + | export const F3_BYTE_U: u32 = 0x4; // LBU |
|
| 35 | + | export const F3_HALF_U: u32 = 0x5; // LHU |
|
| 36 | + | export const F3_WORD_U: u32 = 0x6; // LWU (RV64) |
|
| 37 | 37 | ||
| 38 | 38 | // ALU operations |
|
| 39 | 39 | ||
| 40 | - | pub const F3_ADD: u32 = 0x0; // ADD/SUB/ADDI |
|
| 41 | - | pub const F3_SLL: u32 = 0x1; // SLL/SLLI |
|
| 42 | - | pub const F3_SLT: u32 = 0x2; // SLT/SLTI |
|
| 43 | - | pub const F3_SLTU: u32 = 0x3; // SLTU/SLTIU |
|
| 44 | - | pub const F3_XOR: u32 = 0x4; // XOR/XORI |
|
| 45 | - | pub const F3_SRL: u32 = 0x5; // SRL/SRA/SRLI/SRAI |
|
| 46 | - | pub const F3_OR: u32 = 0x6; // OR/ORI |
|
| 47 | - | pub const F3_AND: u32 = 0x7; // AND/ANDI |
|
| 40 | + | export const F3_ADD: u32 = 0x0; // ADD/SUB/ADDI |
|
| 41 | + | export const F3_SLL: u32 = 0x1; // SLL/SLLI |
|
| 42 | + | export const F3_SLT: u32 = 0x2; // SLT/SLTI |
|
| 43 | + | export const F3_SLTU: u32 = 0x3; // SLTU/SLTIU |
|
| 44 | + | export const F3_XOR: u32 = 0x4; // XOR/XORI |
|
| 45 | + | export const F3_SRL: u32 = 0x5; // SRL/SRA/SRLI/SRAI |
|
| 46 | + | export const F3_OR: u32 = 0x6; // OR/ORI |
|
| 47 | + | export const F3_AND: u32 = 0x7; // AND/ANDI |
|
| 48 | 48 | ||
| 49 | 49 | // Branch operations |
|
| 50 | 50 | ||
| 51 | - | pub const F3_BEQ: u32 = 0x0; |
|
| 52 | - | pub const F3_BNE: u32 = 0x1; |
|
| 53 | - | pub const F3_BLT: u32 = 0x4; |
|
| 54 | - | pub const F3_BGE: u32 = 0x5; |
|
| 55 | - | pub const F3_BLTU: u32 = 0x6; |
|
| 56 | - | pub const F3_BGEU: u32 = 0x7; |
|
| 51 | + | export const F3_BEQ: u32 = 0x0; |
|
| 52 | + | export const F3_BNE: u32 = 0x1; |
|
| 53 | + | export const F3_BLT: u32 = 0x4; |
|
| 54 | + | export const F3_BGE: u32 = 0x5; |
|
| 55 | + | export const F3_BLTU: u32 = 0x6; |
|
| 56 | + | export const F3_BGEU: u32 = 0x7; |
|
| 57 | 57 | ||
| 58 | 58 | ////////////////////// |
|
| 59 | 59 | // Funct7 Constants // |
|
| 60 | 60 | ////////////////////// |
|
| 61 | 61 | ||
| 62 | - | pub const F7_NORMAL: u32 = 0b0000000; |
|
| 63 | - | pub const F7_SUB: u32 = 0b0100000; // Bit 5 set |
|
| 64 | - | pub const F7_SRA: u32 = 0b0100000; // Bit 5 set |
|
| 65 | - | pub const F7_MUL: u32 = 0b0000001; // Bit 0 set |
|
| 62 | + | export const F7_NORMAL: u32 = 0b0000000; |
|
| 63 | + | export const F7_SUB: u32 = 0b0100000; // Bit 5 set |
|
| 64 | + | export const F7_SRA: u32 = 0b0100000; // Bit 5 set |
|
| 65 | + | export const F7_MUL: u32 = 0b0000001; // Bit 0 set |
|
| 66 | 66 | ||
| 67 | 67 | ///////////////////////// |
|
| 68 | 68 | // Validation Helpers // |
|
| 69 | 69 | ///////////////////////// |
|
| 70 | 70 | ||
| 71 | 71 | /// Returns `true` if the value fits in a signed 12-bit immediate. |
|
| 72 | - | pub fn isSmallImm(value: i32) -> bool { |
|
| 72 | + | export fn isSmallImm(value: i32) -> bool { |
|
| 73 | 73 | return value >= super::MIN_IMM and value <= super::MAX_IMM; |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | 76 | /// Returns `true` if a 64-bit value fits in a 12-bit signed immediate. |
|
| 77 | - | pub fn isSmallImm64(value: i64) -> bool { |
|
| 77 | + | export fn isSmallImm64(value: i64) -> bool { |
|
| 78 | 78 | return value >= (super::MIN_IMM as i64) and value <= (super::MAX_IMM as i64); |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Returns `true` if the value is valid for branch immediates. |
|
| 82 | 82 | /// Branch immediates are 13-bit signed, even aligned. |
|
| 83 | - | pub fn isBranchImm(value: i32) -> bool { |
|
| 83 | + | export fn isBranchImm(value: i32) -> bool { |
|
| 84 | 84 | return value >= -(1 << 12) and value <= ((1 << 12) - 2) and (value & 1) == 0; |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Returns `true` if the value is valid for jump immediates (JAL). |
|
| 88 | 88 | /// Jump immediates are 21-bit signed, even aligned. |
|
| 89 | - | pub fn isJumpImm(value: i32) -> bool { |
|
| 89 | + | export fn isJumpImm(value: i32) -> bool { |
|
| 90 | 90 | return value >= -(1 << 20) and value <= ((1 << 20) - 2) and (value & 1) == 0; |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | ////////////////////// |
|
| 94 | 94 | // Format Encoders // |
| 173 | 173 | //////////////////////////// |
|
| 174 | 174 | // ALU Immediate (I-type) // |
|
| 175 | 175 | //////////////////////////// |
|
| 176 | 176 | ||
| 177 | 177 | /// Add immediate: `rd = rs1 + imm`. |
|
| 178 | - | pub fn addi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 178 | + | export fn addi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 179 | 179 | return encodeI(OP_IMM, rd, rs1, F3_ADD, imm); |
|
| 180 | 180 | } |
|
| 181 | 181 | ||
| 182 | 182 | /// Set less than immediate (signed): `rd = (rs1 < imm) ? 1 : 0`. |
|
| 183 | - | pub fn slti(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 183 | + | export fn slti(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 184 | 184 | return encodeI(OP_IMM, rd, rs1, F3_SLT, imm); |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// Set less than immediate unsigned: `rd = (rs1 < imm) ? 1 : 0`. |
|
| 188 | - | pub fn sltiu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 188 | + | export fn sltiu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 189 | 189 | return encodeI(OP_IMM, rd, rs1, F3_SLTU, imm); |
|
| 190 | 190 | } |
|
| 191 | 191 | ||
| 192 | 192 | /// XOR immediate: `rd = rs1 ^ imm`. |
|
| 193 | - | pub fn xori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 193 | + | export fn xori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 194 | 194 | return encodeI(OP_IMM, rd, rs1, F3_XOR, imm); |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// OR immediate: `rd = rs1 | imm`. |
|
| 198 | - | pub fn ori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 198 | + | export fn ori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 199 | 199 | return encodeI(OP_IMM, rd, rs1, F3_OR, imm); |
|
| 200 | 200 | } |
|
| 201 | 201 | ||
| 202 | 202 | /// AND immediate: `rd = rs1 & imm`. |
|
| 203 | - | pub fn andi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 203 | + | export fn andi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 204 | 204 | return encodeI(OP_IMM, rd, rs1, F3_AND, imm); |
|
| 205 | 205 | } |
|
| 206 | 206 | ||
| 207 | 207 | /// Shift left logical immediate: `rd = rs1 << shamt`. |
|
| 208 | - | pub fn slli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 208 | + | export fn slli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 209 | 209 | assert shamt >= 0 and shamt < 64; |
|
| 210 | 210 | return encodeI(OP_IMM, rd, rs1, F3_SLL, shamt & 0x3F); |
|
| 211 | 211 | } |
|
| 212 | 212 | ||
| 213 | 213 | /// Shift right logical immediate: `rd = rs1 >> shamt` (zero-extend). |
|
| 214 | - | pub fn srli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 214 | + | export fn srli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 215 | 215 | assert shamt >= 0 and shamt < 64; |
|
| 216 | 216 | return encodeI(OP_IMM, rd, rs1, F3_SRL, shamt & 0x3F); |
|
| 217 | 217 | } |
|
| 218 | 218 | ||
| 219 | 219 | /// Shift right arithmetic immediate: `rd = rs1 >> shamt` (sign-extend). |
|
| 220 | - | pub fn srai(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 220 | + | export fn srai(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 221 | 221 | assert shamt >= 0 and shamt < 64; |
|
| 222 | 222 | // SRAI has bit 10 set in immediate field (becomes bit 30 in instruction) |
|
| 223 | 223 | return encodeI(OP_IMM, rd, rs1, F3_SRL, (shamt & 0x3F) | 0b10000000000); |
|
| 224 | 224 | } |
|
| 225 | 225 | ||
| 226 | 226 | /////////////////////////// |
|
| 227 | 227 | // ALU Register (R-type) // |
|
| 228 | 228 | /////////////////////////// |
|
| 229 | 229 | ||
| 230 | 230 | /// Add: `rd = rs1 + rs2`. |
|
| 231 | - | pub fn add(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 231 | + | export fn add(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 232 | 232 | return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_NORMAL); |
|
| 233 | 233 | } |
|
| 234 | 234 | ||
| 235 | 235 | /// Subtract: `rd = rs1 - rs2`. |
|
| 236 | - | pub fn sub(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 236 | + | export fn sub(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 237 | 237 | return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_SUB); |
|
| 238 | 238 | } |
|
| 239 | 239 | ||
| 240 | 240 | /// Shift left logical: `rd = rs1 << rs2[5:0]`. |
|
| 241 | - | pub fn sll(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 241 | + | export fn sll(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 242 | 242 | return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_NORMAL); |
|
| 243 | 243 | } |
|
| 244 | 244 | ||
| 245 | 245 | /// Set less than (signed): `rd = (rs1 < rs2) ? 1 : 0`. |
|
| 246 | - | pub fn slt(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 246 | + | export fn slt(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 247 | 247 | return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_NORMAL); |
|
| 248 | 248 | } |
|
| 249 | 249 | ||
| 250 | 250 | /// Set less than unsigned: `rd = (rs1 < rs2) ? 1 : 0`. |
|
| 251 | - | pub fn sltu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 251 | + | export fn sltu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 252 | 252 | return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_NORMAL); |
|
| 253 | 253 | } |
|
| 254 | 254 | ||
| 255 | 255 | /// XOR: `rd = rs1 ^ rs2`. |
|
| 256 | - | pub fn xor(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 256 | + | export fn xor(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 257 | 257 | return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_NORMAL); |
|
| 258 | 258 | } |
|
| 259 | 259 | ||
| 260 | 260 | /// Shift right logical: `rd = rs1 >> rs2[5:0]` (zero-extend). |
|
| 261 | - | pub fn srl(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 261 | + | export fn srl(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 262 | 262 | return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_NORMAL); |
|
| 263 | 263 | } |
|
| 264 | 264 | ||
| 265 | 265 | /// Shift right arithmetic: `rd = rs1 >> rs2[5:0]` (sign-extend). |
|
| 266 | - | pub fn sra(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 266 | + | export fn sra(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 267 | 267 | return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_SRA); |
|
| 268 | 268 | } |
|
| 269 | 269 | ||
| 270 | 270 | /// OR: `rd = rs1 | rs2`. |
|
| 271 | - | pub fn or_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 271 | + | export fn or_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 272 | 272 | return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_NORMAL); |
|
| 273 | 273 | } |
|
| 274 | 274 | ||
| 275 | 275 | /// AND: `rd = rs1 & rs2`. |
|
| 276 | - | pub fn and_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 276 | + | export fn and_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 277 | 277 | return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_NORMAL); |
|
| 278 | 278 | } |
|
| 279 | 279 | ||
| 280 | 280 | ///////////////// |
|
| 281 | 281 | // M Extension // |
|
| 282 | 282 | ///////////////// |
|
| 283 | 283 | ||
| 284 | 284 | /// Multiply: `rd = (rs1 * rs2)[63:0]`. |
|
| 285 | - | pub fn mul(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 285 | + | export fn mul(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 286 | 286 | return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_MUL); |
|
| 287 | 287 | } |
|
| 288 | 288 | ||
| 289 | 289 | /// Multiply high (signed x signed): `rd = (rs1 * rs2)[127:64]`. |
|
| 290 | - | pub fn mulh(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 290 | + | export fn mulh(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 291 | 291 | return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_MUL); |
|
| 292 | 292 | } |
|
| 293 | 293 | ||
| 294 | 294 | /// Multiply high (signed x unsigned): `rd = (rs1 * rs2)[127:64]`. |
|
| 295 | - | pub fn mulhsu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 295 | + | export fn mulhsu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 296 | 296 | return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_MUL); |
|
| 297 | 297 | } |
|
| 298 | 298 | ||
| 299 | 299 | /// Multiply high (unsigned x unsigned): `rd = (rs1 * rs2)[127:64]`. |
|
| 300 | - | pub fn mulhu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 300 | + | export fn mulhu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 301 | 301 | return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_MUL); |
|
| 302 | 302 | } |
|
| 303 | 303 | ||
| 304 | 304 | /// Divide (signed): `rd = rs1 / rs2`. |
|
| 305 | - | pub fn div(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 305 | + | export fn div(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 306 | 306 | return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_MUL); |
|
| 307 | 307 | } |
|
| 308 | 308 | ||
| 309 | 309 | /// Divide (unsigned): `rd = rs1 / rs2`. |
|
| 310 | - | pub fn divu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 310 | + | export fn divu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 311 | 311 | return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_MUL); |
|
| 312 | 312 | } |
|
| 313 | 313 | ||
| 314 | 314 | /// Remainder (signed): `rd = rs1 % rs2`. |
|
| 315 | - | pub fn rem(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 315 | + | export fn rem(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 316 | 316 | return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_MUL); |
|
| 317 | 317 | } |
|
| 318 | 318 | ||
| 319 | 319 | /// Remainder (unsigned): `rd = rs1 % rs2`. |
|
| 320 | - | pub fn remu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 320 | + | export fn remu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 321 | 321 | return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_MUL); |
|
| 322 | 322 | } |
|
| 323 | 323 | ||
| 324 | 324 | ////////////////////////// |
|
| 325 | 325 | // RV64 Word Operations // |
|
| 326 | 326 | ////////////////////////// |
|
| 327 | 327 | ||
| 328 | 328 | /// Add immediate word (32-bit, sign-extended): `rd = sign_ext((rs1 + imm)[31:0])`. |
|
| 329 | - | pub fn addiw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 329 | + | export fn addiw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 330 | 330 | return encodeI(OP_IMM32, rd, rs1, F3_ADD, imm); |
|
| 331 | 331 | } |
|
| 332 | 332 | ||
| 333 | 333 | /// Shift left logical immediate word: `rd = sign_ext((rs1 << shamt)[31:0])`. |
|
| 334 | - | pub fn slliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 334 | + | export fn slliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 335 | 335 | assert shamt >= 0 and shamt < 32; |
|
| 336 | 336 | return encodeI(OP_IMM32, rd, rs1, F3_SLL, shamt & 0x1F); |
|
| 337 | 337 | } |
|
| 338 | 338 | ||
| 339 | 339 | /// Shift right logical immediate word: `rd = sign_ext((rs1[31:0] >> shamt))`. |
|
| 340 | - | pub fn srliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 340 | + | export fn srliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 341 | 341 | assert shamt >= 0 and shamt < 32; |
|
| 342 | 342 | return encodeI(OP_IMM32, rd, rs1, F3_SRL, shamt & 0x1F); |
|
| 343 | 343 | } |
|
| 344 | 344 | ||
| 345 | 345 | /// Shift right arithmetic immediate word: `rd = sign_ext((rs1[31:0] >> shamt))` (sign-extended). |
|
| 346 | - | pub fn sraiw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 346 | + | export fn sraiw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { |
|
| 347 | 347 | assert shamt >= 0 and shamt < 32; |
|
| 348 | 348 | return encodeI(OP_IMM32, rd, rs1, F3_SRL, (shamt & 0x1F) | 0b10000000000); |
|
| 349 | 349 | } |
|
| 350 | 350 | ||
| 351 | 351 | /// Add word: `rd = sign_ext((rs1 + rs2)[31:0])`. |
|
| 352 | - | pub fn addw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 352 | + | export fn addw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 353 | 353 | return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_NORMAL); |
|
| 354 | 354 | } |
|
| 355 | 355 | ||
| 356 | 356 | /// Subtract word: `rd = sign_ext((rs1 - rs2)[31:0])`. |
|
| 357 | - | pub fn subw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 357 | + | export fn subw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 358 | 358 | return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_SUB); |
|
| 359 | 359 | } |
|
| 360 | 360 | ||
| 361 | 361 | /// Shift left logical word: `rd = sign_ext((rs1 << rs2[4:0])[31:0])`. |
|
| 362 | - | pub fn sllw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 362 | + | export fn sllw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 363 | 363 | return encodeR(OP_OP32, rd, rs1, rs2, F3_SLL, F7_NORMAL); |
|
| 364 | 364 | } |
|
| 365 | 365 | ||
| 366 | 366 | /// Shift right logical word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))`. |
|
| 367 | - | pub fn srlw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 367 | + | export fn srlw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 368 | 368 | return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_NORMAL); |
|
| 369 | 369 | } |
|
| 370 | 370 | ||
| 371 | 371 | /// Shift right arithmetic word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))` (sign-extended). |
|
| 372 | - | pub fn sraw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 372 | + | export fn sraw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 373 | 373 | return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_SRA); |
|
| 374 | 374 | } |
|
| 375 | 375 | ||
| 376 | 376 | /// Multiply word: `rd = sign_ext((rs1 * rs2)[31:0])`. |
|
| 377 | - | pub fn mulw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 377 | + | export fn mulw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 378 | 378 | return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_MUL); |
|
| 379 | 379 | } |
|
| 380 | 380 | ||
| 381 | 381 | /// Divide word (signed): `rd = sign_ext(rs1[31:0] / rs2[31:0])`. |
|
| 382 | - | pub fn divw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 382 | + | export fn divw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 383 | 383 | return encodeR(OP_OP32, rd, rs1, rs2, F3_XOR, F7_MUL); |
|
| 384 | 384 | } |
|
| 385 | 385 | ||
| 386 | 386 | /// Divide word (unsigned): `rd = sign_ext(rs1[31:0] / rs2[31:0])`. |
|
| 387 | - | pub fn divuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 387 | + | export fn divuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 388 | 388 | return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_MUL); |
|
| 389 | 389 | } |
|
| 390 | 390 | ||
| 391 | 391 | /// Remainder word (signed): `rd = sign_ext(rs1[31:0] % rs2[31:0])`. |
|
| 392 | - | pub fn remw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 392 | + | export fn remw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 393 | 393 | return encodeR(OP_OP32, rd, rs1, rs2, F3_OR, F7_MUL); |
|
| 394 | 394 | } |
|
| 395 | 395 | ||
| 396 | 396 | /// Remainder word (unsigned): `rd = sign_ext(rs1[31:0] % rs2[31:0])`. |
|
| 397 | - | pub fn remuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 397 | + | export fn remuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { |
|
| 398 | 398 | return encodeR(OP_OP32, rd, rs1, rs2, F3_AND, F7_MUL); |
|
| 399 | 399 | } |
|
| 400 | 400 | ||
| 401 | 401 | /////////////////// |
|
| 402 | 402 | // Load (I-type) // |
|
| 403 | 403 | /////////////////// |
|
| 404 | 404 | ||
| 405 | 405 | /// Load byte (sign-extend): `rd = mem[rs1 + imm][7:0]`. |
|
| 406 | - | pub fn lb(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 406 | + | export fn lb(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 407 | 407 | return encodeI(OP_LOAD, rd, rs1, F3_BYTE, imm); |
|
| 408 | 408 | } |
|
| 409 | 409 | ||
| 410 | 410 | /// Load halfword (sign-extend): `rd = mem[rs1 + imm][15:0]`. |
|
| 411 | - | pub fn lh(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 411 | + | export fn lh(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 412 | 412 | return encodeI(OP_LOAD, rd, rs1, F3_HALF, imm); |
|
| 413 | 413 | } |
|
| 414 | 414 | ||
| 415 | 415 | /// Load word (sign-extend to 64-bit): `rd = sign_ext(mem[rs1 + imm][31:0])`. |
|
| 416 | - | pub fn lw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 416 | + | export fn lw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 417 | 417 | return encodeI(OP_LOAD, rd, rs1, F3_WORD, imm); |
|
| 418 | 418 | } |
|
| 419 | 419 | ||
| 420 | 420 | /// Load byte unsigned (zero-extend): `rd = mem[rs1 + imm][7:0]`. |
|
| 421 | - | pub fn lbu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 421 | + | export fn lbu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 422 | 422 | return encodeI(OP_LOAD, rd, rs1, F3_BYTE_U, imm); |
|
| 423 | 423 | } |
|
| 424 | 424 | ||
| 425 | 425 | /// Load halfword unsigned (zero-extend): `rd = mem[rs1 + imm][15:0]`. |
|
| 426 | - | pub fn lhu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 426 | + | export fn lhu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 427 | 427 | return encodeI(OP_LOAD, rd, rs1, F3_HALF_U, imm); |
|
| 428 | 428 | } |
|
| 429 | 429 | ||
| 430 | 430 | /// Load word unsigned (zero-extend to 64-bit): `rd = mem[rs1 + imm][31:0]`. |
|
| 431 | - | pub fn lwu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 431 | + | export fn lwu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 432 | 432 | return encodeI(OP_LOAD, rd, rs1, F3_WORD_U, imm); |
|
| 433 | 433 | } |
|
| 434 | 434 | ||
| 435 | 435 | /// Load doubleword: `rd = mem[rs1 + imm][63:0]`. |
|
| 436 | - | pub fn ld(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 436 | + | export fn ld(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 437 | 437 | return encodeI(OP_LOAD, rd, rs1, F3_DWORD, imm); |
|
| 438 | 438 | } |
|
| 439 | 439 | ||
| 440 | 440 | //////////////////// |
|
| 441 | 441 | // Store (S-type) // |
|
| 442 | 442 | //////////////////// |
|
| 443 | 443 | ||
| 444 | 444 | /// Store byte: `mem[rs1 + imm] = rs2[7:0]`. |
|
| 445 | - | pub fn sb(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 445 | + | export fn sb(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 446 | 446 | return encodeS(OP_STORE, rs1, rs2, F3_BYTE, imm); |
|
| 447 | 447 | } |
|
| 448 | 448 | ||
| 449 | 449 | /// Store halfword: `mem[rs1 + imm] = rs2[15:0]`. |
|
| 450 | - | pub fn sh(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 450 | + | export fn sh(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 451 | 451 | return encodeS(OP_STORE, rs1, rs2, F3_HALF, imm); |
|
| 452 | 452 | } |
|
| 453 | 453 | ||
| 454 | 454 | /// Store word: `mem[rs1 + imm] = rs2[31:0]`. |
|
| 455 | - | pub fn sw(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 455 | + | export fn sw(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 456 | 456 | return encodeS(OP_STORE, rs1, rs2, F3_WORD, imm); |
|
| 457 | 457 | } |
|
| 458 | 458 | ||
| 459 | 459 | /// Store doubleword: `mem[rs1 + imm] = rs2[63:0]`. |
|
| 460 | - | pub fn sd(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 460 | + | export fn sd(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 461 | 461 | return encodeS(OP_STORE, rs1, rs2, F3_DWORD, imm); |
|
| 462 | 462 | } |
|
| 463 | 463 | ||
| 464 | 464 | ///////////////////// |
|
| 465 | 465 | // Branch (B-type) // |
|
| 466 | 466 | ///////////////////// |
|
| 467 | 467 | ||
| 468 | 468 | /// Branch if equal: `if (rs1 == rs2) pc += imm`. |
|
| 469 | - | pub fn beq(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 469 | + | export fn beq(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 470 | 470 | return encodeB(OP_BRANCH, rs1, rs2, F3_BEQ, imm); |
|
| 471 | 471 | } |
|
| 472 | 472 | ||
| 473 | 473 | /// Branch if not equal: `if (rs1 != rs2) pc += imm`. |
|
| 474 | - | pub fn bne(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 474 | + | export fn bne(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 475 | 475 | return encodeB(OP_BRANCH, rs1, rs2, F3_BNE, imm); |
|
| 476 | 476 | } |
|
| 477 | 477 | ||
| 478 | 478 | /// Branch if less than (signed): `if (rs1 < rs2) pc += imm`. |
|
| 479 | - | pub fn blt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 479 | + | export fn blt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 480 | 480 | return encodeB(OP_BRANCH, rs1, rs2, F3_BLT, imm); |
|
| 481 | 481 | } |
|
| 482 | 482 | ||
| 483 | 483 | /// Branch if greater or equal (signed): `if (rs1 >= rs2) pc += imm`. |
|
| 484 | - | pub fn bge(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 484 | + | export fn bge(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 485 | 485 | return encodeB(OP_BRANCH, rs1, rs2, F3_BGE, imm); |
|
| 486 | 486 | } |
|
| 487 | 487 | ||
| 488 | 488 | /// Branch if less than unsigned: `if (rs1 < rs2) pc += imm`. |
|
| 489 | - | pub fn bltu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 489 | + | export fn bltu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 490 | 490 | return encodeB(OP_BRANCH, rs1, rs2, F3_BLTU, imm); |
|
| 491 | 491 | } |
|
| 492 | 492 | ||
| 493 | 493 | /// Branch if greater or equal unsigned: `if (rs1 >= rs2) pc += imm`. |
|
| 494 | - | pub fn bgeu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 494 | + | export fn bgeu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 495 | 495 | return encodeB(OP_BRANCH, rs1, rs2, F3_BGEU, imm); |
|
| 496 | 496 | } |
|
| 497 | 497 | ||
| 498 | 498 | ////////// |
|
| 499 | 499 | // Jump // |
|
| 500 | 500 | ////////// |
|
| 501 | 501 | ||
| 502 | 502 | /// Jump and link: `rd = pc + 4; pc += imm`. |
|
| 503 | - | pub fn jal(rd: gen::Reg, imm: i32) -> u32 { |
|
| 503 | + | export fn jal(rd: gen::Reg, imm: i32) -> u32 { |
|
| 504 | 504 | return encodeJ(OP_JAL, rd, imm); |
|
| 505 | 505 | } |
|
| 506 | 506 | ||
| 507 | 507 | /// Jump and link register: `rd = pc + 4; pc = rs1 + imm`. |
|
| 508 | - | pub fn jalr(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 508 | + | export fn jalr(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { |
|
| 509 | 509 | return encodeI(OP_JALR, rd, rs1, 0, imm); |
|
| 510 | 510 | } |
|
| 511 | 511 | ||
| 512 | 512 | ///////////////////// |
|
| 513 | 513 | // Upper Immediate // |
|
| 514 | 514 | ///////////////////// |
|
| 515 | 515 | ||
| 516 | 516 | /// Load upper immediate: `rd = imm << 12`. |
|
| 517 | - | pub fn lui(rd: gen::Reg, imm: i32) -> u32 { |
|
| 517 | + | export fn lui(rd: gen::Reg, imm: i32) -> u32 { |
|
| 518 | 518 | return encodeU(OP_LUI, rd, imm); |
|
| 519 | 519 | } |
|
| 520 | 520 | ||
| 521 | 521 | /// Add upper immediate to PC: `rd = pc + (imm << 12)`. |
|
| 522 | - | pub fn auipc(rd: gen::Reg, imm: i32) -> u32 { |
|
| 522 | + | export fn auipc(rd: gen::Reg, imm: i32) -> u32 { |
|
| 523 | 523 | return encodeU(OP_AUIPC, rd, imm); |
|
| 524 | 524 | } |
|
| 525 | 525 | ||
| 526 | 526 | //////////// |
|
| 527 | 527 | // System // |
|
| 528 | 528 | //////////// |
|
| 529 | 529 | ||
| 530 | 530 | /// Environment call (system call). |
|
| 531 | - | pub fn ecall() -> u32 { |
|
| 531 | + | export fn ecall() -> u32 { |
|
| 532 | 532 | return encodeI(OP_SYSTEM, super::ZERO, super::ZERO, 0, 0); |
|
| 533 | 533 | } |
|
| 534 | 534 | ||
| 535 | 535 | /// Environment break (debugger breakpoint). |
|
| 536 | - | pub fn ebreak() -> u32 { |
|
| 536 | + | export fn ebreak() -> u32 { |
|
| 537 | 537 | return encodeI(OP_SYSTEM, super::ZERO, super::ZERO, 0, 1); |
|
| 538 | 538 | } |
|
| 539 | 539 | ||
| 540 | 540 | ///////////////////////// |
|
| 541 | 541 | // Pseudo-instructions // |
|
| 542 | 542 | ///////////////////////// |
|
| 543 | 543 | ||
| 544 | 544 | /// No operation: `addi zero, zero, 0`. |
|
| 545 | - | pub fn nop() -> u32 { |
|
| 545 | + | export fn nop() -> u32 { |
|
| 546 | 546 | return addi(super::ZERO, super::ZERO, 0); |
|
| 547 | 547 | } |
|
| 548 | 548 | ||
| 549 | 549 | /// Move: `rd = rs` (`addi rd, rs, 0`). |
|
| 550 | - | pub fn mv(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 550 | + | export fn mv(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 551 | 551 | return addi(rd, rs, 0); |
|
| 552 | 552 | } |
|
| 553 | 553 | ||
| 554 | 554 | /// Bitwise NOT: `rd = ~rs` (`xori rd, rs, -1`). |
|
| 555 | - | pub fn not_(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 555 | + | export fn not_(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 556 | 556 | return xori(rd, rs, -1); |
|
| 557 | 557 | } |
|
| 558 | 558 | ||
| 559 | 559 | /// Negate: `rd = -rs` (`sub rd, zero, rs`). |
|
| 560 | - | pub fn neg(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 560 | + | export fn neg(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 561 | 561 | return sub(rd, super::ZERO, rs); |
|
| 562 | 562 | } |
|
| 563 | 563 | ||
| 564 | 564 | /// Return: `jalr zero, ra, 0`. |
|
| 565 | - | pub fn ret() -> u32 { |
|
| 565 | + | export fn ret() -> u32 { |
|
| 566 | 566 | return jalr(super::ZERO, super::RA, 0); |
|
| 567 | 567 | } |
|
| 568 | 568 | ||
| 569 | 569 | /// Jump (unconditional): `jal zero, imm`. |
|
| 570 | - | pub fn j(imm: i32) -> u32 { |
|
| 570 | + | export fn j(imm: i32) -> u32 { |
|
| 571 | 571 | return jal(super::ZERO, imm); |
|
| 572 | 572 | } |
|
| 573 | 573 | ||
| 574 | 574 | /// Branch if less than or equal (signed): `if (rs1 <= rs2) pc += imm`. |
|
| 575 | 575 | /// Implemented as `bge rs2, rs1, imm` (swap operands). |
|
| 576 | - | pub fn ble(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 576 | + | export fn ble(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 577 | 577 | return bge(rs2, rs1, imm); |
|
| 578 | 578 | } |
|
| 579 | 579 | ||
| 580 | 580 | /// Branch if greater than (signed): `if (rs1 > rs2) pc += imm`. |
|
| 581 | 581 | /// Implemented as `blt rs2, rs1, imm` (swap operands). |
|
| 582 | - | pub fn bgt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 582 | + | export fn bgt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { |
|
| 583 | 583 | return blt(rs2, rs1, imm); |
|
| 584 | 584 | } |
|
| 585 | 585 | ||
| 586 | 586 | /// Set if equal to zero: `rd = (rs == 0) ? 1 : 0`. |
|
| 587 | 587 | /// Implemented as `sltiu rd, rs, 1`. |
|
| 588 | - | pub fn seqz(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 588 | + | export fn seqz(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 589 | 589 | return sltiu(rd, rs, 1); |
|
| 590 | 590 | } |
|
| 591 | 591 | ||
| 592 | 592 | /// Set if not equal to zero: `rd = (rs != 0) ? 1 : 0`. |
|
| 593 | 593 | /// Implemented as `sltu rd, zero, rs`. |
|
| 594 | - | pub fn snez(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 594 | + | export fn snez(rd: gen::Reg, rs: gen::Reg) -> u32 { |
|
| 595 | 595 | return sltu(rd, super::ZERO, rs); |
|
| 596 | 596 | } |
|
| 597 | 597 | ||
| 598 | 598 | /// Branch if equal to zero: `if (rs == 0) pc += imm`. |
|
| 599 | - | pub fn beqz(rs: gen::Reg, imm: i32) -> u32 { |
|
| 599 | + | export fn beqz(rs: gen::Reg, imm: i32) -> u32 { |
|
| 600 | 600 | return beq(rs, super::ZERO, imm); |
|
| 601 | 601 | } |
|
| 602 | 602 | ||
| 603 | 603 | /// Branch if not equal to zero: `if (rs != 0) pc += imm`. |
|
| 604 | - | pub fn bnez(rs: gen::Reg, imm: i32) -> u32 { |
|
| 604 | + | export fn bnez(rs: gen::Reg, imm: i32) -> u32 { |
|
| 605 | 605 | return bne(rs, super::ZERO, imm); |
|
| 606 | 606 | } |
|
| 607 | 607 | ||
| 608 | 608 | /// Call: `jal ra, imm`. |
|
| 609 | - | pub fn call(imm: i32) -> u32 { |
|
| 609 | + | export fn call(imm: i32) -> u32 { |
|
| 610 | 610 | return jal(super::RA, imm); |
|
| 611 | 611 | } |
lib/std/arch/rv64/isel.rad
+3 -3
| 71 | 71 | union ShiftOp { Sll, Srl, Sra } |
|
| 72 | 72 | /// Compare operation. |
|
| 73 | 73 | union CmpOp { Slt, Ult } |
|
| 74 | 74 | ||
| 75 | 75 | /// Selector errors. |
|
| 76 | - | pub union SelectorError { |
|
| 76 | + | export union SelectorError { |
|
| 77 | 77 | Internal, |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// A pending spill store to be flushed after instruction selection. |
|
| 81 | 81 | record PendingSpill { |
| 88 | 88 | //////////////////// |
|
| 89 | 89 | // Selector State // |
|
| 90 | 90 | //////////////////// |
|
| 91 | 91 | ||
| 92 | 92 | /// Instruction selector state. |
|
| 93 | - | pub record Selector { |
|
| 93 | + | export record Selector { |
|
| 94 | 94 | /// Emitter for outputting instructions. |
|
| 95 | 95 | e: *mut emit::Emitter, |
|
| 96 | 96 | /// Register allocation result. |
|
| 97 | 97 | ralloc: *regalloc::AllocResult, |
|
| 98 | 98 | /// Hash-indexed data symbol map. |
| 302 | 302 | } |
|
| 303 | 303 | return ReserveInfo { size: offset, isDynamic }; |
|
| 304 | 304 | } |
|
| 305 | 305 | ||
| 306 | 306 | /// Select instructions for a function. |
|
| 307 | - | pub fn selectFn( |
|
| 307 | + | export fn selectFn( |
|
| 308 | 308 | e: *mut emit::Emitter, |
|
| 309 | 309 | dataSymMap: *data::DataSymMap, |
|
| 310 | 310 | ralloc: *regalloc::AllocResult, |
|
| 311 | 311 | func: *il::Fn |
|
| 312 | 312 | ) { |
lib/std/arch/rv64/printer.rad
+2 -2
| 162 | 162 | writeMnem(out, m); |
|
| 163 | 163 | writeDelim(out, &[regNameR(rs1), formatI32(a, imm)]); |
|
| 164 | 164 | } |
|
| 165 | 165 | ||
| 166 | 166 | /// Print a single instruction to output buffer. |
|
| 167 | - | pub fn printInstr(out: *mut sexpr::Output, a: *mut alloc::Arena, instr: u32) { |
|
| 167 | + | export fn printInstr(out: *mut sexpr::Output, a: *mut alloc::Arena, instr: u32) { |
|
| 168 | 168 | let decoded = decode::decode(instr); |
|
| 169 | 169 | ||
| 170 | 170 | match decoded { |
|
| 171 | 171 | case decode::Instr::Lui { rd, imm } => fmtRI(out, a, "lui", rd, imm), |
|
| 172 | 172 | case decode::Instr::Auipc { rd, imm } => fmtRI(out, a, "auipc", rd, imm), |
| 304 | 304 | }, |
|
| 305 | 305 | } |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | 308 | /// Print code with labels to the given output. |
|
| 309 | - | pub fn printCodeTo(out: *mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: *mut alloc::Arena) { |
|
| 309 | + | export fn printCodeTo(out: *mut sexpr::Output, pkgName: *[u8], code: *[u32], funcs: *[types::FuncAddr], arena: *mut alloc::Arena) { |
|
| 310 | 310 | // Package header. |
|
| 311 | 311 | write(out, "# package `"); |
|
| 312 | 312 | write(out, pkgName); |
|
| 313 | 313 | write(out, "`\n\n"); |
|
| 314 | 314 |
lib/std/collections.rad
+1 -1
| 1 | 1 | //! Collection types. |
|
| 2 | - | pub mod dict; |
|
| 2 | + | export mod dict; |
lib/std/collections/dict.rad
+6 -6
| 4 | 4 | //! of two and is provided by the caller via arena-allocated storage. |
|
| 5 | 5 | ||
| 6 | 6 | use std::mem; |
|
| 7 | 7 | ||
| 8 | 8 | /// Hash map entry. |
|
| 9 | - | pub record Entry { |
|
| 9 | + | export record Entry { |
|
| 10 | 10 | /// Key. |
|
| 11 | 11 | key: *[u8], |
|
| 12 | 12 | /// Associated value. |
|
| 13 | 13 | value: i32, |
|
| 14 | 14 | } |
|
| 15 | 15 | ||
| 16 | 16 | /// Open-addressed hash map with caller-provided storage. |
|
| 17 | - | pub record Dict { |
|
| 17 | + | export record Dict { |
|
| 18 | 18 | /// Hash table entries. |
|
| 19 | 19 | entries: *mut [Entry], |
|
| 20 | 20 | /// Number of occupied entries. |
|
| 21 | 21 | count: u32, |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// Create a dict backed by the given entry storage. |
|
| 25 | 25 | /// The storage length must be a power of two. |
|
| 26 | - | pub fn init(entries: *mut [Entry]) -> Dict { |
|
| 26 | + | export fn init(entries: *mut [Entry]) -> Dict { |
|
| 27 | 27 | for i in 0..entries.len { |
|
| 28 | 28 | entries[i] = Entry { key: &[], value: 0 }; |
|
| 29 | 29 | } |
|
| 30 | 30 | return Dict { entries, count: 0 }; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | 33 | /// Insert or update a key-value pair. Panics if the table exceeds 50% load. |
|
| 34 | - | pub fn insert(m: *mut Dict, key: *[u8], value: i32) { |
|
| 34 | + | export fn insert(m: *mut Dict, key: *[u8], value: i32) { |
|
| 35 | 35 | let mask = m.entries.len - 1; |
|
| 36 | 36 | let mut idx = hash(key) & mask; |
|
| 37 | 37 | ||
| 38 | 38 | loop { |
|
| 39 | 39 | let entry = m.entries[idx]; |
| 50 | 50 | idx = (idx + 1) & mask; |
|
| 51 | 51 | } |
|
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Look up a value by key. Returns `nil` if not found. |
|
| 55 | - | pub fn get(m: *Dict, key: *[u8]) -> ?i32 { |
|
| 55 | + | export fn get(m: *Dict, key: *[u8]) -> ?i32 { |
|
| 56 | 56 | let mask = m.entries.len - 1; |
|
| 57 | 57 | let mut idx = hash(key) & mask; |
|
| 58 | 58 | ||
| 59 | 59 | loop { |
|
| 60 | 60 | let entry = m.entries[idx]; |
| 67 | 67 | idx = (idx + 1) & mask; |
|
| 68 | 68 | } |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// DJB2 hash function. |
|
| 72 | - | pub fn hash(str: *[u8]) -> u32 { |
|
| 72 | + | export fn hash(str: *[u8]) -> u32 { |
|
| 73 | 73 | let mut h: u32 = 5381; |
|
| 74 | 74 | for b in str { |
|
| 75 | 75 | h = ((h << 5) + h) + b as u32; |
|
| 76 | 76 | } |
|
| 77 | 77 | return h; |
lib/std/fmt.rad
+14 -14
| 1 | 1 | //! Formatting utilities for converting values to strings. |
|
| 2 | 2 | use super::mem; |
|
| 3 | 3 | ||
| 4 | 4 | /// Maximum string length for a formatted u32 (eg. "4294967295"). |
|
| 5 | - | pub const U32_STR_LEN: u32 = 10; |
|
| 5 | + | export const U32_STR_LEN: u32 = 10; |
|
| 6 | 6 | /// Maximum string length for a formatted i32 (eg. "-2147483648"). |
|
| 7 | - | pub const I32_STR_LEN: u32 = U32_STR_LEN + 1; |
|
| 7 | + | export const I32_STR_LEN: u32 = U32_STR_LEN + 1; |
|
| 8 | 8 | /// Maximum string length for a formatted u64 (eg. "18446744073709551615"). |
|
| 9 | - | pub const U64_STR_LEN: u32 = 20; |
|
| 9 | + | export const U64_STR_LEN: u32 = 20; |
|
| 10 | 10 | /// Maximum string length for a formatted i64 (eg. "-9223372036854775808"). |
|
| 11 | - | pub const I64_STR_LEN: u32 = 20; |
|
| 11 | + | export const I64_STR_LEN: u32 = 20; |
|
| 12 | 12 | /// Maximum string length for a formatted bool (eg. "false"). |
|
| 13 | - | pub const BOOL_STR_LEN: u32 = 5; |
|
| 13 | + | export const BOOL_STR_LEN: u32 = 5; |
|
| 14 | 14 | ||
| 15 | 15 | /// Format a u32 by writing it to the provided buffer. |
|
| 16 | - | pub fn formatU32(val: u32, buffer: *mut [u8]) -> *[u8] { |
|
| 16 | + | export fn formatU32(val: u32, buffer: *mut [u8]) -> *[u8] { |
|
| 17 | 17 | assert buffer.len >= U32_STR_LEN; |
|
| 18 | 18 | ||
| 19 | 19 | let mut x: u32 = val; |
|
| 20 | 20 | let mut i: u32 = buffer.len; |
|
| 21 | 21 |
| 35 | 35 | // the end of the buffer. |
|
| 36 | 36 | return &buffer[i..]; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Format a i32 by writing it to the provided buffer. |
|
| 40 | - | pub fn formatI32(val: i32, buffer: *mut [u8]) -> *[u8] { |
|
| 40 | + | export fn formatI32(val: i32, buffer: *mut [u8]) -> *[u8] { |
|
| 41 | 41 | assert buffer.len >= I32_STR_LEN; |
|
| 42 | 42 | ||
| 43 | 43 | let neg: bool = val < 0; |
|
| 44 | 44 | let mut x: u32 = -val as u32 if neg else val as u32; |
|
| 45 | 45 | let mut i: u32 = buffer.len; |
| 62 | 62 | } |
|
| 63 | 63 | return &buffer[i..]; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Format a u64 by writing it to the provided buffer. |
|
| 67 | - | pub fn formatU64(val: u64, buffer: *mut [u8]) -> *[u8] { |
|
| 67 | + | export fn formatU64(val: u64, buffer: *mut [u8]) -> *[u8] { |
|
| 68 | 68 | assert buffer.len >= U64_STR_LEN; |
|
| 69 | 69 | ||
| 70 | 70 | let mut x: u64 = val; |
|
| 71 | 71 | let mut i: u32 = buffer.len; |
|
| 72 | 72 |
| 82 | 82 | } |
|
| 83 | 83 | return &buffer[i..]; |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// Format a i64 by writing it to the provided buffer. |
|
| 87 | - | pub fn formatI64(val: i64, buffer: *mut [u8]) -> *[u8] { |
|
| 87 | + | export fn formatI64(val: i64, buffer: *mut [u8]) -> *[u8] { |
|
| 88 | 88 | assert buffer.len >= I64_STR_LEN; |
|
| 89 | 89 | ||
| 90 | 90 | let neg: bool = val < 0; |
|
| 91 | 91 | let mut x: u64 = -val as u64 if neg else val as u64; |
|
| 92 | 92 | let mut i: u32 = buffer.len; |
| 106 | 106 | } |
|
| 107 | 107 | return &buffer[i..]; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// Format a i8 by writing it to the provided buffer. |
|
| 111 | - | pub fn formatI8(val: i8, buffer: *mut [u8]) -> *[u8] { |
|
| 111 | + | export fn formatI8(val: i8, buffer: *mut [u8]) -> *[u8] { |
|
| 112 | 112 | return formatI32(val as i32, buffer); |
|
| 113 | 113 | } |
|
| 114 | 114 | ||
| 115 | 115 | /// Format a i16 by writing it to the provided buffer. |
|
| 116 | - | pub fn formatI16(val: i16, buffer: *mut [u8]) -> *[u8] { |
|
| 116 | + | export fn formatI16(val: i16, buffer: *mut [u8]) -> *[u8] { |
|
| 117 | 117 | return formatI32(val as i32, buffer); |
|
| 118 | 118 | } |
|
| 119 | 119 | ||
| 120 | 120 | /// Format a u8 by writing it to the provided buffer. |
|
| 121 | - | pub fn formatU8(val: u8, buffer: *mut [u8]) -> *[u8] { |
|
| 121 | + | export fn formatU8(val: u8, buffer: *mut [u8]) -> *[u8] { |
|
| 122 | 122 | return formatU32(val as u32, buffer); |
|
| 123 | 123 | } |
|
| 124 | 124 | ||
| 125 | 125 | /// Format a u16 by writing it to the provided buffer. |
|
| 126 | - | pub fn formatU16(val: u16, buffer: *mut [u8]) -> *[u8] { |
|
| 126 | + | export fn formatU16(val: u16, buffer: *mut [u8]) -> *[u8] { |
|
| 127 | 127 | return formatU32(val as u32, buffer); |
|
| 128 | 128 | } |
|
| 129 | 129 | ||
| 130 | 130 | /// Format a bool by writing it to the provided buffer. |
|
| 131 | - | pub fn formatBool(val: bool, buffer: *mut [u8]) -> *[u8] { |
|
| 131 | + | export fn formatBool(val: bool, buffer: *mut [u8]) -> *[u8] { |
|
| 132 | 132 | if val { |
|
| 133 | 133 | try! mem::copy(buffer, "true"); |
|
| 134 | 134 | return &buffer[..4]; |
|
| 135 | 135 | } else { |
|
| 136 | 136 | try! mem::copy(buffer, "false"); |
lib/std/intrinsics.rad
+2 -2
| 3 | 3 | /// Environment call. |
|
| 4 | 4 | /// |
|
| 5 | 5 | /// Issues a system call with the given number and arguments. |
|
| 6 | 6 | /// Arguments and the return value are `i64` to support both 32-bit |
|
| 7 | 7 | /// emulator addresses and 64-bit native (AMD64) pointers. |
|
| 8 | - | @intrinsic pub fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 8 | + | @intrinsic export fn ecall(number: u32, arg1: i64, arg2: i64, arg3: i64, arg4: i64) -> i64; |
|
| 9 | 9 | ||
| 10 | 10 | /// Break out of program. |
|
| 11 | - | @intrinsic pub fn ebreak(); |
|
| 11 | + | @intrinsic export fn ebreak(); |
lib/std/io.rad
+8 -8
| 1 | 1 | //! Input/output utilities. |
|
| 2 | 2 | use std::fmt; |
|
| 3 | 3 | use std::intrinsics; |
|
| 4 | 4 | ||
| 5 | - | pub fn print(str: *[u8]) { |
|
| 5 | + | export fn print(str: *[u8]) { |
|
| 6 | 6 | intrinsics::ecall(64, 1, str.ptr as i64, str.len as i64, 0); |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | - | pub fn printError(str: *[u8]) { |
|
| 9 | + | export fn printError(str: *[u8]) { |
|
| 10 | 10 | intrinsics::ecall(64, 2, str.ptr as i64, str.len as i64, 0); |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | pub fn printLn(str: *[u8]) { |
|
| 13 | + | export fn printLn(str: *[u8]) { |
|
| 14 | 14 | print(str); |
|
| 15 | 15 | print("\n"); |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | - | pub fn printI32(val: i32) { |
|
| 18 | + | export fn printI32(val: i32) { |
|
| 19 | 19 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 20 | 20 | let result: *[u8] = fmt::formatI32(val, &mut buffer[..]); |
|
| 21 | 21 | print(result); |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | - | pub fn printU32(val: u32) { |
|
| 24 | + | export fn printU32(val: u32) { |
|
| 25 | 25 | let mut buffer: [u8; 10] = [0; 10]; |
|
| 26 | 26 | let result: *[u8] = fmt::formatU32(val, &mut buffer[..]); |
|
| 27 | 27 | print(result); |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | - | pub fn printBool(val: bool) { |
|
| 30 | + | export fn printBool(val: bool) { |
|
| 31 | 31 | let mut buffer: [u8; 5] = [0; 5]; |
|
| 32 | 32 | let result: *[u8] = fmt::formatBool(val, &mut buffer[..]); |
|
| 33 | 33 | print(result); |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | - | pub fn read(buf: *mut [u8]) -> u32 { |
|
| 36 | + | export fn read(buf: *mut [u8]) -> u32 { |
|
| 37 | 37 | return intrinsics::ecall(63, 0, buf.ptr as i64, buf.len as i64, 0) as u32; |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | - | pub fn readToEnd(buf: *mut [u8]) -> *[u8] { |
|
| 40 | + | export fn readToEnd(buf: *mut [u8]) -> *[u8] { |
|
| 41 | 41 | let mut total: u32 = 0; |
|
| 42 | 42 | ||
| 43 | 43 | while total < buf.len { |
|
| 44 | 44 | let chunk: *mut [u8] = &mut buf[total..]; |
|
| 45 | 45 | let n: u32 = read(chunk); |
lib/std/lang.rad
+12 -12
| 1 | 1 | //! Radiance language implementation. |
|
| 2 | - | pub mod alloc; |
|
| 3 | - | pub mod sexpr; |
|
| 4 | - | pub mod strings; |
|
| 5 | - | pub mod scanner; |
|
| 6 | - | pub mod ast; |
|
| 7 | - | pub mod parser; |
|
| 8 | - | pub mod module; |
|
| 9 | - | pub mod resolver; |
|
| 10 | - | pub mod package; |
|
| 11 | - | pub mod il; |
|
| 12 | - | pub mod lower; |
|
| 13 | - | pub mod gen; |
|
| 2 | + | export mod alloc; |
|
| 3 | + | export mod sexpr; |
|
| 4 | + | export mod strings; |
|
| 5 | + | export mod scanner; |
|
| 6 | + | export mod ast; |
|
| 7 | + | export mod parser; |
|
| 8 | + | export mod module; |
|
| 9 | + | export mod resolver; |
|
| 10 | + | export mod package; |
|
| 11 | + | export mod il; |
|
| 12 | + | export mod lower; |
|
| 13 | + | export mod gen; |
lib/std/lang/alloc.rad
+14 -14
| 7 | 7 | @test mod tests; |
|
| 8 | 8 | ||
| 9 | 9 | use std::mem; |
|
| 10 | 10 | ||
| 11 | 11 | /// Error thrown by allocator. |
|
| 12 | - | pub union AllocError { |
|
| 12 | + | export union AllocError { |
|
| 13 | 13 | /// Allocator is out of memory. |
|
| 14 | 14 | OutOfMemory, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Bump allocator backed by a byte slice. |
|
| 18 | 18 | /// |
|
| 19 | 19 | /// Allocations are made by advancing an offset pointer. Individual allocations |
|
| 20 | 20 | /// cannot be freed; instead, the entire arena is reset at once via [`reset`]. |
|
| 21 | - | pub record Arena { |
|
| 21 | + | export record Arena { |
|
| 22 | 22 | /// Backing storage. |
|
| 23 | 23 | data: *mut [u8], |
|
| 24 | 24 | /// Current allocation offset in bytes. |
|
| 25 | 25 | offset: u32, |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Create a new arena backed by the given byte slice. |
|
| 29 | - | pub fn new(data: *mut [u8]) -> Arena { |
|
| 29 | + | export fn new(data: *mut [u8]) -> Arena { |
|
| 30 | 30 | return Arena { data, offset: 0 }; |
|
| 31 | 31 | } |
|
| 32 | 32 | ||
| 33 | 33 | /// Allocate `size` bytes with the given alignment. |
|
| 34 | 34 | /// |
|
| 35 | 35 | /// Returns an opaque pointer to the allocated memory. Throws `AllocError` if |
|
| 36 | 36 | /// the arena is exhausted. The caller is responsible for casting to the |
|
| 37 | 37 | /// appropriate type and initializing the memory. |
|
| 38 | - | pub fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 38 | + | export fn alloc(arena: *mut Arena, size: u32, alignment: u32) -> *mut opaque throws (AllocError) { |
|
| 39 | 39 | assert alignment > 0; |
|
| 40 | 40 | assert size > 0; |
|
| 41 | 41 | ||
| 42 | 42 | let aligned = mem::alignUp(arena.offset, alignment); |
|
| 43 | 43 | let newOffset = aligned + size; |
| 52 | 52 | } |
|
| 53 | 53 | ||
| 54 | 54 | /// Reset the arena, allowing all memory to be reused. |
|
| 55 | 55 | /// |
|
| 56 | 56 | /// Does not zero the memory. |
|
| 57 | - | pub fn reset(arena: *mut Arena) { |
|
| 57 | + | export fn reset(arena: *mut Arena) { |
|
| 58 | 58 | arena.offset = 0; |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Save the current arena state for later restoration. |
|
| 62 | - | pub fn save(arena: *Arena) -> u32 { |
|
| 62 | + | export fn save(arena: *Arena) -> u32 { |
|
| 63 | 63 | return arena.offset; |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Restore the arena to a previously saved state, reclaiming all |
|
| 67 | 67 | /// allocations made since that point. |
|
| 68 | - | pub fn restore(arena: *mut Arena, savedOffset: u32) { |
|
| 68 | + | export fn restore(arena: *mut Arena, savedOffset: u32) { |
|
| 69 | 69 | arena.offset = savedOffset; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Returns the number of bytes currently allocated. |
|
| 73 | - | pub fn used(arena: *Arena) -> u32 { |
|
| 73 | + | export fn used(arena: *Arena) -> u32 { |
|
| 74 | 74 | return arena.offset; |
|
| 75 | 75 | } |
|
| 76 | 76 | ||
| 77 | 77 | /// Returns the number of bytes remaining in the arena. |
|
| 78 | - | pub fn remaining(arena: *Arena) -> u32 { |
|
| 78 | + | export fn remaining(arena: *Arena) -> u32 { |
|
| 79 | 79 | return arena.data.len as u32 - arena.offset; |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Returns the remaining buffer as a mutable slice. |
|
| 83 | - | pub fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
|
| 83 | + | export fn remainingBuf(arena: *mut Arena) -> *mut [u8] { |
|
| 84 | 84 | return &mut arena.data[arena.offset..]; |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Commits `size` bytes of allocation, advancing the offset. |
|
| 88 | 88 | /// Use after writing to the buffer returned by [`remainingBuf`]. |
|
| 89 | - | pub fn commit(arena: *mut Arena, size: u32) { |
|
| 89 | + | export fn commit(arena: *mut Arena, size: u32) { |
|
| 90 | 90 | arena.offset += size; |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
|
| 94 | 94 | /// |
|
| 95 | 95 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
|
| 96 | 96 | /// The slice length is set to `count` (element count, not bytes). |
|
| 97 | 97 | /// Throws `AllocError` if the arena is exhausted. |
|
| 98 | - | pub fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 98 | + | export fn allocSlice(arena: *mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 99 | 99 | if count == 0 { |
|
| 100 | 100 | return &mut []; |
|
| 101 | 101 | } |
|
| 102 | 102 | let ptr = try alloc(arena, size * count, alignment); |
|
| 103 | 103 |
| 109 | 109 | /// Bundles an allocation function with an opaque context pointer so that |
|
| 110 | 110 | /// any allocation strategy (arena, free-list, mmap, pool) can be used |
|
| 111 | 111 | /// through a uniform interface. The `func` field is called with the context |
|
| 112 | 112 | /// pointer, a byte size and an alignment, and must return a pointer to |
|
| 113 | 113 | /// the allocated memory or panic on failure. |
|
| 114 | - | pub record Allocator { |
|
| 114 | + | export record Allocator { |
|
| 115 | 115 | /// Allocation function. Returns a pointer to `size` bytes |
|
| 116 | 116 | /// aligned to `alignment`, or panics on failure. |
|
| 117 | 117 | func: fn(*mut opaque, u32, u32) -> *mut opaque, |
|
| 118 | 118 | /// Opaque context pointer passed to `func`. |
|
| 119 | 119 | ctx: *mut opaque, |
|
| 120 | 120 | } |
|
| 121 | 121 | ||
| 122 | 122 | /// Create an `Allocator` backed by an `Arena`. |
|
| 123 | - | pub fn arenaAllocator(arena: *mut Arena) -> Allocator { |
|
| 123 | + | export fn arenaAllocator(arena: *mut Arena) -> Allocator { |
|
| 124 | 124 | return Allocator { |
|
| 125 | 125 | func: arenaAllocFn, |
|
| 126 | 126 | ctx: arena as *mut opaque, |
|
| 127 | 127 | }; |
|
| 128 | 128 | } |
lib/std/lang/ast.rad
+60 -60
| 1 | 1 | //! Radiance AST modules. |
|
| 2 | - | pub mod printer; |
|
| 2 | + | export mod printer; |
|
| 3 | 3 | ||
| 4 | 4 | use std::io; |
|
| 5 | 5 | use std::lang::alloc; |
|
| 6 | 6 | ||
| 7 | 7 | /// Maximum number of trait methods. |
|
| 8 | - | pub const MAX_TRAIT_METHODS: u32 = 8; |
|
| 8 | + | export const MAX_TRAIT_METHODS: u32 = 8; |
|
| 9 | 9 | ||
| 10 | 10 | /// Arena for all parser allocations. |
|
| 11 | 11 | /// |
|
| 12 | 12 | /// Uses a bump allocator for both AST nodes and node pointer arrays. |
|
| 13 | - | pub record NodeArena { |
|
| 13 | + | export record NodeArena { |
|
| 14 | 14 | /// Bump allocator for all allocations. |
|
| 15 | 15 | arena: alloc::Arena, |
|
| 16 | 16 | /// Next node ID to assign. Incremented on each node allocation. |
|
| 17 | 17 | nextId: u32, |
|
| 18 | 18 | } |
|
| 19 | 19 | ||
| 20 | 20 | /// Initialize a node arena backed by the given byte slice. |
|
| 21 | - | pub fn nodeArena(data: *mut [u8]) -> NodeArena { |
|
| 21 | + | export fn nodeArena(data: *mut [u8]) -> NodeArena { |
|
| 22 | 22 | return NodeArena { |
|
| 23 | 23 | arena: alloc::new(data), |
|
| 24 | 24 | nextId: 0, |
|
| 25 | 25 | }; |
|
| 26 | 26 | } |
|
| 27 | 27 | ||
| 28 | 28 | /// Create an empty `*mut [*Node]` slice with the given capacity. |
|
| 29 | - | pub fn nodeSlice(arena: *mut NodeArena, capacity: u32) -> *mut [*Node] { |
|
| 29 | + | export fn nodeSlice(arena: *mut NodeArena, capacity: u32) -> *mut [*Node] { |
|
| 30 | 30 | if capacity == 0 { |
|
| 31 | 31 | return &mut []; |
|
| 32 | 32 | } |
|
| 33 | 33 | let ptr = try! alloc::allocSlice(&mut arena.arena, @sizeOf(*Node), @alignOf(*Node), capacity); |
|
| 34 | 34 | ||
| 35 | 35 | return @sliceOf(ptr.ptr as *mut *Node, 0, capacity); |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Attribute bit set applied to declarations or fields. |
|
| 39 | - | pub union Attribute { |
|
| 39 | + | export union Attribute { |
|
| 40 | 40 | /// Public visibility attribute. |
|
| 41 | - | Pub = 0b1, |
|
| 41 | + | Export = 0b1, |
|
| 42 | 42 | /// Default implementation attribute. |
|
| 43 | 43 | Default = 0b10, |
|
| 44 | 44 | /// Extern linkage attribute. |
|
| 45 | 45 | Extern = 0b100, |
|
| 46 | 46 | /// Test-only declaration attribute. |
| 48 | 48 | /// Compiler intrinsic attribute. |
|
| 49 | 49 | Intrinsic = 0b10000, |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Ordered collection of attribute nodes applied to a declaration. |
|
| 53 | - | pub record Attributes { |
|
| 53 | + | export record Attributes { |
|
| 54 | 54 | list: *mut [*Node], |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Check if an attributes list contains an attribute. |
|
| 58 | - | pub fn attributesContains(self: *Attributes, attr: Attribute) -> bool { |
|
| 58 | + | export fn attributesContains(self: *Attributes, attr: Attribute) -> bool { |
|
| 59 | 59 | for node in self.list { |
|
| 60 | 60 | if let case NodeValue::Attribute(a) = node.value; a == attr { |
|
| 61 | 61 | return true; |
|
| 62 | 62 | } |
|
| 63 | 63 | } |
|
| 64 | 64 | return false; |
|
| 65 | 65 | } |
|
| 66 | 66 | ||
| 67 | 67 | /// Check if an attribute set includes the given attribute. |
|
| 68 | - | pub fn hasAttribute(attrs: u32, attr: Attribute) -> bool { |
|
| 68 | + | export fn hasAttribute(attrs: u32, attr: Attribute) -> bool { |
|
| 69 | 69 | return (attrs & (attr as u32)) != 0; |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// Signedness of an integer type. |
|
| 73 | - | pub union Signedness { |
|
| 73 | + | export union Signedness { |
|
| 74 | 74 | /// Signed, eg. `i8`. |
|
| 75 | 75 | Signed, |
|
| 76 | 76 | /// Unsigned, eg. `u32`. |
|
| 77 | 77 | Unsigned, |
|
| 78 | 78 | } |
|
| 79 | 79 | ||
| 80 | 80 | /// Radix/base of a number. |
|
| 81 | - | pub union Radix { |
|
| 81 | + | export union Radix { |
|
| 82 | 82 | /// Binary literal (0b...). |
|
| 83 | 83 | Binary, |
|
| 84 | 84 | /// Decimal literal. |
|
| 85 | 85 | Decimal, |
|
| 86 | 86 | /// Hexadecimal literal (0x...). |
|
| 87 | 87 | Hex, |
|
| 88 | 88 | } |
|
| 89 | 89 | ||
| 90 | 90 | /// Parsed integer literal metadata. |
|
| 91 | - | pub record IntLiteral { |
|
| 91 | + | export record IntLiteral { |
|
| 92 | 92 | /// Raw characters that comprised the literal. |
|
| 93 | 93 | text: *[u8], |
|
| 94 | 94 | /// Absolute magnitude parsed from the literal. |
|
| 95 | 95 | magnitude: u64, |
|
| 96 | 96 | /// Radix used by the literal. |
| 100 | 100 | /// Whether the literal used a negative sign. |
|
| 101 | 101 | negative: bool, |
|
| 102 | 102 | } |
|
| 103 | 103 | ||
| 104 | 104 | /// Binary operator kinds used in numeric expressions. |
|
| 105 | - | pub union BinaryOp { |
|
| 105 | + | export union BinaryOp { |
|
| 106 | 106 | /// Addition (`+`). |
|
| 107 | 107 | Add, |
|
| 108 | 108 | /// Subtraction (`-`). |
|
| 109 | 109 | Sub, |
|
| 110 | 110 | /// Multiplication (`*`). |
| 144 | 144 | /// Logical exclusive disjunction (`xor`). |
|
| 145 | 145 | Xor, |
|
| 146 | 146 | } |
|
| 147 | 147 | ||
| 148 | 148 | /// Unary operator kinds used in expressions. |
|
| 149 | - | pub union UnaryOp { |
|
| 149 | + | export union UnaryOp { |
|
| 150 | 150 | /// Logical negation (`not`). |
|
| 151 | 151 | Not, |
|
| 152 | 152 | /// Arithmetic negation (`-`). |
|
| 153 | 153 | Neg, |
|
| 154 | 154 | /// Bitwise NOT (`~`). |
|
| 155 | 155 | BitNot, |
|
| 156 | 156 | } |
|
| 157 | 157 | ||
| 158 | 158 | /// Builtin function kind. |
|
| 159 | - | pub union Builtin { |
|
| 159 | + | export union Builtin { |
|
| 160 | 160 | /// Size of type in bytes (`@sizeOf`). |
|
| 161 | 161 | SizeOf, |
|
| 162 | 162 | /// Alignment requirement of type (`@alignOf`). |
|
| 163 | 163 | AlignOf, |
|
| 164 | 164 | /// Construct a slice from pointer, length, and optional capacity (`@sliceOf`). |
|
| 165 | 165 | SliceOf, |
|
| 166 | 166 | } |
|
| 167 | 167 | ||
| 168 | 168 | /// Source extent for a node measured in bytes. |
|
| 169 | - | pub record Span { |
|
| 169 | + | export record Span { |
|
| 170 | 170 | /// Byte offset from the start of the source file. |
|
| 171 | 171 | offset: u32, |
|
| 172 | 172 | /// Length of the node in bytes. |
|
| 173 | 173 | length: u32, |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Type signature node. |
|
| 177 | - | pub union TypeSig { |
|
| 177 | + | export union TypeSig { |
|
| 178 | 178 | /// Absence of type. |
|
| 179 | 179 | Void, |
|
| 180 | 180 | /// Opaque type. |
|
| 181 | 181 | Opaque, |
|
| 182 | 182 | /// Boolean type. |
| 233 | 233 | mutable: bool, |
|
| 234 | 234 | }, |
|
| 235 | 235 | } |
|
| 236 | 236 | ||
| 237 | 237 | /// Function signature. |
|
| 238 | - | pub record FnSig { |
|
| 238 | + | export record FnSig { |
|
| 239 | 239 | /// Parameter type nodes in declaration order. |
|
| 240 | 240 | params: *mut [*Node], |
|
| 241 | 241 | /// Optional return type node. |
|
| 242 | 242 | returnType: ?*Node, |
|
| 243 | 243 | /// Throwable type nodes declared in the signature. |
|
| 244 | 244 | throwList: *mut [*Node], |
|
| 245 | 245 | } |
|
| 246 | 246 | ||
| 247 | 247 | /// Address-of expression metadata. |
|
| 248 | - | pub record AddressOf { |
|
| 248 | + | export record AddressOf { |
|
| 249 | 249 | /// Target expression being referenced. |
|
| 250 | 250 | target: *Node, |
|
| 251 | 251 | /// Indicates whether the reference is mutable. |
|
| 252 | 252 | mutable: bool, |
|
| 253 | 253 | } |
|
| 254 | 254 | ||
| 255 | 255 | /// Compound statement block with optional dedicated scope. |
|
| 256 | - | pub record Block { |
|
| 256 | + | export record Block { |
|
| 257 | 257 | /// Statements that belong to this block. |
|
| 258 | 258 | statements: *mut [*Node], |
|
| 259 | 259 | } |
|
| 260 | 260 | ||
| 261 | 261 | /// Function call expression. |
|
| 262 | - | pub record Call { |
|
| 262 | + | export record Call { |
|
| 263 | 263 | /// Callee expression. |
|
| 264 | 264 | callee: *Node, |
|
| 265 | 265 | /// Argument expressions in source order. |
|
| 266 | 266 | args: *mut [*Node], |
|
| 267 | 267 | } |
|
| 268 | 268 | ||
| 269 | 269 | /// Single argument to a function or record literal, optionally labeled. |
|
| 270 | - | pub record Arg { |
|
| 270 | + | export record Arg { |
|
| 271 | 271 | /// Optional label applied to the argument. |
|
| 272 | 272 | label: ?*Node, |
|
| 273 | 273 | /// Expression supplying the argument value. |
|
| 274 | 274 | value: *Node, |
|
| 275 | 275 | } |
|
| 276 | 276 | ||
| 277 | 277 | /// Assignment expression connecting a target and value. |
|
| 278 | - | pub record Assign { |
|
| 278 | + | export record Assign { |
|
| 279 | 279 | /// Expression representing the assignment target. |
|
| 280 | 280 | left: *Node, |
|
| 281 | 281 | /// Expression providing the value being assigned. |
|
| 282 | 282 | right: *Node, |
|
| 283 | 283 | } |
|
| 284 | 284 | ||
| 285 | 285 | /// While loop with an optional alternate branch. |
|
| 286 | - | pub record While { |
|
| 286 | + | export record While { |
|
| 287 | 287 | /// Condition evaluated before each iteration. |
|
| 288 | 288 | condition: *Node, |
|
| 289 | 289 | /// Loop body executed while `condition` is true. |
|
| 290 | 290 | body: *Node, |
|
| 291 | 291 | /// Optional branch executed when the condition is false at entry. |
|
| 292 | 292 | elseBranch: ?*Node, |
|
| 293 | 293 | } |
|
| 294 | 294 | ||
| 295 | 295 | /// `while let` loop binding metadata. |
|
| 296 | - | pub record WhileLet { |
|
| 296 | + | export record WhileLet { |
|
| 297 | 297 | /// Pattern matching structure. |
|
| 298 | 298 | pattern: PatternMatch, |
|
| 299 | 299 | /// Loop body executed when the pattern matches. |
|
| 300 | 300 | body: *Node, |
|
| 301 | 301 | /// Optional branch executed when the match fails immediately. |
|
| 302 | 302 | elseBranch: ?*Node, |
|
| 303 | 303 | } |
|
| 304 | 304 | ||
| 305 | 305 | /// Try expression metadata. |
|
| 306 | - | pub record Try { |
|
| 306 | + | export record Try { |
|
| 307 | 307 | /// Expression evaluated with implicit error propagation. |
|
| 308 | 308 | expr: *Node, |
|
| 309 | 309 | /// Catch clauses. Empty for propagation (`try`), `try!`, or `try?`. |
|
| 310 | 310 | catches: *mut [*Node], |
|
| 311 | 311 | /// Whether the try should panic instead of returning an error. |
| 313 | 313 | /// Whether the try should return an optional instead of propagating error. |
|
| 314 | 314 | returnsOptional: bool, |
|
| 315 | 315 | } |
|
| 316 | 316 | ||
| 317 | 317 | /// A single catch clause in a `try ... catch` expression. |
|
| 318 | - | pub record CatchClause { |
|
| 318 | + | export record CatchClause { |
|
| 319 | 319 | /// Optional identifier binding for the error value (eg. `e`). |
|
| 320 | 320 | binding: ?*Node, |
|
| 321 | 321 | /// Optional type annotation after `as` (eg. `IoError`). |
|
| 322 | 322 | typeNode: ?*Node, |
|
| 323 | 323 | /// Block body executed when this clause matches. |
|
| 324 | 324 | body: *Node, |
|
| 325 | 325 | } |
|
| 326 | 326 | ||
| 327 | 327 | /// `for` loop metadata. |
|
| 328 | - | pub record For { |
|
| 328 | + | export record For { |
|
| 329 | 329 | /// Loop variable binding. |
|
| 330 | 330 | binding: *Node, |
|
| 331 | 331 | /// Optional index binding for enumeration loops. |
|
| 332 | 332 | index: ?*Node, |
|
| 333 | 333 | /// Expression producing the iterable value. |
| 337 | 337 | /// Optional branch executed when the loop body never runs. |
|
| 338 | 338 | elseBranch: ?*Node, |
|
| 339 | 339 | } |
|
| 340 | 340 | ||
| 341 | 341 | /// Conditional `if` statement metadata. |
|
| 342 | - | pub record If { |
|
| 342 | + | export record If { |
|
| 343 | 343 | /// Condition controlling the branch. |
|
| 344 | 344 | condition: *Node, |
|
| 345 | 345 | /// Branch executed when `condition` is true. |
|
| 346 | 346 | thenBranch: *Node, |
|
| 347 | 347 | /// Optional branch executed when `condition` is false. |
|
| 348 | 348 | elseBranch: ?*Node, |
|
| 349 | 349 | } |
|
| 350 | 350 | ||
| 351 | 351 | /// Conditional expression (`<true> if <condition> else <false>`). |
|
| 352 | - | pub record CondExpr { |
|
| 352 | + | export record CondExpr { |
|
| 353 | 353 | /// Condition controlling which branch is evaluated. |
|
| 354 | 354 | condition: *Node, |
|
| 355 | 355 | /// Expression evaluated when `condition` is true. |
|
| 356 | 356 | thenExpr: *Node, |
|
| 357 | 357 | /// Expression evaluated when `condition` is false. |
|
| 358 | 358 | elseExpr: *Node, |
|
| 359 | 359 | } |
|
| 360 | 360 | ||
| 361 | 361 | /// Classification of pattern matches (if-let, while-let, let-else). |
|
| 362 | - | pub union PatternKind { |
|
| 362 | + | export union PatternKind { |
|
| 363 | 363 | /// Case pattern match. |
|
| 364 | 364 | Case, |
|
| 365 | 365 | /// Binding pattern match. |
|
| 366 | 366 | Binding, |
|
| 367 | 367 | } |
|
| 368 | 368 | ||
| 369 | 369 | /// Prong arm. |
|
| 370 | - | pub union ProngArm { |
|
| 370 | + | export union ProngArm { |
|
| 371 | 371 | /// Case arm with pattern list. |
|
| 372 | 372 | Case(*mut [*Node]), |
|
| 373 | 373 | /// Binding arm with single identifier or placeholder. |
|
| 374 | 374 | Binding(*Node), |
|
| 375 | 375 | /// Else arm. |
|
| 376 | 376 | Else, |
|
| 377 | 377 | } |
|
| 378 | 378 | ||
| 379 | 379 | /// Common pattern matching structure used by `if let`, `while let`, and `let-else`. |
|
| 380 | - | pub record PatternMatch { |
|
| 380 | + | export record PatternMatch { |
|
| 381 | 381 | /// Pattern or binding to match against. |
|
| 382 | 382 | pattern: *Node, |
|
| 383 | 383 | /// Scrutinee expression to match against. |
|
| 384 | 384 | scrutinee: *Node, |
|
| 385 | 385 | /// Optional guard that must evaluate to `true`. |
| 389 | 389 | /// Whether the binding is mutable. |
|
| 390 | 390 | mutable: bool, |
|
| 391 | 391 | } |
|
| 392 | 392 | ||
| 393 | 393 | /// `if let` conditional binding metadata. |
|
| 394 | - | pub record IfLet { |
|
| 394 | + | export record IfLet { |
|
| 395 | 395 | /// Pattern matching structure. |
|
| 396 | 396 | pattern: PatternMatch, |
|
| 397 | 397 | /// Branch executed when the pattern matches. |
|
| 398 | 398 | thenBranch: *Node, |
|
| 399 | 399 | /// Optional branch executed when the match fails. |
|
| 400 | 400 | elseBranch: ?*Node, |
|
| 401 | 401 | } |
|
| 402 | 402 | ||
| 403 | 403 | /// `let-else` statement metadata. |
|
| 404 | - | pub record LetElse { |
|
| 404 | + | export record LetElse { |
|
| 405 | 405 | /// Pattern matching structure. |
|
| 406 | 406 | pattern: PatternMatch, |
|
| 407 | 407 | /// Else branch executed if match fails (must diverge). |
|
| 408 | 408 | elseBranch: *Node, |
|
| 409 | 409 | } |
|
| 410 | 410 | ||
| 411 | 411 | /// `match` statement metadata. |
|
| 412 | - | pub record Match { |
|
| 412 | + | export record Match { |
|
| 413 | 413 | /// Expression whose value controls the match. |
|
| 414 | 414 | subject: *Node, |
|
| 415 | 415 | /// Prong nodes evaluated in order. |
|
| 416 | 416 | prongs: *mut [*Node], |
|
| 417 | 417 | } |
|
| 418 | 418 | ||
| 419 | 419 | /// `match` prong metadata. |
|
| 420 | - | pub record MatchProng { |
|
| 420 | + | export record MatchProng { |
|
| 421 | 421 | /// Prong arm. |
|
| 422 | 422 | arm: ProngArm, |
|
| 423 | 423 | /// Optional guard that must evaluate to `true`. |
|
| 424 | 424 | guard: ?*Node, |
|
| 425 | 425 | /// Body executed when patterns match and guard passes. |
|
| 426 | 426 | body: *Node, |
|
| 427 | 427 | } |
|
| 428 | 428 | ||
| 429 | 429 | /// `let` binding. |
|
| 430 | - | pub record Let { |
|
| 430 | + | export record Let { |
|
| 431 | 431 | /// Identifier bound by the declaration. |
|
| 432 | 432 | ident: *Node, |
|
| 433 | 433 | /// Declared type annotation. |
|
| 434 | 434 | type: ?*Node, |
|
| 435 | 435 | /// Initializer expression. |
| 439 | 439 | /// Whether the variable is mutable. |
|
| 440 | 440 | mutable: bool, |
|
| 441 | 441 | } |
|
| 442 | 442 | ||
| 443 | 443 | /// Constant declaration. |
|
| 444 | - | pub record ConstDecl { |
|
| 444 | + | export record ConstDecl { |
|
| 445 | 445 | /// Identifier bound by the declaration. |
|
| 446 | 446 | ident: *Node, |
|
| 447 | 447 | /// Declared type annotation. |
|
| 448 | 448 | type: *Node, |
|
| 449 | 449 | /// Constant initializer expression. |
| 451 | 451 | /// Optional attribute list applied to the constant. |
|
| 452 | 452 | attrs: ?Attributes, |
|
| 453 | 453 | } |
|
| 454 | 454 | ||
| 455 | 455 | /// Static storage declaration. |
|
| 456 | - | pub record StaticDecl { |
|
| 456 | + | export record StaticDecl { |
|
| 457 | 457 | /// Identifier bound by the declaration. |
|
| 458 | 458 | ident: *Node, |
|
| 459 | 459 | /// Declared storage type. |
|
| 460 | 460 | type: *Node, |
|
| 461 | 461 | /// Initialization expression. |
| 463 | 463 | /// Optional attribute list applied to the static. |
|
| 464 | 464 | attrs: ?Attributes, |
|
| 465 | 465 | } |
|
| 466 | 466 | ||
| 467 | 467 | /// Function parameter declaration. |
|
| 468 | - | pub record FnParam { |
|
| 468 | + | export record FnParam { |
|
| 469 | 469 | /// Parameter identifier. |
|
| 470 | 470 | name: *Node, |
|
| 471 | 471 | /// Parameter type annotation. |
|
| 472 | 472 | type: *Node, |
|
| 473 | 473 | } |
|
| 474 | 474 | ||
| 475 | 475 | /// Record literal expression metadata. |
|
| 476 | - | pub record RecordLit { |
|
| 476 | + | export record RecordLit { |
|
| 477 | 477 | /// Type name associated with the literal. |
|
| 478 | 478 | /// If `nil`, it's an anonymous record literal. |
|
| 479 | 479 | typeName: ?*Node, |
|
| 480 | 480 | /// Field initializer nodes. |
|
| 481 | 481 | fields: *mut [*Node], |
|
| 482 | 482 | /// When true, remaining fields are discarded (`{ x, .. }`). |
|
| 483 | 483 | ignoreRest: bool, |
|
| 484 | 484 | } |
|
| 485 | 485 | ||
| 486 | 486 | /// Record declaration. |
|
| 487 | - | pub record RecordDecl { |
|
| 487 | + | export record RecordDecl { |
|
| 488 | 488 | /// Identifier naming the record. |
|
| 489 | 489 | name: *Node, |
|
| 490 | 490 | /// Field declaration nodes. |
|
| 491 | 491 | fields: *mut [*Node], |
|
| 492 | 492 | /// Optional attribute list applied to the record. |
| 496 | 496 | /// Whether this record has labeled fields. |
|
| 497 | 497 | labeled: bool, |
|
| 498 | 498 | } |
|
| 499 | 499 | ||
| 500 | 500 | /// Union declarations. |
|
| 501 | - | pub record UnionDecl { |
|
| 501 | + | export record UnionDecl { |
|
| 502 | 502 | /// Identifier naming the union. |
|
| 503 | 503 | name: *Node, |
|
| 504 | 504 | /// Variant nodes making up the union. |
|
| 505 | 505 | variants: *mut [*Node], |
|
| 506 | 506 | /// Optional attribute list applied to the union. |
| 508 | 508 | /// Trait derivations attached to the union. |
|
| 509 | 509 | derives: *mut [*Node], |
|
| 510 | 510 | } |
|
| 511 | 511 | ||
| 512 | 512 | /// Union variant declaration. |
|
| 513 | - | pub record UnionDeclVariant { |
|
| 513 | + | export record UnionDeclVariant { |
|
| 514 | 514 | /// Identifier naming the variant. |
|
| 515 | 515 | name: *Node, |
|
| 516 | 516 | /// Variant index. |
|
| 517 | 517 | index: u32, |
|
| 518 | 518 | /// Explicit discriminant value, if provided. |
| 520 | 520 | /// Optional payload type. |
|
| 521 | 521 | type: ?*Node, |
|
| 522 | 522 | } |
|
| 523 | 523 | ||
| 524 | 524 | /// Function declaration. |
|
| 525 | - | pub record FnDecl { |
|
| 525 | + | export record FnDecl { |
|
| 526 | 526 | /// Identifier naming the function. |
|
| 527 | 527 | name: *Node, |
|
| 528 | 528 | /// Function type signature. |
|
| 529 | 529 | sig: FnSig, |
|
| 530 | 530 | /// Optional function body (`nil` for extern functions). |
| 532 | 532 | /// Optional attribute list applied to the function. |
|
| 533 | 533 | attrs: ?Attributes, |
|
| 534 | 534 | } |
|
| 535 | 535 | ||
| 536 | 536 | /// Array repeat literal metadata. |
|
| 537 | - | pub record ArrayRepeatLit { |
|
| 537 | + | export record ArrayRepeatLit { |
|
| 538 | 538 | /// Expression providing the repeated value. |
|
| 539 | 539 | item: *Node, |
|
| 540 | 540 | /// Expression providing the repetition count. |
|
| 541 | 541 | count: *Node, |
|
| 542 | 542 | } |
|
| 543 | 543 | ||
| 544 | 544 | /// Module declaration. |
|
| 545 | - | pub record Mod { |
|
| 545 | + | export record Mod { |
|
| 546 | 546 | /// Identifier naming the module. |
|
| 547 | 547 | name: *Node, |
|
| 548 | 548 | /// Optional attribute list applied to the module. |
|
| 549 | 549 | attrs: ?Attributes, |
|
| 550 | 550 | } |
|
| 551 | 551 | ||
| 552 | 552 | /// Use declaration for importing modules. |
|
| 553 | - | pub record Use { |
|
| 553 | + | export record Use { |
|
| 554 | 554 | /// Access node identifying the imported module. |
|
| 555 | 555 | path: *Node, |
|
| 556 | 556 | /// Whether this is a wildcard import (e.g. `use ast::*`). |
|
| 557 | 557 | wildcard: bool, |
|
| 558 | 558 | /// Optional attribute list applied to the use declaration. |
|
| 559 | 559 | attrs: ?Attributes, |
|
| 560 | 560 | } |
|
| 561 | 561 | ||
| 562 | 562 | /// Access expression used for field, scope, and index lookups. |
|
| 563 | - | pub record Access { |
|
| 563 | + | export record Access { |
|
| 564 | 564 | /// Expression providing the container or namespace. |
|
| 565 | 565 | parent: *Node, |
|
| 566 | 566 | /// Expression identifying the member, scope element, or index. |
|
| 567 | 567 | child: *Node, |
|
| 568 | 568 | } |
|
| 569 | 569 | ||
| 570 | 570 | /// `as` cast expression metadata. |
|
| 571 | - | pub record As { |
|
| 571 | + | export record As { |
|
| 572 | 572 | /// Expression being coerced. |
|
| 573 | 573 | value: *Node, |
|
| 574 | 574 | /// Target type annotation. |
|
| 575 | 575 | type: *Node, |
|
| 576 | 576 | } |
|
| 577 | 577 | ||
| 578 | 578 | /// Range expression metadata. |
|
| 579 | - | pub record Range { |
|
| 579 | + | export record Range { |
|
| 580 | 580 | /// Optional inclusive start expression. |
|
| 581 | 581 | start: ?*Node, |
|
| 582 | 582 | /// Optional exclusive end expression. |
|
| 583 | 583 | end: ?*Node, |
|
| 584 | 584 | } |
|
| 585 | 585 | ||
| 586 | 586 | /// Binary operation expression, eg. `x * y`. |
|
| 587 | - | pub record BinOp { |
|
| 587 | + | export record BinOp { |
|
| 588 | 588 | /// Operator applied to the operands. |
|
| 589 | 589 | op: BinaryOp, |
|
| 590 | 590 | /// Left-hand operand. |
|
| 591 | 591 | left: *Node, |
|
| 592 | 592 | /// Right-hand operand. |
|
| 593 | 593 | right: *Node, |
|
| 594 | 594 | } |
|
| 595 | 595 | ||
| 596 | 596 | /// Unary operation expression, eg. `-x`. |
|
| 597 | - | pub record UnOp { |
|
| 597 | + | export record UnOp { |
|
| 598 | 598 | /// Operator applied to the operand. |
|
| 599 | 599 | op: UnaryOp, |
|
| 600 | 600 | /// Operand expression. |
|
| 601 | 601 | value: *Node, |
|
| 602 | 602 | } |
|
| 603 | 603 | ||
| 604 | 604 | /// Tagged union describing every possible AST node payload. |
|
| 605 | - | pub union NodeValue { |
|
| 605 | + | export union NodeValue { |
|
| 606 | 606 | /// Placeholder `_` expression. |
|
| 607 | 607 | Placeholder, |
|
| 608 | 608 | /// Nil literal (`nil`). |
|
| 609 | 609 | Nil, |
|
| 610 | 610 | /// Undefined literal (`undefined`). |
| 804 | 804 | attrs: ?Attributes, |
|
| 805 | 805 | }, |
|
| 806 | 806 | } |
|
| 807 | 807 | ||
| 808 | 808 | /// Full AST node with shared metadata and variant-specific payload. |
|
| 809 | - | pub record Node { |
|
| 809 | + | export record Node { |
|
| 810 | 810 | /// Unique identifier for this node. |
|
| 811 | 811 | id: u32, |
|
| 812 | 812 | /// Source span describing where the node originated. |
|
| 813 | 813 | span: Span, |
|
| 814 | 814 | /// Variant-specific payload for the node. |
|
| 815 | 815 | value: NodeValue, |
|
| 816 | 816 | } |
|
| 817 | 817 | ||
| 818 | 818 | /// Allocate a new AST node from the arena with the given span and value. |
|
| 819 | - | pub fn allocNode(arena: *mut NodeArena, span: Span, value: NodeValue) -> *mut Node { |
|
| 819 | + | export fn allocNode(arena: *mut NodeArena, span: Span, value: NodeValue) -> *mut Node { |
|
| 820 | 820 | let p = try! alloc::alloc(&mut arena.arena, @sizeOf(Node), @alignOf(Node)); |
|
| 821 | 821 | let node = p as *mut Node; |
|
| 822 | 822 | let nodeId = arena.nextId; |
|
| 823 | 823 | arena.nextId = nodeId + 1; |
|
| 824 | 824 |
| 826 | 826 | ||
| 827 | 827 | return node; |
|
| 828 | 828 | } |
|
| 829 | 829 | ||
| 830 | 830 | /// Allocate a synthetic AST node with a zero-length span. |
|
| 831 | - | pub fn synthNode(arena: *mut NodeArena, value: NodeValue) -> *mut Node { |
|
| 831 | + | export fn synthNode(arena: *mut NodeArena, value: NodeValue) -> *mut Node { |
|
| 832 | 832 | return allocNode(arena, Span { offset: 0, length: 0 }, value); |
|
| 833 | 833 | } |
|
| 834 | 834 | ||
| 835 | 835 | /// Synthetic module with a single function in it. |
|
| 836 | 836 | record SynthFnMod { |
| 839 | 839 | /// The function block. |
|
| 840 | 840 | fnBody: *Node |
|
| 841 | 841 | } |
|
| 842 | 842 | ||
| 843 | 843 | /// Synthesize a module with a function in it with the given name and statements. |
|
| 844 | - | pub fn synthFnModule( |
|
| 844 | + | export fn synthFnModule( |
|
| 845 | 845 | arena: *mut NodeArena, name: *[u8], bodyStmts: *mut [*Node] |
|
| 846 | 846 | ) -> SynthFnMod { |
|
| 847 | 847 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 848 | 848 | let fnName = synthNode(arena, NodeValue::Ident(name)); |
|
| 849 | 849 | let params: *mut [*Node] = &mut []; |
lib/std/lang/ast/printer.rad
+3 -3
| 229 | 229 | } |
|
| 230 | 230 | return buf; |
|
| 231 | 231 | } |
|
| 232 | 232 | ||
| 233 | 233 | /// Convert an AST node to an S-expression. |
|
| 234 | - | pub fn toExpr(a: *mut alloc::Arena, node: *super::Node) -> sexpr::Expr { |
|
| 234 | + | export fn toExpr(a: *mut alloc::Arena, node: *super::Node) -> sexpr::Expr { |
|
| 235 | 235 | match node.value { |
|
| 236 | 236 | case super::NodeValue::Placeholder => return sexpr::sym("_"), |
|
| 237 | 237 | case super::NodeValue::Nil => return sexpr::sym("nil"), |
|
| 238 | 238 | case super::NodeValue::Undef => return sexpr::sym("undefined"), |
|
| 239 | 239 | case super::NodeValue::Bool(v) => { |
| 300 | 300 | case super::NodeValue::TypeSig(sig) => return typeSigToExpr(a, sig), |
|
| 301 | 301 | case super::NodeValue::FnParam(p) => |
|
| 302 | 302 | return sexpr::list(a, "param", &[toExpr(a, p.name), toExpr(a, p.type)]), |
|
| 303 | 303 | case super::NodeValue::Attribute(attr) => { |
|
| 304 | 304 | match attr { |
|
| 305 | - | case super::Attribute::Pub => return sexpr::sym("@pub"), |
|
| 305 | + | case super::Attribute::Export => return sexpr::sym("@export"), |
|
| 306 | 306 | case super::Attribute::Default => return sexpr::sym("@default"), |
|
| 307 | 307 | case super::Attribute::Extern => return sexpr::sym("@extern"), |
|
| 308 | 308 | case super::Attribute::Test => return sexpr::sym("@test"), |
|
| 309 | 309 | case super::Attribute::Intrinsic => return sexpr::sym("@intrinsic"), |
|
| 310 | 310 | } |
| 463 | 463 | else => return sexpr::sym("?"), |
|
| 464 | 464 | } |
|
| 465 | 465 | } |
|
| 466 | 466 | ||
| 467 | 467 | /// Dump the tree rooted at `root`, using the provided arena for allocation. |
|
| 468 | - | pub fn printTree(root: *super::Node, arena: *mut alloc::Arena) { |
|
| 468 | + | export fn printTree(root: *super::Node, arena: *mut alloc::Arena) { |
|
| 469 | 469 | match root.value { |
|
| 470 | 470 | case super::NodeValue::Block(blk) => { |
|
| 471 | 471 | for stmt, i in blk.statements { |
|
| 472 | 472 | sexpr::print(toExpr(arena, stmt), 0); |
|
| 473 | 473 | if i < blk.statements.len - 1 { io::print("\n\n"); } |
lib/std/lang/gen.rad
+6 -6
| 2 | 2 | //! |
|
| 3 | 3 | //! Target-independent infrastructure for lowering IL to machine code. |
|
| 4 | 4 | //! Target-specific backends (e.g., `std::arch::rv64`) provide |
|
| 5 | 5 | //! instruction selection and emission. |
|
| 6 | 6 | ||
| 7 | - | pub mod labels; |
|
| 8 | - | pub mod bitset; |
|
| 9 | - | pub mod regalloc; |
|
| 10 | - | pub mod data; |
|
| 11 | - | pub mod types; |
|
| 7 | + | export mod labels; |
|
| 8 | + | export mod bitset; |
|
| 9 | + | export mod regalloc; |
|
| 10 | + | export mod data; |
|
| 11 | + | export mod types; |
|
| 12 | 12 | ||
| 13 | 13 | /// Physical register. |
|
| 14 | 14 | /// |
|
| 15 | 15 | /// Lightweight wrapper around a register number. Used both in the |
|
| 16 | 16 | /// target-independent register allocator and in architecture-specific |
|
| 17 | 17 | /// backends. |
|
| 18 | - | pub record Reg(u8); |
|
| 18 | + | export record Reg(u8); |
lib/std/lang/gen/bitset.rad
+17 -17
| 21 | 21 | } |
|
| 22 | 22 | return b; |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Calculate the number of 32-bit words needed to store `n` bits. |
|
| 26 | - | pub fn wordsFor(n: u32) -> u32 { |
|
| 26 | + | export fn wordsFor(n: u32) -> u32 { |
|
| 27 | 27 | return (n + 31) / 32; |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// A fixed-size bitset backed by an array of 32-bit words. |
|
| 31 | - | pub record Bitset { |
|
| 31 | + | export record Bitset { |
|
| 32 | 32 | /// Backing storage for bits, organized as 32-bit words. |
|
| 33 | 33 | bits: *mut [u32], |
|
| 34 | 34 | /// Number of bits this bitset can hold. |
|
| 35 | 35 | len: u32, |
|
| 36 | 36 | } |
|
| 37 | 37 | ||
| 38 | 38 | /// Create a new bitset backed by the given zero-initialized storage. |
|
| 39 | - | pub fn new(bits: *mut [u32]) -> Bitset { |
|
| 39 | + | export fn new(bits: *mut [u32]) -> Bitset { |
|
| 40 | 40 | return Bitset { bits, len: bits.len * 32 }; |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | /// Create a new bitset backed by the given storage, zeroing it first. |
|
| 44 | - | pub fn init(bits: *mut [u32]) -> Bitset { |
|
| 44 | + | export fn init(bits: *mut [u32]) -> Bitset { |
|
| 45 | 45 | for i in 0..bits.len { |
|
| 46 | 46 | bits[i] = 0; |
|
| 47 | 47 | } |
|
| 48 | 48 | return new(bits); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Create a bitset from arena allocation. |
|
| 52 | - | pub fn allocate(arena: *mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) { |
|
| 52 | + | export fn allocate(arena: *mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) { |
|
| 53 | 53 | let numWords = wordsFor(len); |
|
| 54 | 54 | let bits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *mut [u32]; |
|
| 55 | 55 | ||
| 56 | 56 | return init(bits); |
|
| 57 | 57 | } |
|
| 58 | 58 | ||
| 59 | 59 | /// Set bit `n` in the bitset. |
|
| 60 | - | pub fn set(bs: *mut Bitset, n: u32) { |
|
| 60 | + | export fn set(bs: *mut Bitset, n: u32) { |
|
| 61 | 61 | if n >= bs.len { |
|
| 62 | 62 | return; |
|
| 63 | 63 | } |
|
| 64 | 64 | let word = n / 32; |
|
| 65 | 65 | let b = n % 32; |
|
| 66 | 66 | ||
| 67 | 67 | bs.bits[word] |= (1 << b); |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Clear bit `n` in the bitset. |
|
| 71 | - | pub fn clear(bs: *mut Bitset, n: u32) { |
|
| 71 | + | export fn clear(bs: *mut Bitset, n: u32) { |
|
| 72 | 72 | if n >= bs.len { |
|
| 73 | 73 | return; |
|
| 74 | 74 | } |
|
| 75 | 75 | let word = n / 32; |
|
| 76 | 76 | let b = n % 32; |
|
| 77 | 77 | ||
| 78 | 78 | bs.bits[word] &= ~(1 << b); |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Check if bit `n` is set. |
|
| 82 | - | pub fn contains(bs: *Bitset, n: u32) -> bool { |
|
| 82 | + | export fn contains(bs: *Bitset, n: u32) -> bool { |
|
| 83 | 83 | if n >= bs.len { |
|
| 84 | 84 | return false; |
|
| 85 | 85 | } |
|
| 86 | 86 | let word = n / 32; |
|
| 87 | 87 | let b = n % 32; |
|
| 88 | 88 | ||
| 89 | 89 | return (bs.bits[word] & (1 << b)) != 0; |
|
| 90 | 90 | } |
|
| 91 | 91 | ||
| 92 | 92 | /// Count the number of set bits. |
|
| 93 | - | pub fn count(bs: *Bitset) -> u32 { |
|
| 93 | + | export fn count(bs: *Bitset) -> u32 { |
|
| 94 | 94 | let mut total: u32 = 0; |
|
| 95 | 95 | let numWords = bs.bits.len; |
|
| 96 | 96 | for i in 0..numWords { |
|
| 97 | 97 | let word = bs.bits[i]; |
|
| 98 | 98 | if word != 0 { |
| 113 | 113 | ||
| 114 | 114 | return n & 0x3F; |
|
| 115 | 115 | } |
|
| 116 | 116 | ||
| 117 | 117 | /// Union: `dst = dst | src`. |
|
| 118 | - | pub fn union_(dst: *mut Bitset, src: *Bitset) { |
|
| 118 | + | export fn union_(dst: *mut Bitset, src: *Bitset) { |
|
| 119 | 119 | let numWords = dst.bits.len; |
|
| 120 | 120 | let srcWords = src.bits.len; |
|
| 121 | 121 | let minWords = min(numWords, srcWords); |
|
| 122 | 122 | for i in 0..minWords { |
|
| 123 | 123 | dst.bits[i] |= src.bits[i]; |
|
| 124 | 124 | } |
|
| 125 | 125 | } |
|
| 126 | 126 | ||
| 127 | 127 | /// Subtract: `dst = dst - src`. |
|
| 128 | - | pub fn subtract(dst: *mut Bitset, src: *Bitset) { |
|
| 128 | + | export fn subtract(dst: *mut Bitset, src: *Bitset) { |
|
| 129 | 129 | let numWords = dst.bits.len; |
|
| 130 | 130 | let srcWords = src.bits.len; |
|
| 131 | 131 | let minWords = min(numWords, srcWords); |
|
| 132 | 132 | for i in 0..minWords { |
|
| 133 | 133 | dst.bits[i] &= ~src.bits[i]; |
|
| 134 | 134 | } |
|
| 135 | 135 | } |
|
| 136 | 136 | ||
| 137 | 137 | /// Check if two bitsets are equal. |
|
| 138 | - | pub fn eq(a: *Bitset, b: *Bitset) -> bool { |
|
| 138 | + | export fn eq(a: *Bitset, b: *Bitset) -> bool { |
|
| 139 | 139 | let numWordsA = a.bits.len; |
|
| 140 | 140 | let numWordsB = b.bits.len; |
|
| 141 | 141 | let maxWords = max(numWordsA, numWordsB); |
|
| 142 | 142 | ||
| 143 | 143 | for i in 0..maxWords { |
| 149 | 149 | } |
|
| 150 | 150 | return true; |
|
| 151 | 151 | } |
|
| 152 | 152 | ||
| 153 | 153 | /// Copy bits from source to destination. |
|
| 154 | - | pub fn copy(dst: *mut Bitset, src: *Bitset) { |
|
| 154 | + | export fn copy(dst: *mut Bitset, src: *Bitset) { |
|
| 155 | 155 | let numWords = dst.bits.len; |
|
| 156 | 156 | let srcWords = src.bits.len; |
|
| 157 | 157 | let minWords = min(numWords, srcWords); |
|
| 158 | 158 | ||
| 159 | 159 | for i in 0..minWords { |
| 164 | 164 | dst.bits[i] = 0; |
|
| 165 | 165 | } |
|
| 166 | 166 | } |
|
| 167 | 167 | ||
| 168 | 168 | /// Clear all bits. |
|
| 169 | - | pub fn clearAll(bs: *mut Bitset) { |
|
| 169 | + | export fn clearAll(bs: *mut Bitset) { |
|
| 170 | 170 | let numWords = bs.bits.len; |
|
| 171 | 171 | for i in 0..numWords { |
|
| 172 | 172 | bs.bits[i] = 0; |
|
| 173 | 173 | } |
|
| 174 | 174 | } |
|
| 175 | 175 | ||
| 176 | 176 | /// Iterator state for iterating set bits. |
|
| 177 | - | pub record BitIter { |
|
| 177 | + | export record BitIter { |
|
| 178 | 178 | /// Bitset being iterated. |
|
| 179 | 179 | bs: *Bitset, |
|
| 180 | 180 | /// Current word index. |
|
| 181 | 181 | wordIdx: u32, |
|
| 182 | 182 | /// Remaining bits in the current word (visited bits cleared). |
|
| 183 | 183 | remaining: u32, |
|
| 184 | 184 | } |
|
| 185 | 185 | ||
| 186 | 186 | /// Create an iterator over set bits. |
|
| 187 | - | pub fn iter(bs: *Bitset) -> BitIter { |
|
| 187 | + | export fn iter(bs: *Bitset) -> BitIter { |
|
| 188 | 188 | let remaining = bs.bits[0] if bs.len > 0 else 0; |
|
| 189 | 189 | return BitIter { bs, wordIdx: 0, remaining }; |
|
| 190 | 190 | } |
|
| 191 | 191 | ||
| 192 | 192 | /// Get the next set bit, or nil if none remain. |
|
| 193 | - | pub fn iterNext(it: *mut BitIter) -> ?u32 { |
|
| 193 | + | export fn iterNext(it: *mut BitIter) -> ?u32 { |
|
| 194 | 194 | let numWords = it.bs.bits.len; |
|
| 195 | 195 | // Skip to next non-zero word. |
|
| 196 | 196 | while it.remaining == 0 { |
|
| 197 | 197 | it.wordIdx += 1; |
|
| 198 | 198 | if it.wordIdx >= numWords { |
lib/std/lang/gen/data.rad
+8 -8
| 7 | 7 | use std::collections::dict; |
|
| 8 | 8 | use std::lang::il; |
|
| 9 | 9 | use std::lang::gen::labels; |
|
| 10 | 10 | ||
| 11 | 11 | /// Maximum number of data symbols. |
|
| 12 | - | pub const MAX_DATA_SYMS: u32 = 8192; |
|
| 12 | + | export const MAX_DATA_SYMS: u32 = 8192; |
|
| 13 | 13 | ||
| 14 | 14 | /// Size of the data symbol hash table. Must be a power of two |
|
| 15 | 15 | /// and at least twice the size of [`MAX_DATA_SYMS`]. |
|
| 16 | - | pub const DATA_SYM_TABLE_SIZE: u32 = MAX_DATA_SYMS * 2; |
|
| 16 | + | export const DATA_SYM_TABLE_SIZE: u32 = MAX_DATA_SYMS * 2; |
|
| 17 | 17 | ||
| 18 | 18 | /// Data symbol entry mapping name to address. |
|
| 19 | - | pub record DataSym { |
|
| 19 | + | export record DataSym { |
|
| 20 | 20 | /// Symbol name. |
|
| 21 | 21 | name: *[u8], |
|
| 22 | 22 | /// Absolute address, including data base address. |
|
| 23 | 23 | addr: u32, |
|
| 24 | 24 | } |
|
| 25 | 25 | ||
| 26 | 26 | /// Hash-indexed data symbol map. |
|
| 27 | - | pub record DataSymMap { |
|
| 27 | + | export record DataSymMap { |
|
| 28 | 28 | /// Underlying hash table. |
|
| 29 | 29 | dict: dict::Dict, |
|
| 30 | 30 | /// Fallback linear array for edge cases. |
|
| 31 | 31 | syms: *[DataSym], |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Lay out data symbols for a single section. |
|
| 35 | 35 | /// Initialized data is placed first, then uninitialized, so that only |
|
| 36 | 36 | /// initialized data needs to be written to the output file. |
|
| 37 | 37 | /// Returns the updated offset past all placed symbols. |
|
| 38 | - | pub fn layoutSection( |
|
| 38 | + | export fn layoutSection( |
|
| 39 | 39 | program: *il::Program, |
|
| 40 | 40 | syms: *mut [DataSym], |
|
| 41 | 41 | count: *mut u32, |
|
| 42 | 42 | base: u32, |
|
| 43 | 43 | readOnly: bool |
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Emit data bytes for a single section (read-only or read-write) into `buf`. |
|
| 71 | 71 | /// Iterates initialized data in the IL program, serializing each data item. |
|
| 72 | 72 | /// Returns the total number of bytes written. |
|
| 73 | - | pub fn emitSection( |
|
| 73 | + | export fn emitSection( |
|
| 74 | 74 | program: *il::Program, |
|
| 75 | 75 | dataSymMap: *DataSymMap, |
|
| 76 | 76 | fnLabels: *labels::Labels, |
|
| 77 | 77 | codeBase: u32, |
|
| 78 | 78 | buf: *mut [u8], |
| 131 | 131 | return offset; |
|
| 132 | 132 | } |
|
| 133 | 133 | ||
| 134 | 134 | /// Build a hash-indexed data symbol map from the laid-out symbols. |
|
| 135 | 135 | /// The `entries` slice must have length `DATA_SYM_TABLE_SIZE`. |
|
| 136 | - | pub fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap { |
|
| 136 | + | export fn buildMap(syms: *[DataSym], entries: *mut [dict::Entry]) -> DataSymMap { |
|
| 137 | 137 | let mut d = dict::init(entries); |
|
| 138 | 138 | for i in 0..syms.len { |
|
| 139 | 139 | dict::insert(&mut d, syms[i].name, syms[i].addr as i32); |
|
| 140 | 140 | } |
|
| 141 | 141 | return DataSymMap { dict: d, syms }; |
|
| 142 | 142 | } |
|
| 143 | 143 | ||
| 144 | 144 | /// Resolve a data symbol to its final absolute address using the hash map. |
|
| 145 | - | pub fn lookupAddr(m: *DataSymMap, name: *[u8]) -> ?u32 { |
|
| 145 | + | export fn lookupAddr(m: *DataSymMap, name: *[u8]) -> ?u32 { |
|
| 146 | 146 | if let v = dict::get(&m.dict, name) { |
|
| 147 | 147 | return v as u32; |
|
| 148 | 148 | } |
|
| 149 | 149 | return nil; |
|
| 150 | 150 | } |
lib/std/lang/gen/labels.rad
+11 -11
| 3 | 3 | //! Provides target-independent block/label tracking for branch resolution. |
|
| 4 | 4 | ||
| 5 | 5 | use std::collections::dict; |
|
| 6 | 6 | ||
| 7 | 7 | /// Maximum number of blocks per function. |
|
| 8 | - | pub const MAX_BLOCKS_PER_FN: u32 = 4096; |
|
| 8 | + | export const MAX_BLOCKS_PER_FN: u32 = 4096; |
|
| 9 | 9 | /// Maximum number of functions. |
|
| 10 | - | pub const MAX_FUNCS: u32 = 8192; |
|
| 10 | + | export const MAX_FUNCS: u32 = 8192; |
|
| 11 | 11 | /// Size of the function hash table. Must be a power of two. |
|
| 12 | - | pub const FUNC_TABLE_SIZE: u32 = MAX_FUNCS * 2; |
|
| 12 | + | export const FUNC_TABLE_SIZE: u32 = MAX_FUNCS * 2; |
|
| 13 | 13 | ||
| 14 | 14 | /// Label tracking for code emission. |
|
| 15 | - | pub record Labels { |
|
| 15 | + | export record Labels { |
|
| 16 | 16 | /// Block offsets indexed by block index. |
|
| 17 | 17 | /// Per-function, reset each function. |
|
| 18 | 18 | blockOffsets: *mut [i32], |
|
| 19 | 19 | /// Number of blocks recorded in current function. |
|
| 20 | 20 | blockCount: u32, |
|
| 21 | 21 | /// Function name to byte offset mapping. |
|
| 22 | 22 | funcs: dict::Dict, |
|
| 23 | 23 | } |
|
| 24 | 24 | ||
| 25 | 25 | /// Create a new labels tracker with caller-provided storage. |
|
| 26 | - | pub fn init(blockOffsets: *mut [i32], funcEntries: *mut [dict::Entry]) -> Labels { |
|
| 26 | + | export fn init(blockOffsets: *mut [i32], funcEntries: *mut [dict::Entry]) -> Labels { |
|
| 27 | 27 | return Labels { |
|
| 28 | 28 | blockOffsets, |
|
| 29 | 29 | blockCount: 0, |
|
| 30 | 30 | funcs: dict::init(funcEntries), |
|
| 31 | 31 | }; |
|
| 32 | 32 | } |
|
| 33 | 33 | ||
| 34 | 34 | /// Reset block count for a new function. |
|
| 35 | - | pub fn resetBlocks(l: *mut Labels) { |
|
| 35 | + | export fn resetBlocks(l: *mut Labels) { |
|
| 36 | 36 | l.blockCount = 0; |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | /// Record a block's code offset by its index. O(1). |
|
| 40 | - | pub fn recordBlock(l: *mut Labels, blockIdx: u32, offset: i32) { |
|
| 40 | + | export fn recordBlock(l: *mut Labels, blockIdx: u32, offset: i32) { |
|
| 41 | 41 | assert blockIdx < l.blockOffsets.len, "recordBlock: block index out of range"; |
|
| 42 | 42 | l.blockOffsets[blockIdx] = offset; |
|
| 43 | 43 | l.blockCount += 1; |
|
| 44 | 44 | } |
|
| 45 | 45 | ||
| 46 | 46 | /// Look up a block's byte offset by index. O(1). |
|
| 47 | - | pub fn blockOffset(l: *Labels, blockIdx: u32) -> i32 { |
|
| 47 | + | export fn blockOffset(l: *Labels, blockIdx: u32) -> i32 { |
|
| 48 | 48 | assert blockIdx < l.blockCount, "blockOffset: block not recorded"; |
|
| 49 | 49 | return l.blockOffsets[blockIdx]; |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Look up a function's byte offset by name. |
|
| 53 | - | pub fn funcOffset(l: *Labels, name: *[u8]) -> i32 { |
|
| 53 | + | export fn funcOffset(l: *Labels, name: *[u8]) -> i32 { |
|
| 54 | 54 | if let offset = dict::get(&l.funcs, name) { |
|
| 55 | 55 | return offset; |
|
| 56 | 56 | } |
|
| 57 | 57 | panic "funcOffset: unknown function"; |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Compute branch offset to a block given source instruction index. |
|
| 61 | - | pub fn branchToBlock(l: *Labels, srcIndex: u32, blockIdx: u32, instrSize: i32) -> i32 { |
|
| 61 | + | export fn branchToBlock(l: *Labels, srcIndex: u32, blockIdx: u32, instrSize: i32) -> i32 { |
|
| 62 | 62 | let targetOffset = blockOffset(l, blockIdx); |
|
| 63 | 63 | let srcOffset = srcIndex as i32 * instrSize; |
|
| 64 | 64 | ||
| 65 | 65 | return targetOffset - srcOffset; |
|
| 66 | 66 | } |
|
| 67 | 67 | ||
| 68 | 68 | /// Compute branch offset to a function given source instruction index. |
|
| 69 | - | pub fn branchToFunc(l: *Labels, srcIndex: u32, name: *[u8], instrSize: i32) -> i32 { |
|
| 69 | + | export fn branchToFunc(l: *Labels, srcIndex: u32, name: *[u8], instrSize: i32) -> i32 { |
|
| 70 | 70 | let targetOffset = funcOffset(l, name); |
|
| 71 | 71 | let srcOffset = srcIndex as i32 * instrSize; |
|
| 72 | 72 | ||
| 73 | 73 | return targetOffset - srcOffset; |
|
| 74 | 74 | } |
lib/std/lang/gen/regalloc.rad
+6 -6
| 12 | 12 | //! |
|
| 13 | 13 | //! Note that the IL is not modified. The allocator produces a mapping that |
|
| 14 | 14 | //! instruction selection uses to emit physical registers. Spilled values are |
|
| 15 | 15 | //! handled by [`isel`] inserting loads/stores. |
|
| 16 | 16 | ||
| 17 | - | pub mod liveness; |
|
| 18 | - | pub mod spill; |
|
| 19 | - | pub mod assign; |
|
| 17 | + | export mod liveness; |
|
| 18 | + | export mod spill; |
|
| 19 | + | export mod assign; |
|
| 20 | 20 | ||
| 21 | 21 | use std::lang::il; |
|
| 22 | 22 | use std::lang::alloc; |
|
| 23 | 23 | ||
| 24 | 24 | /// Target configuration for register allocation. |
|
| 25 | - | pub record TargetConfig { |
|
| 25 | + | export record TargetConfig { |
|
| 26 | 26 | /// List of allocatable physical registers. |
|
| 27 | 27 | /// Order determines allocation preference. |
|
| 28 | 28 | allocatable: *[super::Reg], |
|
| 29 | 29 | /// Function argument registers. |
|
| 30 | 30 | argRegs: *[super::Reg], |
| 33 | 33 | /// Size of a spill slot in bytes. |
|
| 34 | 34 | slotSize: u32, |
|
| 35 | 35 | } |
|
| 36 | 36 | ||
| 37 | 37 | /// Complete register allocation result. |
|
| 38 | - | pub record AllocResult { |
|
| 38 | + | export record AllocResult { |
|
| 39 | 39 | /// SSA register to physical register mapping. |
|
| 40 | 40 | assignments: *[?super::Reg], |
|
| 41 | 41 | /// Spill slot information. |
|
| 42 | 42 | spill: spill::SpillInfo, |
|
| 43 | 43 | /// Bitmask of used callee-saved registers. |
| 46 | 46 | ||
| 47 | 47 | /// Run register allocation on a function. |
|
| 48 | 48 | /// |
|
| 49 | 49 | /// Returns a mapping from SSA registers to physical registers, plus |
|
| 50 | 50 | /// spill information. |
|
| 51 | - | pub fn allocate( |
|
| 51 | + | export fn allocate( |
|
| 52 | 52 | func: *il::Fn, |
|
| 53 | 53 | config: *TargetConfig, |
|
| 54 | 54 | arena: *mut alloc::Arena |
|
| 55 | 55 | ) -> AllocResult throws (alloc::AllocError) { |
|
| 56 | 56 | // Phase 1: Liveness analysis. |
lib/std/lang/gen/regalloc/assign.rad
+3 -3
| 16 | 16 | /// Maximum number of active register mappings. |
|
| 17 | 17 | const MAX_ACTIVE: u32 = 64; |
|
| 18 | 18 | ||
| 19 | 19 | /// Register mapping at a program point. |
|
| 20 | 20 | /// Maps SSA registers to physical registers. |
|
| 21 | - | pub record RegMap { |
|
| 21 | + | export record RegMap { |
|
| 22 | 22 | /// SSA (virtual) registers that have mappings. |
|
| 23 | 23 | virtRegs: *mut [u32], |
|
| 24 | 24 | /// Physical register for each virtual register. |
|
| 25 | 25 | physRegs: *mut [gen::Reg], |
|
| 26 | 26 | /// Number of active mappings. |
|
| 27 | 27 | n: u32, |
|
| 28 | 28 | } |
|
| 29 | 29 | ||
| 30 | 30 | /// Register assignment result, per function. |
|
| 31 | - | pub record AssignInfo { |
|
| 31 | + | export record AssignInfo { |
|
| 32 | 32 | /// SSA register -> physical register mapping. |
|
| 33 | 33 | assignments: *mut [?gen::Reg], |
|
| 34 | 34 | /// Bitmask of used callee-saved registers. |
|
| 35 | 35 | usedCalleeSaved: u32, |
|
| 36 | 36 | } |
| 48 | 48 | assignments: *mut [?gen::Reg], |
|
| 49 | 49 | spillInfo: *spill::SpillInfo, |
|
| 50 | 50 | } |
|
| 51 | 51 | ||
| 52 | 52 | /// Compute register assignment. |
|
| 53 | - | pub fn assign( |
|
| 53 | + | export fn assign( |
|
| 54 | 54 | func: *il::Fn, |
|
| 55 | 55 | live: *liveness::LiveInfo, |
|
| 56 | 56 | spillInfo: *spill::SpillInfo, |
|
| 57 | 57 | config: *super::TargetConfig, |
|
| 58 | 58 | arena: *mut alloc::Arena |
lib/std/lang/gen/regalloc/liveness.rad
+4 -4
| 27 | 27 | use std::lang::il; |
|
| 28 | 28 | use std::lang::alloc; |
|
| 29 | 29 | use std::lang::gen::bitset; |
|
| 30 | 30 | ||
| 31 | 31 | /// Maximum number of SSA registers supported. |
|
| 32 | - | pub const MAX_SSA_REGS: u32 = 8192; |
|
| 32 | + | export const MAX_SSA_REGS: u32 = 8192; |
|
| 33 | 33 | ||
| 34 | 34 | /// Liveness information for a function. |
|
| 35 | - | pub record LiveInfo { |
|
| 35 | + | export record LiveInfo { |
|
| 36 | 36 | /// Per-block live-in sets (indexed by block index). |
|
| 37 | 37 | liveIn: *mut [bitset::Bitset], |
|
| 38 | 38 | /// Per-block live-out sets (indexed by block index). |
|
| 39 | 39 | liveOut: *mut [bitset::Bitset], |
|
| 40 | 40 | /// Per-block defs sets (registers defined in block). |
| 58 | 58 | target: u32, |
|
| 59 | 59 | found: bool, |
|
| 60 | 60 | } |
|
| 61 | 61 | ||
| 62 | 62 | /// Compute liveness information for a function. |
|
| 63 | - | pub fn analyze(func: *il::Fn, arena: *mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 63 | + | export fn analyze(func: *il::Fn, arena: *mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) { |
|
| 64 | 64 | let blockCount = func.blocks.len; |
|
| 65 | 65 | if blockCount == 0 { |
|
| 66 | 66 | return LiveInfo { |
|
| 67 | 67 | liveIn: &mut [], |
|
| 68 | 68 | liveOut: &mut [], |
| 223 | 223 | fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], scratch: *mut bitset::Bitset) { |
|
| 224 | 224 | bitset::union_(scratch, &liveIn[target]); |
|
| 225 | 225 | } |
|
| 226 | 226 | ||
| 227 | 227 | /// Check if this is the last use of a register at this instruction. |
|
| 228 | - | pub fn isLastUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 228 | + | export fn isLastUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool { |
|
| 229 | 229 | let block = &func.blocks[blockIdx]; |
|
| 230 | 230 | let instr = block.instrs[instrIdx]; |
|
| 231 | 231 | ||
| 232 | 232 | if not instrUsesReg(instr, reg) { |
|
| 233 | 233 | return false; |
lib/std/lang/gen/regalloc/spill.rad
+4 -4
| 47 | 47 | /// Number of uses (weighted by loop depth). |
|
| 48 | 48 | uses: u32, |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Spill decision for a function. |
|
| 52 | - | pub record SpillInfo { |
|
| 52 | + | export record SpillInfo { |
|
| 53 | 53 | /// SSA register mapped to stack slot offset. `-1` means not spilled. |
|
| 54 | 54 | slots: *mut [i32], |
|
| 55 | 55 | /// Total spill frame size needed in bytes. |
|
| 56 | 56 | frameSize: i32, |
|
| 57 | 57 | /// Values that must be allocated in callee-saved registers. |
| 77 | 77 | costs: *mut [SpillCost], |
|
| 78 | 78 | weight: u32, |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Analyze a function and determine which values need spill slots. |
|
| 82 | - | pub fn analyze( |
|
| 82 | + | export fn analyze( |
|
| 83 | 83 | func: *il::Fn, |
|
| 84 | 84 | live: *liveness::LiveInfo, |
|
| 85 | 85 | numRegs: u32, |
|
| 86 | 86 | numCalleeSaved: u32, |
|
| 87 | 87 | slotSize: u32, |
| 319 | 319 | fn addRegToSetCallback(reg: il::Reg, ctx: *mut opaque) { |
|
| 320 | 320 | bitset::set(ctx as *mut bitset::Bitset, reg.n); |
|
| 321 | 321 | } |
|
| 322 | 322 | ||
| 323 | 323 | /// Check if a register is spilled. |
|
| 324 | - | pub fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool { |
|
| 324 | + | export fn isSpilled(info: *SpillInfo, reg: il::Reg) -> bool { |
|
| 325 | 325 | if reg.n >= info.maxReg { |
|
| 326 | 326 | return false; |
|
| 327 | 327 | } |
|
| 328 | 328 | return info.slots[reg.n] >= 0; |
|
| 329 | 329 | } |
|
| 330 | 330 | ||
| 331 | 331 | /// Get spill slot offset for a register, or `nil` if not spilled. |
|
| 332 | - | pub fn spillSlot(info: *SpillInfo, reg: il::Reg) -> ?i32 { |
|
| 332 | + | export fn spillSlot(info: *SpillInfo, reg: il::Reg) -> ?i32 { |
|
| 333 | 333 | if isSpilled(info, reg) { |
|
| 334 | 334 | return info.slots[reg.n]; |
|
| 335 | 335 | } |
|
| 336 | 336 | return nil; |
|
| 337 | 337 | } |
lib/std/lang/gen/types.rad
+2 -2
| 1 | 1 | //! Target-independent code generation types. |
|
| 2 | 2 | //! |
|
| 3 | 3 | //! Defines common records used by all code generation backends. |
|
| 4 | 4 | ||
| 5 | 5 | /// Function address entry for printing. |
|
| 6 | - | pub record FuncAddr { |
|
| 6 | + | export record FuncAddr { |
|
| 7 | 7 | /// Function name. |
|
| 8 | 8 | name: *[u8], |
|
| 9 | 9 | /// Instruction index where this function starts. |
|
| 10 | 10 | index: u32, |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | 13 | /// Debug entry mapping an instruction to a source location. |
|
| 14 | - | pub record DebugEntry { |
|
| 14 | + | export record DebugEntry { |
|
| 15 | 15 | /// Byte offset of the instruction from the start of the program. |
|
| 16 | 16 | pc: u32, |
|
| 17 | 17 | /// Module identifier. |
|
| 18 | 18 | moduleId: u16, |
|
| 19 | 19 | /// Byte offset into the module's source file. |
lib/std/lang/il.rad
+23 -23
| 56 | 56 | //! can declare parameters, and jumps/branches pass arguments to their targets. |
|
| 57 | 57 | ||
| 58 | 58 | // TODO: Labels should have their own type. |
|
| 59 | 59 | // TODO: Blocks should have an instruction in `Instr`. |
|
| 60 | 60 | ||
| 61 | - | pub mod printer; |
|
| 61 | + | export mod printer; |
|
| 62 | 62 | ||
| 63 | 63 | use std::mem; |
|
| 64 | 64 | use std::lang::alloc; |
|
| 65 | 65 | ||
| 66 | 66 | /// Source location for debug info. |
|
| 67 | 67 | /// |
|
| 68 | 68 | /// Associates an IL instruction with its originating source module and |
|
| 69 | 69 | /// byte offset. |
|
| 70 | - | pub record SrcLoc { |
|
| 70 | + | export record SrcLoc { |
|
| 71 | 71 | /// Module identifier. |
|
| 72 | 72 | moduleId: u16, |
|
| 73 | 73 | /// Byte offset into the module's source file. |
|
| 74 | 74 | offset: u32, |
|
| 75 | 75 | } |
| 77 | 77 | /////////////////////// |
|
| 78 | 78 | // Name Formatting // |
|
| 79 | 79 | /////////////////////// |
|
| 80 | 80 | ||
| 81 | 81 | /// Separator for qualified symbol names. |
|
| 82 | - | pub const PATH_SEPARATOR: *[u8] = "::"; |
|
| 82 | + | export const PATH_SEPARATOR: *[u8] = "::"; |
|
| 83 | 83 | ||
| 84 | 84 | /// Format a qualified symbol name: `pkg::mod::path::name`. |
|
| 85 | - | pub fn formatQualifiedName(arena: *mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] { |
|
| 85 | + | export fn formatQualifiedName(arena: *mut alloc::Arena, path: *[*[u8]], name: *[u8]) -> *[u8] { |
|
| 86 | 86 | let mut totalLen: u32 = name.len; |
|
| 87 | 87 | for segment in path { |
|
| 88 | 88 | totalLen += segment.len + PATH_SEPARATOR.len; |
|
| 89 | 89 | } |
|
| 90 | 90 | let buf = try! alloc::allocSlice(arena, 1, 1, totalLen) as *mut [u8]; |
| 102 | 102 | /////////// |
|
| 103 | 103 | // Types // |
|
| 104 | 104 | /////////// |
|
| 105 | 105 | ||
| 106 | 106 | /// IL type representation. Integer signedness is encoded in operations, not types. |
|
| 107 | - | pub union Type { |
|
| 107 | + | export union Type { |
|
| 108 | 108 | /// 8-bit value. |
|
| 109 | 109 | W8, |
|
| 110 | 110 | /// 16-bit value. |
|
| 111 | 111 | W16, |
|
| 112 | 112 | /// 32-bit value. |
| 114 | 114 | /// 64-bit value (used for pointers on RV64). |
|
| 115 | 115 | W64, |
|
| 116 | 116 | } |
|
| 117 | 117 | ||
| 118 | 118 | /// Get the size of a type in bytes. |
|
| 119 | - | pub fn typeSize(t: Type) -> u32 { |
|
| 119 | + | export fn typeSize(t: Type) -> u32 { |
|
| 120 | 120 | match t { |
|
| 121 | 121 | case Type::W8 => return 1, |
|
| 122 | 122 | case Type::W16 => return 2, |
|
| 123 | 123 | case Type::W32 => return 4, |
|
| 124 | 124 | case Type::W64 => return 8, |
|
| 125 | 125 | } |
|
| 126 | 126 | } |
|
| 127 | 127 | ||
| 128 | 128 | /// SSA register reference. |
|
| 129 | - | pub record Reg { n: u32 } |
|
| 129 | + | export record Reg { n: u32 } |
|
| 130 | 130 | ||
| 131 | 131 | /// Instruction value. |
|
| 132 | - | pub union Val { |
|
| 132 | + | export union Val { |
|
| 133 | 133 | /// Register reference. |
|
| 134 | 134 | Reg(Reg), |
|
| 135 | 135 | /// Immediate integer value. |
|
| 136 | 136 | Imm(i64), |
|
| 137 | 137 | /// Data symbol address (globals, constants, string literals). |
| 143 | 143 | /// eg. in void returns. |
|
| 144 | 144 | Undef, |
|
| 145 | 145 | } |
|
| 146 | 146 | ||
| 147 | 147 | /// Comparison operation for compare-and-branch. |
|
| 148 | - | pub union CmpOp { Eq, Ne, Slt, Ult } |
|
| 148 | + | export union CmpOp { Eq, Ne, Slt, Ult } |
|
| 149 | 149 | ||
| 150 | 150 | /// Binary ALU operation kind. |
|
| 151 | - | pub union BinOp { |
|
| 151 | + | export union BinOp { |
|
| 152 | 152 | // Arithmetic. |
|
| 153 | 153 | Add, Sub, Mul, Sdiv, Udiv, Srem, Urem, |
|
| 154 | 154 | // Comparison. |
|
| 155 | 155 | Eq, Ne, Slt, Sge, Ult, Uge, |
|
| 156 | 156 | // Bitwise. |
|
| 157 | 157 | And, Or, Xor, Shl, Sshr, Ushr, |
|
| 158 | 158 | } |
|
| 159 | 159 | ||
| 160 | 160 | /// Unary ALU operation kind. |
|
| 161 | - | pub union UnOp { |
|
| 161 | + | export union UnOp { |
|
| 162 | 162 | Neg, Not, |
|
| 163 | 163 | } |
|
| 164 | 164 | ||
| 165 | 165 | ////////////////// |
|
| 166 | 166 | // Instructions // |
|
| 167 | 167 | ////////////////// |
|
| 168 | 168 | ||
| 169 | 169 | /// Block parameter. |
|
| 170 | - | pub record Param { |
|
| 170 | + | export record Param { |
|
| 171 | 171 | /// SSA register. |
|
| 172 | 172 | value: Reg, |
|
| 173 | 173 | /// Parameter type. |
|
| 174 | 174 | type: Type, |
|
| 175 | 175 | } |
|
| 176 | 176 | ||
| 177 | 177 | /// Switch case mapping a constant value to a branch target. |
|
| 178 | - | pub record SwitchCase { |
|
| 178 | + | export record SwitchCase { |
|
| 179 | 179 | /// The constant value to match against. |
|
| 180 | 180 | value: i64, |
|
| 181 | 181 | /// The target block index. |
|
| 182 | 182 | target: u32, |
|
| 183 | 183 | /// Arguments to pass to the target block. |
|
| 184 | 184 | args: *mut [Val], |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// IL instruction. |
|
| 188 | 188 | /// SSA registers are represented as `Reg`, values as `Val`. |
|
| 189 | - | pub union Instr { |
|
| 189 | + | export union Instr { |
|
| 190 | 190 | /////////////////////// |
|
| 191 | 191 | // Memory operations // |
|
| 192 | 192 | /////////////////////// |
|
| 193 | 193 | ||
| 194 | 194 | /// Allocate space on the stack: `reserve %dst <size> <alignment>;` |
| 305 | 305 | /// |
|
| 306 | 306 | /// Basic blocks are instruction sequences with a single entry point and no branch |
|
| 307 | 307 | /// instruction, except possibly at the end of the sequence, where a terminator |
|
| 308 | 308 | /// may be found, ie. an instruction that terminates the sequence by jumping |
|
| 309 | 309 | /// to another sequence. |
|
| 310 | - | pub record Block { |
|
| 310 | + | export record Block { |
|
| 311 | 311 | /// Block label. |
|
| 312 | 312 | label: *[u8], |
|
| 313 | 313 | /// Block parameters. |
|
| 314 | 314 | params: *[Param], |
|
| 315 | 315 | /// Instructions in the block. The last instruction must be a terminator. |
| 323 | 323 | /// Used for spill cost weighting in register allocation. |
|
| 324 | 324 | loopDepth: u32, |
|
| 325 | 325 | } |
|
| 326 | 326 | ||
| 327 | 327 | /// An IL function. |
|
| 328 | - | pub record Fn { |
|
| 328 | + | export record Fn { |
|
| 329 | 329 | /// Qualified function name (e.g. `$mod$path$func`). |
|
| 330 | 330 | name: *[u8], |
|
| 331 | 331 | /// Function parameters. |
|
| 332 | 332 | params: *[Param], |
|
| 333 | 333 | /// Return type. |
| 343 | 343 | ///////////// |
|
| 344 | 344 | // Program // |
|
| 345 | 345 | ///////////// |
|
| 346 | 346 | ||
| 347 | 347 | /// Data initializer item. |
|
| 348 | - | pub union DataItem { |
|
| 348 | + | export union DataItem { |
|
| 349 | 349 | /// Typed value: `w32 42;` or `w8 255;` |
|
| 350 | 350 | Val { typ: Type, val: i64 }, |
|
| 351 | 351 | /// Symbol reference: `$symbol` |
|
| 352 | 352 | Sym(*[u8]), |
|
| 353 | 353 | /// Function reference: `$fnName` |
| 357 | 357 | /// Undefined value. Used for padding and `void` returns. |
|
| 358 | 358 | Undef, |
|
| 359 | 359 | } |
|
| 360 | 360 | ||
| 361 | 361 | /// Data initializer value with optional repeat count. |
|
| 362 | - | pub record DataValue { |
|
| 362 | + | export record DataValue { |
|
| 363 | 363 | /// The item contained in the value. |
|
| 364 | 364 | item: DataItem, |
|
| 365 | 365 | /// The number of times the item should be repeated. |
|
| 366 | 366 | count: u32, |
|
| 367 | 367 | } |
|
| 368 | 368 | ||
| 369 | 369 | /// Global data definition. |
|
| 370 | - | pub record Data { |
|
| 370 | + | export record Data { |
|
| 371 | 371 | /// Data name. |
|
| 372 | 372 | name: *[u8], |
|
| 373 | 373 | /// Size in bytes. |
|
| 374 | 374 | size: u32, |
|
| 375 | 375 | /// Alignment requirement. |
| 384 | 384 | /// Initializer values. |
|
| 385 | 385 | values: *[DataValue], |
|
| 386 | 386 | } |
|
| 387 | 387 | ||
| 388 | 388 | /// An IL program (compilation unit). |
|
| 389 | - | pub record Program { |
|
| 389 | + | export record Program { |
|
| 390 | 390 | /// Global data. |
|
| 391 | 391 | data: *[Data], |
|
| 392 | 392 | /// Functions. |
|
| 393 | 393 | fns: *[*Fn], |
|
| 394 | 394 | /// Index of entry point function, if any. |
| 398 | 398 | /////////////////////// |
|
| 399 | 399 | // Utility Functions // |
|
| 400 | 400 | /////////////////////// |
|
| 401 | 401 | ||
| 402 | 402 | /// Get the destination register of an instruction, if any. |
|
| 403 | - | pub fn instrDst(instr: Instr) -> ?Reg { |
|
| 403 | + | export fn instrDst(instr: Instr) -> ?Reg { |
|
| 404 | 404 | match instr { |
|
| 405 | 405 | case Instr::Reserve { dst, .. } => return dst, |
|
| 406 | 406 | case Instr::Load { dst, .. } => return dst, |
|
| 407 | 407 | case Instr::Sload { dst, .. } => return dst, |
|
| 408 | 408 | case Instr::Copy { dst, .. } => return dst, |
| 415 | 415 | else => return nil, |
|
| 416 | 416 | } |
|
| 417 | 417 | } |
|
| 418 | 418 | ||
| 419 | 419 | /// Check if an instruction is a function call. |
|
| 420 | - | pub fn isCall(instr: Instr) -> bool { |
|
| 420 | + | export fn isCall(instr: Instr) -> bool { |
|
| 421 | 421 | match instr { |
|
| 422 | 422 | case Instr::Call { .. }, |
|
| 423 | 423 | Instr::Ecall { .. } => return true, |
|
| 424 | 424 | else => return false, |
|
| 425 | 425 | } |
|
| 426 | 426 | } |
|
| 427 | 427 | ||
| 428 | 428 | /// Call a function for each register used by an instruction. |
|
| 429 | 429 | /// This is called by the register allocator to analyze register usage. |
|
| 430 | - | pub fn forEachReg(instr: Instr, f: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 430 | + | export fn forEachReg(instr: Instr, f: fn(Reg, *mut opaque), ctx: *mut opaque) { |
|
| 431 | 431 | match instr { |
|
| 432 | 432 | case Instr::Reserve { size, .. } => |
|
| 433 | 433 | withReg(size, f, ctx), |
|
| 434 | 434 | case Instr::Load { src, .. } => f(src, ctx), |
|
| 435 | 435 | case Instr::Sload { src, .. } => f(src, ctx), |
lib/std/lang/il/printer.rad
+2 -2
| 523 | 523 | ////////////////////// |
|
| 524 | 524 | // Program printing // |
|
| 525 | 525 | ////////////////////// |
|
| 526 | 526 | ||
| 527 | 527 | /// Print a program. |
|
| 528 | - | pub fn printProgram(out: *mut sexpr::Output, a: *mut alloc::Arena, program: *super::Program) { |
|
| 528 | + | export fn printProgram(out: *mut sexpr::Output, a: *mut alloc::Arena, program: *super::Program) { |
|
| 529 | 529 | // Data declarations. |
|
| 530 | 530 | for data, i in program.data { |
|
| 531 | 531 | writeData(out, a, data); |
|
| 532 | 532 | if i < program.data.len - 1 or program.fns.len > 0 { |
|
| 533 | 533 | write(out, "\n"); |
| 541 | 541 | } |
|
| 542 | 542 | } |
|
| 543 | 543 | } |
|
| 544 | 544 | ||
| 545 | 545 | /// Print a program to a buffer, returning the written slice. |
|
| 546 | - | pub fn printProgramToBuffer( |
|
| 546 | + | export fn printProgramToBuffer( |
|
| 547 | 547 | program: *super::Program, |
|
| 548 | 548 | arena: *mut alloc::Arena, |
|
| 549 | 549 | buf: *mut [u8] |
|
| 550 | 550 | ) -> *[u8] { |
|
| 551 | 551 | let mut pos: u32 = 0; |
lib/std/lang/lower.rad
+12 -12
| 106 | 106 | // Error Handling // |
|
| 107 | 107 | //////////////////// |
|
| 108 | 108 | ||
| 109 | 109 | /// Lowering errors are typically unrecoverable since they indicate bugs in |
|
| 110 | 110 | /// the resolver or malformed AST that should have been caught earlier. |
|
| 111 | - | pub union LowerError { |
|
| 111 | + | export union LowerError { |
|
| 112 | 112 | /// A node's symbol was not set before lowering. |
|
| 113 | 113 | MissingSymbol(*ast::Node), |
|
| 114 | 114 | /// A node's type was not set before lowering. |
|
| 115 | 115 | MissingType(*ast::Node), |
|
| 116 | 116 | /// A node's constant value was not set before lowering. |
| 163 | 163 | /// Allocation failure. |
|
| 164 | 164 | AllocationFailed, |
|
| 165 | 165 | } |
|
| 166 | 166 | ||
| 167 | 167 | /// Print a LowerError for debugging. |
|
| 168 | - | pub fn printError(err: LowerError) { |
|
| 168 | + | export fn printError(err: LowerError) { |
|
| 169 | 169 | match err { |
|
| 170 | 170 | case LowerError::MissingSymbol(_) => io::print("MissingSymbol"), |
|
| 171 | 171 | case LowerError::MissingType(_) => io::print("MissingType"), |
|
| 172 | 172 | case LowerError::MissingConst(_) => io::print("MissingConst"), |
|
| 173 | 173 | case LowerError::ExpectedOptional => io::print("ExpectedOptional"), |
| 246 | 246 | ////////////////////////// |
|
| 247 | 247 | // Core Data Structures // |
|
| 248 | 248 | ////////////////////////// |
|
| 249 | 249 | ||
| 250 | 250 | /// Options controlling the lowering pass. |
|
| 251 | - | pub record LowerOptions { |
|
| 251 | + | export record LowerOptions { |
|
| 252 | 252 | /// Whether to emit source location info. |
|
| 253 | 253 | debug: bool, |
|
| 254 | 254 | /// Whether to lower `@test` functions. |
|
| 255 | 255 | buildTest: bool, |
|
| 256 | 256 | } |
|
| 257 | 257 | ||
| 258 | 258 | /// Module-level lowering context. Shared across all function lowerings. |
|
| 259 | 259 | /// Holds global state like the data section (strings, constants) and provides |
|
| 260 | 260 | /// access to the resolver for type queries. |
|
| 261 | - | pub record Lowerer { |
|
| 261 | + | export record Lowerer { |
|
| 262 | 262 | /// Arena for IL allocations. All IL nodes are allocated here. |
|
| 263 | 263 | arena: *mut alloc::Arena, |
|
| 264 | 264 | /// Allocator backed by the arena. |
|
| 265 | 265 | allocator: alloc::Allocator, |
|
| 266 | 266 | /// Resolver for type information. Used to query types, symbols, and |
| 374 | 374 | // a mapping from [`Var`] to current SSA value. When control flow merges, |
|
| 375 | 375 | // block parameters are inserted to merge values from different control flow paths. |
|
| 376 | 376 | ||
| 377 | 377 | /// A variable handle. Represents a source-level variable during lowering. |
|
| 378 | 378 | /// The same [`Var`] can have different SSA values in different blocks. |
|
| 379 | - | pub record Var(u32); |
|
| 379 | + | export record Var(u32); |
|
| 380 | 380 | ||
| 381 | 381 | /// Metadata for a source-level variable, stored once per function. |
|
| 382 | 382 | /// |
|
| 383 | 383 | /// Each variable declaration in the source creates one [`VarData`] entry in the |
|
| 384 | 384 | /// function's `variables` array, indexed by `id`. This contains static |
| 421 | 421 | // instructions until terminated by a jump, branch, return, or unreachable. |
|
| 422 | 422 | // Blocks can be created before they're filled (forward references for jumps). |
|
| 423 | 423 | ||
| 424 | 424 | /// A handle to a basic block within the current function. |
|
| 425 | 425 | /// Block handles are stable, they don't change as more blocks are added. |
|
| 426 | - | pub record BlockId(u32); |
|
| 426 | + | export record BlockId(u32); |
|
| 427 | 427 | ||
| 428 | 428 | /// Internal block state during construction. |
|
| 429 | 429 | /// |
|
| 430 | 430 | /// The key invariants: |
|
| 431 | 431 | /// |
| 480 | 480 | // Loop and Control Flow Context // |
|
| 481 | 481 | /////////////////////////////////// |
|
| 482 | 482 | ||
| 483 | 483 | /// Context for break/continue statements within a loop. |
|
| 484 | 484 | /// Each nested loop pushes a new context onto the loop stack. |
|
| 485 | - | pub record LoopCtx { |
|
| 485 | + | export record LoopCtx { |
|
| 486 | 486 | /// Where `break` should transfer control (the loop's exit block). |
|
| 487 | 487 | breakTarget: BlockId, |
|
| 488 | 488 | /// Where `continue` should transfer control. |
|
| 489 | 489 | continueTarget: ?BlockId, |
|
| 490 | 490 | } |
| 672 | 672 | /// 2. Iterates over top-level declarations, lowering each. |
|
| 673 | 673 | /// 3. Returns the complete IL program with functions and data section. |
|
| 674 | 674 | /// |
|
| 675 | 675 | /// The resolver must have already processed the AST -- we rely on its type |
|
| 676 | 676 | /// annotations, symbol table, and constant evaluations. |
|
| 677 | - | pub fn lower( |
|
| 677 | + | export fn lower( |
|
| 678 | 678 | res: *resolver::Resolver, |
|
| 679 | 679 | root: *ast::Node, |
|
| 680 | 680 | pkgName: *[u8], |
|
| 681 | 681 | arena: *mut alloc::Arena |
|
| 682 | 682 | ) -> il::Program throws (LowerError) { |
| 706 | 706 | ///////////////////////////////// |
|
| 707 | 707 | // Multi-Module Lowering API // |
|
| 708 | 708 | ///////////////////////////////// |
|
| 709 | 709 | ||
| 710 | 710 | /// Create a lowerer for multi-module compilation. |
|
| 711 | - | pub fn lowerer( |
|
| 711 | + | export fn lowerer( |
|
| 712 | 712 | res: *resolver::Resolver, |
|
| 713 | 713 | graph: *module::ModuleGraph, |
|
| 714 | 714 | pkgName: *[u8], |
|
| 715 | 715 | arena: *mut alloc::Arena, |
|
| 716 | 716 | options: LowerOptions |
| 731 | 731 | }; |
|
| 732 | 732 | } |
|
| 733 | 733 | ||
| 734 | 734 | /// Lower a module's AST into the lowerer accumulator. |
|
| 735 | 735 | /// Call this for each module in the package, then use `finalize` to get the program. |
|
| 736 | - | pub fn lowerModule( |
|
| 736 | + | export fn lowerModule( |
|
| 737 | 737 | low: *mut Lowerer, |
|
| 738 | 738 | moduleId: u16, |
|
| 739 | 739 | root: *ast::Node, |
|
| 740 | 740 | isRoot: bool |
|
| 741 | 741 | ) -> ?u32 throws (LowerError) { |
| 780 | 780 | } |
|
| 781 | 781 | return defaultFnIdx; |
|
| 782 | 782 | } |
|
| 783 | 783 | ||
| 784 | 784 | /// Finalize lowering and return the unified IL program. |
|
| 785 | - | pub fn finalize(low: *Lowerer, defaultFnIdx: ?u32) -> il::Program { |
|
| 785 | + | export fn finalize(low: *Lowerer, defaultFnIdx: ?u32) -> il::Program { |
|
| 786 | 786 | return il::Program { |
|
| 787 | 787 | data: &low.data[..], |
|
| 788 | 788 | fns: &low.fns[..], |
|
| 789 | 789 | defaultFnIdx, |
|
| 790 | 790 | }; |
| 844 | 844 | return nil; |
|
| 845 | 845 | } |
|
| 846 | 846 | ||
| 847 | 847 | /// Set the package context for lowering. |
|
| 848 | 848 | /// Called before lowering each package. |
|
| 849 | - | pub fn setPackage(self: *mut Lowerer, graph: *module::ModuleGraph, pkgName: *[u8]) { |
|
| 849 | + | export fn setPackage(self: *mut Lowerer, graph: *module::ModuleGraph, pkgName: *[u8]) { |
|
| 850 | 850 | self.moduleGraph = graph; |
|
| 851 | 851 | self.pkgName = pkgName; |
|
| 852 | 852 | self.currentMod = nil; |
|
| 853 | 853 | } |
|
| 854 | 854 |
lib/std/lang/module.rad
+21 -21
| 2 | 2 | //! |
|
| 3 | 3 | //! This module tracks source files, parent/child relationships, and basic state for each |
|
| 4 | 4 | //! module so that later phases (parser, semantic analyzer) can resolve imports (`use`) |
|
| 5 | 5 | //! and avoid reloading the same file twice. |
|
| 6 | 6 | ||
| 7 | - | pub mod printer; |
|
| 7 | + | export mod printer; |
|
| 8 | 8 | ||
| 9 | 9 | @test mod tests; |
|
| 10 | 10 | ||
| 11 | 11 | use std::mem; |
|
| 12 | 12 | use std::lang::alloc; |
|
| 13 | 13 | use std::lang::ast; |
|
| 14 | 14 | use std::lang::strings; |
|
| 15 | 15 | ||
| 16 | 16 | /// Maximum number of modules tracked in a single compilation graph. |
|
| 17 | - | pub const MAX_MODULES: u32 = 128; |
|
| 17 | + | export const MAX_MODULES: u32 = 128; |
|
| 18 | 18 | /// Maximum number of characters for a module path. |
|
| 19 | 19 | const MAX_PATH_LEN: u32 = 256; |
|
| 20 | 20 | /// Maximum number of components that make up a logical module path. |
|
| 21 | 21 | const MAX_MODULE_PATH_DEPTH: u32 = 16; |
|
| 22 | 22 | /// Filesystem separator used when constructing child paths. |
|
| 23 | 23 | const PATH_SEP: u8 = '/'; |
|
| 24 | 24 | /// Source file extension handled by the loader. |
|
| 25 | 25 | const SOURCE_EXT: *[u8] = ".rad"; |
|
| 26 | 26 | ||
| 27 | 27 | /// Lifecycle state for modules in the dependency graph. |
|
| 28 | - | pub union ModuleState { |
|
| 28 | + | export union ModuleState { |
|
| 29 | 29 | /// Slot unused or yet to be initialized. |
|
| 30 | 30 | Vacant, |
|
| 31 | 31 | /// Module registered with a known path but not parsed yet. |
|
| 32 | 32 | Registered, |
|
| 33 | 33 | /// Module is currently being parsed (used for cycle detection). |
| 39 | 39 | /// Module finished semantic analysis and is ready for codegen. |
|
| 40 | 40 | Ready, |
|
| 41 | 41 | } |
|
| 42 | 42 | ||
| 43 | 43 | /// Error categories that can be produced by the module graph. |
|
| 44 | - | pub union ModuleError { |
|
| 44 | + | export union ModuleError { |
|
| 45 | 45 | /// Module not found. |
|
| 46 | 46 | NotFound(u16), |
|
| 47 | 47 | /// Adding the module would exceed the fixed storage. |
|
| 48 | 48 | CapacityExceeded, |
|
| 49 | 49 | /// Provided path is missing the required `.rad` extension or otherwise invalid. |
| 53 | 53 | /// Attempt to register a module before its parent. |
|
| 54 | 54 | MissingParent, |
|
| 55 | 55 | } |
|
| 56 | 56 | ||
| 57 | 57 | /// Module metadata recorded inside the dependency graph. |
|
| 58 | - | pub record ModuleEntry { |
|
| 58 | + | export record ModuleEntry { |
|
| 59 | 59 | /// Numeric identifier for the slot (index in the graph array). |
|
| 60 | 60 | id: u16, |
|
| 61 | 61 | /// Package identifier this module belongs to. |
|
| 62 | 62 | packageId: u16, |
|
| 63 | 63 | /// Parent module identifier, or `nil` for root modules. |
| 83 | 83 | /// Source text for this module (for error reporting). |
|
| 84 | 84 | source: ?*[u8], |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Dense storage for all modules referenced by the compilation unit. |
|
| 88 | - | pub record ModuleGraph { |
|
| 88 | + | export record ModuleGraph { |
|
| 89 | 89 | entries: *mut [ModuleEntry], |
|
| 90 | 90 | entriesLen: u32, |
|
| 91 | 91 | pool: *mut strings::Pool, |
|
| 92 | 92 | /// Arena used for all AST node allocations. |
|
| 93 | 93 | arena: ?*ast::NodeArena, |
|
| 94 | 94 | } |
|
| 95 | 95 | ||
| 96 | 96 | /// Initialize an empty module graph backed by the provided storage. |
|
| 97 | - | pub fn moduleGraph( |
|
| 97 | + | export fn moduleGraph( |
|
| 98 | 98 | storage: *mut [ModuleEntry], |
|
| 99 | 99 | pool: *mut strings::Pool, |
|
| 100 | 100 | arena: *mut ast::NodeArena |
|
| 101 | 101 | ) -> ModuleGraph { |
|
| 102 | 102 | return ModuleGraph { |
| 106 | 106 | arena, |
|
| 107 | 107 | }; |
|
| 108 | 108 | } |
|
| 109 | 109 | ||
| 110 | 110 | /// Register a root module residing at `path` for a package. |
|
| 111 | - | pub fn registerRoot(graph: *mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 111 | + | export fn registerRoot(graph: *mut ModuleGraph, packageId: u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 112 | 112 | let name = try basenameSlice(filePath); |
|
| 113 | 113 | return try registerRootWithName(graph, packageId, name, filePath); |
|
| 114 | 114 | } |
|
| 115 | 115 | ||
| 116 | 116 | /// Register a root module with an explicit name and file path for a package. |
|
| 117 | - | pub fn registerRootWithName( |
|
| 117 | + | export fn registerRootWithName( |
|
| 118 | 118 | graph: *mut ModuleGraph, |
|
| 119 | 119 | packageId: u16, |
|
| 120 | 120 | name: *[u8], |
|
| 121 | 121 | filePath: *[u8] |
|
| 122 | 122 | ) -> u16 throws (ModuleError) { |
| 128 | 128 | return m.id; |
|
| 129 | 129 | } |
|
| 130 | 130 | ||
| 131 | 131 | /// Register a child module. |
|
| 132 | 132 | /// Returns the module identifier. |
|
| 133 | - | pub fn registerChild( |
|
| 133 | + | export fn registerChild( |
|
| 134 | 134 | graph: *mut ModuleGraph, |
|
| 135 | 135 | parentId: u16, |
|
| 136 | 136 | name: *[u8], |
|
| 137 | 137 | filePath: *[u8] |
|
| 138 | 138 | ) -> u16 throws (ModuleError) { |
| 161 | 161 | ||
| 162 | 162 | return try addChild(parent, m.id); |
|
| 163 | 163 | } |
|
| 164 | 164 | ||
| 165 | 165 | /// Fetch a read-only view of the module identified by `id`. |
|
| 166 | - | pub fn get(graph: *ModuleGraph, id: u16) -> ?*ModuleEntry { |
|
| 166 | + | export fn get(graph: *ModuleGraph, id: u16) -> ?*ModuleEntry { |
|
| 167 | 167 | if not isValidId(graph, id) { |
|
| 168 | 168 | return nil; |
|
| 169 | 169 | } |
|
| 170 | 170 | return &graph.entries[id as u32]; |
|
| 171 | 171 | } |
| 177 | 177 | } |
|
| 178 | 178 | return &mut graph.entries[id as u32]; |
|
| 179 | 179 | } |
|
| 180 | 180 | ||
| 181 | 181 | /// Return the identifier of the child stored at `index`. |
|
| 182 | - | pub fn childAt(m: *ModuleEntry, index: u32) -> u16 { |
|
| 182 | + | export fn childAt(m: *ModuleEntry, index: u32) -> u16 { |
|
| 183 | 183 | assert index < m.childrenLen, "childAt: index must be valid"; |
|
| 184 | 184 | return m.children[index]; |
|
| 185 | 185 | } |
|
| 186 | 186 | ||
| 187 | 187 | /// Public accessor for a module's directory prefix. |
|
| 188 | - | pub fn moduleDir(m: *ModuleEntry) -> *[u8] { |
|
| 188 | + | export fn moduleDir(m: *ModuleEntry) -> *[u8] { |
|
| 189 | 189 | return &m.filePath[..m.dirLen]; |
|
| 190 | 190 | } |
|
| 191 | 191 | ||
| 192 | 192 | /// Public accessor to the logical module path segments. |
|
| 193 | - | pub fn moduleQualifiedPath(m: *ModuleEntry) -> *[*[u8]] { |
|
| 193 | + | export fn moduleQualifiedPath(m: *ModuleEntry) -> *[*[u8]] { |
|
| 194 | 194 | assert m.pathDepth > 0, "moduleQualifiedPath: path must not be empty"; |
|
| 195 | 195 | return &m.path[..m.pathDepth]; |
|
| 196 | 196 | } |
|
| 197 | 197 | ||
| 198 | 198 | /// Retrieve the lifecycle state for `id`. |
|
| 199 | - | pub fn state(graph: *ModuleGraph, id: u16) -> ModuleState throws (ModuleError) { |
|
| 199 | + | export fn state(graph: *ModuleGraph, id: u16) -> ModuleState throws (ModuleError) { |
|
| 200 | 200 | let m = get(graph, id) else { |
|
| 201 | 201 | throw ModuleError::NotFound(id); |
|
| 202 | 202 | }; |
|
| 203 | 203 | return m.state; |
|
| 204 | 204 | } |
|
| 205 | 205 | ||
| 206 | 206 | /// Record the parsed AST root for `id`. |
|
| 207 | - | pub fn setAst(graph: *mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) { |
|
| 207 | + | export fn setAst(graph: *mut ModuleGraph, id: u16, root: *mut ast::Node) throws (ModuleError) { |
|
| 208 | 208 | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 209 | 209 | m.ast = root; |
|
| 210 | 210 | m.state = ModuleState::Parsed; |
|
| 211 | 211 | } |
|
| 212 | 212 | ||
| 213 | 213 | /// Set the source text for a module. |
|
| 214 | - | pub fn setSource(graph: *mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 214 | + | export fn setSource(graph: *mut ModuleGraph, id: u16, source: *[u8]) throws (ModuleError) { |
|
| 215 | 215 | let m = getMut(graph, id) else throw ModuleError::NotFound(id); |
|
| 216 | 216 | m.source = source; |
|
| 217 | 217 | } |
|
| 218 | 218 | ||
| 219 | 219 | /// Look up a child module by name under the given parent. |
|
| 220 | - | pub fn findChild(graph: *ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 220 | + | export fn findChild(graph: *ModuleGraph, name: *[u8], parentId: u16) -> ?*ModuleEntry { |
|
| 221 | 221 | assert isValidId(graph, parentId), "findChild: parent identifier is valid"; |
|
| 222 | 222 | ||
| 223 | 223 | let parent = &graph.entries[parentId as u32]; |
|
| 224 | 224 | for i in 0..parent.childrenLen { |
|
| 225 | 225 | let childId = parent.children[i]; |
| 232 | 232 | } |
|
| 233 | 233 | ||
| 234 | 234 | /// Parse a file path into components by splitting on '/'. |
|
| 235 | 235 | /// Expects and removes the '.rad' extension from the last component. |
|
| 236 | 236 | /// Returns the number of components extracted, or `nil` if the path is invalid. |
|
| 237 | - | pub fn parsePath(filePath: *[u8], components: *mut [*[u8]]) -> ?u32 { |
|
| 237 | + | export fn parsePath(filePath: *[u8], components: *mut [*[u8]]) -> ?u32 { |
|
| 238 | 238 | let mut count: u32 = 0; |
|
| 239 | 239 | let mut last: u32 = 0; |
|
| 240 | 240 | ||
| 241 | 241 | // Split on '/' to extract all but the last component. |
|
| 242 | 242 | for i in 0..filePath.len { |
| 267 | 267 | ||
| 268 | 268 | /// Register a module from a file path, creating the full hierarchy as needed. |
|
| 269 | 269 | /// The path is split into components and the module hierarchy is built accordingly. |
|
| 270 | 270 | /// If `rootId` is `nil`, registers a new root for the given package. |
|
| 271 | 271 | /// Returns the module ID of the last component. |
|
| 272 | - | pub fn registerFromPath(graph: *mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 272 | + | export fn registerFromPath(graph: *mut ModuleGraph, packageId: u16, rootId: ?u16, filePath: *[u8]) -> u16 throws (ModuleError) { |
|
| 273 | 273 | let root = rootId else { |
|
| 274 | 274 | return try registerRoot(graph, packageId, filePath); |
|
| 275 | 275 | }; |
|
| 276 | 276 | let rootEntry = get(graph, root) else { |
|
| 277 | 277 | panic "registerFromPath: root is missing from storage"; |
| 405 | 405 | return start; |
|
| 406 | 406 | } |
|
| 407 | 407 | ||
| 408 | 408 | /// Trim the `.rad` extension from `path` if present. |
|
| 409 | 409 | /// Returns the path slice without the extension. |
|
| 410 | - | pub fn trimExtension(path: *[u8]) -> ?*[u8] { |
|
| 410 | + | export fn trimExtension(path: *[u8]) -> ?*[u8] { |
|
| 411 | 411 | if path.len < SOURCE_EXT.len { |
|
| 412 | 412 | return nil; |
|
| 413 | 413 | } |
|
| 414 | 414 | let extStart = path.len - SOURCE_EXT.len; |
|
| 415 | 415 | for ext, i in SOURCE_EXT { |
lib/std/lang/module/printer.rad
+1 -1
| 62 | 62 | sexpr::Expr::List { head: "::", tail: pathBuf, multiline: false } |
|
| 63 | 63 | ], childBuf); |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Print the entire module graph in S-expression format. |
|
| 67 | - | pub fn printGraph(graph: *super::ModuleGraph, arena: *mut alloc::Arena) { |
|
| 67 | + | export fn printGraph(graph: *super::ModuleGraph, arena: *mut alloc::Arena) { |
|
| 68 | 68 | // Print all root modules. |
|
| 69 | 69 | for i in 0..graph.entriesLen { |
|
| 70 | 70 | let entry = &graph.entries[i]; |
|
| 71 | 71 | if entry.parent == nil { |
|
| 72 | 72 | sexpr::print(subtreeToExpr(arena, graph, entry), 0); |
lib/std/lang/package.rad
+4 -4
| 4 | 4 | use std::lang::ast; |
|
| 5 | 5 | use std::lang::module; |
|
| 6 | 6 | use std::lang::strings; |
|
| 7 | 7 | ||
| 8 | 8 | /// Maximum number of packages processed in a single invocation. |
|
| 9 | - | pub const MAX_PACKAGES: u32 = 4; |
|
| 9 | + | export const MAX_PACKAGES: u32 = 4; |
|
| 10 | 10 | ||
| 11 | 11 | /// A compilation unit. |
|
| 12 | - | pub record Package { |
|
| 12 | + | export record Package { |
|
| 13 | 13 | /// Package identifier (index in the package array). |
|
| 14 | 14 | id: u16, |
|
| 15 | 15 | /// Package name. |
|
| 16 | 16 | name: *[u8], |
|
| 17 | 17 | /// Root module identifier for this package, or `nil` if not yet registered. |
|
| 18 | 18 | // TODO: This shouldn't be optional. |
|
| 19 | 19 | rootModuleId: ?u16, |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Initialize `pkg` with the provided name and ID. |
|
| 23 | - | pub fn init( |
|
| 23 | + | export fn init( |
|
| 24 | 24 | pkg: *mut Package, |
|
| 25 | 25 | id: u16, |
|
| 26 | 26 | name: *[u8], |
|
| 27 | 27 | pool: *mut strings::Pool |
|
| 28 | 28 | ) -> *mut Package { |
| 32 | 32 | ||
| 33 | 33 | return pkg; |
|
| 34 | 34 | } |
|
| 35 | 35 | ||
| 36 | 36 | /// Register a module described by the file path. |
|
| 37 | - | pub fn registerModule(pkg: *mut Package, graph: *mut module::ModuleGraph, filePath: *[u8]) -> u16 |
|
| 37 | + | export fn registerModule(pkg: *mut Package, graph: *mut module::ModuleGraph, filePath: *[u8]) -> u16 |
|
| 38 | 38 | throws (module::ModuleError) |
|
| 39 | 39 | { |
|
| 40 | 40 | let modId = try module::registerFromPath(graph, pkg.id, pkg.rootModuleId, filePath); |
|
| 41 | 41 | // First registered module becomes the root. |
|
| 42 | 42 | if pkg.rootModuleId == nil { |
lib/std/lang/parser.rad
+26 -26
| 1 | 1 | //! Recursive descent parser for the Radiance programming language. |
|
| 2 | - | @test pub mod tests; |
|
| 2 | + | @test export mod tests; |
|
| 3 | 3 | ||
| 4 | 4 | use std::mem; |
|
| 5 | 5 | use std::io; |
|
| 6 | 6 | use std::lang::alloc; |
|
| 7 | 7 | use std::lang::ast; |
|
| 8 | 8 | use std::lang::strings; |
|
| 9 | 9 | use std::lang::scanner; |
|
| 10 | 10 | ||
| 11 | 11 | /// Maximum `u32` value. |
|
| 12 | - | pub const U32_MAX: u32 = 0xFFFFFFFF; |
|
| 12 | + | export const U32_MAX: u32 = 0xFFFFFFFF; |
|
| 13 | 13 | /// Maximum representable `u64` value. |
|
| 14 | - | pub const U64_MAX: u64 = 0xFFFFFFFFFFFFFFFF; |
|
| 14 | + | export const U64_MAX: u64 = 0xFFFFFFFFFFFFFFFF; |
|
| 15 | 15 | /// Maximum number of fields in a record. |
|
| 16 | - | pub const MAX_RECORD_FIELDS: u32 = 32; |
|
| 16 | + | export const MAX_RECORD_FIELDS: u32 = 32; |
|
| 17 | 17 | ||
| 18 | 18 | /// Maximum number of parser errors before aborting. |
|
| 19 | 19 | const MAX_ERRORS: u32 = 8; |
|
| 20 | 20 | ||
| 21 | 21 | /// Parser error type. |
|
| 22 | - | pub union ParseError { |
|
| 22 | + | export union ParseError { |
|
| 23 | 23 | /// Encountered a token that was not expected in the current context. |
|
| 24 | 24 | UnexpectedToken, |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Represents a parsed name-type-value triple. |
| 60 | 60 | /// and `if` is reserved for guards. |
|
| 61 | 61 | Condition, |
|
| 62 | 62 | } |
|
| 63 | 63 | ||
| 64 | 64 | /// A single parser error with location information. |
|
| 65 | - | pub record Error { |
|
| 65 | + | export record Error { |
|
| 66 | 66 | /// Human-readable error message. |
|
| 67 | 67 | message: *[u8], |
|
| 68 | 68 | /// The token where the error occurred. |
|
| 69 | 69 | token: scanner::Token, |
|
| 70 | 70 | } |
| 134 | 134 | return nil, |
|
| 135 | 135 | } |
|
| 136 | 136 | } |
|
| 137 | 137 | ||
| 138 | 138 | /// Parser state. |
|
| 139 | - | pub record Parser { |
|
| 139 | + | export record Parser { |
|
| 140 | 140 | /// The scanner that provides tokens. |
|
| 141 | 141 | scanner: scanner::Scanner, |
|
| 142 | 142 | /// The current token being examined. |
|
| 143 | 143 | current: scanner::Token, |
|
| 144 | 144 | /// The most recently consumed token. |
| 152 | 152 | /// Current parsing context (normal or conditional). |
|
| 153 | 153 | context: Context, |
|
| 154 | 154 | } |
|
| 155 | 155 | ||
| 156 | 156 | /// Create a new parser initialized with the given source kind, source and node arena. |
|
| 157 | - | pub fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> Parser { |
|
| 157 | + | export fn mkParser(sourceLoc: scanner::SourceLoc, source: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> Parser { |
|
| 158 | 158 | return Parser { |
|
| 159 | 159 | scanner: scanner::scanner(sourceLoc, source, pool), |
|
| 160 | 160 | current: scanner::invalid(0, ""), |
|
| 161 | 161 | previous: scanner::invalid(0, ""), |
|
| 162 | 162 | errors: ErrorList { list: undefined, count: 0 }, |
| 170 | 170 | fn nodeBool(p: *mut Parser, value: bool) -> *ast::Node { |
|
| 171 | 171 | return node(p, ast::NodeValue::Bool(value)); |
|
| 172 | 172 | } |
|
| 173 | 173 | ||
| 174 | 174 | /// Convert a single ASCII digit into its numeric value for the given radix. |
|
| 175 | - | pub fn digitFromAscii(ch: u8, radix: u32) -> ?u32 { |
|
| 175 | + | export fn digitFromAscii(ch: u8, radix: u32) -> ?u32 { |
|
| 176 | 176 | assert radix >= 2 and radix <= 36; |
|
| 177 | 177 | ||
| 178 | 178 | // Default to an out-of-range value so non-digits fall through to `nil`. |
|
| 179 | 179 | let mut value: u32 = 36; |
|
| 180 | 180 |
| 489 | 489 | } |
|
| 490 | 490 | return result; |
|
| 491 | 491 | } |
|
| 492 | 492 | ||
| 493 | 493 | /// Parse a conditional expression. |
|
| 494 | - | pub fn parseCond(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 494 | + | export fn parseCond(p: *mut Parser) -> *ast::Node throws (ParseError) { |
|
| 495 | 495 | let saved = p.context; |
|
| 496 | 496 | p.context = Context::Condition; |
|
| 497 | 497 | let expr = try parseExpr(p); |
|
| 498 | 498 | p.context = saved; |
|
| 499 | 499 |
| 806 | 806 | ||
| 807 | 807 | /// Parse a single expression. |
|
| 808 | 808 | /// |
|
| 809 | 809 | /// Parses unary and binary operators using precedence climbing. |
|
| 810 | 810 | /// Conditional expressions (`x if cond else y`) have lowest precedence. |
|
| 811 | - | pub fn parseExpr(p: *mut Parser) -> *ast::Node |
|
| 811 | + | export fn parseExpr(p: *mut Parser) -> *ast::Node |
|
| 812 | 812 | throws (ParseError) |
|
| 813 | 813 | { |
|
| 814 | 814 | let left = try parseUnary(p); |
|
| 815 | 815 | let expr = try parseBinary(p, left, -1); |
|
| 816 | 816 | return try parseCondExpr(p, expr); |
| 832 | 832 | else => return nil, |
|
| 833 | 833 | } |
|
| 834 | 834 | } |
|
| 835 | 835 | ||
| 836 | 836 | /// Parse an expression statement. |
|
| 837 | - | pub fn parseExprStmt(p: *mut Parser) -> *ast::Node |
|
| 837 | + | export fn parseExprStmt(p: *mut Parser) -> *ast::Node |
|
| 838 | 838 | throws (ParseError) |
|
| 839 | 839 | { |
|
| 840 | 840 | let expr = try parseExpr(p); |
|
| 841 | 841 | ||
| 842 | 842 | if consume(p, scanner::TokenKind::Equal) { |
| 874 | 874 | let mut attrs = ast::nodeSlice(p.arena, 4); |
|
| 875 | 875 | ||
| 876 | 876 | if let attr = tryParseAnnotation(p) { |
|
| 877 | 877 | attrs.append(attr, p.allocator); |
|
| 878 | 878 | } |
|
| 879 | - | if consume(p, scanner::TokenKind::Pub) or consume(p, scanner::TokenKind::Export) { |
|
| 880 | - | let attrNode = nodeAttribute(p, ast::Attribute::Pub); |
|
| 879 | + | if consume(p, scanner::TokenKind::Export) { |
|
| 880 | + | let attrNode = nodeAttribute(p, ast::Attribute::Export); |
|
| 881 | 881 | attrs.append(attrNode, p.allocator); |
|
| 882 | 882 | } |
|
| 883 | 883 | if attrs.len > 0 { |
|
| 884 | 884 | return ast::Attributes { list: attrs }; |
|
| 885 | 885 | } |
| 912 | 912 | } |
|
| 913 | 913 | ||
| 914 | 914 | /// Parse a single statement. |
|
| 915 | 915 | /// |
|
| 916 | 916 | /// Dispatches to the appropriate statement parser based on the current token. |
|
| 917 | - | pub fn parseStmt(p: *mut Parser) -> *ast::Node |
|
| 917 | + | export fn parseStmt(p: *mut Parser) -> *ast::Node |
|
| 918 | 918 | throws (ParseError) |
|
| 919 | 919 | { |
|
| 920 | 920 | // TODO: Why is `parseStmt` checking for attributes? |
|
| 921 | 921 | // We should have a `parseDecl` which is top-level, and `parseStmt` which |
|
| 922 | 922 | // is inside functions. |
| 1020 | 1020 | } |
|
| 1021 | 1021 | ||
| 1022 | 1022 | /// Parse statements until the specified ending token is encountered. |
|
| 1023 | 1023 | /// |
|
| 1024 | 1024 | /// Adds each parsed statement to the given block's statement list. |
|
| 1025 | - | pub fn parseStmtsUntil(p: *mut Parser, end: scanner::TokenKind, blk: *mut ast::Block) |
|
| 1025 | + | export fn parseStmtsUntil(p: *mut Parser, end: scanner::TokenKind, blk: *mut ast::Block) |
|
| 1026 | 1026 | throws (ParseError) |
|
| 1027 | 1027 | { |
|
| 1028 | 1028 | while not check(p, end) { |
|
| 1029 | 1029 | let stmt = try parseStmt(p); |
|
| 1030 | 1030 | blk.statements.append(stmt, p.allocator); |
| 1040 | 1040 | } |
|
| 1041 | 1041 | } |
|
| 1042 | 1042 | } |
|
| 1043 | 1043 | ||
| 1044 | 1044 | /// Parse a block of statements enclosed in curly braces. |
|
| 1045 | - | pub fn parseBlock(p: *mut Parser) -> *ast::Node |
|
| 1045 | + | export fn parseBlock(p: *mut Parser) -> *ast::Node |
|
| 1046 | 1046 | throws (ParseError) |
|
| 1047 | 1047 | { |
|
| 1048 | 1048 | let start = p.current; |
|
| 1049 | 1049 | let mut blk = mkBlock(p, 64); |
|
| 1050 | 1050 |
| 1143 | 1143 | reportError(p, p.current, err); |
|
| 1144 | 1144 | return ParseError::UnexpectedToken; |
|
| 1145 | 1145 | } |
|
| 1146 | 1146 | ||
| 1147 | 1147 | /// Print all errors that have been collected during parsing. |
|
| 1148 | - | pub fn printErrors(p: *Parser) { |
|
| 1148 | + | export fn printErrors(p: *Parser) { |
|
| 1149 | 1149 | for i in 0..p.errors.count { |
|
| 1150 | 1150 | let e = p.errors.list[i]; |
|
| 1151 | 1151 | if let loc = scanner::getLocation( |
|
| 1152 | 1152 | p.scanner.sourceLoc, p.scanner.source, e.token.offset |
|
| 1153 | 1153 | ) { |
| 1174 | 1174 | io::print("\n"); |
|
| 1175 | 1175 | } |
|
| 1176 | 1176 | } |
|
| 1177 | 1177 | ||
| 1178 | 1178 | /// Check whether the current token matches the expected kind. |
|
| 1179 | - | pub fn check(p: *Parser, kind: scanner::TokenKind) -> bool { |
|
| 1179 | + | export fn check(p: *Parser, kind: scanner::TokenKind) -> bool { |
|
| 1180 | 1180 | return p.current.kind == kind; |
|
| 1181 | 1181 | } |
|
| 1182 | 1182 | ||
| 1183 | 1183 | /// Advance the parser by one token. |
|
| 1184 | - | pub fn advance(p: *mut Parser) { |
|
| 1184 | + | export fn advance(p: *mut Parser) { |
|
| 1185 | 1185 | p.previous = p.current; |
|
| 1186 | 1186 | p.current = scanner::next(&mut p.scanner); |
|
| 1187 | 1187 | } |
|
| 1188 | 1188 | ||
| 1189 | 1189 | /// Parse an `if let` pattern matching statement. |
| 2016 | 2016 | } |
|
| 2017 | 2017 | return path; |
|
| 2018 | 2018 | } |
|
| 2019 | 2019 | ||
| 2020 | 2020 | /// Parse a type annotation. |
|
| 2021 | - | pub fn parseType(p: *mut Parser) -> *ast::Node |
|
| 2021 | + | export fn parseType(p: *mut Parser) -> *ast::Node |
|
| 2022 | 2022 | throws (ParseError) |
|
| 2023 | 2023 | { |
|
| 2024 | 2024 | match p.current.kind { |
|
| 2025 | 2025 | case scanner::TokenKind::Question => { |
|
| 2026 | 2026 | advance(p); |
| 2250 | 2250 | ident: binding.name, type: binding.type, value, alignment: binding.alignment, mutable, |
|
| 2251 | 2251 | })); |
|
| 2252 | 2252 | } |
|
| 2253 | 2253 | ||
| 2254 | 2254 | /// Parse a module from source text using the provided arena for node storage. |
|
| 2255 | - | pub fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node |
|
| 2255 | + | export fn parse(sourceLoc: scanner::SourceLoc, input: *[u8], arena: *mut ast::NodeArena, pool: *mut strings::Pool) -> *mut ast::Node |
|
| 2256 | 2256 | throws (ParseError) |
|
| 2257 | 2257 | { |
|
| 2258 | 2258 | let mut p = mkParser(sourceLoc, input, arena, pool); |
|
| 2259 | 2259 | return try parseModule(&mut p) catch { |
|
| 2260 | 2260 | printErrors(&p); |
| 2264 | 2264 | ||
| 2265 | 2265 | /// Parse a complete module into a block of top-level statements. |
|
| 2266 | 2266 | /// |
|
| 2267 | 2267 | /// This is the main entry point for parsing an entire Radiance source file. |
|
| 2268 | 2268 | /// The parser must already be initialized with source code. |
|
| 2269 | - | pub fn parseModule(p: *mut Parser) -> *mut ast::Node |
|
| 2269 | + | export fn parseModule(p: *mut Parser) -> *mut ast::Node |
|
| 2270 | 2270 | throws (ParseError) |
|
| 2271 | 2271 | { |
|
| 2272 | 2272 | advance(p); // Set the parser up with a first token. |
|
| 2273 | 2273 | ||
| 2274 | 2274 | let mut blk = mkBlock(p, 512); |
| 2277 | 2277 | ||
| 2278 | 2278 | return node(p, ast::NodeValue::Block(blk)); |
|
| 2279 | 2279 | } |
|
| 2280 | 2280 | ||
| 2281 | 2281 | /// Consume a token of the given kind if present. |
|
| 2282 | - | pub fn consume(p: *mut Parser, kind: scanner::TokenKind) -> bool { |
|
| 2282 | + | export fn consume(p: *mut Parser, kind: scanner::TokenKind) -> bool { |
|
| 2283 | 2283 | if check(p, kind) { |
|
| 2284 | 2284 | advance(p); |
|
| 2285 | 2285 | return true; |
|
| 2286 | 2286 | } |
|
| 2287 | 2287 | return false; |
|
| 2288 | 2288 | } |
|
| 2289 | 2289 | ||
| 2290 | 2290 | /// Expect a token of the given kind or report an error. |
|
| 2291 | - | pub fn expect(p: *mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8] |
|
| 2291 | + | export fn expect(p: *mut Parser, kind: scanner::TokenKind, message: *[u8]) -> *[u8] |
|
| 2292 | 2292 | throws (ParseError) |
|
| 2293 | 2293 | { |
|
| 2294 | 2294 | if not consume(p, kind) { |
|
| 2295 | 2295 | reportError(p, p.current, message); |
|
| 2296 | 2296 | throw ParseError::UnexpectedToken; |
| 2353 | 2353 | } |
|
| 2354 | 2354 | ||
| 2355 | 2355 | /// Parse an instance block. |
|
| 2356 | 2356 | /// Syntax: `instance Trait for Type { fn (t: *mut Type) fnord(..) {..} }` |
|
| 2357 | 2357 | /// |
|
| 2358 | - | /// Instance declarations do not accept attributes (e.g. `pub`). |
|
| 2358 | + | /// Instance declarations do not accept attributes (e.g. `export`). |
|
| 2359 | 2359 | /// Visibility is determined by the trait declaration itself. |
|
| 2360 | 2360 | fn parseInstanceDecl(p: *mut Parser) -> *ast::Node |
|
| 2361 | 2361 | throws (ParseError) |
|
| 2362 | 2362 | { |
|
| 2363 | 2363 | try expect(p, scanner::TokenKind::Instance, "expected `instance`"); |
lib/std/lang/parser/tests.rad
+11 -11
| 89 | 89 | ||
| 90 | 90 | return root; |
|
| 91 | 91 | } |
|
| 92 | 92 | ||
| 93 | 93 | /// Parse a single expression from a string. |
|
| 94 | - | pub fn parseExprStr(input: *[u8]) -> *ast::Node |
|
| 94 | + | export fn parseExprStr(input: *[u8]) -> *ast::Node |
|
| 95 | 95 | throws (super::ParseError) |
|
| 96 | 96 | { |
|
| 97 | 97 | let mut arena = ast::nodeArena(&mut ARENA_STORAGE[..]); |
|
| 98 | 98 | let mut parser = super::mkParser(scanner::SourceLoc::String, input, &mut arena, &mut STRING_POOL); |
|
| 99 | 99 | super::advance(&mut parser); |
| 229 | 229 | } |
|
| 230 | 230 | panic "expectBlockExprStmt: comparing values we don't know how to compare"; |
|
| 231 | 231 | } |
|
| 232 | 232 | ||
| 233 | 233 | /// Get the first statement from a block node. |
|
| 234 | - | pub fn getBlockFirstStmt(blk: *ast::Node) -> *ast::Node |
|
| 234 | + | export fn getBlockFirstStmt(blk: *ast::Node) -> *ast::Node |
|
| 235 | 235 | throws (testing::TestError) |
|
| 236 | 236 | { |
|
| 237 | 237 | let case ast::NodeValue::Block(block) = blk.value |
|
| 238 | 238 | else throw testing::TestError::Failed; |
|
| 239 | 239 |
| 242 | 242 | } |
|
| 243 | 243 | return block.statements[0]; |
|
| 244 | 244 | } |
|
| 245 | 245 | ||
| 246 | 246 | /// Get the last statement from a block node. |
|
| 247 | - | pub fn getBlockLastStmt(blk: *ast::Node) -> *ast::Node |
|
| 247 | + | export fn getBlockLastStmt(blk: *ast::Node) -> *ast::Node |
|
| 248 | 248 | throws (testing::TestError) |
|
| 249 | 249 | { |
|
| 250 | 250 | let case ast::NodeValue::Block(block) = blk.value |
|
| 251 | 251 | else throw testing::TestError::Failed; |
|
| 252 | 252 |
| 797 | 797 | try testing::expect(decl.attrs == nil); |
|
| 798 | 798 | } |
|
| 799 | 799 | ||
| 800 | 800 | /// Test parsing a module declaration with attributes. |
|
| 801 | 801 | @test fn testParseModAttributes() throws (testing::TestError) { |
|
| 802 | - | let node = try! parseStmtStr("pub mod io;"); |
|
| 802 | + | let node = try! parseStmtStr("export mod io;"); |
|
| 803 | 803 | let case ast::NodeValue::Mod(decl) = node.value |
|
| 804 | 804 | else throw testing::TestError::Failed; |
|
| 805 | 805 | ||
| 806 | 806 | try expectIdent(decl.name, "io"); |
|
| 807 | 807 | let attrs = decl.attrs |
| 810 | 810 | try testing::expect(attrs.list.len == 1); |
|
| 811 | 811 | ||
| 812 | 812 | let case ast::NodeValue::Attribute(attr) = attrs.list[0].value |
|
| 813 | 813 | else throw testing::TestError::Failed; |
|
| 814 | 814 | ||
| 815 | - | try testing::expect(attr == ast::Attribute::Pub); |
|
| 816 | - | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Pub)); |
|
| 815 | + | try testing::expect(attr == ast::Attribute::Export); |
|
| 816 | + | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Export)); |
|
| 817 | 817 | } |
|
| 818 | 818 | ||
| 819 | 819 | /// Test parsing an optional type. |
|
| 820 | 820 | @test fn testParseTypeOptional() throws (testing::TestError) { |
|
| 821 | 821 | let node = try! parseTypeStr("?i32"); |
| 1115 | 1115 | try testing::expect(super::check(&parser, scanner::TokenKind::Ident)); |
|
| 1116 | 1116 | } |
|
| 1117 | 1117 | ||
| 1118 | 1118 | /// Test parsing a function declaration with attributes. |
|
| 1119 | 1119 | @test fn testParseFnDeclAttributes() throws (testing::TestError) { |
|
| 1120 | - | let node = try! parseStmtStr("pub fn run();"); |
|
| 1120 | + | let node = try! parseStmtStr("export fn run();"); |
|
| 1121 | 1121 | let case ast::NodeValue::FnDecl(decl) = node.value |
|
| 1122 | 1122 | else throw testing::TestError::Failed; |
|
| 1123 | 1123 | ||
| 1124 | 1124 | try expectIdent(decl.name, "run"); |
|
| 1125 | 1125 |
| 1128 | 1128 | ||
| 1129 | 1129 | try testing::expect(attrs.list.len == 2); |
|
| 1130 | 1130 | ||
| 1131 | 1131 | let case ast::NodeValue::Attribute(attr0) = attrs.list[0].value |
|
| 1132 | 1132 | else throw testing::TestError::Failed; |
|
| 1133 | - | try testing::expect(attr0 == ast::Attribute::Pub); |
|
| 1133 | + | try testing::expect(attr0 == ast::Attribute::Export); |
|
| 1134 | 1134 | ||
| 1135 | 1135 | let case ast::NodeValue::Attribute(attr1) = attrs.list[1].value |
|
| 1136 | 1136 | else throw testing::TestError::Failed; |
|
| 1137 | 1137 | try testing::expect(attr1 == ast::Attribute::Extern); |
|
| 1138 | 1138 | ||
| 1139 | - | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Pub)); |
|
| 1139 | + | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Export)); |
|
| 1140 | 1140 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern)); |
|
| 1141 | 1141 | try testing::expect(decl.body == nil); |
|
| 1142 | 1142 | } |
|
| 1143 | 1143 | ||
| 1144 | 1144 | /// Test parsing a top-level function declaration with inferred extern from `;`. |
| 1175 | 1175 | ||
| 1176 | 1176 | try testing::expect(attrs.list.len == 2); |
|
| 1177 | 1177 | ||
| 1178 | 1178 | let case ast::NodeValue::Attribute(attr0) = attrs.list[0].value |
|
| 1179 | 1179 | else throw testing::TestError::Failed; |
|
| 1180 | - | try testing::expect(attr0 == ast::Attribute::Pub); |
|
| 1180 | + | try testing::expect(attr0 == ast::Attribute::Export); |
|
| 1181 | 1181 | ||
| 1182 | 1182 | let case ast::NodeValue::Attribute(attr1) = attrs.list[1].value |
|
| 1183 | 1183 | else throw testing::TestError::Failed; |
|
| 1184 | 1184 | try testing::expect(attr1 == ast::Attribute::Extern); |
|
| 1185 | 1185 | ||
| 1186 | - | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Pub)); |
|
| 1186 | + | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Export)); |
|
| 1187 | 1187 | try testing::expect(ast::attributesContains(&attrs, ast::Attribute::Extern)); |
|
| 1188 | 1188 | try testing::expect(decl.body == nil); |
|
| 1189 | 1189 | } |
|
| 1190 | 1190 | ||
| 1191 | 1191 | /// Test parsing a scoped identifier type. |
lib/std/lang/resolver.rad
+104 -104
| 1 | 1 | //! Radiance semantic analyzer and type resolver. |
|
| 2 | 2 | //! |
|
| 3 | 3 | //! This module performs scope construction, symbol binding, and identifier |
|
| 4 | 4 | //! resolution on top of the AST produced by the parser. |
|
| 5 | 5 | ||
| 6 | - | pub mod printer; |
|
| 6 | + | export mod printer; |
|
| 7 | 7 | ||
| 8 | 8 | /// Unit tests for the resolver. |
|
| 9 | 9 | @test mod tests; |
|
| 10 | 10 | ||
| 11 | 11 | // TODO: Move to raw vectors to reduce list duplication? |
| 19 | 19 | use std::lang::ast; |
|
| 20 | 20 | use std::lang::parser; |
|
| 21 | 21 | use std::lang::module; |
|
| 22 | 22 | ||
| 23 | 23 | /// Maximum number of diagnostics recorded. |
|
| 24 | - | pub const MAX_ERRORS: u32 = 64; |
|
| 24 | + | export const MAX_ERRORS: u32 = 64; |
|
| 25 | 25 | ||
| 26 | 26 | /// Synthetic function name used when wrapping a bare expression for analysis. |
|
| 27 | - | pub const ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__"; |
|
| 27 | + | export const ANALYZE_EXPR_FN_NAME: *[u8] = "__expr__"; |
|
| 28 | 28 | /// Synthetic function name used when wrapping a block for analysis. |
|
| 29 | - | pub const ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__"; |
|
| 29 | + | export const ANALYZE_BLOCK_FN_NAME: *[u8] = "__block__"; |
|
| 30 | 30 | ||
| 31 | 31 | /// Maximum number of symbols stored within a module scope. |
|
| 32 | - | pub const MAX_MODULE_SYMBOLS: u32 = 512; |
|
| 32 | + | export const MAX_MODULE_SYMBOLS: u32 = 512; |
|
| 33 | 33 | /// Maximum number of symbols stored within a local scope. |
|
| 34 | - | pub const MAX_LOCAL_SYMBOLS: u32 = 32; |
|
| 34 | + | export const MAX_LOCAL_SYMBOLS: u32 = 32; |
|
| 35 | 35 | /// Maximum function parameters. |
|
| 36 | - | pub const MAX_FN_PARAMS: u32 = 8; |
|
| 36 | + | export const MAX_FN_PARAMS: u32 = 8; |
|
| 37 | 37 | /// Maximum function thrown types. |
|
| 38 | - | pub const MAX_FN_THROWS: u32 = 8; |
|
| 38 | + | export const MAX_FN_THROWS: u32 = 8; |
|
| 39 | 39 | /// Maximum number of variants in a union. |
|
| 40 | 40 | /// Nb. This should not be raised above `255`, |
|
| 41 | 41 | /// as tags are stored using 8-bits only. |
|
| 42 | - | pub const MAX_UNION_VARIANTS: u32 = 128; |
|
| 42 | + | export const MAX_UNION_VARIANTS: u32 = 128; |
|
| 43 | 43 | /// Maximum nesting of loops. |
|
| 44 | - | pub const MAX_LOOP_DEPTH: u32 = 16; |
|
| 44 | + | export const MAX_LOOP_DEPTH: u32 = 16; |
|
| 45 | 45 | /// Maximum trait instances. |
|
| 46 | - | pub const MAX_INSTANCES: u32 = 128; |
|
| 46 | + | export const MAX_INSTANCES: u32 = 128; |
|
| 47 | 47 | /// Maximum standalone methods (across all types). |
|
| 48 | - | pub const MAX_METHODS: u32 = 256; |
|
| 48 | + | export const MAX_METHODS: u32 = 256; |
|
| 49 | 49 | ||
| 50 | 50 | /// Trait definition stored in the resolver. |
|
| 51 | - | pub record TraitType { |
|
| 51 | + | export record TraitType { |
|
| 52 | 52 | /// Trait name. |
|
| 53 | 53 | name: *[u8], |
|
| 54 | 54 | /// Method signatures, including from supertraits. |
|
| 55 | 55 | methods: *mut [TraitMethod], |
|
| 56 | 56 | /// Supertraits that must also be implemented. |
|
| 57 | 57 | supertraits: *mut [*TraitType], |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// A single method signature within a trait. |
|
| 61 | - | pub record TraitMethod { |
|
| 61 | + | export record TraitMethod { |
|
| 62 | 62 | /// Method name. |
|
| 63 | 63 | name: *[u8], |
|
| 64 | 64 | /// Function type for the method, excluding the receiver. |
|
| 65 | 65 | fnType: *FnType, |
|
| 66 | 66 | /// Whether the receiver is mutable. |
| 68 | 68 | /// V-table slot index. |
|
| 69 | 69 | index: u32, |
|
| 70 | 70 | } |
|
| 71 | 71 | ||
| 72 | 72 | /// An entry in the trait instance registry. |
|
| 73 | - | pub record InstanceEntry { |
|
| 73 | + | export record InstanceEntry { |
|
| 74 | 74 | /// Trait type descriptor. |
|
| 75 | 75 | traitType: *TraitType, |
|
| 76 | 76 | /// Concrete type that implements the trait. |
|
| 77 | 77 | concreteType: Type, |
|
| 78 | 78 | /// Name of the concrete type. |
| 82 | 82 | /// Method symbols for each trait method, in declaration order. |
|
| 83 | 83 | methods: *mut [*mut Symbol], |
|
| 84 | 84 | } |
|
| 85 | 85 | ||
| 86 | 86 | /// An entry in the method registry. |
|
| 87 | - | pub record MethodEntry { |
|
| 87 | + | export record MethodEntry { |
|
| 88 | 88 | /// Concrete type that owns the method. |
|
| 89 | 89 | concreteType: Type, |
|
| 90 | 90 | /// Name of the concrete type. |
|
| 91 | 91 | concreteTypeName: *[u8], |
|
| 92 | 92 | /// Method name. |
| 98 | 98 | /// Symbol for the method. |
|
| 99 | 99 | symbol: *mut Symbol, |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | 102 | /// Identifier for the synthetic `len` field. |
|
| 103 | - | pub const LEN_FIELD: *[u8] = "len"; |
|
| 103 | + | export const LEN_FIELD: *[u8] = "len"; |
|
| 104 | 104 | /// Identifier for the synthetic `ptr` field. |
|
| 105 | - | pub const PTR_FIELD: *[u8] = "ptr"; |
|
| 105 | + | export const PTR_FIELD: *[u8] = "ptr"; |
|
| 106 | 106 | /// Identifier for the synthetic `cap` field. |
|
| 107 | - | pub const CAP_FIELD: *[u8] = "cap"; |
|
| 107 | + | export const CAP_FIELD: *[u8] = "cap"; |
|
| 108 | 108 | ||
| 109 | 109 | /// Maximum `u16` value. |
|
| 110 | 110 | const U16_MAX: u16 = 0xFFFF; |
|
| 111 | 111 | /// Maximum `u8` value. |
|
| 112 | 112 | const U8_MAX: u16 = 0xFF; |
| 128 | 128 | const I64_MIN: i64 = -9223372036854775808; |
|
| 129 | 129 | /// Maximum `i64` value: 2^63 - 1. |
|
| 130 | 130 | const I64_MAX: i64 = 9223372036854775807; |
|
| 131 | 131 | ||
| 132 | 132 | /// Size of a pointer in bytes. |
|
| 133 | - | pub const PTR_SIZE: u32 = 8; |
|
| 133 | + | export const PTR_SIZE: u32 = 8; |
|
| 134 | 134 | ||
| 135 | 135 | /// Information about a record or tuple field. |
|
| 136 | - | pub record RecordField { |
|
| 136 | + | export record RecordField { |
|
| 137 | 137 | /// Field name, `nil` for positional fields. |
|
| 138 | 138 | name: ?*[u8], |
|
| 139 | 139 | /// Field type. |
|
| 140 | 140 | fieldType: Type, |
|
| 141 | 141 | /// Byte offset from the start of the record. |
| 148 | 148 | valueType: Type, |
|
| 149 | 149 | symbol: *mut Symbol, |
|
| 150 | 150 | } |
|
| 151 | 151 | ||
| 152 | 152 | /// Array type payload. |
|
| 153 | - | pub record ArrayType { |
|
| 153 | + | export record ArrayType { |
|
| 154 | 154 | item: *Type, |
|
| 155 | 155 | length: u32, |
|
| 156 | 156 | } |
|
| 157 | 157 | ||
| 158 | 158 | /// Record nominal type. |
|
| 159 | - | pub record RecordType { |
|
| 159 | + | export record RecordType { |
|
| 160 | 160 | fields: *[RecordField], |
|
| 161 | 161 | labeled: bool, |
|
| 162 | 162 | /// Cached layout. |
|
| 163 | 163 | layout: Layout, |
|
| 164 | 164 | } |
|
| 165 | 165 | ||
| 166 | 166 | /// Union nominal type. |
|
| 167 | - | pub record UnionType { |
|
| 167 | + | export record UnionType { |
|
| 168 | 168 | variants: *[UnionVariant], |
|
| 169 | 169 | /// Cached layout. |
|
| 170 | 170 | layout: Layout, |
|
| 171 | 171 | /// Cached payload offset within the union aggregate. |
|
| 172 | 172 | valOffset: u32, |
|
| 173 | 173 | /// If all variants have void payloads. |
|
| 174 | 174 | isAllVoid: bool, |
|
| 175 | 175 | } |
|
| 176 | 176 | ||
| 177 | 177 | /// Metadata for user-defined types. |
|
| 178 | - | pub union NominalType { |
|
| 178 | + | export union NominalType { |
|
| 179 | 179 | /// Placeholder for a type that hasn't been fully resolved yet. |
|
| 180 | 180 | /// Stores the declaration node for lazy resolution. |
|
| 181 | 181 | Placeholder(*ast::Node), |
|
| 182 | 182 | Record(RecordType), |
|
| 183 | 183 | Union(UnionType), |
|
| 184 | 184 | } |
|
| 185 | 185 | ||
| 186 | 186 | /// Coercion plan, when coercion from one type to another. |
|
| 187 | - | pub union Coercion { |
|
| 187 | + | export union Coercion { |
|
| 188 | 188 | /// No coercion, eg. `T -> T`. |
|
| 189 | 189 | Identity, |
|
| 190 | 190 | /// Eg. `u8 -> i32`. Stores both source and target types for lowering. |
|
| 191 | 191 | NumericCast { from: Type, to: Type }, |
|
| 192 | 192 | /// Eg. `T -> ?T`. Stores the inner value type. |
| 209 | 209 | /// Scope containing the module's declarations. |
|
| 210 | 210 | scope: *mut Scope, |
|
| 211 | 211 | } |
|
| 212 | 212 | ||
| 213 | 213 | /// Type layout. |
|
| 214 | - | pub record Layout { |
|
| 214 | + | export record Layout { |
|
| 215 | 215 | /// Size in bytes. |
|
| 216 | 216 | size: u32, |
|
| 217 | 217 | /// Alignment in bytes. |
|
| 218 | 218 | alignment: u32, |
|
| 219 | 219 | } |
| 225 | 225 | isAllVoid: bool, |
|
| 226 | 226 | } |
|
| 227 | 227 | ||
| 228 | 228 | /// Pre-computed metadata for slice range expressions. |
|
| 229 | 229 | /// Used by the lowerer. |
|
| 230 | - | pub record SliceRangeInfo { |
|
| 230 | + | export record SliceRangeInfo { |
|
| 231 | 231 | /// Element type of the resulting slice. |
|
| 232 | 232 | itemType: *Type, |
|
| 233 | 233 | /// Whether the resulting slice is mutable. |
|
| 234 | 234 | mutable: bool, |
|
| 235 | 235 | /// Static capacity if container is an array. |
|
| 236 | 236 | capacity: ?u32, |
|
| 237 | 237 | } |
|
| 238 | 238 | ||
| 239 | 239 | /// Pre-computed metadata for `for` loop iteration. |
|
| 240 | 240 | /// Used by the lowerer to avoid re-analyzing the iterable type. |
|
| 241 | - | pub union ForLoopInfo { |
|
| 241 | + | export union ForLoopInfo { |
|
| 242 | 242 | /// Iterating over a range expression (e.g., `for i in 0..n`). |
|
| 243 | 243 | Range { |
|
| 244 | 244 | valType: *Type, |
|
| 245 | 245 | range: ast::Range, |
|
| 246 | 246 | bindingName: ?*[u8], |
| 254 | 254 | indexName: ?*[u8] |
|
| 255 | 255 | }, |
|
| 256 | 256 | } |
|
| 257 | 257 | ||
| 258 | 258 | /// Resolved function signature details. |
|
| 259 | - | pub record FnType { |
|
| 259 | + | export record FnType { |
|
| 260 | 260 | paramTypes: *[*Type], |
|
| 261 | 261 | returnType: *Type, |
|
| 262 | 262 | throwList: *[*Type], |
|
| 263 | 263 | localCount: u32, |
|
| 264 | 264 | } |
|
| 265 | 265 | ||
| 266 | 266 | /// Describes a type computed during semantic analysis. |
|
| 267 | - | pub union Type { |
|
| 267 | + | export union Type { |
|
| 268 | 268 | /// A type that couldn't be decided. |
|
| 269 | 269 | Unknown, |
|
| 270 | 270 | /// Types only used during inference. |
|
| 271 | 271 | Nil, Undefined, Int, |
|
| 272 | 272 | /// Primitive types. |
| 304 | 304 | mutable: bool, |
|
| 305 | 305 | }, |
|
| 306 | 306 | } |
|
| 307 | 307 | ||
| 308 | 308 | /// Structured diagnostic payload for type mismatches. |
|
| 309 | - | pub record TypeMismatch { |
|
| 309 | + | export record TypeMismatch { |
|
| 310 | 310 | expected: Type, |
|
| 311 | 311 | actual: Type, |
|
| 312 | 312 | } |
|
| 313 | 313 | ||
| 314 | 314 | /// Structured diagnostic payload for invalid `as` casts. |
|
| 315 | - | pub record InvalidAsCast { |
|
| 315 | + | export record InvalidAsCast { |
|
| 316 | 316 | from: Type, |
|
| 317 | 317 | to: Type, |
|
| 318 | 318 | } |
|
| 319 | 319 | ||
| 320 | 320 | /// Diagnostic payload for argument count mismatches. |
|
| 321 | - | pub record CountMismatch { |
|
| 321 | + | export record CountMismatch { |
|
| 322 | 322 | expected: u32, |
|
| 323 | 323 | actual: u32, |
|
| 324 | 324 | } |
|
| 325 | 325 | ||
| 326 | 326 | /// Detailed payload attached to a symbol, specialized per symbol kind. |
|
| 327 | - | pub union SymbolData { |
|
| 327 | + | export union SymbolData { |
|
| 328 | 328 | /// Payload describing mutable bindings like variables or functions. |
|
| 329 | 329 | Value { |
|
| 330 | 330 | /// Whether the binding permits mutation. |
|
| 331 | 331 | mutable: bool, |
|
| 332 | 332 | /// Custom alignment requirement, or 0 for default. |
| 367 | 367 | /// Trait symbol. |
|
| 368 | 368 | Trait(*mut TraitType), |
|
| 369 | 369 | } |
|
| 370 | 370 | ||
| 371 | 371 | /// Resolved symbol allocated during semantic analysis. |
|
| 372 | - | pub record Symbol { |
|
| 372 | + | export record Symbol { |
|
| 373 | 373 | /// Symbol name in source code. |
|
| 374 | 374 | name: *[u8], |
|
| 375 | 375 | /// Data associated with the symbol. |
|
| 376 | 376 | data: SymbolData, |
|
| 377 | 377 | /// Bitset of attributes applied to the declaration. |
| 381 | 381 | /// Module ID this symbol belongs to. Only for module-level symbols. |
|
| 382 | 382 | moduleId: ?u16, |
|
| 383 | 383 | } |
|
| 384 | 384 | ||
| 385 | 385 | /// Integer constant payload. |
|
| 386 | - | pub record ConstInt { |
|
| 386 | + | export record ConstInt { |
|
| 387 | 387 | /// Absolute magnitude of the value. |
|
| 388 | 388 | magnitude: u64, |
|
| 389 | 389 | /// Bit width of the integer. |
|
| 390 | 390 | bits: u8, |
|
| 391 | 391 | /// Whether the integer is signed. |
| 393 | 393 | /// Whether the value is negative (only valid when `signed` is true). |
|
| 394 | 394 | negative: bool, |
|
| 395 | 395 | } |
|
| 396 | 396 | ||
| 397 | 397 | /// Constant value recorded for literal nodes. |
|
| 398 | - | pub union ConstValue { |
|
| 398 | + | export union ConstValue { |
|
| 399 | 399 | Bool(bool), |
|
| 400 | 400 | Char(u8), |
|
| 401 | 401 | String(*[u8]), |
|
| 402 | 402 | Int(ConstInt), |
|
| 403 | 403 | } |
| 415 | 415 | max: u64, |
|
| 416 | 416 | }, |
|
| 417 | 417 | } |
|
| 418 | 418 | ||
| 419 | 419 | /// Diagnostic emitted by the analyzer. |
|
| 420 | - | pub record Error { |
|
| 420 | + | export record Error { |
|
| 421 | 421 | /// Error category. |
|
| 422 | 422 | kind: ErrorKind, |
|
| 423 | 423 | /// Node associated with the error, if known. |
|
| 424 | 424 | node: ?*ast::Node, |
|
| 425 | 425 | /// Module ID where this error occurred. |
|
| 426 | 426 | moduleId: u16, |
|
| 427 | 427 | } |
|
| 428 | 428 | ||
| 429 | 429 | /// High-level classification for semantic diagnostics. |
|
| 430 | - | pub union ErrorKind { |
|
| 430 | + | export union ErrorKind { |
|
| 431 | 431 | /// Identifier declared more than once in the same scope. |
|
| 432 | 432 | DuplicateBinding(*[u8]), |
|
| 433 | 433 | /// Identifier referenced before it was declared. |
|
| 434 | 434 | UnresolvedSymbol(*[u8]), |
|
| 435 | 435 | /// Attempted to assign to an immutable binding. |
| 577 | 577 | /// Internal error. |
|
| 578 | 578 | Internal, |
|
| 579 | 579 | } |
|
| 580 | 580 | ||
| 581 | 581 | /// Diagnostics returned by the analyzer. |
|
| 582 | - | pub record Diagnostics { |
|
| 582 | + | export record Diagnostics { |
|
| 583 | 583 | errors: *mut [Error], |
|
| 584 | 584 | } |
|
| 585 | 585 | ||
| 586 | 586 | /// Call context. |
|
| 587 | 587 | union CallCtx { |
| 604 | 604 | scope: *mut Scope, |
|
| 605 | 605 | child: *ast::Node, |
|
| 606 | 606 | } |
|
| 607 | 607 | ||
| 608 | 608 | /// Node-specific resolver metadata. |
|
| 609 | - | pub union NodeExtra { |
|
| 609 | + | export union NodeExtra { |
|
| 610 | 610 | /// No extra data for this node. |
|
| 611 | 611 | None, |
|
| 612 | 612 | /// Resolved field index for record literal fields. |
|
| 613 | 613 | RecordField { index: u32 }, |
|
| 614 | 614 | /// Slice range metadata for subscript expressions with ranges. |
| 635 | 635 | /// Slice `.delete(index)` method call. |
|
| 636 | 636 | SliceDelete { elemType: *Type }, |
|
| 637 | 637 | } |
|
| 638 | 638 | ||
| 639 | 639 | /// Combined resolver metadata for a single AST node. |
|
| 640 | - | pub record NodeData { |
|
| 640 | + | export record NodeData { |
|
| 641 | 641 | /// Resolved type for this node. |
|
| 642 | 642 | ty: Type, |
|
| 643 | 643 | /// Coercion plan applied to this node. |
|
| 644 | 644 | coercion: Coercion, |
|
| 645 | 645 | /// Symbol associated with this node. |
| 656 | 656 | record NodeDataTable { |
|
| 657 | 657 | entries: *mut [NodeData], |
|
| 658 | 658 | } |
|
| 659 | 659 | ||
| 660 | 660 | /// Lexical scope. |
|
| 661 | - | pub record Scope { |
|
| 661 | + | export record Scope { |
|
| 662 | 662 | /// Owning AST node, or `nil` for the root scope. |
|
| 663 | 663 | owner: ?*ast::Node, |
|
| 664 | 664 | /// Parent/enclosing scope. |
|
| 665 | 665 | parent: ?*mut Scope, |
|
| 666 | 666 | /// Module ID if this is a module scope. |
| 691 | 691 | /// This is used to determine whether a loop diverges. |
|
| 692 | 692 | hasBreak: bool, |
|
| 693 | 693 | } |
|
| 694 | 694 | ||
| 695 | 695 | /// Configuration for semantic analysis. |
|
| 696 | - | pub record Config { |
|
| 696 | + | export record Config { |
|
| 697 | 697 | /// Whether we're building in test mode. |
|
| 698 | 698 | buildTest: bool, |
|
| 699 | 699 | } |
|
| 700 | 700 | ||
| 701 | 701 | /// How pattern bindings are created during match. |
|
| 702 | - | pub union MatchBy { |
|
| 702 | + | export union MatchBy { |
|
| 703 | 703 | /// Match by value. |
|
| 704 | 704 | Value, |
|
| 705 | 705 | /// Match by immutable reference. |
|
| 706 | 706 | Ref, |
|
| 707 | 707 | /// Match by mutable reference. |
| 716 | 716 | /// Is the match constant? |
|
| 717 | 717 | isConst: bool |
|
| 718 | 718 | } |
|
| 719 | 719 | ||
| 720 | 720 | /// Result of unwrapping a type for pattern matching. |
|
| 721 | - | pub record MatchSubject { |
|
| 721 | + | export record MatchSubject { |
|
| 722 | 722 | /// The effective type to match against. |
|
| 723 | 723 | effectiveTy: Type, |
|
| 724 | 724 | /// How bindings should be created. |
|
| 725 | 725 | by: MatchBy, |
|
| 726 | 726 | } |
|
| 727 | 727 | ||
| 728 | 728 | /// Unwrap a pointer type for pattern matching. |
|
| 729 | - | pub fn unwrapMatchSubject(ty: Type) -> MatchSubject { |
|
| 729 | + | export fn unwrapMatchSubject(ty: Type) -> MatchSubject { |
|
| 730 | 730 | if let case Type::Pointer { target, mutable } = ty { |
|
| 731 | 731 | let by = MatchBy::MutRef if mutable else MatchBy::Ref; |
|
| 732 | 732 | return MatchSubject { effectiveTy: *target, by }; |
|
| 733 | 733 | } |
|
| 734 | 734 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 735 | 735 | } |
|
| 736 | 736 | ||
| 737 | 737 | /// Global resolver state. |
|
| 738 | - | pub record Resolver { |
|
| 738 | + | export record Resolver { |
|
| 739 | 739 | /// Current scope. |
|
| 740 | 740 | scope: *mut Scope, |
|
| 741 | 741 | /// Package scope containing package roots and top-level symbols. |
|
| 742 | 742 | pkgScope: *mut Scope, |
|
| 743 | 743 | /// Stack of loop contexts for nested loops. |
| 771 | 771 | /// Number of registered standalone methods. |
|
| 772 | 772 | methodsLen: u32, |
|
| 773 | 773 | } |
|
| 774 | 774 | ||
| 775 | 775 | /// Internal error sentinel thrown when analysis cannot proceed. |
|
| 776 | - | pub union ResolveError { |
|
| 776 | + | export union ResolveError { |
|
| 777 | 777 | Failure, |
|
| 778 | 778 | } |
|
| 779 | 779 | ||
| 780 | 780 | /// Node in the type interning linked list. |
|
| 781 | 781 | record TypeNode { |
|
| 782 | 782 | ty: Type, |
|
| 783 | 783 | next: ?*TypeNode, |
|
| 784 | 784 | } |
|
| 785 | 785 | ||
| 786 | 786 | /// Allocate and intern a type in the arena, returning a pointer for deduplication. |
|
| 787 | - | pub fn allocType(self: *mut Resolver, ty: Type) -> *Type { |
|
| 787 | + | export fn allocType(self: *mut Resolver, ty: Type) -> *Type { |
|
| 788 | 788 | // Search existing types for a match. |
|
| 789 | 789 | let mut cursor = self.types; |
|
| 790 | 790 | while let node = cursor { |
|
| 791 | 791 | if node.ty == ty { |
|
| 792 | 792 | return &node.ty; |
| 839 | 839 | } |
|
| 840 | 840 | return nil; |
|
| 841 | 841 | } |
|
| 842 | 842 | ||
| 843 | 843 | /// Storage buffers used by the analyzer. |
|
| 844 | - | pub record ResolverStorage { |
|
| 844 | + | export record ResolverStorage { |
|
| 845 | 845 | /// Unified arena for symbols, scopes, and nominal type. |
|
| 846 | 846 | arena: alloc::Arena, |
|
| 847 | 847 | /// Node semantic metadata indexed by node ID. |
|
| 848 | 848 | nodeData: *mut [NodeData], |
|
| 849 | 849 | /// Package scope. |
| 851 | 851 | /// Error storage. |
|
| 852 | 852 | errors: *mut [Error], |
|
| 853 | 853 | } |
|
| 854 | 854 | ||
| 855 | 855 | /// Input for resolving a single package. |
|
| 856 | - | pub record Pkg { |
|
| 856 | + | export record Pkg { |
|
| 857 | 857 | /// Root module entry. |
|
| 858 | 858 | rootEntry: *module::ModuleEntry, |
|
| 859 | 859 | /// Root AST node. |
|
| 860 | 860 | rootAst: *ast::Node, |
|
| 861 | 861 | } |
|
| 862 | 862 | ||
| 863 | 863 | /// Construct a resolver with module context and backing storage. |
|
| 864 | - | pub fn resolver( |
|
| 864 | + | export fn resolver( |
|
| 865 | 865 | storage: ResolverStorage, |
|
| 866 | 866 | config: Config |
|
| 867 | 867 | ) -> Resolver { |
|
| 868 | 868 | let mut arena = storage.arena; |
|
| 869 | 869 | let symbols = try! alloc::allocSlice( |
| 919 | 919 | methodsLen: 0, |
|
| 920 | 920 | }; |
|
| 921 | 921 | } |
|
| 922 | 922 | ||
| 923 | 923 | /// Return `true` if there are no errors in the diagnostics. |
|
| 924 | - | pub fn success(diag: *Diagnostics) -> bool { |
|
| 924 | + | export fn success(diag: *Diagnostics) -> bool { |
|
| 925 | 925 | return diag.errors.len == 0; |
|
| 926 | 926 | } |
|
| 927 | 927 | ||
| 928 | 928 | /// Retrieve an error diagnostic by index, if present. |
|
| 929 | - | pub fn errorAt(errs: *[Error], index: u32) -> ?*Error { |
|
| 929 | + | export fn errorAt(errs: *[Error], index: u32) -> ?*Error { |
|
| 930 | 930 | if index >= errs.len { |
|
| 931 | 931 | return nil; |
|
| 932 | 932 | } |
|
| 933 | 933 | return &errs[index]; |
|
| 934 | 934 | } |
| 978 | 978 | } |
|
| 979 | 979 | ||
| 980 | 980 | /// Enter a new local scope that is the child of the current scope. |
|
| 981 | 981 | /// This creates a parent/child relationship that means that lookups in the |
|
| 982 | 982 | /// child scope can recurse upwards. |
|
| 983 | - | pub fn enterScope(self: *mut Resolver, owner: *ast::Node) -> *Scope { |
|
| 983 | + | export fn enterScope(self: *mut Resolver, owner: *ast::Node) -> *Scope { |
|
| 984 | 984 | let scope = allocScope(self, owner, MAX_LOCAL_SYMBOLS); |
|
| 985 | 985 | scope.parent = self.scope; |
|
| 986 | 986 | self.scope = scope; |
|
| 987 | 987 | return scope; |
|
| 988 | 988 | } |
|
| 989 | 989 | ||
| 990 | 990 | /// Enter a module scope. Returns an object that can be used to exit the scope. |
|
| 991 | - | pub fn enterModuleScope(self: *mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
|
| 991 | + | export fn enterModuleScope(self: *mut Resolver, owner: *ast::Node, module: *module::ModuleEntry) -> ModuleScope { |
|
| 992 | 992 | let prevScope = self.scope; |
|
| 993 | 993 | let prevMod = self.currentMod; |
|
| 994 | 994 | let scope = allocScope(self, owner, MAX_MODULE_SYMBOLS); |
|
| 995 | 995 | ||
| 996 | 996 | self.scope = scope; |
| 1011 | 1011 | ||
| 1012 | 1012 | return enterModuleScope(self, modRoot, modEntry); |
|
| 1013 | 1013 | } |
|
| 1014 | 1014 | ||
| 1015 | 1015 | /// Exit a module scope, given the object returned by `enterModuleScope`. |
|
| 1016 | - | pub fn exitModuleScope(self: *mut Resolver, entry: ModuleScope) { |
|
| 1016 | + | export fn exitModuleScope(self: *mut Resolver, entry: ModuleScope) { |
|
| 1017 | 1017 | self.scope = entry.prevScope; |
|
| 1018 | 1018 | self.currentMod = entry.prevMod; |
|
| 1019 | 1019 | } |
|
| 1020 | 1020 | ||
| 1021 | 1021 | /// Exit the most recent scope. |
|
| 1022 | - | pub fn exitScope(self: *mut Resolver) { |
|
| 1022 | + | export fn exitScope(self: *mut Resolver) { |
|
| 1023 | 1023 | let parent = self.scope.parent else { |
|
| 1024 | 1024 | // TODO: This should be a panic, but one of the tests hits this |
|
| 1025 | 1025 | // clause, which might be a bug in the generator. |
|
| 1026 | 1026 | return; |
|
| 1027 | 1027 | }; |
| 1169 | 1169 | fn setForLoopInfo(self: *mut Resolver, node: *ast::Node, info: ForLoopInfo) { |
|
| 1170 | 1170 | self.nodeData.entries[node.id].extra = NodeExtra::ForLoop(info); |
|
| 1171 | 1171 | } |
|
| 1172 | 1172 | ||
| 1173 | 1173 | /// Retrieve the constant value associated with a node, if any. |
|
| 1174 | - | pub fn constValueEntry(self: *Resolver, node: *ast::Node) -> ?ConstValue { |
|
| 1174 | + | export fn constValueEntry(self: *Resolver, node: *ast::Node) -> ?ConstValue { |
|
| 1175 | 1175 | return self.nodeData.entries[node.id].constValue; |
|
| 1176 | 1176 | } |
|
| 1177 | 1177 | ||
| 1178 | 1178 | /// Get the resolved record field index for a record literal field node. |
|
| 1179 | - | pub fn recordFieldIndexFor(self: *Resolver, node: *ast::Node) -> ?u32 { |
|
| 1179 | + | export fn recordFieldIndexFor(self: *Resolver, node: *ast::Node) -> ?u32 { |
|
| 1180 | 1180 | if let case NodeExtra::RecordField { index } = self.nodeData.entries[node.id].extra { |
|
| 1181 | 1181 | return index; |
|
| 1182 | 1182 | } |
|
| 1183 | 1183 | return nil; |
|
| 1184 | 1184 | } |
|
| 1185 | 1185 | ||
| 1186 | 1186 | /// Get the slice range metadata for a subscript expression with a range index. |
|
| 1187 | - | pub fn sliceRangeInfoFor(self: *Resolver, node: *ast::Node) -> ?SliceRangeInfo { |
|
| 1187 | + | export fn sliceRangeInfoFor(self: *Resolver, node: *ast::Node) -> ?SliceRangeInfo { |
|
| 1188 | 1188 | if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra { |
|
| 1189 | 1189 | return info; |
|
| 1190 | 1190 | } |
|
| 1191 | 1191 | return nil; |
|
| 1192 | 1192 | } |
|
| 1193 | 1193 | ||
| 1194 | 1194 | /// Get the for-loop metadata for a for-loop node. |
|
| 1195 | - | pub fn forLoopInfoFor(self: *Resolver, node: *ast::Node) -> ?ForLoopInfo { |
|
| 1195 | + | export fn forLoopInfoFor(self: *Resolver, node: *ast::Node) -> ?ForLoopInfo { |
|
| 1196 | 1196 | if let case NodeExtra::ForLoop(info) = self.nodeData.entries[node.id].extra { |
|
| 1197 | 1197 | return info; |
|
| 1198 | 1198 | } |
|
| 1199 | 1199 | return nil; |
|
| 1200 | 1200 | } |
| 1203 | 1203 | fn setProngCatchAll(self: *mut Resolver, node: *ast::Node, catchAll: bool) { |
|
| 1204 | 1204 | self.nodeData.entries[node.id].extra = NodeExtra::MatchProng { catchAll }; |
|
| 1205 | 1205 | } |
|
| 1206 | 1206 | ||
| 1207 | 1207 | /// Check if a prong is catch-all. |
|
| 1208 | - | pub fn isProngCatchAll(self: *Resolver, node: *ast::Node) -> bool { |
|
| 1208 | + | export fn isProngCatchAll(self: *Resolver, node: *ast::Node) -> bool { |
|
| 1209 | 1209 | if let case NodeExtra::MatchProng { catchAll } = self.nodeData.entries[node.id].extra { |
|
| 1210 | 1210 | return catchAll; |
|
| 1211 | 1211 | } |
|
| 1212 | 1212 | return false; |
|
| 1213 | 1213 | } |
| 1216 | 1216 | fn setMatchConst(self: *mut Resolver, node: *ast::Node, isConst: bool) { |
|
| 1217 | 1217 | self.nodeData.entries[node.id].extra = NodeExtra::Match { isConst }; |
|
| 1218 | 1218 | } |
|
| 1219 | 1219 | ||
| 1220 | 1220 | /// Check if a match has all constant patterns. |
|
| 1221 | - | pub fn isMatchConst(self: *Resolver, node: *ast::Node) -> bool { |
|
| 1221 | + | export fn isMatchConst(self: *Resolver, node: *ast::Node) -> bool { |
|
| 1222 | 1222 | if let case NodeExtra::Match { isConst } = self.nodeData.entries[node.id].extra { |
|
| 1223 | 1223 | return isConst; |
|
| 1224 | 1224 | } |
|
| 1225 | 1225 | return false; |
|
| 1226 | 1226 | } |
|
| 1227 | 1227 | ||
| 1228 | 1228 | /// Get the resolver metadata for a node. |
|
| 1229 | - | pub fn nodeData(self: *Resolver, node: *ast::Node) -> *NodeData { |
|
| 1229 | + | export fn nodeData(self: *Resolver, node: *ast::Node) -> *NodeData { |
|
| 1230 | 1230 | return &self.nodeData.entries[node.id]; |
|
| 1231 | 1231 | } |
|
| 1232 | 1232 | ||
| 1233 | 1233 | /// Get the type for a node, or `nil` if unknown. |
|
| 1234 | - | pub fn typeFor(self: *Resolver, node: *ast::Node) -> ?Type { |
|
| 1234 | + | export fn typeFor(self: *Resolver, node: *ast::Node) -> ?Type { |
|
| 1235 | 1235 | let ty = self.nodeData.entries[node.id].ty; |
|
| 1236 | 1236 | if ty == Type::Unknown { |
|
| 1237 | 1237 | return nil; |
|
| 1238 | 1238 | } |
|
| 1239 | 1239 | return ty; |
|
| 1240 | 1240 | } |
|
| 1241 | 1241 | ||
| 1242 | 1242 | /// Get the scope associated with a node. |
|
| 1243 | - | pub fn scopeFor(self: *Resolver, node: *ast::Node) -> ?*mut Scope { |
|
| 1243 | + | export fn scopeFor(self: *Resolver, node: *ast::Node) -> ?*mut Scope { |
|
| 1244 | 1244 | return self.nodeData.entries[node.id].scope; |
|
| 1245 | 1245 | } |
|
| 1246 | 1246 | ||
| 1247 | 1247 | /// Get the symbol bound to a node. |
|
| 1248 | - | pub fn symbolFor(self: *Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 1248 | + | export fn symbolFor(self: *Resolver, node: *ast::Node) -> ?*mut Symbol { |
|
| 1249 | 1249 | return self.nodeData.entries[node.id].sym; |
|
| 1250 | 1250 | } |
|
| 1251 | 1251 | ||
| 1252 | 1252 | /// Get the coercion plan associated with a node, if any. |
|
| 1253 | - | pub fn coercionFor(self: *Resolver, node: *ast::Node) -> ?Coercion { |
|
| 1253 | + | export fn coercionFor(self: *Resolver, node: *ast::Node) -> ?Coercion { |
|
| 1254 | 1254 | let c = self.nodeData.entries[node.id].coercion; |
|
| 1255 | 1255 | if c == Coercion::Identity { |
|
| 1256 | 1256 | return nil; |
|
| 1257 | 1257 | } |
|
| 1258 | 1258 | return c; |
|
| 1259 | 1259 | } |
|
| 1260 | 1260 | ||
| 1261 | 1261 | /// Get the module ID for a symbol by walking up its scope chain. |
|
| 1262 | - | pub fn moduleIdForSymbol(self: *Resolver, sym: *Symbol) -> ?u16 { |
|
| 1262 | + | export fn moduleIdForSymbol(self: *Resolver, sym: *Symbol) -> ?u16 { |
|
| 1263 | 1263 | // For module-level symbols, return the cached module ID. |
|
| 1264 | 1264 | if let id = sym.moduleId { |
|
| 1265 | 1265 | return id; |
|
| 1266 | 1266 | } |
|
| 1267 | 1267 | // For module symbols, return the module ID directly. |
| 1275 | 1275 | return nil; |
|
| 1276 | 1276 | } |
|
| 1277 | 1277 | ||
| 1278 | 1278 | /// Get the binding node for a variant pattern. |
|
| 1279 | 1279 | /// Returns the argument node if this is a variant constructor with a non-placeholder binding. |
|
| 1280 | - | pub fn variantPatternBinding(self: *Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1280 | + | export fn variantPatternBinding(self: *Resolver, pattern: *ast::Node) -> ?*ast::Node { |
|
| 1281 | 1281 | let case ast::NodeValue::Call(call) = pattern.value |
|
| 1282 | 1282 | else return nil; |
|
| 1283 | 1283 | let sym = symbolFor(self, call.callee) |
|
| 1284 | 1284 | else return nil; |
|
| 1285 | 1285 | let case SymbolData::Variant { .. } = sym.data |
| 1327 | 1327 | else => return false, |
|
| 1328 | 1328 | } |
|
| 1329 | 1329 | } |
|
| 1330 | 1330 | ||
| 1331 | 1331 | /// Check if a type is an unsigned integer type. |
|
| 1332 | - | pub fn isUnsignedIntegerType(ty: Type) -> bool { |
|
| 1332 | + | export fn isUnsignedIntegerType(ty: Type) -> bool { |
|
| 1333 | 1333 | match ty { |
|
| 1334 | 1334 | case Type::U8, Type::U16, Type::U32, Type::U64 => return true, |
|
| 1335 | 1335 | else => return false, |
|
| 1336 | 1336 | } |
|
| 1337 | 1337 | } |
| 1343 | 1343 | } |
|
| 1344 | 1344 | return b; |
|
| 1345 | 1345 | } |
|
| 1346 | 1346 | ||
| 1347 | 1347 | /// Get the layout of a type. |
|
| 1348 | - | pub fn getTypeLayout(ty: Type) -> Layout { |
|
| 1348 | + | export fn getTypeLayout(ty: Type) -> Layout { |
|
| 1349 | 1349 | match ty { |
|
| 1350 | 1350 | case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 }, |
|
| 1351 | 1351 | case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 }, |
|
| 1352 | 1352 | case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 }, |
|
| 1353 | 1353 | case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 }, |
| 1365 | 1365 | } |
|
| 1366 | 1366 | } |
|
| 1367 | 1367 | } |
|
| 1368 | 1368 | ||
| 1369 | 1369 | /// Get the layout of a type or value. |
|
| 1370 | - | pub fn getLayout(self: *Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1370 | + | export fn getLayout(self: *Resolver, node: *ast::Node, ty: Type) -> Layout { |
|
| 1371 | 1371 | let mut layout = getTypeLayout(ty); |
|
| 1372 | 1372 | // Check for symbol-specific alignment override. |
|
| 1373 | 1373 | if let sym = symbolFor(self, node) { |
|
| 1374 | 1374 | if let case SymbolData::Value { alignment, .. } = sym.data { |
|
| 1375 | 1375 | if alignment > 0 { |
| 1379 | 1379 | } |
|
| 1380 | 1380 | return layout; |
|
| 1381 | 1381 | } |
|
| 1382 | 1382 | ||
| 1383 | 1383 | /// Get the layout of an array type. |
|
| 1384 | - | pub fn getArrayLayout(arr: ArrayType) -> Layout { |
|
| 1384 | + | export fn getArrayLayout(arr: ArrayType) -> Layout { |
|
| 1385 | 1385 | let itemLayout = getTypeLayout(*arr.item); |
|
| 1386 | 1386 | return Layout { |
|
| 1387 | 1387 | size: itemLayout.size * arr.length, |
|
| 1388 | 1388 | alignment: itemLayout.alignment, |
|
| 1389 | 1389 | }; |
|
| 1390 | 1390 | } |
|
| 1391 | 1391 | ||
| 1392 | 1392 | /// Get the layout of an optional type. |
|
| 1393 | - | pub fn getOptionalLayout(inner: Type) -> Layout { |
|
| 1393 | + | export fn getOptionalLayout(inner: Type) -> Layout { |
|
| 1394 | 1394 | // Nullable types use null pointer optimization -- no tag byte needed. |
|
| 1395 | 1395 | if isNullableType(inner) { |
|
| 1396 | 1396 | return getTypeLayout(inner); |
|
| 1397 | 1397 | } |
|
| 1398 | 1398 | let innerLayout = getTypeLayout(inner); |
| 1405 | 1405 | alignment, |
|
| 1406 | 1406 | }; |
|
| 1407 | 1407 | } |
|
| 1408 | 1408 | ||
| 1409 | 1409 | /// Get the payload offset within an optional aggregate. |
|
| 1410 | - | pub fn getOptionalValOffset(inner: Type) -> u32 { |
|
| 1410 | + | export fn getOptionalValOffset(inner: Type) -> u32 { |
|
| 1411 | 1411 | let innerLayout = getTypeLayout(inner); |
|
| 1412 | 1412 | return mem::alignUp(1, innerLayout.alignment); |
|
| 1413 | 1413 | } |
|
| 1414 | 1414 | ||
| 1415 | 1415 | /// Check if a type is optional. |
|
| 1416 | - | pub fn isOptionalType(ty: Type) -> bool { |
|
| 1416 | + | export fn isOptionalType(ty: Type) -> bool { |
|
| 1417 | 1417 | match ty { |
|
| 1418 | 1418 | case Type::Optional(_) => return true, |
|
| 1419 | 1419 | else => return false, |
|
| 1420 | 1420 | } |
|
| 1421 | 1421 | } |
|
| 1422 | 1422 | ||
| 1423 | 1423 | /// Check if a type uses null pointer optimization. |
|
| 1424 | 1424 | /// This applies to optional pointers `?*T` and optional slices `?*[T]`, |
|
| 1425 | 1425 | /// where `nil` is represented as a null data pointer with no tag byte. |
|
| 1426 | - | pub fn isOptionalPointer(ty: Type) -> bool { |
|
| 1426 | + | export fn isOptionalPointer(ty: Type) -> bool { |
|
| 1427 | 1427 | if let case Type::Optional(inner) = ty { |
|
| 1428 | 1428 | return isNullableType(*inner); |
|
| 1429 | 1429 | } |
|
| 1430 | 1430 | return false; |
|
| 1431 | 1431 | } |
|
| 1432 | 1432 | ||
| 1433 | 1433 | /// Check if a type uses the optional aggregate representation. |
|
| 1434 | - | pub fn isOptionalAggregate(ty: Type) -> bool { |
|
| 1434 | + | export fn isOptionalAggregate(ty: Type) -> bool { |
|
| 1435 | 1435 | if let case Type::Optional(inner) = ty { |
|
| 1436 | 1436 | return not isNullableType(*inner); |
|
| 1437 | 1437 | } |
|
| 1438 | 1438 | return false; |
|
| 1439 | 1439 | } |
|
| 1440 | 1440 | ||
| 1441 | 1441 | /// Check if a type can use null to represent `nil`. |
|
| 1442 | 1442 | /// Pointers and slices have a data pointer that is never null when valid. |
|
| 1443 | - | pub fn isNullableType(ty: Type) -> bool { |
|
| 1443 | + | export fn isNullableType(ty: Type) -> bool { |
|
| 1444 | 1444 | match ty { |
|
| 1445 | 1445 | case Type::Pointer { .. }, Type::Slice { .. } => return true, |
|
| 1446 | 1446 | else => return false, |
|
| 1447 | 1447 | } |
|
| 1448 | 1448 | } |
|
| 1449 | 1449 | ||
| 1450 | 1450 | /// Get the layout of a nominal type. |
|
| 1451 | - | pub fn getNominalLayout(info: NominalType) -> Layout { |
|
| 1451 | + | export fn getNominalLayout(info: NominalType) -> Layout { |
|
| 1452 | 1452 | match info { |
|
| 1453 | 1453 | case NominalType::Placeholder(_) => { |
|
| 1454 | 1454 | panic "getNominalLayout: placeholder type"; |
|
| 1455 | 1455 | } |
|
| 1456 | 1456 | case NominalType::Record(recordType) => { |
| 1461 | 1461 | } |
|
| 1462 | 1462 | } |
|
| 1463 | 1463 | } |
|
| 1464 | 1464 | ||
| 1465 | 1465 | /// Get the layout of a result aggregate with a tag and the larger payload. |
|
| 1466 | - | pub fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout { |
|
| 1466 | + | export fn getResultLayout(payload: Type, throwList: *[*Type]) -> Layout { |
|
| 1467 | 1467 | let payloadLayout = getTypeLayout(payload); |
|
| 1468 | 1468 | let mut maxSize = payloadLayout.size; |
|
| 1469 | 1469 | let mut maxAlign = payloadLayout.alignment; |
|
| 1470 | 1470 | ||
| 1471 | 1471 | for errType in throwList { |
| 1515 | 1515 | *iota = tag + 1; |
|
| 1516 | 1516 | return tag; |
|
| 1517 | 1517 | } |
|
| 1518 | 1518 | ||
| 1519 | 1519 | /// Check if a type is a union without payloads. |
|
| 1520 | - | pub fn isVoidUnion(ty: Type) -> bool { |
|
| 1520 | + | export fn isVoidUnion(ty: Type) -> bool { |
|
| 1521 | 1521 | let case Type::Nominal(NominalType::Union(unionType)) = ty |
|
| 1522 | 1522 | else return false; |
|
| 1523 | 1523 | return unionType.isAllVoid; |
|
| 1524 | 1524 | } |
|
| 1525 | 1525 |
| 1841 | 1841 | } |
|
| 1842 | 1842 | return true; |
|
| 1843 | 1843 | } |
|
| 1844 | 1844 | ||
| 1845 | 1845 | /// Check if two types are structurally equal. |
|
| 1846 | - | pub fn typesEqual(a: Type, b: Type) -> bool { |
|
| 1846 | + | export fn typesEqual(a: Type, b: Type) -> bool { |
|
| 1847 | 1847 | if a == b { |
|
| 1848 | 1848 | return true; |
|
| 1849 | 1849 | } |
|
| 1850 | 1850 | match a { |
|
| 1851 | 1851 | case Type::Pointer { target: aTarget, mutable: aMutable } => { |
| 1871 | 1871 | else => return false, |
|
| 1872 | 1872 | } |
|
| 1873 | 1873 | } |
|
| 1874 | 1874 | ||
| 1875 | 1875 | /// Get the record info from a record type. |
|
| 1876 | - | pub fn getRecord(ty: Type) -> ?RecordType { |
|
| 1876 | + | export fn getRecord(ty: Type) -> ?RecordType { |
|
| 1877 | 1877 | let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil; |
|
| 1878 | 1878 | return recInfo; |
|
| 1879 | 1879 | } |
|
| 1880 | 1880 | ||
| 1881 | 1881 | /// Auto-dereference a type: if it's a pointer, return the target type. |
|
| 1882 | - | pub fn autoDeref(ty: Type) -> Type { |
|
| 1882 | + | export fn autoDeref(ty: Type) -> Type { |
|
| 1883 | 1883 | if let case Type::Pointer { target, .. } = ty { |
|
| 1884 | 1884 | return *target; |
|
| 1885 | 1885 | } |
|
| 1886 | 1886 | return ty; |
|
| 1887 | 1887 | } |
|
| 1888 | 1888 | ||
| 1889 | 1889 | /// Get field info for a record-like type (records, slices) by field index. |
|
| 1890 | - | pub fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
|
| 1890 | + | export fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
|
| 1891 | 1891 | match ty { |
|
| 1892 | 1892 | case Type::Nominal(NominalType::Record(recInfo)) => { |
|
| 1893 | 1893 | if index >= recInfo.fields.len { |
|
| 1894 | 1894 | return nil; |
|
| 1895 | 1895 | } |
| 2146 | 2146 | } |
|
| 2147 | 2147 | return nil; |
|
| 2148 | 2148 | } |
|
| 2149 | 2149 | ||
| 2150 | 2150 | /// Find a symbol by name in a specific scope (matches any symbol kind). |
|
| 2151 | - | pub fn findSymbolInScope(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
|
| 2151 | + | export fn findSymbolInScope(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
|
| 2152 | 2152 | return findInScope(scope, name, isAnySymbol); |
|
| 2153 | 2153 | } |
|
| 2154 | 2154 | ||
| 2155 | 2155 | /// Look up a value symbol by name, searching from the given scope outward. |
|
| 2156 | 2156 | fn findValueSymbol(scope: *Scope, name: *[u8]) -> ?*mut Symbol { |
| 2258 | 2258 | return nil; |
|
| 2259 | 2259 | } |
|
| 2260 | 2260 | ||
| 2261 | 2261 | /// Check if a symbol is accessible from the given scope. |
|
| 2262 | 2262 | /// A symbol is accessible if: |
|
| 2263 | - | /// * It has the `pub` attribute, OR |
|
| 2263 | + | /// * It has the `export` attribute, OR |
|
| 2264 | 2264 | /// * It's being accessed from within the module where it was defined. |
|
| 2265 | 2265 | fn isSymbolVisible(sym: *Symbol, symScope: *Scope, fromScope: *Scope) -> bool { |
|
| 2266 | 2266 | // Public symbols are visible from anywhere. |
|
| 2267 | - | if ast::hasAttribute(sym.attrs, ast::Attribute::Pub) { |
|
| 2267 | + | if ast::hasAttribute(sym.attrs, ast::Attribute::Export) { |
|
| 2268 | 2268 | return true; |
|
| 2269 | 2269 | } |
|
| 2270 | 2270 | // In test mode, @test symbols are visible from anywhere |
|
| 2271 | 2271 | // so the test runner can reference them. |
|
| 2272 | 2272 | if ast::hasAttribute(sym.attrs, ast::Attribute::Test) { |
| 2805 | 2805 | } |
|
| 2806 | 2806 | return false; |
|
| 2807 | 2807 | } |
|
| 2808 | 2808 | ||
| 2809 | 2809 | /// Determine whether a node represents a compile-time constant expression. |
|
| 2810 | - | pub fn isConstExpr(self: *Resolver, node: *ast::Node) -> bool { |
|
| 2810 | + | export fn isConstExpr(self: *Resolver, node: *ast::Node) -> bool { |
|
| 2811 | 2811 | match node.value { |
|
| 2812 | 2812 | case ast::NodeValue::Bool(_), |
|
| 2813 | 2813 | ast::NodeValue::Char(_), |
|
| 2814 | 2814 | ast::NodeValue::Number(_), |
|
| 2815 | 2815 | ast::NodeValue::String(_), |
| 3235 | 3235 | ||
| 3236 | 3236 | return sym; |
|
| 3237 | 3237 | } |
|
| 3238 | 3238 | ||
| 3239 | 3239 | /// Find a trait method by name. |
|
| 3240 | - | pub fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod { |
|
| 3240 | + | export fn findTraitMethod(traitType: *TraitType, name: *[u8]) -> ?*TraitMethod { |
|
| 3241 | 3241 | for i in 0..traitType.methods.len { |
|
| 3242 | 3242 | if traitType.methods[i].name == name { |
|
| 3243 | 3243 | return &traitType.methods[i]; |
|
| 3244 | 3244 | } |
|
| 3245 | 3245 | } |
| 3773 | 3773 | } |
|
| 3774 | 3774 | return nil; |
|
| 3775 | 3775 | } |
|
| 3776 | 3776 | ||
| 3777 | 3777 | /// Look up a standalone method by concrete type and name. |
|
| 3778 | - | pub fn findMethod(self: *Resolver, concreteType: Type, name: *[u8]) -> ?*MethodEntry { |
|
| 3778 | + | export fn findMethod(self: *Resolver, concreteType: Type, name: *[u8]) -> ?*MethodEntry { |
|
| 3779 | 3779 | for i in 0..self.methodsLen { |
|
| 3780 | 3780 | let entry = &self.methods[i]; |
|
| 3781 | 3781 | if typesEqual(entry.concreteType, concreteType) and entry.name == name { |
|
| 3782 | 3782 | return entry; |
|
| 3783 | 3783 | } |
|
| 3784 | 3784 | } |
|
| 3785 | 3785 | return nil; |
|
| 3786 | 3786 | } |
|
| 3787 | 3787 | ||
| 3788 | 3788 | /// Look up a standalone method entry by its symbol. |
|
| 3789 | - | pub fn findMethodBySymbol(self: *Resolver, sym: *mut Symbol) -> ?*MethodEntry { |
|
| 3789 | + | export fn findMethodBySymbol(self: *Resolver, sym: *mut Symbol) -> ?*MethodEntry { |
|
| 3790 | 3790 | for i in 0..self.methodsLen { |
|
| 3791 | 3791 | let entry = &self.methods[i]; |
|
| 3792 | 3792 | if entry.symbol == sym { |
|
| 3793 | 3793 | return entry; |
|
| 3794 | 3794 | } |
| 3920 | 3920 | ||
| 3921 | 3921 | if decl.wildcard { |
|
| 3922 | 3922 | // Import all public symbols from the target module. |
|
| 3923 | 3923 | for i in 0..resolved.scope.symbolsLen { |
|
| 3924 | 3924 | let sym = resolved.scope.symbols[i]; |
|
| 3925 | - | if ast::hasAttribute(sym.attrs, ast::Attribute::Pub) { |
|
| 3925 | + | if ast::hasAttribute(sym.attrs, ast::Attribute::Export) { |
|
| 3926 | 3926 | try addSymbolToScope(self, sym, self.scope, node); |
|
| 3927 | 3927 | } |
|
| 3928 | 3928 | } |
|
| 3929 | 3929 | } else { |
|
| 3930 | 3930 | // Regular module import. |
| 4004 | 4004 | } |
|
| 4005 | 4005 | ||
| 4006 | 4006 | /// Check whether a pattern node is a destructuring pattern that looks |
|
| 4007 | 4007 | /// through structure (union variant, record literal, scope access). |
|
| 4008 | 4008 | /// Identifiers, placeholders, and plain literals are not destructuring. |
|
| 4009 | - | pub fn isDestructuringPattern(pattern: *ast::Node) -> bool { |
|
| 4009 | + | export fn isDestructuringPattern(pattern: *ast::Node) -> bool { |
|
| 4010 | 4010 | match pattern.value { |
|
| 4011 | 4011 | case ast::NodeValue::Call(_), |
|
| 4012 | 4012 | ast::NodeValue::RecordLit(_), |
|
| 4013 | 4013 | ast::NodeValue::ScopeAccess(_) => return true, |
|
| 4014 | 4014 | else => return false, |
| 4186 | 4186 | } |
|
| 4187 | 4187 | ||
| 4188 | 4188 | /// Get the node within a pattern that carries the `UnionVariant` extra. |
|
| 4189 | 4189 | /// For `ScopeAccess` it is the pattern itself, for `RecordLit` it is the |
|
| 4190 | 4190 | /// type name, and for `Call` it is the callee. |
|
| 4191 | - | pub fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node { |
|
| 4191 | + | export fn patternVariantKeyNode(pattern: *ast::Node) -> ?*ast::Node { |
|
| 4192 | 4192 | match pattern.value { |
|
| 4193 | 4193 | case ast::NodeValue::ScopeAccess(_) => return pattern, |
|
| 4194 | 4194 | case ast::NodeValue::RecordLit(lit) => return lit.typeName, |
|
| 4195 | 4195 | case ast::NodeValue::Call(call) => return call.callee, |
|
| 4196 | 4196 | else => return nil, |
| 6578 | 6578 | else => return true, |
|
| 6579 | 6579 | } |
|
| 6580 | 6580 | } |
|
| 6581 | 6581 | ||
| 6582 | 6582 | /// Analyze a standalone expression by wrapping it in a synthetic function. |
|
| 6583 | - | pub fn resolveExpr( |
|
| 6583 | + | export fn resolveExpr( |
|
| 6584 | 6584 | self: *mut Resolver, expr: *ast::Node, arena: *mut ast::NodeArena |
|
| 6585 | 6585 | ) -> Diagnostics throws (ResolveError) { |
|
| 6586 | 6586 | let a = alloc::arenaAllocator(&mut arena.arena); |
|
| 6587 | 6587 | let exprStmt = ast::synthNode(arena, ast::NodeValue::ExprStmt(expr)); |
|
| 6588 | 6588 | let bodyStmts = ast::nodeSlice(arena, 1).append(exprStmt, a); |
| 6601 | 6601 | ||
| 6602 | 6602 | return Diagnostics { errors: self.errors }; |
|
| 6603 | 6603 | } |
|
| 6604 | 6604 | ||
| 6605 | 6605 | /// Analyze a parsed module root, ie. a block of top-level statements. |
|
| 6606 | - | pub fn resolveModuleRoot(self: *mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 6606 | + | export fn resolveModuleRoot(self: *mut Resolver, root: *ast::Node) -> Diagnostics throws (ResolveError) { |
|
| 6607 | 6607 | let case ast::NodeValue::Block(block) = root.value |
|
| 6608 | 6608 | else panic "resolveModuleRoot: expected block for module root"; |
|
| 6609 | 6609 | ||
| 6610 | 6610 | enterScope(self, root); |
|
| 6611 | 6611 | try resolveModuleDecls(self, &block) catch { |
| 6726 | 6726 | if let case ast::NodeValue::Mod(decl) = node.value { |
|
| 6727 | 6727 | try resolveModDecl(res, node, decl); |
|
| 6728 | 6728 | } |
|
| 6729 | 6729 | } |
|
| 6730 | 6730 | // Phase 5b: Process wildcard imports after submodules are resolved, |
|
| 6731 | - | // so that transitive re-exports (pub use foo::*) are visible. |
|
| 6731 | + | // so that transitive re-exports (export use foo::*) are visible. |
|
| 6732 | 6732 | for node in block.statements { |
|
| 6733 | 6733 | if let case ast::NodeValue::Use(decl) = node.value { |
|
| 6734 | 6734 | if decl.wildcard { |
|
| 6735 | 6735 | try resolveUse(res, node, decl); |
|
| 6736 | 6736 | } |
| 6750 | 6750 | try visitDef(self, stmt); |
|
| 6751 | 6751 | } |
|
| 6752 | 6752 | } |
|
| 6753 | 6753 | ||
| 6754 | 6754 | /// Resolve all packages. |
|
| 6755 | - | pub fn resolve(self: *mut Resolver, graph: *module::ModuleGraph, packages: *[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 6755 | + | export fn resolve(self: *mut Resolver, graph: *module::ModuleGraph, packages: *[Pkg]) -> Diagnostics throws (ResolveError) { |
|
| 6756 | 6756 | self.moduleGraph = graph; |
|
| 6757 | 6757 | ||
| 6758 | 6758 | // 1. Bind all package roots to enable cross-package references. |
|
| 6759 | 6759 | for i in 0..packages.len { |
|
| 6760 | 6760 | let pkg = &packages[i]; |
lib/std/lang/resolver/printer.rad
+1 -1
| 506 | 506 | } |
|
| 507 | 507 | io::print("\n"); |
|
| 508 | 508 | } |
|
| 509 | 509 | ||
| 510 | 510 | /// Entry point for printing resolver diagnostics in vim quickfix format. |
|
| 511 | - | pub fn printDiagnostics(diag: *super::Diagnostics, res: *super::Resolver) { |
|
| 511 | + | export fn printDiagnostics(diag: *super::Diagnostics, res: *super::Resolver) { |
|
| 512 | 512 | for i in 0..diag.errors.len { |
|
| 513 | 513 | printError(&diag.errors[i], res); |
|
| 514 | 514 | } |
|
| 515 | 515 | } |
lib/std/lang/resolver/tests.rad
+55 -55
| 597 | 597 | else throw testing::TestError::Failed; |
|
| 598 | 598 | } |
|
| 599 | 599 | ||
| 600 | 600 | @test fn testSymbolStoresFnAttributes() throws (testing::TestError) { |
|
| 601 | 601 | let mut a = testResolver(); |
|
| 602 | - | let program = "@default pub fn f() { return; }"; |
|
| 602 | + | let program = "@default export fn f() { return; }"; |
|
| 603 | 603 | let result = try resolveProgramStr(&mut a, program); |
|
| 604 | 604 | try expectNoErrors(&result); |
|
| 605 | 605 | ||
| 606 | 606 | let scope = super::scopeFor(&a, result.root) |
|
| 607 | 607 | else throw testing::TestError::Failed; |
|
| 608 | 608 | let sym = super::findSymbolInScope(scope, "f") |
|
| 609 | 609 | else throw testing::TestError::Failed; |
|
| 610 | 610 | ||
| 611 | - | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Pub)); |
|
| 611 | + | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 612 | 612 | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 613 | 613 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Extern)); |
|
| 614 | 614 | } |
|
| 615 | 615 | ||
| 616 | 616 | @test fn testSymbolStoresRecordAttributes() throws (testing::TestError) { |
|
| 617 | 617 | let mut a = testResolver(); |
|
| 618 | - | let program = "pub record S { value: i32 }"; |
|
| 618 | + | let program = "export record S { value: i32 }"; |
|
| 619 | 619 | let result = try resolveProgramStr(&mut a, program); |
|
| 620 | 620 | try expectNoErrors(&result); |
|
| 621 | 621 | ||
| 622 | 622 | let scope = super::scopeFor(&a, result.root) |
|
| 623 | 623 | else throw testing::TestError::Failed; |
|
| 624 | 624 | let sym = super::findSymbolInScope(scope, "S") |
|
| 625 | 625 | else throw testing::TestError::Failed; |
|
| 626 | 626 | ||
| 627 | - | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Pub)); |
|
| 627 | + | try testing::expect(ast::hasAttribute(sym.attrs, ast::Attribute::Export)); |
|
| 628 | 628 | try testing::expectNot(ast::hasAttribute(sym.attrs, ast::Attribute::Default)); |
|
| 629 | 629 | } |
|
| 630 | 630 | ||
| 631 | 631 | @test fn testDefaultAttributeRejectedOnRecord() throws (testing::TestError) { |
|
| 632 | 632 | let mut a = testResolver(); |
| 3001 | 3001 | @test fn testResolveModuleCannotAccessParentScope() throws (testing::TestError) { |
|
| 3002 | 3002 | let mut a = testResolver(); |
|
| 3003 | 3003 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3004 | 3004 | ||
| 3005 | 3005 | // Register root and util modules. |
|
| 3006 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; pub fn helper() {}", &mut arena); |
|
| 3006 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; export fn helper() {}", &mut arena); |
|
| 3007 | 3007 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn main() { helper(); }", &mut arena); |
|
| 3008 | 3008 | ||
| 3009 | 3009 | // Resolve should fail: the parent module is not in scope. |
|
| 3010 | 3010 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3011 | 3011 | let err = try expectError(&result); |
| 3018 | 3018 | let mut a = testResolver(); |
|
| 3019 | 3019 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3020 | 3020 | ||
| 3021 | 3021 | // Register root and util modules. |
|
| 3022 | 3022 | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod util; fn main() { util::helper(); }", &mut arena); |
|
| 3023 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "pub fn helper() {}", &mut arena); |
|
| 3023 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "export fn helper() {}", &mut arena); |
|
| 3024 | 3024 | ||
| 3025 | 3025 | // Resolve should succeed: parent can access child. |
|
| 3026 | 3026 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3027 | 3027 | try expectNoErrors(&result); |
|
| 3028 | 3028 | } |
| 3030 | 3030 | @test fn testResolveSiblingModulesCannotAccessDirectly() throws (testing::TestError) { |
|
| 3031 | 3031 | let mut a = testResolver(); |
|
| 3032 | 3032 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3033 | 3033 | ||
| 3034 | 3034 | // Register root with two sibling modules. |
|
| 3035 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; pub mod patrick;", &mut arena); |
|
| 3035 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
|
| 3036 | 3036 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "fn main() { patrick::helper(); }", &mut arena); |
|
| 3037 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "pub fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3037 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3038 | 3038 | ||
| 3039 | 3039 | // Resolve should fail: siblings can't access each other directly. |
|
| 3040 | 3040 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3041 | 3041 | let err = try expectError(&result); |
|
| 3042 | 3042 | let case super::ErrorKind::UnresolvedSymbol(name) = err.kind |
| 3047 | 3047 | @test fn testResolveSiblingModulesViaRoot() throws (testing::TestError) { |
|
| 3048 | 3048 | let mut a = testResolver(); |
|
| 3049 | 3049 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3050 | 3050 | ||
| 3051 | 3051 | // Register root with two sibling modules. |
|
| 3052 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; pub mod patrick;", &mut arena); |
|
| 3052 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod paul; export mod patrick;", &mut arena); |
|
| 3053 | 3053 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "paul", "use root::patrick; fn main() -> i32 { return patrick::helper(); }", &mut arena); |
|
| 3054 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "pub fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3054 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "patrick", "export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3055 | 3055 | ||
| 3056 | 3056 | // Resolve should succeed: siblings can access each other via root. |
|
| 3057 | 3057 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3058 | 3058 | try expectNoErrors(&result); |
|
| 3059 | 3059 | } |
| 3061 | 3061 | @test fn testResolveModuleMutualRecursion() throws (testing::TestError) { |
|
| 3062 | 3062 | let mut a = testResolver(); |
|
| 3063 | 3063 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3064 | 3064 | ||
| 3065 | 3065 | // Register root with two sibling modules that call each other. |
|
| 3066 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod left; pub mod right;", &mut arena); |
|
| 3067 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; pub fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena); |
|
| 3068 | - | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; pub fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena); |
|
| 3066 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod left; export mod right;", &mut arena); |
|
| 3067 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "left", "use root::right; export fn leftHelper() -> i32 { return right::rightHelper(); }", &mut arena); |
|
| 3068 | + | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "right", "use root::left; export fn rightHelper() -> i32 { return left::leftHelper(); }", &mut arena); |
|
| 3069 | 3069 | ||
| 3070 | 3070 | // Resolve should succeed: cyclic use is allowed. |
|
| 3071 | 3071 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3072 | 3072 | try expectNoErrors(&result); |
|
| 3073 | 3073 | } |
| 3075 | 3075 | @test fn testResolveAccessModuleType() throws (testing::TestError) { |
|
| 3076 | 3076 | let mut a = testResolver(); |
|
| 3077 | 3077 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3078 | 3078 | ||
| 3079 | 3079 | // Register root with types module containing a record. |
|
| 3080 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod types; mod app;", &mut arena); |
|
| 3081 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "pub record Point { x: i32, y: i32 }", &mut arena); |
|
| 3080 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 3081 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Point { x: i32, y: i32 }", &mut arena); |
|
| 3082 | 3082 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> i32 { let p = types::Point { x: 1, y: 2 }; return p.x; }", &mut arena); |
|
| 3083 | 3083 | ||
| 3084 | 3084 | // Resolve should succeed: types can be accessed. |
|
| 3085 | 3085 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3086 | 3086 | try expectNoErrors(&result); |
| 3089 | 3089 | @test fn testResolveAccessModuleConstant() throws (testing::TestError) { |
|
| 3090 | 3090 | let mut a = testResolver(); |
|
| 3091 | 3091 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3092 | 3092 | ||
| 3093 | 3093 | // Register root with constants module. |
|
| 3094 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod consts; mod app;", &mut arena); |
|
| 3095 | - | let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "pub const MAX_SIZE: i32 = 100;", &mut arena); |
|
| 3094 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3095 | + | let constantsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export const MAX_SIZE: i32 = 100;", &mut arena); |
|
| 3096 | 3096 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; fn main() -> i32 { return consts::MAX_SIZE; }", &mut arena); |
|
| 3097 | 3097 | ||
| 3098 | 3098 | // Resolve should succeed: constants can be accessed. |
|
| 3099 | 3099 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3100 | 3100 | try expectNoErrors(&result); |
| 3103 | 3103 | @test fn testResolveRootSymbolMustBeImported() throws (testing::TestError) { |
|
| 3104 | 3104 | let mut a = testResolver(); |
|
| 3105 | 3105 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3106 | 3106 | ||
| 3107 | 3107 | // Register deeply nested modules: `root::app::services::auth`. |
|
| 3108 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; pub fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3108 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3109 | 3109 | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "fn run() -> i32 { return root::helper(); }", &mut arena); |
|
| 3110 | 3110 | ||
| 3111 | 3111 | // Resolve should fail: the `root` module must be imported. |
|
| 3112 | 3112 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3113 | 3113 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("root")); |
| 3116 | 3116 | @test fn testResolveUseImportsNestedSymbol() throws (testing::TestError) { |
|
| 3117 | 3117 | let mut a = testResolver(); |
|
| 3118 | 3118 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3119 | 3119 | ||
| 3120 | 3120 | // Register deeply nested modules: `root::app::services::auth`. |
|
| 3121 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod app; mod main;", &mut arena); |
|
| 3122 | - | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "pub mod services;", &mut arena); |
|
| 3123 | - | let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "pub mod auth;", &mut arena); |
|
| 3124 | - | let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "pub fn login() -> i32 { return 1; }", &mut arena); |
|
| 3121 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod app; mod main;", &mut arena); |
|
| 3122 | + | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "export mod services;", &mut arena); |
|
| 3123 | + | let servicesId = try registerModule(&mut MODULE_GRAPH, appId, "services", "export mod auth;", &mut arena); |
|
| 3124 | + | let authId = try registerModule(&mut MODULE_GRAPH, servicesId, "auth", "export fn login() -> i32 { return 1; }", &mut arena); |
|
| 3125 | 3125 | let mainId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::app::services::auth; fn run() -> i32 { return auth::login(); }", &mut arena); |
|
| 3126 | 3126 | let otherId = try registerModule(&mut MODULE_GRAPH, rootId, "other", "use root; fn run() -> i32 { return root::app::services::auth::login(); }", &mut arena); |
|
| 3127 | 3127 | ||
| 3128 | 3128 | // Resolve should succeed: use imports the module symbol. |
|
| 3129 | 3129 | let result = try resolveModuleTree(&mut a, rootId); |
| 3146 | 3146 | @test fn testResolveUsePrivateFn() throws (testing::TestError) { |
|
| 3147 | 3147 | let mut a = testResolver(); |
|
| 3148 | 3148 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3149 | 3149 | ||
| 3150 | 3150 | // Register root with util module containing a private function. |
|
| 3151 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod util; mod app;", &mut arena); |
|
| 3151 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod util; mod app;", &mut arena); |
|
| 3152 | 3152 | let utilId = try registerModule(&mut MODULE_GRAPH, rootId, "util", "fn private() -> i32 { return 42; }", &mut arena); |
|
| 3153 | 3153 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::util; fn main() -> i32 { return util::private(); }", &mut arena); |
|
| 3154 | 3154 | ||
| 3155 | 3155 | // Resolve should fail: function is not public. |
|
| 3156 | 3156 | let result = try resolveModuleTree(&mut a, rootId); |
| 3174 | 3174 | @test fn testResolveUsePublicMod() throws (testing::TestError) { |
|
| 3175 | 3175 | let mut a = testResolver(); |
|
| 3176 | 3176 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3177 | 3177 | ||
| 3178 | 3178 | // Register root with public and private child modules. |
|
| 3179 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; pub mod public;", &mut arena); |
|
| 3179 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod main; export mod public;", &mut arena); |
|
| 3180 | 3180 | let privateId = try registerModule(&mut MODULE_GRAPH, rootId, "public", "{}", &mut arena); |
|
| 3181 | 3181 | let publicId = try registerModule(&mut MODULE_GRAPH, rootId, "main", "use root::public;", &mut arena); |
|
| 3182 | 3182 | ||
| 3183 | 3183 | // Resolve should succeed: module is public. |
|
| 3184 | 3184 | let result = try resolveModuleTree(&mut a, rootId); |
| 3188 | 3188 | @test fn testResolveUseNonPublicType() throws (testing::TestError) { |
|
| 3189 | 3189 | let mut a = testResolver(); |
|
| 3190 | 3190 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3191 | 3191 | ||
| 3192 | 3192 | // Register root with types module containing a private record. |
|
| 3193 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod types; mod app;", &mut arena); |
|
| 3193 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 3194 | 3194 | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "record Priv { x: i32 }", &mut arena); |
|
| 3195 | 3195 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Priv { return types::Priv { x: 1 }; }", &mut arena); |
|
| 3196 | 3196 | ||
| 3197 | 3197 | // Resolve should fail: record is not public. |
|
| 3198 | 3198 | let result = try resolveModuleTree(&mut a, rootId); |
| 3202 | 3202 | @test fn testResolveImportPublicType() throws (testing::TestError) { |
|
| 3203 | 3203 | let mut a = testResolver(); |
|
| 3204 | 3204 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3205 | 3205 | ||
| 3206 | 3206 | // Register root with types module containing a public record. |
|
| 3207 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod types; mod app;", &mut arena); |
|
| 3208 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "pub record Pub { x: i32 }", &mut arena); |
|
| 3207 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod app;", &mut arena); |
|
| 3208 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pub { x: i32 }", &mut arena); |
|
| 3209 | 3209 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::types; fn main() -> types::Pub { return types::Pub { x: 1 }; }", &mut arena); |
|
| 3210 | 3210 | ||
| 3211 | 3211 | // Resolve should succeed: record is public. |
|
| 3212 | 3212 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3213 | 3213 | try expectNoErrors(&result); |
| 3216 | 3216 | @test fn testResolveUseNonPublicStatic() throws (testing::TestError) { |
|
| 3217 | 3217 | let mut a = testResolver(); |
|
| 3218 | 3218 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3219 | 3219 | ||
| 3220 | 3220 | // Register root with statics module containing a private static. |
|
| 3221 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod statics; mod app;", &mut arena); |
|
| 3221 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3222 | 3222 | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "static PRIVATE: i32 = 42;", &mut arena); |
|
| 3223 | 3223 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PRIVATE; }", &mut arena); |
|
| 3224 | 3224 | ||
| 3225 | 3225 | // Resolve should fail: static is not public. |
|
| 3226 | 3226 | let result = try resolveModuleTree(&mut a, rootId); |
| 3230 | 3230 | @test fn testResolveImportPublicStatic() throws (testing::TestError) { |
|
| 3231 | 3231 | let mut a = testResolver(); |
|
| 3232 | 3232 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3233 | 3233 | ||
| 3234 | 3234 | // Register root with statics module containing a public static. |
|
| 3235 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod statics; mod app;", &mut arena); |
|
| 3236 | - | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "pub static PUBLIC: i32 = 42;", &mut arena); |
|
| 3235 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod statics; mod app;", &mut arena); |
|
| 3236 | + | let staticsId = try registerModule(&mut MODULE_GRAPH, rootId, "statics", "export static PUBLIC: i32 = 42;", &mut arena); |
|
| 3237 | 3237 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::statics; fn main() -> i32 { return statics::PUBLIC; }", &mut arena); |
|
| 3238 | 3238 | ||
| 3239 | 3239 | // Resolve should succeed: static is public. |
|
| 3240 | 3240 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3241 | 3241 | try expectNoErrors(&result); |
| 3245 | 3245 | { |
|
| 3246 | 3246 | let mut a = testResolver(); |
|
| 3247 | 3247 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3248 | 3248 | ||
| 3249 | 3249 | // Test function access. |
|
| 3250 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; pub fn parentFn() -> i32 { return 42; }", &mut arena); |
|
| 3250 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export fn parentFn() -> i32 { return 42; }", &mut arena); |
|
| 3251 | 3251 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn main() -> i32 { return super::parentFn(); }", &mut arena); |
|
| 3252 | 3252 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3253 | 3253 | try expectNoErrors(&result); |
|
| 3254 | 3254 | } { |
|
| 3255 | 3255 | let mut a = testResolver(); |
|
| 3256 | 3256 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3257 | 3257 | ||
| 3258 | 3258 | // Test type access. |
|
| 3259 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; pub record Point { x: i32, y: i32 }", &mut arena); |
|
| 3259 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod child; export record Point { x: i32, y: i32 }", &mut arena); |
|
| 3260 | 3260 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "child", "fn make() -> super::Point { return super::Point { x: 1, y: 2 }; }", &mut arena); |
|
| 3261 | 3261 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3262 | 3262 | try expectNoErrors(&result); |
|
| 3263 | 3263 | } |
|
| 3264 | 3264 | } |
| 3266 | 3266 | /// Test nested super access to union variants (e.g. `super::E::A`). |
|
| 3267 | 3267 | @test fn testResolveSuperUnionVariant() throws (testing::TestError) { |
|
| 3268 | 3268 | let mut a = testResolver(); |
|
| 3269 | 3269 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3270 | 3270 | ||
| 3271 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; pub union E { A, B }", &mut arena); |
|
| 3271 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod c; export union E { A, B }", &mut arena); |
|
| 3272 | 3272 | let childId = try registerModule(&mut MODULE_GRAPH, rootId, "c", |
|
| 3273 | 3273 | "fn f(x: super::E) { match x { case super::E::A => {}, case super::E::B => {} } }", |
|
| 3274 | 3274 | &mut arena); |
|
| 3275 | 3275 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3276 | 3276 | try expectNoErrors(&result); |
| 3279 | 3279 | @test fn testResolveUseSuper() throws (testing::TestError) { |
|
| 3280 | 3280 | let mut a = testResolver(); |
|
| 3281 | 3281 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3282 | 3282 | ||
| 3283 | 3283 | // Register root with a function, and a child module that uses super to access it. |
|
| 3284 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; pub mod kate;", &mut arena); |
|
| 3285 | - | let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "pub fn run() {}", &mut arena); |
|
| 3284 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod joe; export mod kate;", &mut arena); |
|
| 3285 | + | let kateId = try registerModule(&mut MODULE_GRAPH, rootId, "kate", "export fn run() {}", &mut arena); |
|
| 3286 | 3286 | let joeId = try registerModule(&mut MODULE_GRAPH, rootId, "joe", "use super::kate; fn main() { kate::run(); }", &mut arena); |
|
| 3287 | 3287 | ||
| 3288 | 3288 | // Resolve should succeed - super allows accessing parent module. |
|
| 3289 | 3289 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3290 | 3290 | try expectNoErrors(&result); |
| 3626 | 3626 | /// Test transitive re-export. |
|
| 3627 | 3627 | @test fn testWildcardReexportTransitive() throws (testing::TestError) { |
|
| 3628 | 3628 | let mut a = testResolver(); |
|
| 3629 | 3629 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3630 | 3630 | ||
| 3631 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; pub mod b;", &mut arena); |
|
| 3631 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "mod a; export mod b;", &mut arena); |
|
| 3632 | 3632 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b; fn main() -> i32 { return b::helper() + b::MAX; }", &mut arena); |
|
| 3633 | - | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; pub use c::*;", &mut arena); |
|
| 3634 | - | let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; pub use d::*; pub fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3635 | - | let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "pub const MAX: i32 = 100;", &mut arena); |
|
| 3633 | + | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "mod c; export use c::*;", &mut arena); |
|
| 3634 | + | let cId = try registerModule(&mut MODULE_GRAPH, bId, "c", "mod d; export use d::*; export fn helper() -> i32 { return 42; }", &mut arena); |
|
| 3635 | + | let dId = try registerModule(&mut MODULE_GRAPH, cId, "d", "export const MAX: i32 = 100;", &mut arena); |
|
| 3636 | 3636 | ||
| 3637 | 3637 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3638 | 3638 | try expectNoErrors(&result); |
|
| 3639 | 3639 | } |
|
| 3640 | 3640 | ||
| 3641 | 3641 | /// Test that wildcard import can access public symbols. |
|
| 3642 | 3642 | @test fn testWildcardImportPublicOnly() throws (testing::TestError) { |
|
| 3643 | 3643 | let mut a = testResolver(); |
|
| 3644 | 3644 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3645 | 3645 | ||
| 3646 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod b; mod a;", &mut arena); |
|
| 3647 | - | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "pub fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 3646 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 3647 | + | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 3648 | 3648 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return public(); }", &mut arena); |
|
| 3649 | 3649 | ||
| 3650 | 3650 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3651 | 3651 | try expectNoErrors(&result); |
|
| 3652 | 3652 | } |
| 3654 | 3654 | /// Test that wildcard import cannot access private symbols. |
|
| 3655 | 3655 | @test fn testWildcardImportSkipsPrivate() throws (testing::TestError) { |
|
| 3656 | 3656 | let mut a = testResolver(); |
|
| 3657 | 3657 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3658 | 3658 | ||
| 3659 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod b; mod a;", &mut arena); |
|
| 3660 | - | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "pub fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 3659 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod b; mod a;", &mut arena); |
|
| 3660 | + | let bId = try registerModule(&mut MODULE_GRAPH, rootId, "b", "export fn public() -> i32 { return 1; } fn private() -> i32 { return 2; }", &mut arena); |
|
| 3661 | 3661 | let aId = try registerModule(&mut MODULE_GRAPH, rootId, "a", "use root::b::*; fn main() -> i32 { return private(); }", &mut arena); |
|
| 3662 | 3662 | ||
| 3663 | 3663 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3664 | 3664 | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("private")); |
|
| 3665 | 3665 | } |
| 3710 | 3710 | /// allowing record fields to use types from imported modules. |
|
| 3711 | 3711 | @test fn testRecordFieldUsesImportedType() throws (testing::TestError) { |
|
| 3712 | 3712 | let mut a = testResolver(); |
|
| 3713 | 3713 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3714 | 3714 | ||
| 3715 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod types; mod scanner;", &mut arena); |
|
| 3716 | - | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "pub record Pool { count: u32 }", &mut arena); |
|
| 3715 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod types; mod scanner;", &mut arena); |
|
| 3716 | + | let typesId = try registerModule(&mut MODULE_GRAPH, rootId, "types", "export record Pool { count: u32 }", &mut arena); |
|
| 3717 | 3717 | let scannerId = try registerModule(&mut MODULE_GRAPH, rootId, "scanner", "use root::types; record Scanner { pool: *types::Pool }", &mut arena); |
|
| 3718 | 3718 | ||
| 3719 | 3719 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3720 | 3720 | try expectNoErrors(&result); |
|
| 3721 | 3721 | } |
| 3726 | 3726 | /// enabling compile-time evaluation of array sizes using imported constants. |
|
| 3727 | 3727 | @test fn testImportedConstantInArraySize() throws (testing::TestError) { |
|
| 3728 | 3728 | let mut a = testResolver(); |
|
| 3729 | 3729 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 3730 | 3730 | ||
| 3731 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod consts; mod app;", &mut arena); |
|
| 3732 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "pub const SIZE: u32 = 8;", &mut arena); |
|
| 3731 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 3732 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export const SIZE: u32 = 8;", &mut arena); |
|
| 3733 | 3733 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; static BUFFER: [u8; consts::SIZE] = undefined;", &mut arena); |
|
| 3734 | 3734 | ||
| 3735 | 3735 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 3736 | 3736 | try expectNoErrors(&result); |
|
| 3737 | 3737 | } |
| 4772 | 4772 | /// Cross-module trait: coerce to trait object and dispatch from a different module. |
|
| 4773 | 4773 | @test fn testResolveTraitCrossModuleCoercion() throws (testing::TestError) { |
|
| 4774 | 4774 | let mut a = testResolver(); |
|
| 4775 | 4775 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4776 | 4776 | ||
| 4777 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod defs; mod app;", &mut arena); |
|
| 4778 | - | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "pub record Counter { value: i32 } pub trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4777 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; mod app;", &mut arena); |
|
| 4778 | + | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; } instance Adder for Counter { fn (c: *mut Counter) add(n: i32) -> i32 { c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4779 | 4779 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4780 | 4780 | ||
| 4781 | 4781 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4782 | 4782 | try expectNoErrors(&result); |
|
| 4783 | 4783 | } |
| 4785 | 4785 | /// Instance in a different module from trait and type. |
|
| 4786 | 4786 | @test fn testResolveInstanceCrossModule() throws (testing::TestError) { |
|
| 4787 | 4787 | let mut a = testResolver(); |
|
| 4788 | 4788 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4789 | 4789 | ||
| 4790 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod defs; pub mod impls; mod app;", &mut arena); |
|
| 4791 | - | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "pub record Counter { value: i32 } pub trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena); |
|
| 4790 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod defs; export mod impls; mod app;", &mut arena); |
|
| 4791 | + | let defsId = try registerModule(&mut MODULE_GRAPH, rootId, "defs", "export record Counter { value: i32 } export trait Adder { fn (*mut Adder) add(n: i32) -> i32; }", &mut arena); |
|
| 4792 | 4792 | let implsId = try registerModule(&mut MODULE_GRAPH, rootId, "impls", "use root::defs; instance defs::Adder for defs::Counter { fn (c: *mut defs::Counter) add(n: i32) -> i32 { c.value = c.value + n; return c.value; } }", &mut arena); |
|
| 4793 | 4793 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::defs; fn test() -> i32 { let mut c = defs::Counter { value: 10 }; let a: *mut opaque defs::Adder = &mut c; return a.add(5); }", &mut arena); |
|
| 4794 | 4794 | ||
| 4795 | 4795 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4796 | 4796 | try expectNoErrors(&result); |
| 4976 | 4976 | /// a constant from another module via scope access. |
|
| 4977 | 4977 | @test fn testCrossModuleConstExpr() throws (testing::TestError) { |
|
| 4978 | 4978 | let mut a = testResolver(); |
|
| 4979 | 4979 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4980 | 4980 | ||
| 4981 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod consts; mod app;", &mut arena); |
|
| 4982 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "pub const BASE: i32 = 100;", &mut arena); |
|
| 4981 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 4982 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export const BASE: i32 = 100;", &mut arena); |
|
| 4983 | 4983 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; const DERIVED: i32 = consts::BASE + 50;", &mut arena); |
|
| 4984 | 4984 | ||
| 4985 | 4985 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4986 | 4986 | try expectNoErrors(&result); |
|
| 4987 | 4987 | } |
| 4989 | 4989 | /// Test cross-module constant expression used as array size. |
|
| 4990 | 4990 | @test fn testCrossModuleConstExprArraySize() throws (testing::TestError) { |
|
| 4991 | 4991 | let mut a = testResolver(); |
|
| 4992 | 4992 | let mut arena = ast::nodeArena(&mut AST_ARENA[..]); |
|
| 4993 | 4993 | ||
| 4994 | - | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "pub mod consts; mod app;", &mut arena); |
|
| 4995 | - | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "pub const WIDTH: u32 = 8; pub const HEIGHT: u32 = 4;", &mut arena); |
|
| 4994 | + | let rootId = try registerModule(&mut MODULE_GRAPH, nil, "root", "export mod consts; mod app;", &mut arena); |
|
| 4995 | + | let constsId = try registerModule(&mut MODULE_GRAPH, rootId, "consts", "export const WIDTH: u32 = 8; export const HEIGHT: u32 = 4;", &mut arena); |
|
| 4996 | 4996 | let appId = try registerModule(&mut MODULE_GRAPH, rootId, "app", "use root::consts; const TOTAL: u32 = consts::WIDTH * consts::HEIGHT; static BUF: [u8; TOTAL] = undefined;", &mut arena); |
|
| 4997 | 4997 | ||
| 4998 | 4998 | let result = try resolveModuleTree(&mut a, rootId); |
|
| 4999 | 4999 | try expectNoErrors(&result); |
|
| 5000 | 5000 | } |
lib/std/lang/scanner.rad
+13 -14
| 9 | 9 | ||
| 10 | 10 | /// Token kinds representing all lexical elements in Radiance. |
|
| 11 | 11 | /// |
|
| 12 | 12 | /// This enum covers operators, keywords, literals, and structural |
|
| 13 | 13 | /// elements used by the parser to build the AST. |
|
| 14 | - | pub union TokenKind { |
|
| 14 | + | export union TokenKind { |
|
| 15 | 15 | /// Special end of file token generated when the input is exhausted. |
|
| 16 | 16 | Eof, |
|
| 17 | 17 | /// Special invalid token. |
|
| 18 | 18 | Invalid, |
|
| 19 | 19 |
| 95 | 95 | ||
| 96 | 96 | // Module-related tokens. |
|
| 97 | 97 | Mod, Use, Super, |
|
| 98 | 98 | ||
| 99 | 99 | // Type or function attributes. |
|
| 100 | - | Pub, Export, Static, |
|
| 100 | + | Export, Static, |
|
| 101 | 101 | ||
| 102 | 102 | // Trait-related tokens. |
|
| 103 | 103 | Trait, Instance, |
|
| 104 | 104 | ||
| 105 | 105 | // Type-related tokens. |
| 114 | 114 | /// Corresponding token. |
|
| 115 | 115 | tok: TokenKind, |
|
| 116 | 116 | } |
|
| 117 | 117 | ||
| 118 | 118 | /// Sorted keyword table for binary search. |
|
| 119 | - | const KEYWORDS: [Keyword; 52] = [ |
|
| 119 | + | const KEYWORDS: [Keyword; 51] = [ |
|
| 120 | 120 | { name: "align", tok: TokenKind::Align }, |
|
| 121 | 121 | { name: "and", tok: TokenKind::And }, |
|
| 122 | 122 | { name: "as", tok: TokenKind::As }, |
|
| 123 | 123 | { name: "assert", tok: TokenKind::Assert }, |
|
| 124 | 124 | { name: "bool", tok: TokenKind::Bool }, |
| 149 | 149 | { name: "nil", tok: TokenKind::Nil }, |
|
| 150 | 150 | { name: "not", tok: TokenKind::Not }, |
|
| 151 | 151 | { name: "opaque", tok: TokenKind::Opaque }, |
|
| 152 | 152 | { name: "or", tok: TokenKind::Or }, |
|
| 153 | 153 | { name: "panic", tok: TokenKind::Panic }, |
|
| 154 | - | { name: "pub", tok: TokenKind::Pub }, |
|
| 155 | 154 | { name: "record", tok: TokenKind::Record }, |
|
| 156 | 155 | { name: "return", tok: TokenKind::Return }, |
|
| 157 | 156 | { name: "static", tok: TokenKind::Static }, |
|
| 158 | 157 | { name: "super", tok: TokenKind::Super }, |
|
| 159 | 158 | { name: "throw", tok: TokenKind::Throw }, |
| 170 | 169 | { name: "use", tok: TokenKind::Use }, |
|
| 171 | 170 | { name: "while", tok: TokenKind::While }, |
|
| 172 | 171 | ]; |
|
| 173 | 172 | ||
| 174 | 173 | /// Describes where source code originated from. |
|
| 175 | - | pub union SourceLoc { |
|
| 174 | + | export union SourceLoc { |
|
| 176 | 175 | /// Source loaded from a file at the given path. |
|
| 177 | 176 | File(*[u8]), |
|
| 178 | 177 | /// Source provided as an inline string (no file path). |
|
| 179 | 178 | String, |
|
| 180 | 179 | } |
|
| 181 | 180 | ||
| 182 | 181 | /// Lexical scanner state for tokenizing Radiance source code. |
|
| 183 | 182 | /// |
|
| 184 | 183 | /// Maintains position information and source buffer reference. |
|
| 185 | - | pub record Scanner { |
|
| 184 | + | export record Scanner { |
|
| 186 | 185 | /// Origin of the source being scanned. |
|
| 187 | 186 | sourceLoc: SourceLoc, |
|
| 188 | 187 | /// Source buffer. |
|
| 189 | 188 | source: *[u8], |
|
| 190 | 189 | /// Offset of current token into buffer. |
| 197 | 196 | ||
| 198 | 197 | /// Individual token with kind, source text, and position. |
|
| 199 | 198 | /// |
|
| 200 | 199 | /// Represents a single lexical element extracted from source, |
|
| 201 | 200 | /// including its original text and byte offset for error reporting. |
|
| 202 | - | pub record Token { |
|
| 201 | + | export record Token { |
|
| 203 | 202 | /// Token kind. |
|
| 204 | 203 | kind: TokenKind, |
|
| 205 | 204 | /// Token source string. |
|
| 206 | 205 | source: *[u8], |
|
| 207 | 206 | /// Byte offset of `source` in input buffer. |
| 209 | 208 | } |
|
| 210 | 209 | ||
| 211 | 210 | /// Source code location with line/column information. |
|
| 212 | 211 | /// |
|
| 213 | 212 | /// Used for error reporting and debugging. |
|
| 214 | - | pub record Location { |
|
| 213 | + | export record Location { |
|
| 215 | 214 | /// Origin of the source. |
|
| 216 | 215 | source: SourceLoc, |
|
| 217 | 216 | /// Line number. |
|
| 218 | 217 | line: u16, |
|
| 219 | 218 | /// Column number. |
|
| 220 | 219 | col: u16, |
|
| 221 | 220 | } |
|
| 222 | 221 | ||
| 223 | 222 | /// Create a new scanner object. |
|
| 224 | - | pub fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *mut strings::Pool) -> Scanner { |
|
| 223 | + | export fn scanner(sourceLoc: SourceLoc, source: *[u8], pool: *mut strings::Pool) -> Scanner { |
|
| 225 | 224 | // Intern built-in functions and attributes. |
|
| 226 | 225 | strings::intern(pool, "@sizeOf"); |
|
| 227 | 226 | strings::intern(pool, "@alignOf"); |
|
| 228 | 227 | strings::intern(pool, "@sliceOf"); |
|
| 229 | 228 | strings::intern(pool, "@default"); |
| 235 | 234 | ||
| 236 | 235 | return Scanner { sourceLoc, source, token: 0, cursor: 0, pool }; |
|
| 237 | 236 | } |
|
| 238 | 237 | ||
| 239 | 238 | /// Check if we've reached the end of input. |
|
| 240 | - | pub fn isEof(s: *Scanner) -> bool { |
|
| 239 | + | export fn isEof(s: *Scanner) -> bool { |
|
| 241 | 240 | return s.cursor >= s.source.len; |
|
| 242 | 241 | } |
|
| 243 | 242 | ||
| 244 | 243 | /// Get the current character, if any. |
|
| 245 | - | pub fn current(s: *Scanner) -> ?u8 { |
|
| 244 | + | export fn current(s: *Scanner) -> ?u8 { |
|
| 246 | 245 | if isEof(s) { |
|
| 247 | 246 | return nil; |
|
| 248 | 247 | } |
|
| 249 | 248 | return s.source[s.cursor]; |
|
| 250 | 249 | } |
| 276 | 275 | fn tok(s: *Scanner, kind: TokenKind) -> Token { |
|
| 277 | 276 | return Token { kind, source: &s.source[s.token..s.cursor], offset: s.token }; |
|
| 278 | 277 | } |
|
| 279 | 278 | ||
| 280 | 279 | /// Create an invalid token with the given message. |
|
| 281 | - | pub fn invalid(offset: u32, message: *[u8]) -> Token { |
|
| 280 | + | export fn invalid(offset: u32, message: *[u8]) -> Token { |
|
| 282 | 281 | return Token { kind: TokenKind::Invalid, source: message, offset }; |
|
| 283 | 282 | } |
|
| 284 | 283 | ||
| 285 | 284 | /// Skip whitespace characters and line comments. |
|
| 286 | 285 | fn skipWhitespace(s: *mut Scanner) { |
| 442 | 441 | } |
|
| 443 | 442 | return tok(s, kind); |
|
| 444 | 443 | } |
|
| 445 | 444 | ||
| 446 | 445 | /// Scan the next token. |
|
| 447 | - | pub fn next(s: *mut Scanner) -> Token { |
|
| 446 | + | export fn next(s: *mut Scanner) -> Token { |
|
| 448 | 447 | skipWhitespace(s); // Skip any whitespace between tokens. |
|
| 449 | 448 | s.token = s.cursor; // Token starts at current position. |
|
| 450 | 449 | ||
| 451 | 450 | if isEof(s) { |
|
| 452 | 451 | return tok(s, TokenKind::Eof); |
| 610 | 609 | else => return invalid(s.token, "unexpected character"), |
|
| 611 | 610 | } |
|
| 612 | 611 | } |
|
| 613 | 612 | ||
| 614 | 613 | /// Get the source code location from a byte offset. |
|
| 615 | - | pub fn getLocation(sourceLoc: SourceLoc, source: *[u8], offset: u32) -> ?Location { |
|
| 614 | + | export fn getLocation(sourceLoc: SourceLoc, source: *[u8], offset: u32) -> ?Location { |
|
| 616 | 615 | let mut l: u16 = 1; |
|
| 617 | 616 | let mut c: u16 = 1; |
|
| 618 | 617 | ||
| 619 | 618 | if offset >= source.len { |
|
| 620 | 619 | return nil; |
lib/std/lang/sexpr.rad
+16 -16
| 5 | 5 | ||
| 6 | 6 | use std::io; |
|
| 7 | 7 | use std::lang::alloc; |
|
| 8 | 8 | ||
| 9 | 9 | /// Output target for S-expression printing. |
|
| 10 | - | pub union Output { |
|
| 10 | + | export union Output { |
|
| 11 | 11 | /// Print to stdout. |
|
| 12 | 12 | Stdout, |
|
| 13 | 13 | /// Write to a buffer, tracking position. |
|
| 14 | 14 | Buffer { buf: *mut [u8], pos: *mut u32 }, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// An S-expression element. |
|
| 18 | - | pub union Expr { |
|
| 18 | + | export union Expr { |
|
| 19 | 19 | /// An empty expression. |
|
| 20 | 20 | Null, |
|
| 21 | 21 | /// A symbol/identifier. |
|
| 22 | 22 | Sym(*[u8]), |
|
| 23 | 23 | /// A quoted string literal. |
| 31 | 31 | /// A block with a name, inline items, and child statements on separate lines. |
|
| 32 | 32 | Block { name: *[u8], items: *[Expr], children: *[Expr] }, |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Allocate an array of Expr in the arena. |
|
| 36 | - | pub fn allocExprs(arena: *mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) { |
|
| 36 | + | export fn allocExprs(arena: *mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) { |
|
| 37 | 37 | if len == 0 { |
|
| 38 | 38 | throw alloc::AllocError::OutOfMemory; |
|
| 39 | 39 | } |
|
| 40 | 40 | let ptr = try alloc::allocSlice(arena, @sizeOf(Expr), @alignOf(Expr), len); |
|
| 41 | 41 | return ptr as *mut [Expr]; |
|
| 42 | 42 | } |
|
| 43 | 43 | ||
| 44 | 44 | /// Allocate and copy items into the arena. |
|
| 45 | - | pub fn allocItems(a: *mut alloc::Arena, items: *[Expr]) -> *[Expr] { |
|
| 45 | + | export fn allocItems(a: *mut alloc::Arena, items: *[Expr]) -> *[Expr] { |
|
| 46 | 46 | if items.len == 0 { |
|
| 47 | 47 | return &[]; |
|
| 48 | 48 | } |
|
| 49 | 49 | let buf = try! allocExprs(a, items.len); |
|
| 50 | 50 | for item, i in items { |
| 52 | 52 | } |
|
| 53 | 53 | return buf; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Shorthand for creating a symbol. |
|
| 57 | - | pub fn sym(s: *[u8]) -> Expr { |
|
| 57 | + | export fn sym(s: *[u8]) -> Expr { |
|
| 58 | 58 | return Expr::Sym(s); |
|
| 59 | 59 | } |
|
| 60 | 60 | ||
| 61 | 61 | /// Shorthand for creating a string literal. |
|
| 62 | - | pub fn str(s: *[u8]) -> Expr { |
|
| 62 | + | export fn str(s: *[u8]) -> Expr { |
|
| 63 | 63 | return Expr::Str(s); |
|
| 64 | 64 | } |
|
| 65 | 65 | ||
| 66 | 66 | /// Shorthand for creating a list. |
|
| 67 | - | pub fn list(a: *mut alloc::Arena, head: *[u8], tail: *[Expr]) -> Expr { |
|
| 67 | + | export fn list(a: *mut alloc::Arena, head: *[u8], tail: *[Expr]) -> Expr { |
|
| 68 | 68 | return Expr::List { head, tail: allocItems(a, tail), multiline: false }; |
|
| 69 | 69 | } |
|
| 70 | 70 | ||
| 71 | 71 | /// Shorthand for creating a bracket-delimited vector. |
|
| 72 | - | pub fn vec(a: *mut alloc::Arena, items: *[Expr]) -> Expr { |
|
| 72 | + | export fn vec(a: *mut alloc::Arena, items: *[Expr]) -> Expr { |
|
| 73 | 73 | return Expr::Vec { items: allocItems(a, items) }; |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | 76 | /// Shorthand for creating a block with inline items and child expressions. |
|
| 77 | - | pub fn block(a: *mut alloc::Arena, name: *[u8], items: *[Expr], children: *[Expr]) -> Expr { |
|
| 77 | + | export fn block(a: *mut alloc::Arena, name: *[u8], items: *[Expr], children: *[Expr]) -> Expr { |
|
| 78 | 78 | return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) }; |
|
| 79 | 79 | } |
|
| 80 | 80 | ||
| 81 | 81 | /// Write a string to the output target. |
|
| 82 | - | pub fn write(out: *mut Output, s: *[u8]) { |
|
| 82 | + | export fn write(out: *mut Output, s: *[u8]) { |
|
| 83 | 83 | match *out { |
|
| 84 | 84 | case Output::Stdout => io::print(s), |
|
| 85 | 85 | case Output::Buffer { buf, pos } => { |
|
| 86 | 86 | let remaining = buf.len - *pos; |
|
| 87 | 87 | let toWrite = remaining if s.len > remaining else s.len; |
| 99 | 99 | write(out, " "); |
|
| 100 | 100 | } |
|
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | /// Print a single character with escaping to the output target. |
|
| 104 | - | pub fn printEscapedTo(out: *mut Output, c: u8) { |
|
| 104 | + | export fn printEscapedTo(out: *mut Output, c: u8) { |
|
| 105 | 105 | match c { |
|
| 106 | 106 | case '\n' => write(out, "\\n"), |
|
| 107 | 107 | case '\r' => write(out, "\\r"), |
|
| 108 | 108 | case '\t' => write(out, "\\t"), |
|
| 109 | 109 | case '\\' => write(out, "\\\\"), |
| 111 | 111 | else => write(out, &[c]), |
|
| 112 | 112 | } |
|
| 113 | 113 | } |
|
| 114 | 114 | ||
| 115 | 115 | /// Print a quoted string with escape sequences to the output target. |
|
| 116 | - | pub fn printStringTo(out: *mut Output, s: *[u8]) { |
|
| 116 | + | export fn printStringTo(out: *mut Output, s: *[u8]) { |
|
| 117 | 117 | write(out, "\""); |
|
| 118 | 118 | for i in 0..s.len { |
|
| 119 | 119 | printEscapedTo(out, s[i]); |
|
| 120 | 120 | } |
|
| 121 | 121 | write(out, "\""); |
|
| 122 | 122 | } |
|
| 123 | 123 | ||
| 124 | 124 | /// Print a character literal with escape sequences to the output target. |
|
| 125 | - | pub fn printCharTo(out: *mut Output, c: u8) { |
|
| 125 | + | export fn printCharTo(out: *mut Output, c: u8) { |
|
| 126 | 126 | write(out, "'"); |
|
| 127 | 127 | printEscapedTo(out, c); |
|
| 128 | 128 | write(out, "'"); |
|
| 129 | 129 | } |
|
| 130 | 130 | ||
| 131 | 131 | /// Print an S-expression to the given output target at the given depth. |
|
| 132 | - | pub fn printTo(expr: Expr, depth: u32, out: *mut Output) { |
|
| 132 | + | export fn printTo(expr: Expr, depth: u32, out: *mut Output) { |
|
| 133 | 133 | match expr { |
|
| 134 | 134 | case Expr::Null => {}, |
|
| 135 | 135 | case Expr::Sym(s) => write(out, s), |
|
| 136 | 136 | case Expr::Str(s) => printStringTo(out, s), |
|
| 137 | 137 | case Expr::Char(c) => printCharTo(out, c), |
| 193 | 193 | } |
|
| 194 | 194 | } |
|
| 195 | 195 | } |
|
| 196 | 196 | ||
| 197 | 197 | /// Print an S-expression to stdout at the given indentation depth. |
|
| 198 | - | pub fn print(expr: Expr, depth: u32) { |
|
| 198 | + | export fn print(expr: Expr, depth: u32) { |
|
| 199 | 199 | let mut out = Output::Stdout; |
|
| 200 | 200 | printTo(expr, depth, &mut out); |
|
| 201 | 201 | } |
|
| 202 | 202 | ||
| 203 | 203 | /// Emit indentation for `depth` levels to stdout. |
|
| 204 | - | pub fn indent(depth: u32) { |
|
| 204 | + | export fn indent(depth: u32) { |
|
| 205 | 205 | let mut out = Output::Stdout; |
|
| 206 | 206 | indentTo(&mut out, depth); |
|
| 207 | 207 | } |
|
| 208 | 208 |
lib/std/lang/strings.rad
+3 -3
| 14 | 14 | ||
| 15 | 15 | /// String interning pool using open-addressed hash table. |
|
| 16 | 16 | /// |
|
| 17 | 17 | /// Each unique string content is stored only once, allowing pointer equality |
|
| 18 | 18 | /// to be used instead of content comparison for symbol lookups and module names. |
|
| 19 | - | pub record Pool { |
|
| 19 | + | export record Pool { |
|
| 20 | 20 | /// Hash table slots. |
|
| 21 | 21 | table: [*[u8]; TABLE_SIZE], |
|
| 22 | 22 | /// Number of strings currently in the pool. |
|
| 23 | 23 | count: u32, |
|
| 24 | 24 | } |
| 48 | 48 | } |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Look up a string in the pool. |
|
| 52 | 52 | /// Returns the canonical interned pointer if the string exists. |
|
| 53 | - | pub fn find(sp: *Pool, str: *[u8]) -> ?*[u8] { |
|
| 53 | + | export fn find(sp: *Pool, str: *[u8]) -> ?*[u8] { |
|
| 54 | 54 | match lookup(sp, str) { |
|
| 55 | 55 | case Lookup::Found(entry) => return entry, |
|
| 56 | 56 | case Lookup::Empty(_) => return nil, |
|
| 57 | 57 | } |
|
| 58 | 58 | } |
|
| 59 | 59 | ||
| 60 | 60 | /// Intern a string, returning a canonical pointer for equality comparison. |
|
| 61 | 61 | /// |
|
| 62 | 62 | /// If the string content already exists in the pool, returns the existing pointer. |
|
| 63 | 63 | /// Otherwise, adds the string pointer to the pool and returns it. |
|
| 64 | - | pub fn intern(sp: *mut Pool, str: *[u8]) -> *[u8] { |
|
| 64 | + | export fn intern(sp: *mut Pool, str: *[u8]) -> *[u8] { |
|
| 65 | 65 | match lookup(sp, str) { |
|
| 66 | 66 | case Lookup::Found(entry) => return entry, |
|
| 67 | 67 | case Lookup::Empty(idx) => { |
|
| 68 | 68 | assert sp.count < TABLE_SIZE / 2, "intern: string pool is full"; |
|
| 69 | 69 | sp.table[idx] = str; |
lib/std/mem.rad
+7 -7
| 3 | 3 | /// Buffer is too small. |
|
| 4 | 4 | BufferTooSmall, |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | /// Copy bytes between two slices. Returns the number of bytes copied. |
|
| 8 | - | pub fn copy(into: *mut [u8], from: *[u8]) -> u32 throws (MemoryError) { |
|
| 8 | + | export fn copy(into: *mut [u8], from: *[u8]) -> u32 throws (MemoryError) { |
|
| 9 | 9 | if into.len < from.len { |
|
| 10 | 10 | throw MemoryError::BufferTooSmall; |
|
| 11 | 11 | } |
|
| 12 | 12 | for x, i in from { |
|
| 13 | 13 | into[i] = x; |
| 15 | 15 | return from.len; |
|
| 16 | 16 | } |
|
| 17 | 17 | ||
| 18 | 18 | /// Strip a byte-level prefix from the input, and return the suffix. |
|
| 19 | 19 | /// Returns `nil` if the prefix wasn't found. |
|
| 20 | - | pub fn stripPrefix(prefix: *[u8], input: *[u8]) -> ?*[u8] { |
|
| 20 | + | export fn stripPrefix(prefix: *[u8], input: *[u8]) -> ?*[u8] { |
|
| 21 | 21 | if prefix.len == 0 { |
|
| 22 | 22 | return input; |
|
| 23 | 23 | } |
|
| 24 | 24 | if prefix.len > input.len { |
|
| 25 | 25 | return nil; |
| 31 | 31 | } |
|
| 32 | 32 | return &input[prefix.len..]; |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Align value up to alignment boundary. |
|
| 36 | - | pub fn alignUp(value: u32, alignment: u32) -> u32 { |
|
| 36 | + | export fn alignUp(value: u32, alignment: u32) -> u32 { |
|
| 37 | 37 | return (value + alignment - 1) & ~(alignment - 1); |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Align signed value up to alignment boundary. |
|
| 41 | - | pub fn alignUpI32(value: i32, alignment: i32) -> i32 { |
|
| 41 | + | export fn alignUpI32(value: i32, alignment: i32) -> i32 { |
|
| 42 | 42 | return (value + alignment - 1) & ~(alignment - 1); |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | /// Count number of set bits in a 32-bit value. |
|
| 46 | - | pub fn popCount(x: u32) -> i32 { |
|
| 46 | + | export fn popCount(x: u32) -> i32 { |
|
| 47 | 47 | let mut n = x; |
|
| 48 | 48 | let mut count: i32 = 0; |
|
| 49 | 49 | while n != 0 { |
|
| 50 | 50 | count += (n & 1) as i32; |
|
| 51 | 51 | n >>= 1; |
|
| 52 | 52 | } |
|
| 53 | 53 | return count; |
|
| 54 | 54 | } |
|
| 55 | 55 | ||
| 56 | 56 | /// Check whether two byte slices have the same length and contents. |
|
| 57 | - | pub fn eq(a: *[u8], b: *[u8]) -> bool { |
|
| 57 | + | export fn eq(a: *[u8], b: *[u8]) -> bool { |
|
| 58 | 58 | if a.len != b.len { |
|
| 59 | 59 | return false; |
|
| 60 | 60 | } |
|
| 61 | 61 | for i in 0..a.len { |
|
| 62 | 62 | if a[i] != b[i] { |
| 67 | 67 | } |
|
| 68 | 68 | ||
| 69 | 69 | /// Compare two byte slices lexicographically. |
|
| 70 | 70 | /// |
|
| 71 | 71 | /// Returns `-1` when `a < b`, `1` when `a > b`, and `0` when equal. |
|
| 72 | - | pub fn cmp(a: *[u8], b: *[u8]) -> i32 { |
|
| 72 | + | export fn cmp(a: *[u8], b: *[u8]) -> i32 { |
|
| 73 | 73 | let aLen = a.len; |
|
| 74 | 74 | let bLen = b.len; |
|
| 75 | 75 | ||
| 76 | 76 | let common = bLen if bLen < aLen else aLen; |
|
| 77 | 77 | for i in 0..common { |
lib/std/sys.rad
+2 -2
| 1 | 1 | //! System utilities. |
|
| 2 | - | pub mod unix; |
|
| 2 | + | export mod unix; |
|
| 3 | 3 | ||
| 4 | 4 | /// Process environment passed to the program entry point. |
|
| 5 | - | pub record Env { |
|
| 5 | + | export record Env { |
|
| 6 | 6 | /// Command-line arguments. |
|
| 7 | 7 | args: *[*[u8]], |
|
| 8 | 8 | } |
lib/std/sys/unix.rad
+18 -18
| 1 | 1 | //! Unix-specific system calls and utilities. |
|
| 2 | 2 | use std::intrinsics; |
|
| 3 | 3 | ||
| 4 | 4 | /// File access modes. |
|
| 5 | - | pub record OpenFlags(i64); |
|
| 5 | + | export record OpenFlags(i64); |
|
| 6 | 6 | ||
| 7 | 7 | /// Open file for reading only. |
|
| 8 | - | pub const O_RDONLY: OpenFlags = OpenFlags(0); |
|
| 8 | + | export const O_RDONLY: OpenFlags = OpenFlags(0); |
|
| 9 | 9 | ||
| 10 | 10 | /// Open file for writing only. |
|
| 11 | - | pub const O_WRONLY: OpenFlags = OpenFlags(1); |
|
| 11 | + | export const O_WRONLY: OpenFlags = OpenFlags(1); |
|
| 12 | 12 | ||
| 13 | 13 | /// Open file for reading and writing. |
|
| 14 | - | pub const O_RDWR: OpenFlags = OpenFlags(2); |
|
| 14 | + | export const O_RDWR: OpenFlags = OpenFlags(2); |
|
| 15 | 15 | ||
| 16 | 16 | /// Create file if it doesn't exist. |
|
| 17 | - | pub const O_CREAT: OpenFlags = OpenFlags(64); |
|
| 17 | + | export const O_CREAT: OpenFlags = OpenFlags(64); |
|
| 18 | 18 | ||
| 19 | 19 | /// Truncate file to zero length. |
|
| 20 | - | pub const O_TRUNC: OpenFlags = OpenFlags(512); |
|
| 20 | + | export const O_TRUNC: OpenFlags = OpenFlags(512); |
|
| 21 | 21 | ||
| 22 | 22 | /// Standard file descriptor for stdin. |
|
| 23 | - | pub const STDIN: i64 = 0; |
|
| 23 | + | export const STDIN: i64 = 0; |
|
| 24 | 24 | ||
| 25 | 25 | /// Standard file descriptor for stdout. |
|
| 26 | - | pub const STDOUT: i64 = 1; |
|
| 26 | + | export const STDOUT: i64 = 1; |
|
| 27 | 27 | ||
| 28 | 28 | /// Standard file descriptor for stderr. |
|
| 29 | - | pub const STDERR: i64 = 2; |
|
| 29 | + | export const STDERR: i64 = 2; |
|
| 30 | 30 | ||
| 31 | 31 | /// Special value representing current working directory for `openat()`. |
|
| 32 | 32 | const AT_FDCWD: i64 = -100; |
|
| 33 | 33 | ||
| 34 | 34 | /// Opens a file at the given path and returns a file descriptor. |
|
| 35 | 35 | /// Returns a negative value on error. |
|
| 36 | - | pub fn open(path: *[u8], flags: OpenFlags) -> i64 { |
|
| 36 | + | export fn open(path: *[u8], flags: OpenFlags) -> i64 { |
|
| 37 | 37 | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0); |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Opens a file at the given path with mode, returns a file descriptor. |
|
| 41 | - | pub fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 41 | + | export fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 42 | 42 | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode); |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | /// Reads from a file descriptor into the provided buffer. |
|
| 46 | 46 | /// Returns the number of bytes read, or a negative value on error. |
|
| 47 | - | pub fn read(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 47 | + | export fn read(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 48 | 48 | return intrinsics::ecall(63, fd, buf.ptr as i64, buf.len as i64, 0); |
|
| 49 | 49 | } |
|
| 50 | 50 | ||
| 51 | 51 | /// Reads from a file descriptor until EOF or buffer is full. |
|
| 52 | 52 | /// Returns the total number of bytes read, or a negative value on error. |
|
| 53 | - | pub fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 53 | + | export fn readToEnd(fd: i64, buf: *mut [u8]) -> i64 { |
|
| 54 | 54 | let mut total: u32 = 0; |
|
| 55 | 55 | while total < buf.len { |
|
| 56 | 56 | let chunk = &mut buf[total..]; |
|
| 57 | 57 | let n = read(fd, chunk); |
|
| 58 | 58 |
| 67 | 67 | return total as i64; |
|
| 68 | 68 | } |
|
| 69 | 69 | ||
| 70 | 70 | /// Writes to a file descriptor from the provided buffer. |
|
| 71 | 71 | /// Returns the number of bytes written, or a negative value on error. |
|
| 72 | - | pub fn write(fd: i64, buf: *[u8]) -> i64 { |
|
| 72 | + | export fn write(fd: i64, buf: *[u8]) -> i64 { |
|
| 73 | 73 | return intrinsics::ecall(64, fd, buf.ptr as i64, buf.len as i64, 0); |
|
| 74 | 74 | } |
|
| 75 | 75 | ||
| 76 | 76 | /// Closes a file descriptor. |
|
| 77 | 77 | /// Returns `0` on success, or a negative value on error. |
|
| 78 | - | pub fn close(fd: i64) -> i64 { |
|
| 78 | + | export fn close(fd: i64) -> i64 { |
|
| 79 | 79 | return intrinsics::ecall(57, fd, 0, 0, 0); |
|
| 80 | 80 | } |
|
| 81 | 81 | ||
| 82 | 82 | /// Reads the entire contents of a file at the given path into the provided buffer. |
|
| 83 | 83 | /// Returns a slice containing the data read, or `nil` on error. |
|
| 84 | - | pub fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 84 | + | export fn readFile(path: *[u8], buf: *mut [u8]) -> ?*[u8] { |
|
| 85 | 85 | let fd = open(path, O_RDONLY); |
|
| 86 | 86 | if fd < 0 { |
|
| 87 | 87 | return nil; |
|
| 88 | 88 | } |
|
| 89 | 89 | let n = readToEnd(fd, buf); |
| 96 | 96 | } |
|
| 97 | 97 | return &buf[..n as u32]; |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | 100 | /// Exit the current process with the given status code. |
|
| 101 | - | pub fn exit(status: i64) { |
|
| 101 | + | export fn exit(status: i64) { |
|
| 102 | 102 | intrinsics::ecall(93, status, 0, 0, 0); |
|
| 103 | 103 | } |
|
| 104 | 104 | ||
| 105 | 105 | /// Writes the entire contents of a buffer to a file at the given path. |
|
| 106 | 106 | /// Creates the file if it doesn't exist, truncates if it does. |
|
| 107 | 107 | /// Returns `true` on success. |
|
| 108 | - | pub fn writeFile(path: *[u8], data: *[u8]) -> bool { |
|
| 108 | + | export fn writeFile(path: *[u8], data: *[u8]) -> bool { |
|
| 109 | 109 | let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC); |
|
| 110 | 110 | let fd = openOpts(path, flags, 420); // 0644 in octal. |
|
| 111 | 111 | if fd < 0 { |
|
| 112 | 112 | return false; |
|
| 113 | 113 | } |
lib/std/testing.rad
+7 -7
| 8 | 8 | passed: u32, |
|
| 9 | 9 | failed: u32, |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | /// Error thrown when a test assertion fails. |
|
| 13 | - | pub union TestError { |
|
| 13 | + | export union TestError { |
|
| 14 | 14 | Failed, |
|
| 15 | 15 | } |
|
| 16 | 16 | ||
| 17 | 17 | /// Descriptor for a single test case, holding its module path, name, and entry point. |
|
| 18 | - | pub record TestInfo { |
|
| 18 | + | export record TestInfo { |
|
| 19 | 19 | module: *[u8], |
|
| 20 | 20 | name: *[u8], |
|
| 21 | 21 | func: fn() throws (TestError), |
|
| 22 | 22 | } |
|
| 23 | 23 | ||
| 24 | 24 | /// Construct a [`TestInfo`]. Used by the compiler's synthetic test harness. |
|
| 25 | - | pub fn test(module: *[u8], name: *[u8], func: fn() throws (TestError)) -> TestInfo { |
|
| 25 | + | export fn test(module: *[u8], name: *[u8], func: fn() throws (TestError)) -> TestInfo { |
|
| 26 | 26 | return TestInfo { module, name, func }; |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | /// Run all tests and return `0` on success or `1` if any test failed. |
|
| 30 | - | pub fn runAllTests(tests: *[TestInfo]) -> i32 { |
|
| 30 | + | export fn runAllTests(tests: *[TestInfo]) -> i32 { |
|
| 31 | 31 | let mut ctx: Ctx = Ctx { passed: 0, failed: 0 }; |
|
| 32 | 32 | ||
| 33 | 33 | io::print("Running "); |
|
| 34 | 34 | io::printU32(tests.len); |
|
| 35 | 35 | io::print(" test(s)...\n\n"); |
| 83 | 83 | io::printLn(" passed; 0 failed"); |
|
| 84 | 84 | } |
|
| 85 | 85 | } |
|
| 86 | 86 | ||
| 87 | 87 | /// Assert that two byte slices are equal. |
|
| 88 | - | pub fn expectBytesEq(left: *[u8], right: *[u8]) |
|
| 88 | + | export fn expectBytesEq(left: *[u8], right: *[u8]) |
|
| 89 | 89 | throws (TestError) |
|
| 90 | 90 | { |
|
| 91 | 91 | try expect(mem::eq(left, right)); |
|
| 92 | 92 | } |
|
| 93 | 93 | ||
| 94 | 94 | /// Assert that a boolean condition is true. |
|
| 95 | - | pub fn expect(cond: bool) |
|
| 95 | + | export fn expect(cond: bool) |
|
| 96 | 96 | throws (TestError) |
|
| 97 | 97 | { |
|
| 98 | 98 | if not cond { |
|
| 99 | 99 | throw TestError::Failed; |
|
| 100 | 100 | } |
|
| 101 | 101 | } |
|
| 102 | 102 | ||
| 103 | 103 | /// Assert that a boolean condition is false. |
|
| 104 | - | pub fn expectNot(cond: bool) |
|
| 104 | + | export fn expectNot(cond: bool) |
|
| 105 | 105 | throws (TestError) |
|
| 106 | 106 | { |
|
| 107 | 107 | if cond { |
|
| 108 | 108 | throw TestError::Failed; |
|
| 109 | 109 | } |
lib/std/vec.rad
+9 -9
| 5 | 5 | //! to the element type's requirements. |
|
| 6 | 6 | ||
| 7 | 7 | /// Raw vector metadata structure. |
|
| 8 | 8 | /// |
|
| 9 | 9 | /// Does not own storage, points to user-provided arena. |
|
| 10 | - | pub record RawVec { |
|
| 10 | + | export record RawVec { |
|
| 11 | 11 | /// Pointer to user-provided byte arena. |
|
| 12 | 12 | data: *mut [u8], |
|
| 13 | 13 | /// Current number of elements stored. |
|
| 14 | 14 | len: u32, |
|
| 15 | 15 | /// Size of each element in bytes (stride between elements). |
| 21 | 21 | /// Create a new raw vector with external arena. |
|
| 22 | 22 | /// |
|
| 23 | 23 | /// * `arena` is a pointer to static array backing storage. |
|
| 24 | 24 | /// * `stride` is the size of each element. |
|
| 25 | 25 | /// * `alignment` is the required alignment for elements. |
|
| 26 | - | pub fn new(arena: *mut [u8], stride: u32, alignment: u32) -> RawVec { |
|
| 26 | + | export fn new(arena: *mut [u8], stride: u32, alignment: u32) -> RawVec { |
|
| 27 | 27 | assert stride > 0; |
|
| 28 | 28 | assert alignment > 0; |
|
| 29 | 29 | assert (arena.ptr as u32) % alignment == 0; |
|
| 30 | 30 | assert (arena.len % stride) == 0; |
|
| 31 | 31 | ||
| 32 | 32 | return RawVec { data: arena, len: 0, stride, alignment }; |
|
| 33 | 33 | } |
|
| 34 | 34 | ||
| 35 | 35 | /// Get the current number of elements in the vector. |
|
| 36 | - | pub fn len(vec: *RawVec) -> u32 { |
|
| 36 | + | export fn len(vec: *RawVec) -> u32 { |
|
| 37 | 37 | return vec.len; |
|
| 38 | 38 | } |
|
| 39 | 39 | ||
| 40 | 40 | /// Get the maximum capacity of the vector. |
|
| 41 | - | pub fn capacity(vec: *RawVec) -> u32 { |
|
| 41 | + | export fn capacity(vec: *RawVec) -> u32 { |
|
| 42 | 42 | return vec.data.len / vec.stride; |
|
| 43 | 43 | } |
|
| 44 | 44 | ||
| 45 | 45 | /// Reset the vector to empty (does not clear memory). |
|
| 46 | - | pub fn reset(vec: *mut RawVec) { |
|
| 46 | + | export fn reset(vec: *mut RawVec) { |
|
| 47 | 47 | vec.len = 0; |
|
| 48 | 48 | } |
|
| 49 | 49 | ||
| 50 | 50 | /// Get a pointer to the element at the given index. |
|
| 51 | 51 | /// |
|
| 52 | 52 | /// Returns nil if index is out of bounds. |
|
| 53 | - | pub fn get(vec: *RawVec, index: u32) -> ?*opaque { |
|
| 53 | + | export fn get(vec: *RawVec, index: u32) -> ?*opaque { |
|
| 54 | 54 | if index >= vec.len { |
|
| 55 | 55 | return nil; |
|
| 56 | 56 | } |
|
| 57 | 57 | let offset: u32 = index * vec.stride; |
|
| 58 | 58 | let ptr: *u8 = &vec.data[offset]; |
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | /// Push an element onto the end of the vector. |
|
| 64 | 64 | /// |
|
| 65 | 65 | /// Returns false if the vector is at capacity. |
|
| 66 | - | pub fn push(vec: *mut RawVec, elem: *opaque) -> bool { |
|
| 66 | + | export fn push(vec: *mut RawVec, elem: *opaque) -> bool { |
|
| 67 | 67 | if vec.len >= capacity(vec) { |
|
| 68 | 68 | return false; |
|
| 69 | 69 | } |
|
| 70 | 70 | let off: u32 = vec.len * vec.stride; |
|
| 71 | 71 | let dst: *mut u8 = &mut vec.data[off]; |
| 79 | 79 | ||
| 80 | 80 | /// Pop an element from the end of the vector. |
|
| 81 | 81 | /// |
|
| 82 | 82 | /// Copies the element into the provided output pointer. |
|
| 83 | 83 | /// Returns false if the vector is empty. |
|
| 84 | - | pub fn pop(vec: *mut RawVec, out: *mut opaque) -> bool { |
|
| 84 | + | export fn pop(vec: *mut RawVec, out: *mut opaque) -> bool { |
|
| 85 | 85 | if vec.len == 0 { |
|
| 86 | 86 | return false; |
|
| 87 | 87 | } |
|
| 88 | 88 | vec.len -= 1; |
|
| 89 | 89 |
| 97 | 97 | } |
|
| 98 | 98 | ||
| 99 | 99 | /// Set the element at the given index. |
|
| 100 | 100 | /// |
|
| 101 | 101 | /// Returns false if index is out of bounds. |
|
| 102 | - | pub fn set(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 102 | + | export fn set(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 103 | 103 | if index >= vec.len { |
|
| 104 | 104 | return false; |
|
| 105 | 105 | } |
|
| 106 | 106 | let off: u32 = index * vec.stride; |
|
| 107 | 107 | let dst: *mut u8 = &mut vec.data[off]; |
test/tests/edge.cases.2.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test scanner peek with optional return. |
|
| 3 | 3 | ||
| 4 | - | pub record Scanner { |
|
| 4 | + | export record Scanner { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | 8 | fn peek(s: *Scanner) -> ?u8 { |
|
| 9 | 9 | if 0 + 0 >= s.source.len { |
test/tests/edge.cases.3.rad
+2 -2
| 1 | 1 | //! returns: 97 |
|
| 2 | 2 | //! Test scanner current character access. |
|
| 3 | 3 | ||
| 4 | - | pub record Scanner { |
|
| 4 | + | export record Scanner { |
|
| 5 | 5 | source: *[u8], |
|
| 6 | 6 | cursor: u32, |
|
| 7 | 7 | } |
|
| 8 | 8 | ||
| 9 | 9 | fn isEof(s: *Scanner) -> bool { |
|
| 10 | 10 | return s.cursor >= s.source.len; |
|
| 11 | 11 | } |
|
| 12 | 12 | ||
| 13 | - | pub fn current(s: *Scanner) -> ?u8 { |
|
| 13 | + | export fn current(s: *Scanner) -> ?u8 { |
|
| 14 | 14 | if isEof(s) { |
|
| 15 | 15 | return nil; |
|
| 16 | 16 | } |
|
| 17 | 17 | return s.source[s.cursor]; |
|
| 18 | 18 | } |
test/tests/edge.cases.rad
+1 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | ||
| 3 | - | pub record Scanner { |
|
| 3 | + | export record Scanner { |
|
| 4 | 4 | source: *[u8], |
|
| 5 | 5 | } |
|
| 6 | 6 | ||
| 7 | 7 | fn peek(s: *Scanner) -> i32 { |
|
| 8 | 8 | assert 0 + 0 <= s.source.len; |
test/tests/method.pub.rad
+1 -1
| 3 | 3 | ||
| 4 | 4 | record Foo { |
|
| 5 | 5 | x: i32, |
|
| 6 | 6 | } |
|
| 7 | 7 | ||
| 8 | - | pub fn (f: *Foo) getX() -> i32 { |
|
| 8 | + | export fn (f: *Foo) getX() -> i32 { |
|
| 9 | 9 | return f.x; |
|
| 10 | 10 | } |
|
| 11 | 11 | ||
| 12 | 12 | @default fn main() -> i32 { |
|
| 13 | 13 | let f = Foo { x: 42 }; |
test/tests/union.bitfield.rad
+7 -7
| 1 | 1 | //! returns: 42 |
|
| 2 | 2 | ||
| 3 | 3 | union Attribute { |
|
| 4 | 4 | None = 0b0, |
|
| 5 | - | Pub = 0b10, |
|
| 5 | + | Export = 0b10, |
|
| 6 | 6 | Default = 0b100, |
|
| 7 | 7 | Extern = 0b1000, |
|
| 8 | 8 | Test = 0b10000, |
|
| 9 | 9 | } |
|
| 10 | 10 | ||
| 11 | 11 | fn hasFlag(bits: i32, flag: i32) -> bool { |
|
| 12 | 12 | return (bits & flag) == flag; |
|
| 13 | 13 | } |
|
| 14 | 14 | ||
| 15 | 15 | fn testCombineDefaults() -> bool { |
|
| 16 | - | let combined: i32 = (Attribute::Pub as i32) | (Attribute::Default as i32); |
|
| 16 | + | let combined: i32 = (Attribute::Export as i32) | (Attribute::Default as i32); |
|
| 17 | 17 | ||
| 18 | - | return hasFlag(combined, Attribute::Pub as i32) and |
|
| 18 | + | return hasFlag(combined, Attribute::Export as i32) and |
|
| 19 | 19 | hasFlag(combined, Attribute::Default as i32) and |
|
| 20 | 20 | not hasFlag(combined, Attribute::Extern as i32); |
|
| 21 | 21 | } |
|
| 22 | 22 | ||
| 23 | 23 | fn testToggleExternFlag() -> bool { |
|
| 24 | - | let initial: i32 = (Attribute::Pub as i32) | (Attribute::Extern as i32); |
|
| 24 | + | let initial: i32 = (Attribute::Export as i32) | (Attribute::Extern as i32); |
|
| 25 | 25 | let toggled: i32 = initial ^ (Attribute::Extern as i32); |
|
| 26 | 26 | ||
| 27 | - | return hasFlag(toggled, Attribute::Pub as i32) |
|
| 27 | + | return hasFlag(toggled, Attribute::Export as i32) |
|
| 28 | 28 | and not hasFlag(toggled, Attribute::Extern as i32); |
|
| 29 | 29 | } |
|
| 30 | 30 | ||
| 31 | 31 | fn testMaskTestFlag() -> bool { |
|
| 32 | - | let mask: i32 = (Attribute::Pub as i32) | (Attribute::Test as i32); |
|
| 32 | + | let mask: i32 = (Attribute::Export as i32) | (Attribute::Test as i32); |
|
| 33 | 33 | let cleared: i32 = mask & (~(Attribute::Test as i32)); |
|
| 34 | 34 | ||
| 35 | - | return hasFlag(cleared, Attribute::Pub as i32) |
|
| 35 | + | return hasFlag(cleared, Attribute::Export as i32) |
|
| 36 | 36 | and not hasFlag(cleared, Attribute::Test as i32); |
|
| 37 | 37 | } |
|
| 38 | 38 | ||
| 39 | 39 | @default fn main() -> i32 { |
|
| 40 | 40 | if testCombineDefaults() |