• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

henrythasler / rust-tiny-wasm / 24300720238

12 Apr 2026 06:42AM UTC coverage: 94.139% (-0.2%) from 94.373%
24300720238

push

github

henrythasler
switch to stack-based register allocation strategy

43 of 45 new or added lines in 6 files covered. (95.56%)

1 existing line in 1 file now uncovered.

1317 of 1399 relevant lines covered (94.14%)

37.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.0
/src/assembler/aarch64.rs
1
#![allow(dead_code)]
2

3
use num_enum::TryFromPrimitive;
4
use std::ops::{BitAnd, BitXor};
5

6
pub mod arithmetic;
7
pub mod bit;
8
pub mod branch;
9
pub mod compound;
10
pub mod conditionals;
11
pub mod hint;
12
pub mod memory;
13
pub mod processing;
14

15
pub const INSTRUCTION_SIZE: usize = std::mem::size_of::<u32>();
16

17
pub const INT32_SIZE: usize = std::mem::size_of::<i32>();
18
pub const INT64_SIZE: usize = std::mem::size_of::<i64>();
19
pub const FLOAT32_SIZE: usize = std::mem::size_of::<f32>();
20
pub const FLOAT64_SIZE: usize = std::mem::size_of::<f64>();
21

22
pub const STACK_ALIGNMENT: usize = 16;
23

24
#[repr(u32)]
25
#[derive(TryFromPrimitive, Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
26
#[allow(clippy::upper_case_acronyms)]
27
pub enum Reg {
28
    X0 = 0,
29
    X1,
30
    X2,
31
    X3,
32
    X4,
33
    X5,
34
    X6,
35
    X7,
36
    X8,
37
    X9,
38
    X10,
39
    X11,
40
    X12,
41
    X13,
42
    X14,
43
    X15,
44
    X16,
45
    X17,
46
    X18,
47
    X19,
48
    X20,
49
    X21,
50
    X22,
51
    X23,
52
    X24,
53
    X25,
54
    X26,
55
    X27,
56
    X28,
57
    X29,
58
    X30,
59
    XZR = 31, // Zero Register (context dependent)
60
}
61

62
impl Reg {
63
    // 32-bit register aliases
64
    pub const W0: Reg = Reg::X0;
65
    pub const W1: Reg = Reg::X1;
66
    pub const W2: Reg = Reg::X2;
67
    pub const W3: Reg = Reg::X3;
68
    pub const W4: Reg = Reg::X4;
69
    pub const W5: Reg = Reg::X5;
70
    pub const W6: Reg = Reg::X6;
71
    pub const W7: Reg = Reg::X7;
72
    pub const W8: Reg = Reg::X8;
73
    pub const W9: Reg = Reg::X9;
74
    pub const W10: Reg = Reg::X10;
75
    pub const W11: Reg = Reg::X11;
76
    pub const W12: Reg = Reg::X12;
77
    pub const W13: Reg = Reg::X13;
78
    pub const W14: Reg = Reg::X14;
79
    pub const W15: Reg = Reg::X15;
80
    pub const W16: Reg = Reg::X16;
81
    pub const W17: Reg = Reg::X17;
82
    pub const W18: Reg = Reg::X18;
83
    pub const W19: Reg = Reg::X19;
84
    pub const W20: Reg = Reg::X20;
85
    pub const W21: Reg = Reg::X21;
86
    pub const W22: Reg = Reg::X22;
87
    pub const W23: Reg = Reg::X23;
88
    pub const W24: Reg = Reg::X24;
89
    pub const W25: Reg = Reg::X25;
90
    pub const W26: Reg = Reg::X26;
91
    pub const W27: Reg = Reg::X27;
92
    pub const W28: Reg = Reg::X28;
93
    pub const W29: Reg = Reg::X29;
94
    pub const W30: Reg = Reg::X30;
95
    pub const WZR: Reg = Reg::XZR;
96

97
    // Special register aliases
98
    pub const SP: Reg = Reg::XZR; // Stack Pointer
99
    pub const WSP: Reg = Reg::XZR;
100

101
    pub const FP: Reg = Reg::X29; // Frame Pointer (X29)
102
    pub const LR: Reg = Reg::X30; // Link Register (X30)
103
}
104

