Move and refactor `Reg` type

e291fc3612da96e9248d05c63bffa3219e52e868ef0697fb8734c8b8bb695ad3
It isn't architecture specific.
Alexis Sellier committed ago 1 parent eb28109d
lib/std/arch/rv64.rad +48 -56
20 20
21 21
use std::mem;
22 22
use std::collections::dict;
23 23
use std::lang::il;
24 24
use std::lang::alloc;
25 +
use std::lang::gen;
25 26
use std::lang::gen::labels;
26 27
use std::lang::gen::regalloc;
27 28
use std::lang::gen::data;
28 29
use std::lang::gen::types;
29 30
30 31
////////////////
31 32
// Registers  //
32 33
////////////////
33 34
34 -
/// RISC-V general-purpose register.
35 -
pub record Reg { n: u8 }
36 -
37 -
pub const ZERO: Reg = { n: 0 };   /// Hard-wired zero.
38 -
pub const RA:   Reg = { n: 1 };   /// Return address.
39 -
pub const SP:   Reg = { n: 2 };   /// Stack pointer.
40 -
pub const GP:   Reg = { n: 3 };   /// Global pointer.
41 -
pub const TP:   Reg = { n: 4 };   /// Thread pointer.
42 -
pub const T0:   Reg = { n: 5 };   /// Temporary/alternate link register.
43 -
pub const T1:   Reg = { n: 6 };   /// Temporary.
44 -
pub const T2:   Reg = { n: 7 };   /// Temporary.
45 -
pub const S0:   Reg = { n: 8 };   /// Saved register/frame pointer.
46 -
pub const FP:   Reg = { n: 8 };   /// Frame pointer (alias for S0).
47 -
pub const S1:   Reg = { n: 9 };   /// Saved register.
48 -
pub const A0:   Reg = { n: 10 };  /// Function argument/return.
49 -
pub const A1:   Reg = { n: 11 };  /// Function argument/return.
50 -
pub const A2:   Reg = { n: 12 };  /// Function argument.
51 -
pub const A3:   Reg = { n: 13 };  /// Function argument.
52 -
pub const A4:   Reg = { n: 14 };  /// Function argument.
53 -
pub const A5:   Reg = { n: 15 };  /// Function argument.
54 -
pub const A6:   Reg = { n: 16 };  /// Function argument.
55 -
pub const A7:   Reg = { n: 17 };  /// Function argument.
56 -
pub const S2:   Reg = { n: 18 };  /// Saved register.
57 -
pub const S3:   Reg = { n: 19 };  /// Saved register.
58 -
pub const S4:   Reg = { n: 20 };  /// Saved register.
59 -
pub const S5:   Reg = { n: 21 };  /// Saved register.
60 -
pub const S6:   Reg = { n: 22 };  /// Saved register.
61 -
pub const S7:   Reg = { n: 23 };  /// Saved register.
62 -
pub const S8:   Reg = { n: 24 };  /// Saved register.
63 -
pub const S9:   Reg = { n: 25 };  /// Saved register.
64 -
pub const S10:  Reg = { n: 26 };  /// Saved register.
65 -
pub const S11:  Reg = { n: 27 };  /// Saved register.
66 -
pub const T3:   Reg = { n: 28 };  /// Temporary.
67 -
pub const T4:   Reg = { n: 29 };  /// Temporary.
68 -
pub const T5:   Reg = { n: 30 };  /// Temporary.
69 -
pub const T6:   Reg = { n: 31 };  /// Temporary.
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.
70 68
71 69
/// Create a register from a number. Panics if `n > 31`.
72 -
pub fn reg(n: u8) -> Reg {
70 +
pub fn reg(n: u8) -> gen::Reg {
73 71
    assert n < 32;
74 -
    return Reg { n };
72 +
    return gen::Reg(n);
75 73
}
76 74
77 75
////////////////////////////
78 76
// Architecture constants //
79 77
////////////////////////////
94 92
/////////////////////////
95 93
// Codegen Allocation  //
96 94
/////////////////////////
97 95
98 96
/// Argument registers for function calls.
99 -
pub const ARG_REGS: [Reg; 8] = [A0, A1, A2, A3, A4, A5, A6, A7];
97 +
pub const ARG_REGS: [gen::Reg; 8] = [A0, A1, A2, A3, A4, A5, A6, A7];
100 98
101 99
/// Scratch register for code gen. Never allocated to user values.
102 -
pub const SCRATCH1: Reg = { n: 30 };
100 +
pub const SCRATCH1: gen::Reg = T5;
103 101
104 102
/// Second scratch register for operations needing two temporaries.
105 -
pub const SCRATCH2: Reg = { n: 31 };
103 +
pub const SCRATCH2: gen::Reg = T6;
106 104
107 105
/// Dedicated scratch for address offset adjustment. Never allocated to user
108 106
/// values and never used for operand materialization, so it can never
109 107
/// conflict with `rd`, `rs`, or `base` in load/store helpers.
110 -
pub const ADDR_SCRATCH: Reg = { n: 29 };
108 +
pub const ADDR_SCRATCH: gen::Reg = T4;
111 109
112 110
/// Callee-saved registers that need save/restore if used.
113 -
pub const CALLEE_SAVED: [Reg; NUM_SAVED_REGISTERS] = [S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11];
111 +
pub const CALLEE_SAVED: [gen::Reg; NUM_SAVED_REGISTERS] = [S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11];
114 112
115 113
/// Maximum 12-bit signed immediate value.
116 114
pub const MAX_IMM: i32 = 2047;
117 115
118 116
/// Minimum 12-bit signed immediate value.
119 117
pub const MIN_IMM: i32 = -2048;
120 118
121 -
/// Allocatable register numbers for register allocation.
122 -
const ALLOCATABLE_REGS: [u8; 23] = [
123 -
    5, 6, 7, 28,                                // T0-T3
124 -
    10, 11, 12, 13, 14, 15, 16, 17,             // A0-A7
125 -
    9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,  // S1-S11
119 +
/// Allocatable registers for register allocation.
120 +
const ALLOCATABLE_REGS: [gen::Reg; 23] = [
121 +
    T0, T1, T2, T3,                             // Temporaries
122 +
    A0, A1, A2, A3, A4, A5, A6, A7,             // Arguments
123 +
    S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, // Saved
126 124
];
127 125
128 -
/// Argument register numbers for register allocation.
129 -
const ARG_REG_NUMS: [u8; 8] = [10, 11, 12, 13, 14, 15, 16, 17]; // A0-A7
130 -
131 -
/// Callee-saved register numbers for frame layout.
132 -
const CALLEE_SAVED_NUMS: [u8; 11] = [9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]; // S1-S11
133 -
134 126
/// Get target configuration for register allocation.
135 127
// TODO: This should be a constant variable.
136 128
pub fn targetConfig() -> regalloc::TargetConfig {
137 129
    return regalloc::TargetConfig {
138 130
        allocatable: &ALLOCATABLE_REGS[..],
139 -
        argRegs: &ARG_REG_NUMS[..],
140 -
        calleeSaved: &CALLEE_SAVED_NUMS[..],
131 +
        argRegs: &ARG_REGS[..],
132 +
        calleeSaved: &CALLEE_SAVED[..],
141 133
        slotSize: DWORD_SIZE,
142 134
    };
143 135
}
144 136
145 137
///////////////////////
lib/std/arch/rv64/decode.rad +63 -62
1 1
//! RV64 instruction decoder.
2 2
//!
3 3
//! Decodes 32-bit instruction words into structured representations.
4 4
5 +
use std::lang::gen;
5 6
use super::encode;
6 7
7 8
///////////////////////
8 9
// Field Extraction  //
9 10
///////////////////////
102 103
/////////////////////////
103 104
104 105
/// Decoded RV64 instruction.
105 106
pub union Instr {
106 107
    // R-type ALU.
107 -
    Add  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
108 -
    Sub  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
109 -
    Sll  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
110 -
    Slt  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
111 -
    Sltu { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
112 -
    Xor  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
113 -
    Srl  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
114 -
    Sra  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
115 -
    Or   { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
116 -
    And  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
108 +
    Add  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
109 +
    Sub  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
110 +
    Sll  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
111 +
    Slt  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
112 +
    Sltu { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
113 +
    Xor  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
114 +
    Srl  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
115 +
    Sra  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
116 +
    Or   { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
117 +
    And  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
117 118
118 119
    // R-type M extension.
119 -
    Mul    { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
120 -
    Mulh   { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
121 -
    Mulhsu { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
122 -
    Mulhu  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
123 -
    Div    { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
124 -
    Divu   { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
125 -
    Rem    { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
126 -
    Remu   { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
120 +
    Mul    { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
121 +
    Mulh   { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
122 +
    Mulhsu { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
123 +
    Mulhu  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
124 +
    Div    { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
125 +
    Divu   { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
126 +
    Rem    { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
127 +
    Remu   { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
127 128
128 129
    // R-type RV64 word operations.
129 -
    Addw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
130 -
    Subw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
131 -
    Sllw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
132 -
    Srlw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
133 -
    Sraw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
134 -
    Mulw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
135 -
    Divw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
136 -
    Divuw { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
137 -
    Remw  { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
138 -
    Remuw { rd: super::Reg, rs1: super::Reg, rs2: super::Reg },
130 +
    Addw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
131 +
    Subw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
132 +
    Sllw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
133 +
    Srlw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
134 +
    Sraw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
135 +
    Mulw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
136 +
    Divw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
137 +
    Divuw { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
138 +
    Remw  { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
139 +
    Remuw { rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg },
139 140
140 141
    // I-type ALU.
141 -
    Addi  { rd: super::Reg, rs1: super::Reg, imm: i32 },
142 -
    Slti  { rd: super::Reg, rs1: super::Reg, imm: i32 },
143 -
    Sltiu { rd: super::Reg, rs1: super::Reg, imm: i32 },
144 -
    Xori  { rd: super::Reg, rs1: super::Reg, imm: i32 },
145 -
    Ori   { rd: super::Reg, rs1: super::Reg, imm: i32 },
146 -
    Andi  { rd: super::Reg, rs1: super::Reg, imm: i32 },
147 -
    Slli  { rd: super::Reg, rs1: super::Reg, shamt: i32 },
148 -
    Srli  { rd: super::Reg, rs1: super::Reg, shamt: i32 },
149 -
    Srai  { rd: super::Reg, rs1: super::Reg, shamt: i32 },
142 +
    Addi  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
143 +
    Slti  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
144 +
    Sltiu { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
145 +
    Xori  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
146 +
    Ori   { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
147 +
    Andi  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
148 +
    Slli  { rd: gen::Reg, rs1: gen::Reg, shamt: i32 },
149 +
    Srli  { rd: gen::Reg, rs1: gen::Reg, shamt: i32 },
150 +
    Srai  { rd: gen::Reg, rs1: gen::Reg, shamt: i32 },
150 151
151 152
    // I-type RV64 word immediate operations.
152 -
    Addiw { rd: super::Reg, rs1: super::Reg, imm: i32 },
153 -
    Slliw { rd: super::Reg, rs1: super::Reg, shamt: i32 },
154 -
    Srliw { rd: super::Reg, rs1: super::Reg, shamt: i32 },
155 -
    Sraiw { rd: super::Reg, rs1: super::Reg, shamt: i32 },
153 +
    Addiw { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
154 +
    Slliw { rd: gen::Reg, rs1: gen::Reg, shamt: i32 },
155 +
    Srliw { rd: gen::Reg, rs1: gen::Reg, shamt: i32 },
156 +
    Sraiw { rd: gen::Reg, rs1: gen::Reg, shamt: i32 },
156 157
157 158
    // Load instructions.
158 -
    Lb  { rd: super::Reg, rs1: super::Reg, imm: i32 },
159 -
    Lh  { rd: super::Reg, rs1: super::Reg, imm: i32 },
160 -
    Lw  { rd: super::Reg, rs1: super::Reg, imm: i32 },
161 -
    Ld  { rd: super::Reg, rs1: super::Reg, imm: i32 },
162 -
    Lbu { rd: super::Reg, rs1: super::Reg, imm: i32 },
163 -
    Lhu { rd: super::Reg, rs1: super::Reg, imm: i32 },
164 -
    Lwu { rd: super::Reg, rs1: super::Reg, imm: i32 },
159 +
    Lb  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
160 +
    Lh  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
161 +
    Lw  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
162 +
    Ld  { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
163 +
    Lbu { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
164 +
    Lhu { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
165 +
    Lwu { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
165 166
166 167
    // Store instructions.
167 -
    Sb { rs2: super::Reg, rs1: super::Reg, imm: i32 },
168 -
    Sh { rs2: super::Reg, rs1: super::Reg, imm: i32 },
169 -
    Sw { rs2: super::Reg, rs1: super::Reg, imm: i32 },
170 -
    Sd { rs2: super::Reg, rs1: super::Reg, imm: i32 },
168 +
    Sb { rs2: gen::Reg, rs1: gen::Reg, imm: i32 },
169 +
    Sh { rs2: gen::Reg, rs1: gen::Reg, imm: i32 },
170 +
    Sw { rs2: gen::Reg, rs1: gen::Reg, imm: i32 },
171 +
    Sd { rs2: gen::Reg, rs1: gen::Reg, imm: i32 },
171 172
172 173
    // Branch instructions.
173 -
    Beq  { rs1: super::Reg, rs2: super::Reg, imm: i32 },
174 -
    Bne  { rs1: super::Reg, rs2: super::Reg, imm: i32 },
175 -
    Blt  { rs1: super::Reg, rs2: super::Reg, imm: i32 },
176 -
    Bge  { rs1: super::Reg, rs2: super::Reg, imm: i32 },
177 -
    Bltu { rs1: super::Reg, rs2: super::Reg, imm: i32 },
178 -
    Bgeu { rs1: super::Reg, rs2: super::Reg, imm: i32 },
174 +
    Beq  { rs1: gen::Reg, rs2: gen::Reg, imm: i32 },
175 +
    Bne  { rs1: gen::Reg, rs2: gen::Reg, imm: i32 },
176 +
    Blt  { rs1: gen::Reg, rs2: gen::Reg, imm: i32 },
177 +
    Bge  { rs1: gen::Reg, rs2: gen::Reg, imm: i32 },
178 +
    Bltu { rs1: gen::Reg, rs2: gen::Reg, imm: i32 },
179 +
    Bgeu { rs1: gen::Reg, rs2: gen::Reg, imm: i32 },
179 180
180 181
    // Jump instructions.
181 -
    Jal  { rd: super::Reg, imm: i32 },
182 -
    Jalr { rd: super::Reg, rs1: super::Reg, imm: i32 },
182 +
    Jal  { rd: gen::Reg, imm: i32 },
183 +
    Jalr { rd: gen::Reg, rs1: gen::Reg, imm: i32 },
183 184
184 185
    // Upper immediate.
185 -
    Lui   { rd: super::Reg, imm: i32 },
186 -
    Auipc { rd: super::Reg, imm: i32 },
186 +
    Lui   { rd: gen::Reg, imm: i32 },
187 +
    Auipc { rd: gen::Reg, imm: i32 },
187 188
188 189
    // System.
189 190
    Ecall,
190 191
    Ebreak,
191 192
lib/std/arch/rv64/emit.rad +21 -20
2 2
//!
3 3
//! Emits RV64 machine code as `u32` list.
4 4
5 5
use std::lang::il;
6 6
use std::lang::alloc;
7 +
use std::lang::gen;
7 8
use std::lang::gen::labels;
8 9
use std::lang::gen::types;
9 10
use std::collections::dict;
10 11
use std::mem;
11 12
37 38
}
38 39
39 40
/// Type of branch instruction.
40 41
pub union BranchKind {
41 42
    /// Conditional branch (B-type encoding).
42 -
    Cond { op: il::CmpOp, rs1: super::Reg, rs2: super::Reg },
43 +
    Cond { op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg },
43 44
    /// Inverted conditional branch (B-type encoding with negated condition).
44 -
    InvertedCond { op: il::CmpOp, rs1: super::Reg, rs2: super::Reg },
45 +
    InvertedCond { op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg },
45 46
    /// Unconditional jump (J-type encoding).
46 47
    Jump,
47 48
}
48 49
49 50
/// Function call that needs offset patching.
60 61
    /// Index in code buffer where the load was emitted.
61 62
    index: u32,
62 63
    /// Target function name.
63 64
    target: *[u8],
64 65
    /// Destination register.
65 -
    rd: super::Reg,
66 +
    rd: gen::Reg,
66 67
}
67 68
68 69
/// Adjusted base register and offset for addressing.
69 70
pub record AdjustedOffset {
70 71
    /// Base register.
71 -
    base: super::Reg,
72 +
    base: gen::Reg,
72 73
    /// Byte offset from register.
73 74
    offset: i32,
74 75
}
75 76
76 77
/// Callee-saved register with its stack offset.
77 78
pub record SavedReg {
78 79
    /// Register to save/restore.
79 -
    reg: super::Reg,
80 +
    reg: gen::Reg,
80 81
    /// Offset from SP.
81 82
    offset: i32,
82 83
}
83 84
84 85
/// Emission context. Tracks state during code generation.
280 281
}
281 282
282 283
/// Record a function address load needing later patching.
283 284
/// Emits placeholder instructions that will be patched to load the function's address.
284 285
/// Uses two slots to compute long-distance addresses.
285 -
pub fn recordAddrLoad(e: *mut Emitter, target: *[u8], rd: super::Reg) {
286 +
pub fn recordAddrLoad(e: *mut Emitter, target: *[u8], rd: gen::Reg) {
286 287
    assert e.pendingAddrLoadsLen < e.pendingAddrLoads.len, "recordAddrLoad: buffer full";
287 288
    e.pendingAddrLoads[e.pendingAddrLoadsLen] = PendingAddrLoad {
288 289
        index: e.codeLen,
289 290
        target,
290 291
        rd: rd,
335 336
    }
336 337
    e.pendingBranchesLen = 0;
337 338
}
338 339
339 340
/// Encode a conditional branch instruction.
340 -
fn encodeCondBranch(op: il::CmpOp, rs1: super::Reg, rs2: super::Reg, offset: i32) -> u32 {
341 +
fn encodeCondBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 {
341 342
    match op {
342 343
        case il::CmpOp::Eq => return encode::beq(rs1, rs2, offset),
343 344
        case il::CmpOp::Ne => return encode::bne(rs1, rs2, offset),
344 345
        case il::CmpOp::Slt => return encode::blt(rs1, rs2, offset),
345 346
        case il::CmpOp::Ult => return encode::bltu(rs1, rs2, offset),
346 347
    }
347 348
}
348 349
349 350
/// Encode an inverted conditional branch instruction.
350 -
fn encodeInvertedBranch(op: il::CmpOp, rs1: super::Reg, rs2: super::Reg, offset: i32) -> u32 {
351 +
fn encodeInvertedBranch(op: il::CmpOp, rs1: gen::Reg, rs2: gen::Reg, offset: i32) -> u32 {
351 352
    match op {
352 353
        case il::CmpOp::Eq => return encode::bne(rs1, rs2, offset),
353 354
        case il::CmpOp::Ne => return encode::beq(rs1, rs2, offset),
354 355
        case il::CmpOp::Slt => return encode::bge(rs1, rs2, offset),
355 356
        case il::CmpOp::Ult => return encode::bgeu(rs1, rs2, offset),
416 417
/// Adjust a large offset by loading *hi* bits into [`super::ADDR_SCRATCH`].
417 418
/// Returns adjusted base register and remaining offset.
418 419
///
419 420
/// When the offset fits a 12-bit signed immediate, returns it unchanged.
420 421
/// Otherwise uses [`super::ADDR_SCRATCH`] for the LUI+ADD decomposition.
421 -
fn adjustOffset(e: *mut Emitter, base: super::Reg, offset: i32) -> AdjustedOffset {
422 +
fn adjustOffset(e: *mut Emitter, base: gen::Reg, offset: i32) -> AdjustedOffset {
422 423
    if offset >= super::MIN_IMM and offset <= super::MAX_IMM {
423 424
        return AdjustedOffset { base, offset };
424 425
    }
425 426
    let s = splitImm(offset);
426 427
    emit(e, encode::lui(super::ADDR_SCRATCH, s.hi));
432 433
/// Load an immediate value into a register.
433 434
/// Handles the full range of 64-bit immediates.
434 435
/// For values fitting in 12 bits, uses a single `ADDI`.
435 436
/// For values fitting in 32 bits, uses `LUI` + `ADDIW`.
436 437
/// For wider values, loads upper and lower halves then combines with shift and add.
437 -
pub fn loadImm(e: *mut Emitter, rd: super::Reg, imm: i64) {
438 +
pub fn loadImm(e: *mut Emitter, rd: gen::Reg, imm: i64) {
438 439
    let immMin = super::MIN_IMM as i64;
439 440
    let immMax = super::MAX_IMM as i64;
440 441
441 442
    if imm >= immMin and imm <= immMax {
442 443
        emit(e, encode::addi(rd, super::ZERO, imm as i32));
471 472
    emit(e, encode::slli(rd, rd, 10));
472 473
    emit(e, encode::addi(rd, rd, lower & 0x3FF));
473 474
}
474 475
475 476
/// Emit add-immediate, handling large immediates.
476 -
pub fn emitAddImm(e: *mut Emitter, rd: super::Reg, rs: super::Reg, imm: i32) {
477 +
pub fn emitAddImm(e: *mut Emitter, rd: gen::Reg, rs: gen::Reg, imm: i32) {
477 478
    if imm >= super::MIN_IMM and imm <= super::MAX_IMM {
478 479
        emit(e, encode::addi(rd, rs, imm));
479 480
    } else {
480 481
        loadImm(e, super::SCRATCH1, imm as i64);
481 482
        emit(e, encode::add(rd, rs, super::SCRATCH1));
485 486
////////////////////////
486 487
// Load/Store Helpers //
487 488
////////////////////////
488 489
489 490
/// Emit unsigned load with automatic offset adjustment.
490 -
pub fn emitLoad(e: *mut Emitter, rd: super::Reg, base: super::Reg, offset: i32, typ: il::Type) {
491 +
pub fn emitLoad(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
491 492
    let adj = adjustOffset(e, base, offset);
492 493
    match typ {
493 494
        case il::Type::W8 => emit(e, encode::lbu(rd, adj.base, adj.offset)),
494 495
        case il::Type::W16 => emit(e, encode::lhu(rd, adj.base, adj.offset)),
495 496
        case il::Type::W32 => emit(e, encode::lwu(rd, adj.base, adj.offset)),
496 497
        case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)),
497 498
    }
498 499
}
499 500
500 501
/// Emit signed load with automatic offset adjustment.
501 -
pub fn emitSload(e: *mut Emitter, rd: super::Reg, base: super::Reg, offset: i32, typ: il::Type) {
502 +
pub fn emitSload(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
502 503
    let adj = adjustOffset(e, base, offset);
503 504
    match typ {
504 505
        case il::Type::W8 => emit(e, encode::lb(rd, adj.base, adj.offset)),
505 506
        case il::Type::W16 => emit(e, encode::lh(rd, adj.base, adj.offset)),
506 507
        case il::Type::W32 => emit(e, encode::lw(rd, adj.base, adj.offset)),
507 508
        case il::Type::W64 => emit(e, encode::ld(rd, adj.base, adj.offset)),
508 509
    }
509 510
}
510 511
511 512
/// Emit store with automatic offset adjustment.
512 -
pub fn emitStore(e: *mut Emitter, rs: super::Reg, base: super::Reg, offset: i32, typ: il::Type) {
513 +
pub fn emitStore(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32, typ: il::Type) {
513 514
    let adj = adjustOffset(e, base, offset);
514 515
    match typ {
515 516
        case il::Type::W8 => emit(e, encode::sb(rs, adj.base, adj.offset)),
516 517
        case il::Type::W16 => emit(e, encode::sh(rs, adj.base, adj.offset)),
517 518
        case il::Type::W32 => emit(e, encode::sw(rs, adj.base, adj.offset)),
518 519
        case il::Type::W64 => emit(e, encode::sd(rs, adj.base, adj.offset)),
519 520
    }
520 521
}
521 522
522 523
/// Emit 64-bit load with automatic offset adjustment.
523 -
pub fn emitLd(e: *mut Emitter, rd: super::Reg, base: super::Reg, offset: i32) {
524 +
pub fn emitLd(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
524 525
    let adj = adjustOffset(e, base, offset);
525 526
    emit(e, encode::ld(rd, adj.base, adj.offset));
526 527
}
527 528
528 529
/// Emit 64-bit store with automatic offset adjustment.
529 -
pub fn emitSd(e: *mut Emitter, rs: super::Reg, base: super::Reg, offset: i32) {
530 +
pub fn emitSd(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
530 531
    let adj = adjustOffset(e, base, offset);
531 532
    emit(e, encode::sd(rs, adj.base, adj.offset));
532 533
}
533 534
534 535
/// Emit 32-bit load with automatic offset adjustment.
535 -
pub fn emitLw(e: *mut Emitter, rd: super::Reg, base: super::Reg, offset: i32) {
536 +
pub fn emitLw(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
536 537
    let adj = adjustOffset(e, base, offset);
537 538
    emit(e, encode::lw(rd, adj.base, adj.offset));
538 539
}
539 540
540 541
/// Emit 32-bit store with automatic offset adjustment.
541 -
pub fn emitSw(e: *mut Emitter, rs: super::Reg, base: super::Reg, offset: i32) {
542 +
pub fn emitSw(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
542 543
    let adj = adjustOffset(e, base, offset);
543 544
    emit(e, encode::sw(rs, adj.base, adj.offset));
544 545
}
545 546
546 547
/// Emit 8-bit load with automatic offset adjustment.
547 -
pub fn emitLb(e: *mut Emitter, rd: super::Reg, base: super::Reg, offset: i32) {
548 +
pub fn emitLb(e: *mut Emitter, rd: gen::Reg, base: gen::Reg, offset: i32) {
548 549
    let adj = adjustOffset(e, base, offset);
549 550
    emit(e, encode::lb(rd, adj.base, adj.offset));
550 551
}
551 552
552 553
/// Emit 8-bit store with automatic offset adjustment.
553 -
pub fn emitSb(e: *mut Emitter, rs: super::Reg, base: super::Reg, offset: i32) {
554 +
pub fn emitSb(e: *mut Emitter, rs: gen::Reg, base: gen::Reg, offset: i32) {
554 555
    let adj = adjustOffset(e, base, offset);
555 556
    emit(e, encode::sb(rs, adj.base, adj.offset));
556 557
}
557 558
558 559
//////////////////////////
lib/std/arch/rv64/encode.rad +93 -91
1 1
//! RISC-V RV64I+M instruction encoding.
2 2
//!
3 3
//! Provides type-safe functions for encoding RV64 instructions.
4 4
5 +
use std::lang::gen;
6 +
5 7
//////////////////////
6 8
// Opcode Constants //
7 9
//////////////////////
8 10
9 11
pub const OP_LOAD:   u32 = 0x03;
91 93
//////////////////////
92 94
// Format Encoders  //
93 95
//////////////////////
94 96
95 97
/// Encode an R-type instruction.
96 -
fn encodeR(opcode: u32, rd: super::Reg, rs1: super::Reg, rs2: super::Reg, funct3: u32, funct7: u32) -> u32 {
98 +
fn encodeR(opcode: u32, rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, funct7: u32) -> u32 {
97 99
    return (opcode        & 0x7F)
98 -
         | ((rd.n  as u32 & 0x1F) << 7)
100 +
         | ((*rd  as u32  & 0x1F) << 7)
99 101
         | ((funct3       & 0x07) << 12)
100 -
         | ((rs1.n as u32 & 0x1F) << 15)
101 -
         | ((rs2.n as u32 & 0x1F) << 20)
102 +
         | ((*rs1 as u32  & 0x1F) << 15)
103 +
         | ((*rs2 as u32  & 0x1F) << 20)
102 104
         | ((funct7       & 0x7F) << 25);
103 105
}
104 106
105 107
/// Encode an I-type instruction.
106 -
fn encodeI(opcode: u32, rd: super::Reg, rs1: super::Reg, funct3: u32, imm: i32) -> u32 {
108 +
fn encodeI(opcode: u32, rd: gen::Reg, rs1: gen::Reg, funct3: u32, imm: i32) -> u32 {
107 109
    assert isSmallImm(imm);
108 110
109 111
    return (opcode        & 0x7F)
110 -
         | ((rd.n  as u32 & 0x1F)  << 7)
112 +
         | ((*rd  as u32  & 0x1F)  << 7)
111 113
         | ((funct3       & 0x07)  << 12)
112 -
         | ((rs1.n as u32 & 0x1F)  << 15)
114 +
         | ((*rs1 as u32  & 0x1F)  << 15)
113 115
         | ((imm as u32   & 0xFFF) << 20);
114 116
}
115 117
116 118
/// Encode an S-type instruction.
117 -
fn encodeS(opcode: u32, rs1: super::Reg, rs2: super::Reg, funct3: u32, imm: i32) -> u32 {
119 +
fn encodeS(opcode: u32, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, imm: i32) -> u32 {
118 120
    assert isSmallImm(imm);
119 121
120 122
    return (opcode  & 0x7F)
121 -
         | ((imm as u32        & 0x1F) << 7)
122 -
         | ((funct3            & 0x07) << 12)
123 -
         | ((rs1.n as u32      & 0x1F) << 15)
124 -
         | ((rs2.n as u32      & 0x1F) << 20)
125 -
         | ((imm as u32 >> 5   & 0x7F) << 25);
123 +
         | ((imm as u32       & 0x1F) << 7)
124 +
         | ((funct3           & 0x07) << 12)
125 +
         | ((*rs1 as u32      & 0x1F) << 15)
126 +
         | ((*rs2 as u32      & 0x1F) << 20)
127 +
         | ((imm as u32 >> 5  & 0x7F) << 25);
126 128
}
127 129
128 130
/// Encode a B-type (branch) instruction.
129 -
fn encodeB(opcode: u32, rs1: super::Reg, rs2: super::Reg, funct3: u32, imm: i32) -> u32 {
131 +
fn encodeB(opcode: u32, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, imm: i32) -> u32 {
130 132
    assert isBranchImm(imm);
131 133
132 134
    let imm11   = (imm as u32 >> 11) & 0x1;
133 135
    let imm4_1  = (imm as u32 >> 1)  & 0xF;
134 136
    let imm10_5 = (imm as u32 >> 5)  & 0x3F;
136 138
137 139
    return (opcode        &  0x7F)
138 140
         | (imm11         << 7)
139 141
         | (imm4_1        << 8)
140 142
         | ((funct3       & 0x07) << 12)
141 -
         | ((rs1.n as u32 & 0x1F) << 15)
142 -
         | ((rs2.n as u32 & 0x1F) << 20)
143 +
         | ((*rs1 as u32  & 0x1F) << 15)
144 +
         | ((*rs2 as u32  & 0x1F) << 20)
143 145
         | (imm10_5       << 25)
144 146
         | (imm12         << 31);
145 147
}
146 148
147 149
/// Encode a U-type instruction.
148 -
fn encodeU(opcode: u32, rd: super::Reg, imm: i32) -> u32 {
150 +
fn encodeU(opcode: u32, rd: gen::Reg, imm: i32) -> u32 {
149 151
    return (opcode       & 0x7F)
150 -
         | ((rd.n as u32 & 0x1F)    << 7)
152 +
         | ((*rd as u32  & 0x1F)    << 7)
151 153
         | ((imm as u32  & 0xFFFFF) << 12);
152 154
}
153 155
154 156
/// Encode a J-type (jump) instruction.
155 -
fn encodeJ(opcode: u32, rd: super::Reg, imm: i32) -> u32 {
157 +
fn encodeJ(opcode: u32, rd: gen::Reg, imm: i32) -> u32 {
156 158
    assert isJumpImm(imm);
157 159
158 160
    let imm20    = (imm as u32 >> 20) & 0x1;
159 161
    let imm10_1  = (imm as u32 >> 1)  & 0x3FF;
160 162
    let imm11    = (imm as u32 >> 11) & 0x1;
161 163
    let imm19_12 = (imm as u32 >> 12) & 0xFF;
162 164
163 165
    return (opcode       & 0x7F)
164 -
         | ((rd.n as u32 & 0x1F) << 7)
166 +
         | ((*rd as u32  & 0x1F) << 7)
165 167
         | (imm19_12     << 12)
166 168
         | (imm11        << 20)
167 169
         | (imm10_1      << 21)
168 170
         | (imm20        << 31);
169 171
}
171 173
////////////////////////////
172 174
// ALU Immediate (I-type) //
173 175
////////////////////////////
174 176
175 177
/// Add immediate: `rd = rs1 + imm`.
176 -
pub fn addi(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
178 +
pub fn addi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
177 179
    return encodeI(OP_IMM, rd, rs1, F3_ADD, imm);
178 180
}
179 181
180 182
/// Set less than immediate (signed): `rd = (rs1 < imm) ? 1 : 0`.
181 -
pub fn slti(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
183 +
pub fn slti(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
182 184
    return encodeI(OP_IMM, rd, rs1, F3_SLT, imm);
183 185
}
184 186
185 187
/// Set less than immediate unsigned: `rd = (rs1 < imm) ? 1 : 0`.
186 -
pub fn sltiu(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
188 +
pub fn sltiu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
187 189
    return encodeI(OP_IMM, rd, rs1, F3_SLTU, imm);
188 190
}
189 191
190 192
/// XOR immediate: `rd = rs1 ^ imm`.
191 -
pub fn xori(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
193 +
pub fn xori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
192 194
    return encodeI(OP_IMM, rd, rs1, F3_XOR, imm);
193 195
}
194 196
195 197
/// OR immediate: `rd = rs1 | imm`.
196 -
pub fn ori(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
198 +
pub fn ori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
197 199
    return encodeI(OP_IMM, rd, rs1, F3_OR, imm);
198 200
}
199 201
200 202
/// AND immediate: `rd = rs1 & imm`.
201 -
pub fn andi(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
203 +
pub fn andi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
202 204
    return encodeI(OP_IMM, rd, rs1, F3_AND, imm);
203 205
}
204 206
205 207
/// Shift left logical immediate: `rd = rs1 << shamt`.
206 -
pub fn slli(rd: super::Reg, rs1: super::Reg, shamt: i32) -> u32 {
208 +
pub fn slli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
207 209
    assert shamt >= 0 and shamt < 64;
208 210
    return encodeI(OP_IMM, rd, rs1, F3_SLL, shamt & 0x3F);
209 211
}
210 212
211 213
/// Shift right logical immediate: `rd = rs1 >> shamt` (zero-extend).
212 -
pub fn srli(rd: super::Reg, rs1: super::Reg, shamt: i32) -> u32 {
214 +
pub fn srli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
213 215
    assert shamt >= 0 and shamt < 64;
214 216
    return encodeI(OP_IMM, rd, rs1, F3_SRL, shamt & 0x3F);
215 217
}
216 218
217 219
/// Shift right arithmetic immediate: `rd = rs1 >> shamt` (sign-extend).
218 -
pub fn srai(rd: super::Reg, rs1: super::Reg, shamt: i32) -> u32 {
220 +
pub fn srai(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
219 221
    assert shamt >= 0 and shamt < 64;
220 222
    // SRAI has bit 10 set in immediate field (becomes bit 30 in instruction)
221 223
    return encodeI(OP_IMM, rd, rs1, F3_SRL, (shamt & 0x3F) | 0b10000000000);
222 224
}
223 225
224 226
///////////////////////////
225 227
// ALU Register (R-type) //
226 228
///////////////////////////
227 229
228 230
/// Add: `rd = rs1 + rs2`.
229 -
pub fn add(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
231 +
pub fn add(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
230 232
    return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_NORMAL);
231 233
}
232 234
233 235
/// Subtract: `rd = rs1 - rs2`.
234 -
pub fn sub(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
236 +
pub fn sub(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
235 237
    return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_SUB);
236 238
}
237 239
238 240
/// Shift left logical: `rd = rs1 << rs2[5:0]`.
239 -
pub fn sll(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
241 +
pub fn sll(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
240 242
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_NORMAL);
241 243
}
242 244
243 245
/// Set less than (signed): `rd = (rs1 < rs2) ? 1 : 0`.
244 -
pub fn slt(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
246 +
pub fn slt(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
245 247
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_NORMAL);
246 248
}
247 249
248 250
/// Set less than unsigned: `rd = (rs1 < rs2) ? 1 : 0`.
249 -
pub fn sltu(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
251 +
pub fn sltu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
250 252
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_NORMAL);
251 253
}
252 254
253 255
/// XOR: `rd = rs1 ^ rs2`.
254 -
pub fn xor(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
256 +
pub fn xor(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
255 257
    return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_NORMAL);
256 258
}
257 259
258 260
/// Shift right logical: `rd = rs1 >> rs2[5:0]` (zero-extend).
259 -
pub fn srl(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
261 +
pub fn srl(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
260 262
    return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_NORMAL);
261 263
}
262 264
263 265
/// Shift right arithmetic: `rd = rs1 >> rs2[5:0]` (sign-extend).
264 -
pub fn sra(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
266 +
pub fn sra(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
265 267
    return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_SRA);
266 268
}
267 269
268 270
/// OR: `rd = rs1 | rs2`.
269 -
pub fn or_(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
271 +
pub fn or_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
270 272
    return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_NORMAL);
271 273
}
272 274
273 275
/// AND: `rd = rs1 & rs2`.
274 -
pub fn and_(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
276 +
pub fn and_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
275 277
    return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_NORMAL);
276 278
}
277 279
278 280
/////////////////
279 281
// M Extension //
280 282
/////////////////
281 283
282 284
/// Multiply: `rd = (rs1 * rs2)[63:0]`.
283 -
pub fn mul(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
285 +
pub fn mul(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
284 286
    return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_MUL);
285 287
}
286 288
287 289
/// Multiply high (signed x signed): `rd = (rs1 * rs2)[127:64]`.
288 -
pub fn mulh(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
290 +
pub fn mulh(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
289 291
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_MUL);
290 292
}
291 293
292 294
/// Multiply high (signed x unsigned): `rd = (rs1 * rs2)[127:64]`.
293 -
pub fn mulhsu(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
295 +
pub fn mulhsu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
294 296
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_MUL);
295 297
}
296 298
297 299
/// Multiply high (unsigned x unsigned): `rd = (rs1 * rs2)[127:64]`.
298 -
pub fn mulhu(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
300 +
pub fn mulhu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
299 301
    return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_MUL);
300 302
}
301 303
302 304
/// Divide (signed): `rd = rs1 / rs2`.
303 -
pub fn div(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
305 +
pub fn div(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
304 306
    return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_MUL);
305 307
}
306 308
307 309
/// Divide (unsigned): `rd = rs1 / rs2`.
308 -
pub fn divu(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
310 +
pub fn divu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
309 311
    return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_MUL);
310 312
}
311 313
312 314
/// Remainder (signed): `rd = rs1 % rs2`.
313 -
pub fn rem(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
315 +
pub fn rem(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
314 316
    return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_MUL);
315 317
}
316 318
317 319
/// Remainder (unsigned): `rd = rs1 % rs2`.
318 -
pub fn remu(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
320 +
pub fn remu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
319 321
    return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_MUL);
320 322
}
321 323
322 324
//////////////////////////
323 325
// RV64 Word Operations //
324 326
//////////////////////////
325 327
326 328
/// Add immediate word (32-bit, sign-extended): `rd = sign_ext((rs1 + imm)[31:0])`.
327 -
pub fn addiw(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
329 +
pub fn addiw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
328 330
    return encodeI(OP_IMM32, rd, rs1, F3_ADD, imm);
329 331
}
330 332
331 333
/// Shift left logical immediate word: `rd = sign_ext((rs1 << shamt)[31:0])`.
332 -
pub fn slliw(rd: super::Reg, rs1: super::Reg, shamt: i32) -> u32 {
334 +
pub fn slliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
333 335
    assert shamt >= 0 and shamt < 32;
334 336
    return encodeI(OP_IMM32, rd, rs1, F3_SLL, shamt & 0x1F);
335 337
}
336 338
337 339
/// Shift right logical immediate word: `rd = sign_ext((rs1[31:0] >> shamt))`.
338 -
pub fn srliw(rd: super::Reg, rs1: super::Reg, shamt: i32) -> u32 {
340 +
pub fn srliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
339 341
    assert shamt >= 0 and shamt < 32;
340 342
    return encodeI(OP_IMM32, rd, rs1, F3_SRL, shamt & 0x1F);
341 343
}
342 344
343 345
/// Shift right arithmetic immediate word: `rd = sign_ext((rs1[31:0] >> shamt))` (sign-extended).
344 -
pub fn sraiw(rd: super::Reg, rs1: super::Reg, shamt: i32) -> u32 {
346 +
pub fn sraiw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 {
345 347
    assert shamt >= 0 and shamt < 32;
346 348
    return encodeI(OP_IMM32, rd, rs1, F3_SRL, (shamt & 0x1F) | 0b10000000000);
347 349
}
348 350
349 351
/// Add word: `rd = sign_ext((rs1 + rs2)[31:0])`.
350 -
pub fn addw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
352 +
pub fn addw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
351 353
    return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_NORMAL);
352 354
}
353 355
354 356
/// Subtract word: `rd = sign_ext((rs1 - rs2)[31:0])`.
355 -
pub fn subw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
357 +
pub fn subw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
356 358
    return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_SUB);
357 359
}
358 360
359 361
/// Shift left logical word: `rd = sign_ext((rs1 << rs2[4:0])[31:0])`.
360 -
pub fn sllw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
362 +
pub fn sllw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
361 363
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SLL, F7_NORMAL);
362 364
}
363 365
364 366
/// Shift right logical word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))`.
365 -
pub fn srlw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
367 +
pub fn srlw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
366 368
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_NORMAL);
367 369
}
368 370
369 371
/// Shift right arithmetic word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))` (sign-extended).
370 -
pub fn sraw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
372 +
pub fn sraw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
371 373
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_SRA);
372 374
}
373 375
374 376
/// Multiply word: `rd = sign_ext((rs1 * rs2)[31:0])`.
375 -
pub fn mulw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
377 +
pub fn mulw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
376 378
    return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_MUL);
377 379
}
378 380
379 381
/// Divide word (signed): `rd = sign_ext(rs1[31:0] / rs2[31:0])`.
380 -
pub fn divw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
382 +
pub fn divw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
381 383
    return encodeR(OP_OP32, rd, rs1, rs2, F3_XOR, F7_MUL);
382 384
}
383 385
384 386
/// Divide word (unsigned): `rd = sign_ext(rs1[31:0] / rs2[31:0])`.
385 -
pub fn divuw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
387 +
pub fn divuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
386 388
    return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_MUL);
387 389
}
388 390
389 391
/// Remainder word (signed): `rd = sign_ext(rs1[31:0] % rs2[31:0])`.
390 -
pub fn remw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
392 +
pub fn remw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
391 393
    return encodeR(OP_OP32, rd, rs1, rs2, F3_OR, F7_MUL);
392 394
}
393 395
394 396
/// Remainder word (unsigned): `rd = sign_ext(rs1[31:0] % rs2[31:0])`.
395 -
pub fn remuw(rd: super::Reg, rs1: super::Reg, rs2: super::Reg) -> u32 {
397 +
pub fn remuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 {
396 398
    return encodeR(OP_OP32, rd, rs1, rs2, F3_AND, F7_MUL);
397 399
}
398 400
399 401
///////////////////
400 402
// Load (I-type) //
401 403
///////////////////
402 404
403 405
/// Load byte (sign-extend): `rd = mem[rs1 + imm][7:0]`.
404 -
pub fn lb(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
406 +
pub fn lb(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
405 407
    return encodeI(OP_LOAD, rd, rs1, F3_BYTE, imm);
406 408
}
407 409
408 410
/// Load halfword (sign-extend): `rd = mem[rs1 + imm][15:0]`.
409 -
pub fn lh(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
411 +
pub fn lh(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
410 412
    return encodeI(OP_LOAD, rd, rs1, F3_HALF, imm);
411 413
}
412 414
413 415
/// Load word (sign-extend to 64-bit): `rd = sign_ext(mem[rs1 + imm][31:0])`.
414 -
pub fn lw(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
416 +
pub fn lw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
415 417
    return encodeI(OP_LOAD, rd, rs1, F3_WORD, imm);
416 418
}
417 419
418 420
/// Load byte unsigned (zero-extend): `rd = mem[rs1 + imm][7:0]`.
419 -
pub fn lbu(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
421 +
pub fn lbu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
420 422
    return encodeI(OP_LOAD, rd, rs1, F3_BYTE_U, imm);
421 423
}
422 424
423 425
/// Load halfword unsigned (zero-extend): `rd = mem[rs1 + imm][15:0]`.
424 -
pub fn lhu(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
426 +
pub fn lhu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
425 427
    return encodeI(OP_LOAD, rd, rs1, F3_HALF_U, imm);
426 428
}
427 429
428 430
/// Load word unsigned (zero-extend to 64-bit): `rd = mem[rs1 + imm][31:0]`.
429 -
pub fn lwu(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
431 +
pub fn lwu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
430 432
    return encodeI(OP_LOAD, rd, rs1, F3_WORD_U, imm);
431 433
}
432 434
433 435
/// Load doubleword: `rd = mem[rs1 + imm][63:0]`.
434 -
pub fn ld(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
436 +
pub fn ld(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
435 437
    return encodeI(OP_LOAD, rd, rs1, F3_DWORD, imm);
436 438
}
437 439
438 440
////////////////////
439 441
// Store (S-type) //
440 442
////////////////////
441 443
442 444
/// Store byte: `mem[rs1 + imm] = rs2[7:0]`.
443 -
pub fn sb(rs2: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
445 +
pub fn sb(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
444 446
    return encodeS(OP_STORE, rs1, rs2, F3_BYTE, imm);
445 447
}
446 448
447 449
/// Store halfword: `mem[rs1 + imm] = rs2[15:0]`.
448 -
pub fn sh(rs2: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
450 +
pub fn sh(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
449 451
    return encodeS(OP_STORE, rs1, rs2, F3_HALF, imm);
450 452
}
451 453
452 454
/// Store word: `mem[rs1 + imm] = rs2[31:0]`.
453 -
pub fn sw(rs2: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
455 +
pub fn sw(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
454 456
    return encodeS(OP_STORE, rs1, rs2, F3_WORD, imm);
455 457
}
456 458
457 459
/// Store doubleword: `mem[rs1 + imm] = rs2[63:0]`.
458 -
pub fn sd(rs2: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
460 +
pub fn sd(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
459 461
    return encodeS(OP_STORE, rs1, rs2, F3_DWORD, imm);
460 462
}
461 463
462 464
/////////////////////
463 465
// Branch (B-type) //
464 466
/////////////////////
465 467
466 468
/// Branch if equal: `if (rs1 == rs2) pc += imm`.
467 -
pub fn beq(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
469 +
pub fn beq(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
468 470
    return encodeB(OP_BRANCH, rs1, rs2, F3_BEQ, imm);
469 471
}
470 472
471 473
/// Branch if not equal: `if (rs1 != rs2) pc += imm`.
472 -
pub fn bne(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
474 +
pub fn bne(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
473 475
    return encodeB(OP_BRANCH, rs1, rs2, F3_BNE, imm);
474 476
}
475 477
476 478
/// Branch if less than (signed): `if (rs1 < rs2) pc += imm`.
477 -
pub fn blt(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
479 +
pub fn blt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
478 480
    return encodeB(OP_BRANCH, rs1, rs2, F3_BLT, imm);
479 481
}
480 482
481 483
/// Branch if greater or equal (signed): `if (rs1 >= rs2) pc += imm`.
482 -
pub fn bge(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
484 +
pub fn bge(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
483 485
    return encodeB(OP_BRANCH, rs1, rs2, F3_BGE, imm);
484 486
}
485 487
486 488
/// Branch if less than unsigned: `if (rs1 < rs2) pc += imm`.
487 -
pub fn bltu(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
489 +
pub fn bltu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
488 490
    return encodeB(OP_BRANCH, rs1, rs2, F3_BLTU, imm);
489 491
}
490 492
491 493
/// Branch if greater or equal unsigned: `if (rs1 >= rs2) pc += imm`.
492 -
pub fn bgeu(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
494 +
pub fn bgeu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
493 495
    return encodeB(OP_BRANCH, rs1, rs2, F3_BGEU, imm);
494 496
}
495 497
496 498
//////////
497 499
// Jump //
498 500
//////////
499 501
500 502
/// Jump and link: `rd = pc + 4; pc += imm`.
501 -
pub fn jal(rd: super::Reg, imm: i32) -> u32 {
503 +
pub fn jal(rd: gen::Reg, imm: i32) -> u32 {
502 504
    return encodeJ(OP_JAL, rd, imm);
503 505
}
504 506
505 507
/// Jump and link register: `rd = pc + 4; pc = rs1 + imm`.
506 -
pub fn jalr(rd: super::Reg, rs1: super::Reg, imm: i32) -> u32 {
508 +
pub fn jalr(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 {
507 509
    return encodeI(OP_JALR, rd, rs1, 0, imm);
508 510
}
509 511
510 512
/////////////////////
511 513
// Upper Immediate //
512 514
/////////////////////
513 515
514 516
/// Load upper immediate: `rd = imm << 12`.
515 -
pub fn lui(rd: super::Reg, imm: i32) -> u32 {
517 +
pub fn lui(rd: gen::Reg, imm: i32) -> u32 {
516 518
    return encodeU(OP_LUI, rd, imm);
517 519
}
518 520
519 521
/// Add upper immediate to PC: `rd = pc + (imm << 12)`.
520 -
pub fn auipc(rd: super::Reg, imm: i32) -> u32 {
522 +
pub fn auipc(rd: gen::Reg, imm: i32) -> u32 {
521 523
    return encodeU(OP_AUIPC, rd, imm);
522 524
}
523 525
524 526
////////////
525 527
// System //
543 545
pub fn nop() -> u32 {
544 546
    return addi(super::ZERO, super::ZERO, 0);
545 547
}
546 548
547 549
/// Move: `rd = rs` (`addi rd, rs, 0`).
548 -
pub fn mv(rd: super::Reg, rs: super::Reg) -> u32 {
550 +
pub fn mv(rd: gen::Reg, rs: gen::Reg) -> u32 {
549 551
    return addi(rd, rs, 0);
550 552
}
551 553
552 554
/// Bitwise NOT: `rd = ~rs` (`xori rd, rs, -1`).
553 -
pub fn not_(rd: super::Reg, rs: super::Reg) -> u32 {
555 +
pub fn not_(rd: gen::Reg, rs: gen::Reg) -> u32 {
554 556
    return xori(rd, rs, -1);
555 557
}
556 558
557 559
/// Negate: `rd = -rs` (`sub rd, zero, rs`).
558 -
pub fn neg(rd: super::Reg, rs: super::Reg) -> u32 {
560 +
pub fn neg(rd: gen::Reg, rs: gen::Reg) -> u32 {
559 561
    return sub(rd, super::ZERO, rs);
560 562
}
561 563
562 564
/// Return: `jalr zero, ra, 0`.
563 565
pub fn ret() -> u32 {
569 571
    return jal(super::ZERO, imm);
570 572
}
571 573
572 574
/// Branch if less than or equal (signed): `if (rs1 <= rs2) pc += imm`.
573 575
/// Implemented as `bge rs2, rs1, imm` (swap operands).
574 -
pub fn ble(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
576 +
pub fn ble(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
575 577
    return bge(rs2, rs1, imm);
576 578
}
577 579
578 580
/// Branch if greater than (signed): `if (rs1 > rs2) pc += imm`.
579 581
/// Implemented as `blt rs2, rs1, imm` (swap operands).
580 -
pub fn bgt(rs1: super::Reg, rs2: super::Reg, imm: i32) -> u32 {
582 +
pub fn bgt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 {
581 583
    return blt(rs2, rs1, imm);
582 584
}
583 585
584 586
/// Set if equal to zero: `rd = (rs == 0) ? 1 : 0`.
585 587
/// Implemented as `sltiu rd, rs, 1`.
586 -
pub fn seqz(rd: super::Reg, rs: super::Reg) -> u32 {
588 +
pub fn seqz(rd: gen::Reg, rs: gen::Reg) -> u32 {
587 589
    return sltiu(rd, rs, 1);
588 590
}
589 591
590 592
/// Set if not equal to zero: `rd = (rs != 0) ? 1 : 0`.
591 593
/// Implemented as `sltu rd, zero, rs`.
592 -
pub fn snez(rd: super::Reg, rs: super::Reg) -> u32 {
594 +
pub fn snez(rd: gen::Reg, rs: gen::Reg) -> u32 {
593 595
    return sltu(rd, super::ZERO, rs);
594 596
}
595 597
596 598
/// Branch if equal to zero: `if (rs == 0) pc += imm`.
597 -
pub fn beqz(rs: super::Reg, imm: i32) -> u32 {
599 +
pub fn beqz(rs: gen::Reg, imm: i32) -> u32 {
598 600
    return beq(rs, super::ZERO, imm);
599 601
}
600 602
601 603
/// Branch if not equal to zero: `if (rs != 0) pc += imm`.
602 -
pub fn bnez(rs: super::Reg, imm: i32) -> u32 {
604 +
pub fn bnez(rs: gen::Reg, imm: i32) -> u32 {
603 605
    return bne(rs, super::ZERO, imm);
604 606
}
605 607
606 608
/// Call: `jal ra, imm`.
607 609
pub fn call(imm: i32) -> u32 {
lib/std/arch/rv64/isel.rad +29 -28
27 27
//!     Force an [`il::Val`] into a specific register `rd`. Built on [`resolveVal`] + [`emitMv`].
28 28
//!     Used when the instruction requires the value in `rd` (e.g. `sub rd, rd, rs2`).
29 29
30 30
use std::mem;
31 31
use std::lang::il;
32 +
use std::lang::gen;
32 33
use std::lang::gen::regalloc;
33 34
use std::lang::gen::labels;
34 35
use std::lang::gen::data;
35 36
36 37
use super::encode;
66 67
/// A pending spill store to be flushed after instruction selection.
67 68
record PendingSpill {
68 69
    /// The SSA register that was spilled.
69 70
    ssa: il::Reg,
70 71
    /// The physical register holding the value to store.
71 -
    rd: super::Reg,
72 +
    rd: gen::Reg,
72 73
}
73 74
74 75
////////////////////
75 76
// Selector State //
76 77
////////////////////
99 100
/////////////////////////
100 101
// Register Allocation //
101 102
/////////////////////////
102 103
103 104
/// Get the physical register for an already-allocated SSA register.
104 -
fn getReg(s: *Selector, ssa: il::Reg) -> super::Reg {
105 +
fn getReg(s: *Selector, ssa: il::Reg) -> gen::Reg {
105 106
    let phys = s.ralloc.assignments[ssa.n] else {
106 107
        panic "getReg: spilled register has no physical assignment";
107 108
    };
108 -
    return super::Reg { n: phys };
109 +
    return phys;
109 110
}
110 111
111 112
/// Compute the offset for a spill slot.
112 113
/// When using FP (dynamic): offset from FP = `slot - totalSize`.
113 114
/// When using SP: offset from SP = `slot`.
117 118
    }
118 119
    return slot;
119 120
}
120 121
121 122
/// Get the base register for spill slot addressing (FP or SP).
122 -
fn spillBase(s: *Selector) -> super::Reg {
123 +
fn spillBase(s: *Selector) -> gen::Reg {
123 124
    if s.isDynamic {
124 125
        return super::FP;
125 126
    }
126 127
    return super::SP;
127 128
}
128 129
129 130
/// Get the destination register for an SSA register.
130 131
/// If the register is spilled, records a pending spill and returns the scratch
131 132
/// register. The pending spill is auto-committed by [`selectBlock`] after each
132 133
/// instruction. If not spilled, returns the physical register.
133 -
fn getDstReg(s: *mut Selector, ssa: il::Reg, scratch: super::Reg) -> super::Reg {
134 +
fn getDstReg(s: *mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
134 135
    if let _ = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) {
135 136
        s.pendingSpill = PendingSpill { ssa, rd: scratch };
136 137
        return scratch;
137 138
    }
138 139
    return getReg(s, ssa);
139 140
}
140 141
141 142
/// Get the source register for an SSA register.
142 143
/// If the register is spilled, loads the value from the spill slot into the
143 144
/// scratch register and returns it. Otherwise returns the physical register.
144 -
fn getSrcReg(s: *mut Selector, ssa: il::Reg, scratch: super::Reg) -> super::Reg {
145 +
fn getSrcReg(s: *mut Selector, ssa: il::Reg, scratch: gen::Reg) -> gen::Reg {
145 146
    if let slot = regalloc::spill::spillSlot(&s.ralloc.spill, ssa) {
146 147
        emit::emitLd(s.e, scratch, spillBase(s), spillOffset(s, slot));
147 148
        return scratch;
148 149
    }
149 150
    return getReg(s, ssa);
158 159
}
159 160
160 161
/// Resolve an IL value to the physical register holding it.
161 162
/// For non-spilled register values, returns the physical register directly.
162 163
/// For immediates, symbols, and spilled registers, materializes into `scratch`.
163 -
fn resolveVal(s: *mut Selector, scratch: super::Reg, val: il::Val) -> super::Reg {
164 +
fn resolveVal(s: *mut Selector, scratch: gen::Reg, val: il::Val) -> gen::Reg {
164 165
    match val {
165 166
        case il::Val::Reg(r) => {
166 167
            return getSrcReg(s, r, scratch);
167 168
        },
168 169
        case il::Val::Imm(imm) => {
189 190
    }
190 191
}
191 192
192 193
/// Load an IL value into a specific physical register.
193 194
/// Like [`resolveVal`], but ensures the value ends up in `rd`.
194 -
fn loadVal(s: *mut Selector, rd: super::Reg, val: il::Val) -> super::Reg {
195 +
fn loadVal(s: *mut Selector, rd: gen::Reg, val: il::Val) -> gen::Reg {
195 196
    let rs = resolveVal(s, rd, val);
196 197
    emitMv(s, rd, rs);
197 198
    return rd;
198 199
}
199 200
200 201
/// Emit a move instruction if source and destination differ.
201 -
fn emitMv(s: *mut Selector, rd: super::Reg, rs: super::Reg) {
202 -
    if rd.n != rs.n {
202 +
fn emitMv(s: *mut Selector, rd: gen::Reg, rs: gen::Reg) {
203 +
    if *rd != *rs {
203 204
        emit::emit(s.e, encode::mv(rd, rs));
204 205
    }
205 206
}
206 207
207 208
/// Emit zero-extension from a sub-word type to the full register width.
208 -
fn emitZext(e: *mut emit::Emitter, rd: super::Reg, rs: super::Reg, typ: il::Type) {
209 +
fn emitZext(e: *mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
209 210
    match typ {
210 211
        case il::Type::W8 => emit::emit(e, encode::andi(rd, rs, MASK_W8)),
211 212
        case il::Type::W16 => {
212 213
            emit::emit(e, encode::slli(rd, rs, SHIFT_W16));
213 214
            emit::emit(e, encode::srli(rd, rd, SHIFT_W16));
219 220
        case il::Type::W64 => {}
220 221
    }
221 222
}
222 223
223 224
/// Emit sign-extension from a sub-word type to the full register width.
224 -
fn emitSext(e: *mut emit::Emitter, rd: super::Reg, rs: super::Reg, typ: il::Type) {
225 +
fn emitSext(e: *mut emit::Emitter, rd: gen::Reg, rs: gen::Reg, typ: il::Type) {
225 226
    match typ {
226 227
        case il::Type::W8 => {
227 228
            emit::emit(e, encode::slli(rd, rs, SHIFT_W8));
228 229
            emit::emit(e, encode::srai(rd, rd, SHIFT_W8));
229 230
        },
237 238
        case il::Type::W64 => {}
238 239
    }
239 240
}
240 241
241 242
/// Resolve a value, trap if zero (unless known non-zero), and return the register.
242 -
fn resolveAndTrapIfZero(s: *mut Selector, b: il::Val) -> super::Reg {
243 +
fn resolveAndTrapIfZero(s: *mut Selector, b: il::Val) -> gen::Reg {
243 244
    let rs2 = resolveVal(s, super::SCRATCH2, b);
244 245
    let mut knownNonZero = false;
245 246
    if let case il::Val::Imm(imm) = b {
246 247
        knownNonZero = imm != 0;
247 248
    }
334 335
335 336
            if let slot = regalloc::spill::spillSlot(&ralloc.spill, param) {
336 337
                // Spilled parameter: store arg register to spill slot.
337 338
                emit::emitSd(s.e, argReg, spillBase(&s), spillOffset(&s, slot));
338 339
            } else if let assigned = ralloc.assignments[param.n] {
339 -
                emitMv(&mut s, super::Reg { n: assigned }, argReg);
340 +
                emitMv(&mut s, assigned, argReg);
340 341
            }
341 342
        }
342 343
    }
343 344
344 345
    // Emit each block.
484 485
            // it does, advance the base registers by the accumulated
485 486
            // offset and reset to zero.
486 487
            while remaining >= super::DWORD_SIZE {
487 488
                if offset > super::MAX_IMM - super::DWORD_SIZE {
488 489
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
489 -
                    if rdst.n != rsrc.n {
490 +
                    if *rdst != *rsrc {
490 491
                        emit::emitAddImm(s.e, rdst, rdst, offset);
491 492
                    }
492 493
                    offset = 0;
493 494
                }
494 495
                if let off = srcReload {
502 503
                remaining -= super::DWORD_SIZE;
503 504
            }
504 505
            if remaining >= super::WORD_SIZE {
505 506
                if offset > super::MAX_IMM - super::WORD_SIZE {
506 507
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
507 -
                    if rdst.n != rsrc.n {
508 +
                    if *rdst != *rsrc {
508 509
                        emit::emitAddImm(s.e, rdst, rdst, offset);
509 510
                    }
510 511
                    offset = 0;
511 512
                }
512 513
                if let off = srcReload {
520 521
                remaining -= super::WORD_SIZE;
521 522
            }
522 523
            while remaining > 0 {
523 524
                if offset > super::MAX_IMM - 1 {
524 525
                    emit::emitAddImm(s.e, rsrc, rsrc, offset);
525 -
                    if rdst.n != rsrc.n {
526 +
                    if *rdst != *rsrc {
526 527
                        emit::emitAddImm(s.e, rdst, rdst, offset);
527 528
                    }
528 529
                    offset = 0;
529 530
                }
530 531
                if let off = srcReload {
541 542
            // in the both-spilled case since size <= MAX_IMM).
542 543
            if not bothSpilled {
543 544
                let advanced = staticSize as i32 - offset;
544 545
                if advanced != 0 {
545 546
                    emit::emitAddImm(s.e, rsrc, rsrc, 0 - advanced);
546 -
                    if rdst.n != rsrc.n {
547 +
                    if *rdst != *rsrc {
547 548
                        emit::emitAddImm(s.e, rdst, rdst, 0 - advanced);
548 549
                    }
549 550
                }
550 551
            }
551 552
        },
699 700
        },
700 701
        case il::Instr::Ecall { dst, num, a0, a1, a2, a3 } => {
701 702
            // Move arguments using parallel move.
702 703
            // TODO: Can't use slice literals here because the lowerer doesn't
703 704
            // support const-evaluating struct/union values in them.
704 -
            let ecallDsts: [super::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3];
705 +
            let ecallDsts: [gen::Reg; 5] = [super::A7, super::A0, super::A1, super::A2, super::A3];
705 706
            let ecallArgs: [il::Val; 5] = [num, a0, a1, a2, a3];
706 707
707 708
            emitParallelMoves(s, &ecallDsts[..], &ecallArgs[..]);
708 709
            emit::emit(s.e, encode::ecall());
709 710
725 726
    return false;
726 727
}
727 728
728 729
/// Select a binary ALU operation, dispatching to the appropriate
729 730
/// instruction pattern based on the operation kind and type.
730 -
fn selectAluBinOp(s: *mut Selector, op: il::BinOp, typ: il::Type, rd: super::Reg, rs1: super::Reg, b: il::Val) {
731 +
fn selectAluBinOp(s: *mut Selector, op: il::BinOp, typ: il::Type, rd: gen::Reg, rs1: gen::Reg, b: il::Val) {
731 732
    match op {
732 733
        case il::BinOp::Add => {
733 734
            if typ == il::Type::W32 {
734 735
                selectBinOpW(s, rd, rs1, b, super::SCRATCH2);
735 736
            } else {
866 867
        }
867 868
    }
868 869
}
869 870
870 871
/// Select a unary ALU operation.
871 -
fn selectAluUnOp(s: *mut Selector, op: il::UnOp, typ: il::Type, rd: super::Reg, rs: super::Reg) {
872 +
fn selectAluUnOp(s: *mut Selector, op: il::UnOp, typ: il::Type, rd: gen::Reg, rs: gen::Reg) {
872 873
    match op {
873 874
        case il::UnOp::Neg => {
874 875
            if typ == il::Type::W32 {
875 876
                emit::emit(s.e, encode::subw(rd, super::ZERO, rs));
876 877
            } else {
882 883
    }
883 884
}
884 885
885 886
/// Select 32-bit addition with immediate optimization.
886 887
/// Uses `addiw`/`addw` to operate on 32 bits and sign-extend the result.
887 -
fn selectBinOpW(s: *mut Selector, rd: super::Reg, rs1: super::Reg, b: il::Val, scratch: super::Reg) {
888 +
fn selectBinOpW(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, scratch: gen::Reg) {
888 889
    if let case il::Val::Imm(imm) = b {
889 890
        if encode::isSmallImm64(imm) {
890 891
            emit::emit(s.e, encode::addiw(rd, rs1, imm as i32));
891 892
            return;
892 893
        }
894 895
    let rs2 = resolveVal(s, scratch, b);
895 896
    emit::emit(s.e, encode::addw(rd, rs1, rs2));
896 897
}
897 898
898 899
/// Select binary operation with immediate optimization.
899 -
fn selectBinOp(s: *mut Selector, rd: super::Reg, rs1: super::Reg, b: il::Val, op: BinOp, scratch: super::Reg) {
900 +
fn selectBinOp(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: BinOp, scratch: gen::Reg) {
900 901
    // Try immediate optimization first.
901 902
    if let case il::Val::Imm(imm) = b {
902 903
        if encode::isSmallImm64(imm) {
903 904
            let simm = imm as i32;
904 905
            match op {
921 922
}
922 923
923 924
/// Select shift operation with immediate optimization.
924 925
/// For 32-bit operations, uses the `*w` variants that operate on the lower 32 bits
925 926
/// and sign-extend the result.
926 -
fn selectShift(s: *mut Selector, rd: super::Reg, rs1: super::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: super::Reg) {
927 +
fn selectShift(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: ShiftOp, typ: il::Type, scratch: gen::Reg) {
927 928
    let isW32: bool = typ == il::Type::W32;
928 929
929 930
    // Try immediate optimization first.
930 931
    if let case il::Val::Imm(shamt) = b {
931 932
        // Keep immediate forms only for encodable shift amounts.
974 975
/// 1. Identifies "ready" moves.
975 976
/// 2. Executes ready moves.
976 977
/// 3. Breaks cycles using scratch register.
977 978
///
978 979
/// Entries with `ZERO` destination are skipped, as they are handled by caller.
979 -
fn emitParallelMoves(s: *mut Selector, dsts: *[super::Reg], args: *[il::Val]) {
980 +
fn emitParallelMoves(s: *mut Selector, dsts: *[gen::Reg], args: *[il::Val]) {
980 981
    let n: u32 = args.len;
981 982
    if n == 0 {
982 983
        return;
983 984
    }
984 985
    assert n <= MAX_BLOCK_ARGS, "emitParallelMoves: too many arguments";
985 986
    // Source registers for each arg.
986 -
    let mut srcRegs: [super::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS];
987 +
    let mut srcRegs: [gen::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS];
987 988
    // If this is a register-to-register move.
988 989
    let mut isRegMove: [bool; MAX_BLOCK_ARGS] = [false; MAX_BLOCK_ARGS];
989 990
    // If this move still needs to be executed.
990 991
    let mut pending: [bool; MAX_BLOCK_ARGS] = [false; MAX_BLOCK_ARGS];
991 992
    // Number of pending moves.
1094 1095
    assert args.len == block.params.len, "emitBlockArgs: argument/parameter count mismatch";
1095 1096
    assert args.len <= MAX_BLOCK_ARGS, "emitBlockArgs: too many block arguments";
1096 1097
1097 1098
    // Destination registers for each arg.
1098 1099
    // Zero means the destination is spilled or skipped.
1099 -
    let mut dsts: [super::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS];
1100 +
    let mut dsts: [gen::Reg; MAX_BLOCK_ARGS] = [super::ZERO; MAX_BLOCK_ARGS];
1100 1101
1101 1102
    for arg, i in args {
1102 1103
        let param = block.params[i].value;
1103 1104
1104 1105
        // Spilled destinations: store directly to spill slot.
1116 1117
    }
1117 1118
    emitParallelMoves(s, &dsts[..], args);
1118 1119
}
1119 1120
1120 1121
/// Select comparison with immediate optimization.
1121 -
fn selectCmp(s: *mut Selector, rd: super::Reg, rs1: super::Reg, b: il::Val, op: CmpOp, scratch: super::Reg) {
1122 +
fn selectCmp(s: *mut Selector, rd: gen::Reg, rs1: gen::Reg, b: il::Val, op: CmpOp, scratch: gen::Reg) {
1122 1123
    // Try immediate optimization first.
1123 1124
    if let case il::Val::Imm(imm) = b {
1124 1125
        if encode::isSmallImm64(imm) {
1125 1126
            let simm = imm as i32;
1126 1127
            match op {
lib/std/arch/rv64/printer.rad +21 -20
3 3
//! Prints 32-bit instructions in assembly text format.
4 4
5 5
use std::fmt;
6 6
use std::mem;
7 7
use std::lang::alloc;
8 +
use std::lang::gen;
8 9
use std::lang::sexpr;
9 10
use std::lang::gen::types;
10 11
11 12
use super::decode;
12 13
use super::emit;
27 28
fn regName(n: u8) -> *[u8] {
28 29
    return "?" if n >= 32 else REG_NAMES[n as u32];
29 30
}
30 31
31 32
/// Get register name from Reg.
32 -
fn regNameR(r: super::Reg) -> *[u8] {
33 -
    return regName(r.n);
33 +
fn regNameR(r: gen::Reg) -> *[u8] {
34 +
    return regName(*r);
34 35
}
35 36
36 37
///////////////////////
37 38
// Output Helpers    //
38 39
///////////////////////
99 100
////////////////////////////////
100 101
// Instruction Format Helpers //
101 102
////////////////////////////////
102 103
103 104
/// R-type: `op rd, rs1, rs2`.
104 -
fn fmtR(out: *mut sexpr::Output, m: *[u8], rd: super::Reg, rs1: super::Reg, rs2: super::Reg) {
105 +
fn fmtR(out: *mut sexpr::Output, m: *[u8], rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) {
105 106
    writeMnem(out, m);
106 107
    writeDelim(out, &[regNameR(rd), regNameR(rs1), regNameR(rs2)]);
107 108
}
108 109
109 110
/// I-type: `op rd, rs1, imm`.
110 -
fn fmtI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: super::Reg, rs1: super::Reg, imm: i32) {
111 +
fn fmtI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
111 112
    writeMnem(out, m);
112 113
    writeDelim(out, &[regNameR(rd), regNameR(rs1), formatI32(a, imm)]);
113 114
}
114 115
115 116
/// 2-reg: `op rd, rs`.
116 -
fn fmt2R(out: *mut sexpr::Output, m: *[u8], rd: super::Reg, rs: super::Reg) {
117 +
fn fmt2R(out: *mut sexpr::Output, m: *[u8], rd: gen::Reg, rs: gen::Reg) {
117 118
    writeMnem(out, m);
118 119
    writeDelim(out, &[regNameR(rd), regNameR(rs)]);
119 120
}
120 121
121 122
/// reg + imm: `op rd, imm`.
122 -
fn fmtRI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: super::Reg, imm: i32) {
123 +
fn fmtRI(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, imm: i32) {
123 124
    writeMnem(out, m);
124 125
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
125 126
}
126 127
127 128
/// imm only: `op imm`.
129 130
    writeMnem(out, m);
130 131
    write(out, formatI32(a, imm));
131 132
}
132 133
133 134
/// 1-reg: `op rs`.
134 -
fn fmt1R(out: *mut sexpr::Output, m: *[u8], rs: super::Reg) {
135 +
fn fmt1R(out: *mut sexpr::Output, m: *[u8], rs: gen::Reg) {
135 136
    writeMnem(out, m);
136 137
    write(out, regNameR(rs));
137 138
}
138 139
139 140
/// Load: `op rd, imm(rs1)`.
140 -
fn fmtLoad(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: super::Reg, rs1: super::Reg, imm: i32) {
141 +
fn fmtLoad(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rd: gen::Reg, rs1: gen::Reg, imm: i32) {
141 142
    writeMnem(out, m);
142 143
    writeDelim(out, &[regNameR(rd), formatI32(a, imm)]);
143 144
    writeParens(out, regNameR(rs1));
144 145
}
145 146
146 147
/// Store: `op rs2, imm(rs1)`.
147 -
fn fmtStore(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs2: super::Reg, rs1: super::Reg, imm: i32) {
148 +
fn fmtStore(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs2: gen::Reg, rs1: gen::Reg, imm: i32) {
148 149
    writeMnem(out, m);
149 150
    writeDelim(out, &[regNameR(rs2), formatI32(a, imm)]);
150 151
    writeParens(out, regNameR(rs1));
151 152
}
152 153
153 154
/// Branch: `op rs1, rs2, imm`.
154 -
fn fmtB(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: super::Reg, rs2: super::Reg, imm: i32) {
155 +
fn fmtB(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: gen::Reg, rs2: gen::Reg, imm: i32) {
155 156
    writeMnem(out, m);
156 157
    writeDelim(out, &[regNameR(rs1), regNameR(rs2), formatI32(a, imm)]);
157 158
}
158 159
159 160
/// Branch zero: `op rs1, imm`.
160 -
fn fmtBz(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: super::Reg, imm: i32) {
161 +
fn fmtBz(out: *mut sexpr::Output, a: *mut alloc::Arena, m: *[u8], rs1: gen::Reg, imm: i32) {
161 162
    writeMnem(out, m);
162 163
    writeDelim(out, &[regNameR(rs1), formatI32(a, imm)]);
163 164
}
164 165
165 166
/// Print a single instruction to output buffer.
168 169
169 170
    match decoded {
170 171
        case decode::Instr::Lui { rd, imm } => fmtRI(out, a, "lui", rd, imm),
171 172
        case decode::Instr::Auipc { rd, imm } => fmtRI(out, a, "auipc", rd, imm),
172 173
        case decode::Instr::Jal { rd, imm } => {
173 -
            if rd.n == 0 {
174 +
            if *rd == 0 {
174 175
                fmtImm(out, a, "j", imm);
175 176
            } else {
176 177
                fmtRI(out, a, "jal", rd, imm);
177 178
            }
178 179
        },
179 180
        case decode::Instr::Jalr { rd, rs1, imm } => {
180 -
            if rd.n == 0 and rs1.n == 1 and imm == 0 {
181 +
            if *rd == 0 and *rs1 == 1 and imm == 0 {
181 182
                write(out, "ret");
182 -
            } else if rd.n == 0 and imm == 0 {
183 +
            } else if *rd == 0 and imm == 0 {
183 184
                fmt1R(out, "jr", rs1);
184 185
            } else {
185 186
                fmtI(out, a, "jalr", rd, rs1, imm);
186 187
            }
187 188
        },
188 189
        case decode::Instr::Beq { rs1, rs2, imm } => {
189 -
            if rs2.n == 0 {
190 +
            if *rs2 == 0 {
190 191
                fmtBz(out, a, "beqz", rs1, imm);
191 192
            } else {
192 193
                fmtB(out, a, "beq", rs1, rs2, imm);
193 194
            }
194 195
        },
195 196
        case decode::Instr::Bne { rs1, rs2, imm } => {
196 -
            if rs2.n == 0 {
197 +
            if *rs2 == 0 {
197 198
                fmtBz(out, a, "bnez", rs1, imm);
198 199
            } else {
199 200
                fmtB(out, a, "bne", rs1, rs2, imm);
200 201
            }
201 202
        },
213 214
        case decode::Instr::Sb { rs2, rs1, imm } => fmtStore(out, a, "sb", rs2, rs1, imm),
214 215
        case decode::Instr::Sh { rs2, rs1, imm } => fmtStore(out, a, "sh", rs2, rs1, imm),
215 216
        case decode::Instr::Sw { rs2, rs1, imm } => fmtStore(out, a, "sw", rs2, rs1, imm),
216 217
        case decode::Instr::Sd { rs2, rs1, imm } => fmtStore(out, a, "sd", rs2, rs1, imm),
217 218
        case decode::Instr::Addi { rd, rs1, imm } => {
218 -
            if rd.n == 0 and rs1.n == 0 and imm == 0 {
219 +
            if *rd == 0 and *rs1 == 0 and imm == 0 {
219 220
                write(out, "nop");
220 221
            } else if imm == 0 {
221 222
                fmt2R(out, "mv", rd, rs1);
222 -
            } else if rs1.n == 0 {
223 +
            } else if *rs1 == 0 {
223 224
                fmtRI(out, a, "li", rd, imm);
224 225
            } else {
225 226
                fmtI(out, a, "addi", rd, rs1, imm);
226 227
            }
227 228
        },
245 246
        case decode::Instr::Slli { rd, rs1, shamt } => fmtI(out, a, "slli", rd, rs1, shamt),
246 247
        case decode::Instr::Srli { rd, rs1, shamt } => fmtI(out, a, "srli", rd, rs1, shamt),
247 248
        case decode::Instr::Srai { rd, rs1, shamt } => fmtI(out, a, "srai", rd, rs1, shamt),
248 249
        case decode::Instr::Add { rd, rs1, rs2 } => fmtR(out, "add", rd, rs1, rs2),
249 250
        case decode::Instr::Sub { rd, rs1, rs2 } => {
250 -
            if rs1.n == 0 {
251 +
            if *rs1 == 0 {
251 252
                fmt2R(out, "neg", rd, rs2);
252 253
            } else {
253 254
                fmtR(out, "sub", rd, rs1, rs2);
254 255
            }
255 256
        },
256 257
        case decode::Instr::Sll { rd, rs1, rs2 }  => fmtR(out, "sll", rd, rs1, rs2),
257 258
        case decode::Instr::Slt { rd, rs1, rs2 }  => fmtR(out, "slt", rd, rs1, rs2),
258 259
        case decode::Instr::Sltu { rd, rs1, rs2 } => {
259 -
            if rs1.n == 0 {
260 +
            if *rs1 == 0 {
260 261
                fmt2R(out, "snez", rd, rs2);
261 262
            } else {
262 263
                fmtR(out, "sltu", rd, rs1, rs2);
263 264
            }
264 265
        },
lib/std/lang/gen.rad +7 -0
7 7
pub mod labels;
8 8
pub mod bitset;
9 9
pub mod regalloc;
10 10
pub mod data;
11 11
pub mod types;
12 +
13 +
/// Physical register.
14 +
///
15 +
/// Lightweight wrapper around a register number. Used both in the
16 +
/// target-independent register allocator and in architecture-specific
17 +
/// backends.
18 +
pub record Reg(u8);
lib/std/lang/gen/regalloc.rad +6 -6
21 21
use std::lang::il;
22 22
use std::lang::alloc;
23 23
24 24
/// Target configuration for register allocation.
25 25
pub record TargetConfig {
26 -
    /// List of allocatable physical register numbers.
26 +
    /// List of allocatable physical registers.
27 27
    /// Order determines allocation preference.
28 -
    allocatable: *[u8],
28 +
    allocatable: *[super::Reg],
29 29
    /// Function argument registers.
30 -
    argRegs: *[u8],
31 -
    /// Callee-saved register numbers.
32 -
    calleeSaved: *[u8],
30 +
    argRegs: *[super::Reg],
31 +
    /// Callee-saved registers.
32 +
    calleeSaved: *[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 38
pub record AllocResult {
39 39
    /// SSA register to physical register mapping.
40 -
    assignments: *[?u8],
40 +
    assignments: *[?super::Reg],
41 41
    /// Spill slot information.
42 42
    spill: spill::SpillInfo,
43 43
    /// Bitmask of used callee-saved registers.
44 44
    usedCalleeSaved: u32,
45 45
}
lib/std/lang/gen/regalloc/assign.rad +20 -19
6 6
//! The IL is not modified. Instead, a mapping from SSA registers to physical
7 7
//! registers is produced for use by instruction selection.
8 8
9 9
use std::lang::il;
10 10
use std::lang::alloc;
11 +
use std::lang::gen;
11 12
use std::lang::gen::bitset;
12 13
use std::lang::gen::regalloc::liveness;
13 14
use std::lang::gen::regalloc::spill;
14 15
15 16
/// Maximum number of active register mappings.
19 20
/// Maps SSA registers to physical registers.
20 21
pub record RegMap {
21 22
    /// SSA (virtual) registers that have mappings.
22 23
    virtRegs: *mut [u32],
23 24
    /// Physical register for each virtual register.
24 -
    physRegs: *mut [u8],
25 +
    physRegs: *mut [gen::Reg],
25 26
    /// Number of active mappings.
26 27
    n: u32,
27 28
}
28 29
29 30
/// Register assignment result, per function.
30 31
pub record AssignInfo {
31 32
    /// SSA register -> physical register mapping.
32 -
    assignments: *mut [?u8],
33 +
    assignments: *mut [?gen::Reg],
33 34
    /// Bitmask of used callee-saved registers.
34 35
    usedCalleeSaved: u32,
35 36
}
36 37
37 38
/// Per-instruction context for freeing and allocating register uses.
40 41
    usedRegs: *mut bitset::Bitset,
41 42
    func: *il::Fn,
42 43
    live: *liveness::LiveInfo,
43 44
    blockIdx: u32,
44 45
    instrIdx: u32,
45 -
    allocatable: *[u8],
46 -
    calleeSaved: *[u8],
47 -
    assignments: *mut [?u8],
46 +
    allocatable: *[gen::Reg],
47 +
    calleeSaved: *[gen::Reg],
48 +
    assignments: *mut [?gen::Reg],
48 49
    spillInfo: *spill::SpillInfo,
49 50
}
50 51
51 52
/// Compute register assignment.
52 53
pub fn assign(
66 67
            usedCalleeSaved: 0,
67 68
        };
68 69
    }
69 70
70 71
    // Allocate output structures.
71 -
    let assignments = try alloc::allocSlice(arena, @sizeOf(?u8), @alignOf(?u8), maxReg) as *mut [?u8];
72 +
    let assignments = try alloc::allocSlice(arena, @sizeOf(?gen::Reg), @alignOf(?gen::Reg), maxReg) as *mut [?gen::Reg];
72 73
    for i in 0..maxReg {
73 74
        assignments[i] = nil;
74 75
    }
75 76
    // Pre-assign function parameters to argument registers.
76 77
    // Cross-call params are NOT pre-assigned here; they will be allocated
109 110
            for k in 0..pred.n {
110 111
                if bitset::contains(&live.liveIn[b], pred.virtRegs[k]) {
111 112
                    current.virtRegs[current.n] = pred.virtRegs[k];
112 113
                    current.physRegs[current.n] = pred.physRegs[k];
113 114
                    current.n += 1;
114 -
                    bitset::set(&mut usedRegs, pred.physRegs[k] as u32);
115 +
                    bitset::set(&mut usedRegs, *pred.physRegs[k] as u32);
115 116
                }
116 117
            }
117 118
        }
118 119
119 120
        // Mark all live-in values' registers as used.
125 126
        let mut liveInIter = bitset::iter(&live.liveIn[b]);
126 127
        while let ssaReg = bitset::iterNext(&mut liveInIter) {
127 128
            let reg = il::Reg { n: ssaReg };
128 129
            if not spill::isSpilled(spillInfo, reg) {
129 130
                if let phys = assignments[ssaReg] {
130 -
                    bitset::set(&mut usedRegs, phys as u32);
131 +
                    bitset::set(&mut usedRegs, *phys as u32);
131 132
                    // Also track in current mapping for block-end state.
132 133
                    if rmapFind(&current, ssaReg) == nil {
133 134
                        rmapSet(&mut current, ssaReg, phys);
134 135
                    }
135 136
                } else {
178 179
    // Compute bitmask of used callee-saved registers.
179 180
    let mut usedCalleeSaved: u32 = 0;
180 181
    for i in 0..maxReg {
181 182
        if let phys = assignments[i] {
182 183
            for saved, j in config.calleeSaved {
183 -
                if phys == saved {
184 +
                if *phys == *saved {
184 185
                    usedCalleeSaved |= (1 << j);
185 186
                }
186 187
            }
187 188
        }
188 189
    }
194 195
}
195 196
196 197
/// Create an empty register map.
197 198
fn createRegMap(arena: *mut alloc::Arena) -> RegMap throws (alloc::AllocError) {
198 199
    let virtRegs = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), MAX_ACTIVE) as *mut [u32];
199 -
    let physRegs = try alloc::allocSlice(arena, @sizeOf(u8), @alignOf(u8), MAX_ACTIVE) as *mut [u8];
200 +
    let physRegs = try alloc::allocSlice(arena, @sizeOf(gen::Reg), @alignOf(gen::Reg), MAX_ACTIVE) as *mut [gen::Reg];
200 201
201 202
    return RegMap { virtRegs, physRegs, n: 0 };
202 203
}
203 204
204 205
/// Find physical register for a virtual register in RegMap.
205 -
fn rmapFind(rmap: *RegMap, virtReg: u32) -> ?u8 {
206 +
fn rmapFind(rmap: *RegMap, virtReg: u32) -> ?gen::Reg {
206 207
    for i in 0..rmap.n {
207 208
        if rmap.virtRegs[i] == virtReg {
208 209
            return rmap.physRegs[i];
209 210
        }
210 211
    }
211 212
    return nil;
212 213
}
213 214
214 215
/// Add a mapping to the register map.
215 -
fn rmapSet(rmap: *mut RegMap, virtReg: u32, physReg: u8) {
216 +
fn rmapSet(rmap: *mut RegMap, virtReg: u32, physReg: gen::Reg) {
216 217
    assert rmap.n < MAX_ACTIVE, "rmapSet: register map overflow";
217 218
    rmap.virtRegs[rmap.n] = virtReg;
218 219
    rmap.physRegs[rmap.n] = physReg;
219 220
    rmap.n += 1;
220 221
}
234 235
    }
235 236
    panic "rmapRemove: register not found";
236 237
}
237 238
238 239
/// Find first free register in pool, allocate it, return it.
239 -
fn findFreeInPool(usedRegs: *mut bitset::Bitset, current: *mut RegMap, ssaReg: u32, pool: *[u8]) -> ?u8 {
240 +
fn findFreeInPool(usedRegs: *mut bitset::Bitset, current: *mut RegMap, ssaReg: u32, pool: *[gen::Reg]) -> ?gen::Reg {
240 241
    for i in 0..pool.len {
241 242
        let r = pool[i];
242 -
        if not bitset::contains(usedRegs, r as u32) {
243 -
            bitset::set(usedRegs, r as u32);
243 +
        if not bitset::contains(usedRegs, *r as u32) {
244 +
            bitset::set(usedRegs, *r as u32);
244 245
            rmapSet(current, ssaReg, r);
245 246
            return r;
246 247
        }
247 248
    }
248 249
    return nil;
252 253
/// Cross-call values are steered to callee-saved registers.
253 254
fn rallocReg(
254 255
    current: *mut RegMap,
255 256
    usedRegs: *mut bitset::Bitset,
256 257
    ssaReg: u32,
257 -
    allocatable: *[u8],
258 -
    calleeSaved: *[u8],
258 +
    allocatable: *[gen::Reg],
259 +
    calleeSaved: *[gen::Reg],
259 260
    spillInfo: *spill::SpillInfo
260 -
) -> u8 {
261 +
) -> gen::Reg {
261 262
    // Check if already assigned.
262 263
    if let phys = rmapFind(current, ssaReg) {
263 264
        return phys;
264 265
    }
265 266
    let crossCall = bitset::contains(&spillInfo.calleeClass, ssaReg);
280 281
/// Callback for [`il::forEachReg`]: free last uses and allocate missing uses.
281 282
fn processInstrRegCb(reg: il::Reg, ctxPtr: *mut opaque) {
282 283
    let ctx = ctxPtr as *mut InstrCtx;
283 284
    if liveness::isLastUse(ctx.live, ctx.func, ctx.blockIdx, ctx.instrIdx, reg) {
284 285
        if let phys = rmapFind(ctx.current, reg.n) {
285 -
            bitset::clear(ctx.usedRegs, phys as u32);
286 +
            bitset::clear(ctx.usedRegs, *phys as u32);
286 287
            rmapRemove(ctx.current, reg.n);
287 288
        }
288 289
    }
289 290
    assert reg.n < ctx.assignments.len, "processInstrRegCb: register out of bounds";
290 291
    if spill::isSpilled(ctx.spillInfo, reg) {