slvm/
chunk.rs

1use std::cmp::Ordering;
2
3use crate::opcodes::*;
4use crate::{Interned, VMError, VMResult, Value};
5
6#[macro_use]
7pub mod disassemble;
8
9#[derive(Clone, Debug)]
10pub struct Chunk {
11    pub code: Vec<u8>,
12    pub file_name: &'static str,
13    start_line: u32,
14    last_line: u32,
15    line_numbers: Vec<u8>,
16    pub constants: Vec<Value>,
17    pub jump_table: Vec<u32>,
18    pub captures: Option<Vec<u32>>,
19    // Registers holding input (arguments and closed over values) plus 1 for the result.
20    pub input_regs: usize,
21    // Number of registers needed beyond input_regs for computations.
22    pub extra_regs: usize,
23    pub args: u16,
24    pub opt_args: u16,
25    pub rest: bool,
26
27    pub dbg_args: Option<Vec<Interned>>,
28}
29
30impl Chunk {
31    pub fn new(file_name: &'static str, start_line: u32) -> Self {
32        Chunk {
33            code: Vec::new(),
34            file_name,
35            start_line,
36            last_line: start_line,
37            line_numbers: Vec::new(),
38            constants: Vec::new(),
39            jump_table: Vec::new(),
40            captures: None,
41            input_regs: 0,
42            extra_regs: 0,
43            args: 0,
44            opt_args: 0,
45            rest: false,
46            dbg_args: None,
47        }
48    }
49
50    fn encode_operand(&mut self, op: u16, wide: bool) {
51        if wide {
52            self.code.push(((op & 0xFF00) >> 8) as u8);
53        }
54        self.code.push((op & 0x00FF) as u8);
55    }
56
57    fn encode_line_number(&mut self, offsets: u8, line_number: Option<u32>) -> VMResult<()> {
58        let line_number = if let Some(ln) = line_number {
59            ln
60        } else {
61            self.last_line
62        };
63        match line_number.cmp(&self.last_line) {
64            Ordering::Equal => {
65                if let Some(line) = self.line_numbers.pop() {
66                    if (line & 0x40) == 0 {
67                        let current_offsets: u16 = (line & 0x3f) as u16;
68                        if current_offsets + offsets as u16 > 0x3f {
69                            self.line_numbers.push(0x3f);
70                            self.line_numbers
71                                .push((offsets - (0x3f - current_offsets) as u8) | (line & 0x80));
72                        } else {
73                            self.line_numbers
74                                .push((current_offsets as u8 + offsets) | (line & 0x80));
75                        }
76                    } else {
77                        self.line_numbers.push(line);
78                        self.line_numbers.push(offsets);
79                    }
80                } else {
81                    self.line_numbers.push(offsets);
82                }
83                Ok(())
84            }
85            Ordering::Less => Err(VMError::new_chunk("Line numbers can not go backwards!")),
86            Ordering::Greater => {
87                let mut delta = line_number - self.last_line;
88                while delta > 1 {
89                    if delta > 0x3f {
90                        self.line_numbers.push(0x7f); // 0x3f plus the 3rd bit of the high nibble.
91                        delta -= 0x3f;
92                    } else {
93                        self.line_numbers.push((delta - 1) as u8 | 0x40);
94                        delta = 0;
95                    }
96                }
97                self.last_line = line_number;
98                self.line_numbers.push(offsets | 0x80);
99                Ok(())
100            }
101        }
102    }
103
104    pub fn offset_to_line(&self, offset: usize) -> Option<u32> {
105        let mut line = self.start_line;
106        let mut current: usize = 0;
107        for o in &self.line_numbers {
108            if (o & 0x40) > 0 {
109                line += (o & 0x3f) as u32;
110            } else {
111                current += (o & 0x3f) as usize;
112            }
113            if (o & 0x80) > 0 {
114                line += 1;
115            }
116            if offset < current {
117                return Some(line);
118            }
119        }
120        None
121    }
122
123    pub fn line_to_offset(&self, line: u32) -> Option<usize> {
124        if line > self.last_line {
125            return None;
126        }
127        let mut current_line = self.start_line;
128        let mut offset: usize = 0;
129        for o in &self.line_numbers {
130            if (o & 0x80) > 0 {
131                current_line += 1;
132            }
133            if current_line == line {
134                return Some(offset);
135            }
136            if (o & 0x40) > 0 {
137                current_line += (o & 0x3f) as u32;
138            } else {
139                offset += (o & 0x3f) as usize;
140            }
141        }
142        None
143    }
144
145    pub fn add_constant(&mut self, value: Value) -> usize {
146        for (i, c) in self.constants.iter().enumerate() {
147            if *c == value {
148                return i;
149            }
150        }
151        self.constants.push(value);
152        self.constants.len() - 1
153    }
154
155    pub fn add_jump(&mut self, offset: u32) -> usize {
156        /*for (i, c) in self.jump_table.iter().enumerate() {
157            if *c == offset {
158                return i;
159            }
160        }*/
161        self.jump_table.push(offset);
162        self.jump_table.len() - 1
163    }
164
165    pub fn update_jump(&mut self, jmp: usize, offset: u32) {
166        self.jump_table[jmp] = offset;
167    }
168
169    pub fn encode0(&mut self, op_code: OpCode, line_number: Option<u32>) -> VMResult<()> {
170        self.encode_line_number(1, line_number)?;
171        self.code.push(op_code);
172        Ok(())
173    }
174
175    pub fn encode1(&mut self, opcode: OpCode, op1: u16, line_number: Option<u32>) -> VMResult<()> {
176        let mut bytes: u8 = 2;
177        let mut wide = false;
178        if op1 > u8::MAX as u16 {
179            wide = true;
180            bytes = 3;
181            self.encode_line_number(1, line_number)?;
182            self.code.push(WIDE);
183        }
184
185        self.encode_line_number(bytes, line_number)?;
186        self.code.push(opcode);
187        self.encode_operand(op1, wide);
188
189        Ok(())
190    }
191
192    pub fn encode2(
193        &mut self,
194        opcode: OpCode,
195        op1: u16,
196        op2: u16,
197        line_number: Option<u32>,
198    ) -> VMResult<()> {
199        let mut bytes: u8 = 3;
200        let mut wide = false;
201        if op1 > u8::MAX as u16 || op2 > u8::MAX as u16 {
202            wide = true;
203            bytes = 5;
204            self.encode_line_number(1, line_number)?;
205            self.code.push(WIDE);
206        }
207
208        self.encode_line_number(bytes, line_number)?;
209        self.code.push(opcode);
210        self.encode_operand(op1, wide);
211        self.encode_operand(op2, wide);
212
213        Ok(())
214    }
215
216    pub fn encode_def(
217        &mut self,
218        reg: u16,
219        global: u32,
220        line_number: Option<u32>,
221        is_defv: bool,
222    ) -> VMResult<()> {
223        let mut bytes: u8 = 4;
224        let mut wide = false;
225        if reg > u8::MAX as u16 || global > u16::MAX as u32 {
226            wide = true;
227            bytes = 7;
228            self.encode_line_number(1, line_number)?;
229            self.code.push(WIDE);
230        }
231
232        self.encode_line_number(bytes, line_number)?;
233        if is_defv {
234            self.code.push(DEFV);
235        } else {
236            self.code.push(DEF);
237        }
238        self.encode_operand(reg, wide);
239        if wide {
240            self.code.push(((global & 0xFF00_0000) >> 24) as u8);
241            self.code.push(((global & 0x00FF_0000) >> 16) as u8);
242        }
243        self.code.push(((global & 0x0000_FF00) >> 8) as u8);
244        self.code.push((global & 0x0000_00FF) as u8);
245
246        Ok(())
247    }
248
249    pub fn encode_refi(&mut self, reg: u16, global: u32, line_number: Option<u32>) -> VMResult<()> {
250        let mut bytes: u8 = 4;
251        let mut wide = false;
252        if reg > u8::MAX as u16 || global > u16::MAX as u32 {
253            wide = true;
254            bytes = 7;
255            self.encode_line_number(1, line_number)?;
256            self.code.push(WIDE);
257        }
258
259        self.encode_line_number(bytes, line_number)?;
260        self.code.push(REFI);
261        self.encode_operand(reg, wide);
262        if wide {
263            self.code.push(((global & 0xFF00_0000) >> 24) as u8);
264            self.code.push(((global & 0x00FF_0000) >> 16) as u8);
265        }
266        self.code.push(((global & 0x0000_FF00) >> 8) as u8);
267        self.code.push((global & 0x0000_00FF) as u8);
268
269        Ok(())
270    }
271
272    pub fn encode_callg(
273        &mut self,
274        global: u32,
275        num_args: u16,
276        first_reg: u16,
277        line_number: Option<u32>,
278    ) -> VMResult<()> {
279        let mut bytes: u8 = 5;
280        let mut wide = false;
281        if num_args > u8::MAX as u16 || first_reg > u8::MAX as u16 || global > u16::MAX as u32 {
282            wide = true;
283            bytes = 9;
284            self.encode_line_number(1, line_number)?;
285            self.code.push(WIDE);
286        }
287
288        self.encode_line_number(bytes, line_number)?;
289        self.code.push(CALLG);
290        if wide {
291            self.code.push(((global & 0xFF00_0000) >> 24) as u8);
292            self.code.push(((global & 0x00FF_0000) >> 16) as u8);
293        }
294        self.code.push(((global & 0x0000_FF00) >> 8) as u8);
295        self.code.push((global & 0x0000_00FF) as u8);
296        self.encode_operand(num_args, wide);
297        self.encode_operand(first_reg, wide);
298
299        Ok(())
300    }
301
302    pub fn encode_tcallg(
303        &mut self,
304        global: u32,
305        num_args: u16,
306        line_number: Option<u32>,
307    ) -> VMResult<()> {
308        let mut bytes: u8 = 4;
309        let mut wide = false;
310        if num_args > u8::MAX as u16 || global > u16::MAX as u32 {
311            wide = true;
312            bytes = 7;
313            self.encode_line_number(1, line_number)?;
314            self.code.push(WIDE);
315        }
316
317        self.encode_line_number(bytes, line_number)?;
318        self.code.push(TCALLG);
319        if wide {
320            self.code.push(((global & 0xFF00_0000) >> 24) as u8);
321            self.code.push(((global & 0x00FF_0000) >> 16) as u8);
322        }
323        self.code.push(((global & 0x0000_FF00) >> 8) as u8);
324        self.code.push((global & 0x0000_00FF) as u8);
325        self.encode_operand(num_args, wide);
326
327        Ok(())
328    }
329
330    pub fn encode3(
331        &mut self,
332        opcode: OpCode,
333        op1: u16,
334        op2: u16,
335        op3: u16,
336        line_number: Option<u32>,
337    ) -> VMResult<()> {
338        let mut bytes: u8 = 4;
339        let mut wide = false;
340        if op1 > u8::MAX as u16 || op2 > u8::MAX as u16 || op3 > u8::MAX as u16 {
341            wide = true;
342            bytes = 7;
343            self.encode_line_number(1, line_number)?;
344            self.code.push(WIDE);
345        }
346
347        self.encode_line_number(bytes, line_number)?;
348        self.code.push(opcode);
349        self.encode_operand(op1, wide);
350        self.encode_operand(op2, wide);
351        self.encode_operand(op3, wide);
352
353        Ok(())
354    }
355}
356
357#[cfg(test)]
358mod tests {
359    use super::*;
360
361    #[test]
362    fn test_encode0() {
363        let mut chunk = Chunk::new("no_file", 0);
364        chunk.encode0(RET, Some(1)).unwrap();
365        chunk.encode0(CAR, None).unwrap();
366        chunk.encode0(RET, None).unwrap();
367        let mut code = chunk.code.iter();
368
369        assert!(*code.next().unwrap() == RET);
370        assert!(*code.next().unwrap() == CAR);
371        assert!(*code.next().unwrap() == RET);
372        assert!(code.next().is_none());
373    }
374
375    #[test]
376    fn test_encode1() {
377        let mut chunk = Chunk::new("no_file", 0);
378        chunk.encode1(CAR, 0, Some(1)).unwrap();
379        chunk.encode1(CAR, 128, None).unwrap();
380        chunk.encode1(CAR, 255, None).unwrap();
381        chunk.encode1(CAR, 256, None).unwrap();
382        chunk.encode1(CAR, 256, None).unwrap();
383        chunk.encode1(CAR, u16::MAX, None).unwrap();
384        let mut code = chunk.code.iter();
385
386        assert!(*code.next().unwrap() == CAR);
387        assert!(*code.next().unwrap() == 0);
388
389        assert!(*code.next().unwrap() == CAR);
390        assert!(*code.next().unwrap() == 128);
391
392        assert!(*code.next().unwrap() == CAR);
393        assert!(*code.next().unwrap() == 255);
394
395        assert!(*code.next().unwrap() == WIDE);
396        assert!(*code.next().unwrap() == CAR);
397        assert!(decode_chunk_u16!(code).unwrap() == 256);
398
399        assert!(*code.next().unwrap() == WIDE);
400        assert!(*code.next().unwrap() == CAR);
401        assert!(decode_chunk_u16!(code).unwrap() == 256);
402
403        assert!(*code.next().unwrap() == WIDE);
404        assert!(*code.next().unwrap() == CAR);
405        assert!(decode_chunk_u16!(code).unwrap() == u16::MAX);
406    }
407
408    #[test]
409    fn test_encode2() {
410        let mut chunk = Chunk::new("no_file", 0);
411        chunk.encode2(MOV, 0, 0, Some(1)).unwrap();
412        chunk.encode2(MOV, 128, 128, None).unwrap();
413        chunk.encode2(MOV, 255, 255, None).unwrap();
414        chunk.encode2(MOV, 256, 256, None).unwrap();
415        chunk.encode2(MOV, 2, 256, None).unwrap();
416        chunk.encode2(MOV, 256, 1, None).unwrap();
417        chunk.encode2(MOV, 257, 257, None).unwrap();
418        chunk.encode2(MOV, u16::MAX, u16::MAX, None).unwrap();
419        let mut code = chunk.code.iter();
420
421        assert!(*code.next().unwrap() == MOV);
422        assert!(*code.next().unwrap() == 0);
423        assert!(*code.next().unwrap() == 0);
424
425        assert!(*code.next().unwrap() == MOV);
426        assert!(*code.next().unwrap() == 128);
427        assert!(*code.next().unwrap() == 128);
428
429        assert!(*code.next().unwrap() == MOV);
430        assert!(*code.next().unwrap() == 255);
431        assert!(*code.next().unwrap() == 255);
432
433        assert!(*code.next().unwrap() == WIDE);
434        assert!(*code.next().unwrap() == MOV);
435        assert!(decode_chunk_u16!(code).unwrap() == 256);
436        assert!(decode_chunk_u16!(code).unwrap() == 256);
437
438        assert!(*code.next().unwrap() == WIDE);
439        assert!(*code.next().unwrap() == MOV);
440        assert!(decode_chunk_u16!(code).unwrap() == 2);
441        assert!(decode_chunk_u16!(code).unwrap() == 256);
442
443        assert!(*code.next().unwrap() == WIDE);
444        assert!(*code.next().unwrap() == MOV);
445        assert!(decode_chunk_u16!(code).unwrap() == 256);
446        assert!(decode_chunk_u16!(code).unwrap() == 1);
447
448        assert!(*code.next().unwrap() == WIDE);
449        assert!(*code.next().unwrap() == MOV);
450        assert!(decode_chunk_u16!(code).unwrap() == 257);
451        assert!(decode_chunk_u16!(code).unwrap() == 257);
452
453        assert!(*code.next().unwrap() == WIDE);
454        assert!(*code.next().unwrap() == MOV);
455        assert!(decode_chunk_u16!(code).unwrap() == u16::MAX);
456        assert!(decode_chunk_u16!(code).unwrap() == u16::MAX);
457    }
458
459    #[test]
460    fn test_encode3() {
461        let mut chunk = Chunk::new("no_file", 0);
462        chunk.encode3(CONS, 0, 0, 0, Some(1)).unwrap();
463        chunk.encode3(CONS, 128, 128, 128, None).unwrap();
464        chunk.encode3(CONS, 255, 255, 255, None).unwrap();
465        chunk.encode3(CONS, 256, 256, 256, None).unwrap();
466        chunk.encode3(CONS, 2, 256, 256, None).unwrap();
467        chunk.encode3(CONS, 256, 1, 1, None).unwrap();
468        chunk.encode3(CONS, 257, 257, 257, None).unwrap();
469        chunk
470            .encode3(CONS, u16::MAX, u16::MAX, u16::MAX, Some(1))
471            .unwrap();
472        let mut code = chunk.code.iter();
473
474        assert!(*code.next().unwrap() == CONS);
475        assert!(*code.next().unwrap() == 0);
476        assert!(*code.next().unwrap() == 0);
477        assert!(*code.next().unwrap() == 0);
478
479        assert!(*code.next().unwrap() == CONS);
480        assert!(*code.next().unwrap() == 128);
481        assert!(*code.next().unwrap() == 128);
482        assert!(*code.next().unwrap() == 128);
483
484        assert!(*code.next().unwrap() == CONS);
485        assert!(*code.next().unwrap() == 255);
486        assert!(*code.next().unwrap() == 255);
487        assert!(*code.next().unwrap() == 255);
488
489        assert!(*code.next().unwrap() == WIDE);
490        assert!(*code.next().unwrap() == CONS);
491        assert!(decode_chunk_u16!(code).unwrap() == 256);
492        assert!(decode_chunk_u16!(code).unwrap() == 256);
493        assert!(decode_chunk_u16!(code).unwrap() == 256);
494
495        assert!(*code.next().unwrap() == WIDE);
496        assert!(*code.next().unwrap() == CONS);
497        assert!(decode_chunk_u16!(code).unwrap() == 2);
498        assert!(decode_chunk_u16!(code).unwrap() == 256);
499        assert!(decode_chunk_u16!(code).unwrap() == 256);
500
501        assert!(*code.next().unwrap() == WIDE);
502        assert!(*code.next().unwrap() == CONS);
503        assert!(decode_chunk_u16!(code).unwrap() == 256);
504        assert!(decode_chunk_u16!(code).unwrap() == 1);
505        assert!(decode_chunk_u16!(code).unwrap() == 1);
506
507        assert!(*code.next().unwrap() == WIDE);
508        assert!(*code.next().unwrap() == CONS);
509        assert!(decode_chunk_u16!(code).unwrap() == 257);
510        assert!(decode_chunk_u16!(code).unwrap() == 257);
511        assert!(decode_chunk_u16!(code).unwrap() == 257);
512
513        assert!(*code.next().unwrap() == WIDE);
514        assert!(*code.next().unwrap() == CONS);
515        assert!(decode_chunk_u16!(code).unwrap() == u16::MAX);
516        assert!(decode_chunk_u16!(code).unwrap() == u16::MAX);
517        assert!(decode_chunk_u16!(code).unwrap() == u16::MAX);
518    }
519
520    #[test]
521    fn test_line_numbers() {
522        let mut chunk = Chunk::new("no_file", 1);
523        chunk.encode2(MOV, 1, 2, Some(1)).unwrap();
524        chunk.encode2(MOV, 1, 2, Some(2)).unwrap();
525        chunk.encode2(MOV, 1, 2, Some(3)).unwrap();
526        chunk.encode2(MOV, 1, 2, Some(4)).unwrap();
527        chunk.encode2(MOV, 1, 2, Some(4)).unwrap();
528        chunk.encode2(MOV, 1, 2, Some(30)).unwrap();
529        chunk.encode2(MOV, 1, 2, Some(200)).unwrap();
530        assert!(chunk.offset_to_line(0).unwrap() == 1);
531        assert!(chunk.offset_to_line(1).unwrap() == 1);
532        assert!(chunk.offset_to_line(2).unwrap() == 1);
533
534        assert_eq!(chunk.offset_to_line(3).unwrap(), 2);
535        assert_eq!(chunk.offset_to_line(4).unwrap(), 2);
536        assert_eq!(chunk.offset_to_line(5).unwrap(), 2);
537
538        assert!(chunk.offset_to_line(6).unwrap() == 3);
539        assert!(chunk.offset_to_line(7).unwrap() == 3);
540        assert!(chunk.offset_to_line(8).unwrap() == 3);
541
542        assert!(chunk.offset_to_line(9).unwrap() == 4);
543        assert!(chunk.offset_to_line(10).unwrap() == 4);
544        assert!(chunk.offset_to_line(11).unwrap() == 4);
545
546        assert!(chunk.offset_to_line(12).unwrap() == 4);
547        assert!(chunk.offset_to_line(13).unwrap() == 4);
548        assert!(chunk.offset_to_line(14).unwrap() == 4);
549
550        assert!(chunk.offset_to_line(15).unwrap() == 30);
551        assert!(chunk.offset_to_line(16).unwrap() == 30);
552        assert!(chunk.offset_to_line(17).unwrap() == 30);
553
554        assert_eq!(chunk.offset_to_line(18).unwrap(), 200);
555        assert_eq!(chunk.offset_to_line(19).unwrap(), 200);
556        assert_eq!(chunk.offset_to_line(20).unwrap(), 200);
557
558        assert_eq!(chunk.line_to_offset(1).unwrap(), 0);
559        assert_eq!(chunk.line_to_offset(2).unwrap(), 3);
560        assert_eq!(chunk.line_to_offset(3).unwrap(), 6);
561        assert_eq!(chunk.line_to_offset(4).unwrap(), 9);
562        assert_eq!(chunk.line_to_offset(30).unwrap(), 15);
563        assert_eq!(chunk.line_to_offset(200).unwrap(), 18);
564        assert!(chunk.line_to_offset(15).is_none());
565        assert!(chunk.line_to_offset(0).is_none());
566        assert!(chunk.line_to_offset(201).is_none());
567        assert!(chunk.line_to_offset(101).is_none());
568        assert!(chunk.encode0(RET, Some(1)).is_err());
569    }
570}