105
impl BitAnd<u32> for Reg {
106
    type Output = u32;
107

108
    fn bitand(self, rhs: u32) -> Self::Output {
1,742✔
109
        (self as u32) & rhs
1,742✔
110
    }
1,742✔
111
}
112

113
#[repr(u32)]
114
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
115
pub enum RegSize {
116
    Reg32bit,
117
    Reg64bit,
118
}
119

120
#[repr(u32)]
121
#[derive(Debug, PartialEq, Eq)]
122
pub enum MemSize {
123
    Mem8bit,
124
    Mem16bit,
125
    Mem32bit,
126
    Mem64bit,
127
}
128

129
#[repr(u32)]
130
#[derive(Debug, PartialEq, Eq)]
131
pub enum AddressingMode {
132
    Simple,
133
    Offset,
134
    PreIndex,
135
    PostIndex,
136
}
137

138
#[repr(u32)]
139
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140
pub enum Shift {
141
    Lsl,
142
    Lsr,
143
    Asr,
144
    Ror,
145
}
146

147
#[repr(u32)]
148
pub enum Extend {
149
    Uxtb = 0, // Unsigned extend byte
150
    Uxth = 1, // Unsigned extend halfword
151
    Uxtw = 2, // Unsigned extend word
152
    Uxtx = 3, // Unsigned extend doubleword
153
    Sxtb = 4, // Signed extend byte
154
    Sxth = 5, // Signed extend halfword
155
    Sxtw = 6, // Signed extend word
156
    Sxtx = 7, // Signed extend doubleword
157
}
158

159
#[repr(u32)]
160
pub enum Condition {
161
    /// equal
162
    EQ = 0b0000,
163
    /// not equal
164
    NE = 0b0001,
165
    /// Carry set (identical to HS)
166
    CS = 0b0010,
167
    /// Carry clear (identical to LO)
168
    CC = 0b0011,
169
    /// Minus or negative result
170
    MI = 0b0100,
171
    /// Positive or zero result
172
    PL = 0b0101,
173
    /// Signed Overflow
174
    VS = 0b0110,
175
    /// No signed Overflow
176
    VC = 0b0111,
177
    /// Unsigned higher
178
    HI = 0b1000,
179
    /// Unsigned lower or same
180
    LS = 0b1001,
181
    /// Signed greater than or equal
182
    GE = 0b1010,
183
    /// Signed less than
184
    LT = 0b1011,
185
    /// Signed greater than
186
    GT = 0b1100,
187
    /// Signed less than or equal
188
    LE = 0b1101,
189
    /// Always (this is the default)
190
    AL = 0b1110,
191
    /// Never executed
192
    NV = 0b1111,
193
}
194

195
impl Condition {
196
    pub const HS: Condition = Condition::CS; // Unsigned Higher or same (identical to CS)
197
    pub const LO: Condition = Condition::CC; // Unsigned Lower (identical to CC)
198

199
    pub fn from_u32(v: u32) -> Option<Self> {
18✔
200
        match v {
18✔
201
            0b0000 => Some(Self::EQ),
×
202
            0b0001 => Some(Self::NE),
1✔
203
            0b0010 => Some(Self::CS),
×
204
            0b0011 => Some(Self::CC),
×
205
            0b0100 => Some(Self::MI),
×
206
            0b0101 => Some(Self::PL),
×
207
            0b0110 => Some(Self::VS),
×
208
            0b0111 => Some(Self::VC),
×
209
            0b1000 => Some(Self::HI),
×
210
            0b1001 => Some(Self::LS),
×
211
            0b1010 => Some(Self::GE),
16✔
212
            0b1011 => Some(Self::LT),
×
213
            0b1100 => Some(Self::GT),
1✔
214
            0b1101 => Some(Self::LE),
×
215
            0b1110 => Some(Self::AL),
×
216
            0b1111 => Some(Self::NV),
×
217
            _ => None,
×
218
        }
219
    }
18✔
220
}
221

222
impl BitXor<u32> for Condition {
223
    type Output = u32;
224

225
    fn bitxor(self, rhs: u32) -> Self::Output {
18✔
226
        (self as u32) ^ rhs
18✔
227
    }
18✔
228
}
229

230
#[derive(Debug, Clone)]
231
pub struct RegisterPool {
232
    registers: Vec<Reg>,
233
    pub index: i32,
234
}
235

236
impl Default for RegisterPool {
237
    fn default() -> Self {
1✔
238
        Self::new()
1✔
239
    }
1✔
240
}
241

242
impl RegisterPool {
243
    pub fn new() -> Self {
68✔
244
        Self {
68✔
245
            registers: vec![
68✔
246
                Reg::X8,
68✔
247
                Reg::X9,
68✔
248
                Reg::X10,
68✔
249
                Reg::X11,
68✔
250
                Reg::X12,
68✔
251
                Reg::X13,
68✔
252
                Reg::X14,
68✔
253
                Reg::X15,
68✔
254
            ],
68✔
255
            index: 0,
68✔
256
        }
68✔
257
    }
68✔
258

NEW
259
    pub fn current(self) -> Reg {
×
NEW
260
        self.registers[self.index as usize]
×
UNCOV
261
    }
×
262

263
    pub fn alloc(&mut self) -> Reg {
198✔
264
        let reg = self.registers[self.index as usize];
198✔
265
        self.index += 1;
198✔
266
        assert!(self.index < self.registers.len() as i32);
198✔
267
        reg
197✔
268
    }
197✔
269

270
    pub fn free(&mut self) {
110✔
271
        self.index -= 1;
110✔
272
        assert!(self.index >= 0);
110✔
273
    }
110✔
274
}
275

276
fn select_instr(instr_32bit: u32, instr_64bit: u32, size: RegSize) -> u32 {
541✔
277
    match size {
541✔
278
        RegSize::Reg32bit => instr_32bit,
123✔
279
        RegSize::Reg64bit => instr_64bit,
418✔
280
    }
281
}
541✔
282

283
pub fn map_valtype_to_regsize(item: &wasmparser::ValType) -> RegSize {
461✔
284
    if *item == wasmparser::ValType::I32 {
461✔
285
        RegSize::Reg32bit
228✔
286
    } else if *item == wasmparser::ValType::I64 {
233✔
287
        RegSize::Reg64bit
232✔
288
    } else {
289
        panic!("can't map {} to RegSize", item)
1✔
290
    }
291
}
460✔
292

293
pub fn map_valtype_to_memsize(item: &wasmparser::ValType) -> MemSize {
280✔
294
    if *item == wasmparser::ValType::I32 || *item == wasmparser::ValType::F32 {
280✔
295
        MemSize::Mem32bit
176✔
296
    } else {
297
        MemSize::Mem64bit
104✔
298
    }
299
}
280✔
300

301
#[cfg(test)]
302
mod tests {
303
    use super::*;
304

305
    #[test]
306
    fn test_select_instr() {
1✔
307
        assert_eq!(select_instr(32, 64, RegSize::Reg32bit), 32);
1✔
308
        assert_eq!(select_instr(32, 64, RegSize::Reg64bit), 64);
1✔
309
    }
1✔
310

311
    #[test]
312
    fn test_registerpool() {
1✔
313
        let mut pool = RegisterPool::new();
1✔
314
        assert_eq!(pool.alloc(), Reg::X8);
1✔
315
        assert_eq!(pool.alloc(), Reg::X9);
1✔
316
        assert_eq!(pool.alloc(), Reg::X10);
1✔
317
        assert_eq!(pool.alloc(), Reg::X11);
1✔
318
        pool.free();
1✔
319
        pool.free();
1✔
320
        pool.free();
1✔
321
        assert_eq!(pool.alloc(), Reg::X9);
1✔
322
        assert_eq!(pool.alloc(), Reg::X10);
1✔
323
        assert_eq!(pool.alloc(), Reg::X11);
1✔
324
    }
1✔
325

326
    #[test]
327
    #[should_panic]
328
    fn test_pool_exhaustion() {
1✔
329
        let mut pool = RegisterPool::new();
1✔
330
        for _ in 0..i32::MAX {
7✔
331
            pool.alloc();
7✔
332
        }
7✔
333
    }
1✔
334

335
    #[test]
336
    #[should_panic]
337
    fn test_map_valtype_to_regsize_invalid() {
1✔
338
        map_valtype_to_regsize(&wasmparser::ValType::V128);
1✔
339
    }
1✔
340
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc