Skip to main content

rustdom_x/
program.rs

1use super::m128::m128i;
2use super::vm::{is_zero_or_power_of_2, Vm, SCRATCHPAD_L3_MASK};
3use std::fmt;
4use strum::Display;
5
6pub const MAX_FLOAT_REG: usize = 4;
7pub const MAX_REG: usize = 8;
8pub const REG_NEEDS_DISPLACEMENT_IX: usize = 5;
9pub const REG_NEEDS_DISPLACEMENT: Store = Store::R(REG_NEEDS_DISPLACEMENT_IX);
10const STORE_L3_CONDITION: u8 = 14;
11
12#[allow(nonstandard_style)]
13#[derive(Display, Debug, PartialEq)]
14pub enum Opcode {
15    NOP = 0,
16    IADD_RS = 0x10,
17    IADD_M = 0x17,
18    ISUB_R = 0x27,
19    ISUB_M = 0x2e,
20    IMUL_R = 0x3e,
21    IMUL_M = 0x42,
22    IMULH_R = 0x46,
23    IMULH_M = 0x47,
24    ISMULH_R = 0x4b,
25    ISMULH_M = 0x4c,
26    IMUL_RCP = 0x54,
27    INEG_R = 0x56,
28    IXOR_R = 0x65,
29    IXOR_M = 0x6a,
30    IROR_R = 0x72,
31    IROL_R = 0x74,
32    ISWAP_R = 0x78,
33    FSWAP_R = 0x7c,
34    FADD_R = 0x8c,
35    FADD_M = 0x91,
36    FSUB_R = 0xa1,
37    FSUB_M = 0xa6,
38    FSCAL_R = 0xac,
39    FMUL_R = 0xcc,
40    FDIV_M = 0xd0,
41    FSQRT_R = 0xd6,
42    CBRANCH = 0xef,
43    CFROUND = 0xf0,
44    ISTORE = 0x100,
45}
46
47#[derive(Display, PartialEq)]
48pub enum Store {
49    NONE,
50    //registers
51    R(usize),
52    F(usize),
53    E(usize),
54    A(usize),
55    #[strum(serialize = "i")]
56    Imm, //non-register based Lx access
57    //Lx memory
58    L1(Box<Store>),
59    L2(Box<Store>),
60    L3(Box<Store>),
61}
62
63#[derive(PartialEq)]
64pub enum Mode {
65    None,
66    Cond(u8),
67    Shft(u8),
68}
69
70impl fmt::Display for Mode {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            Mode::None => write!(f, "NONE"),
74            Mode::Cond(x) => write!(f, "COND {}", x),
75            Mode::Shft(x) => write!(f, "SHFT {}", x),
76        }
77    }
78}
79
80pub struct Instr {
81    pub op: Opcode,
82    pub src: Store,
83    pub dst: Store,
84    pub imm: Option<i32>,
85    pub unsigned_imm: bool,
86    pub mode: Mode,
87    pub target: Option<i32>,
88    pub effect: fn(&mut Vm, &Instr),
89}
90
91fn new_instr(
92    op: Opcode,
93    dst: Store,
94    src: Store,
95    imm: i32,
96    mode: Mode,
97    effect: fn(&mut Vm, &Instr),
98) -> Instr {
99    if src == dst {
100        return Instr {
101            op,
102            dst,
103            src: Store::NONE,
104            imm: Some(imm),
105            unsigned_imm: false,
106            mode,
107            target: None,
108            effect,
109        };
110    }
111    Instr {
112        op,
113        dst,
114        src,
115        imm: None,
116        unsigned_imm: false,
117        mode,
118        target: None,
119        effect,
120    }
121}
122
123fn new_imm_instr(
124    op: Opcode,
125    dst: Store,
126    imm: i32,
127    mode: Mode,
128    effect: fn(&mut Vm, &Instr),
129) -> Instr {
130    Instr {
131        op,
132        dst,
133        src: Store::NONE,
134        imm: Some(imm),
135        unsigned_imm: false,
136        mode,
137        target: None,
138        effect,
139    }
140}
141
142pub fn new_lcache_instr(
143    op: Opcode,
144    dst_reg: Store,
145    src: usize,
146    imm: i32,
147    modi: u8,
148    effect: fn(&mut Vm, &Instr),
149) -> Instr {
150    let src_reg = r_reg(src);
151    if src_reg == dst_reg {
152        return Instr {
153            op,
154            dst: dst_reg,
155            src: Store::L3(Box::new(Store::Imm)),
156            imm: Some(imm & (SCRATCHPAD_L3_MASK as i32)),
157            unsigned_imm: false,
158            mode: Mode::None,
159            target: None,
160            effect,
161        };
162    }
163    let lx = l12_cache(src, modi);
164    Instr {
165        op,
166        dst: dst_reg,
167        src: lx,
168        imm: Some(imm),
169        unsigned_imm: false,
170        mode: Mode::None,
171        target: None,
172        effect,
173    }
174}
175
176impl Instr {
177    pub fn execute(&self, vm: &mut Vm) {
178        (self.effect)(vm, self);
179    }
180}
181
182impl fmt::Display for Instr {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        write!(f, "{} ", self.op)?;
185        match &self.dst {
186            Store::NONE => { /* do nothing */ }
187            Store::L1(reg) => write_l_access(f, self, reg, "L1")?,
188            Store::L2(reg) => write_l_access(f, self, reg, "L2")?,
189            Store::L3(reg) => write_l_access(f, self, reg, "L3")?,
190            Store::R(i) => write!(f, "r{}", i)?,
191            Store::F(i) => write!(f, "f{}", i)?,
192            Store::E(i) => write!(f, "e{}", i)?,
193            Store::A(i) => write!(f, "a{}", i)?,
194            _ => write!(f, "{}", self.dst)?,
195        }
196        if self.dst != Store::NONE && self.src != Store::NONE {
197            write!(f, ", ")?;
198        }
199        match &self.src {
200            Store::NONE => { /* do nothing */ }
201            Store::L1(reg) => write_l_access(f, self, reg, "L1")?,
202            Store::L2(reg) => write_l_access(f, self, reg, "L2")?,
203            Store::L3(reg) => write_l_access(f, self, reg, "L3")?,
204            Store::R(i) => write!(f, "r{}", i)?,
205            Store::F(i) => write!(f, "f{}", i)?,
206            Store::E(i) => write!(f, "e{}", i)?,
207            Store::A(i) => write!(f, "a{}", i)?,
208            _ => write!(f, ", {}", self.src)?,
209        }
210        if self.imm.is_some() && !(is_l_cache(&self.dst) || is_l_cache(&self.src)) {
211            if self.unsigned_imm {
212                write!(f, ", {}", self.imm.unwrap() as u32)?
213            } else {
214                write!(f, ", {}", self.imm.unwrap())?
215            }
216        }
217        if self.mode != Mode::None {
218            write!(f, ", {}", self.mode)?;
219        }
220        Ok(())
221    }
222}
223
224fn write_l_access(
225    f: &mut fmt::Formatter<'_>,
226    instr: &Instr,
227    reg: &Store,
228    lstore: &str,
229) -> fmt::Result {
230    if reg == &Store::Imm {
231        write!(f, "{}[{}]", lstore, instr.imm.unwrap())
232    } else {
233        write!(f, "{}[", lstore)?;
234        match reg {
235            Store::R(i) => write!(f, "r{}", i)?,
236            Store::F(i) => write!(f, "f{}", i)?,
237            Store::E(i) => write!(f, "e{}", i)?,
238            Store::A(i) => write!(f, "a{}", i)?,
239            _ => write!(f, "{}", reg)?,
240        }
241        write!(f, "{:+}]", instr.imm.unwrap())
242    }
243}
244
245pub struct Program {
246    pub entropy: Vec<u64>,
247    pub program: Vec<Instr>,
248    pub register_usage: [i32; MAX_REG],
249}
250
251impl Program {
252    pub fn from_bytes(bytes: Vec<m128i>) -> Program {
253        let mut entropy = Vec::with_capacity(16);
254        let mut program = Vec::with_capacity((bytes.len() - 8) * 2);
255        let mut register_usage = [-1; MAX_REG];
256
257        for byte in bytes.iter().take(8) {
258            let (e1, e0) = byte.as_i64();
259            entropy.push(e0 as u64);
260            entropy.push(e1 as u64);
261        }
262
263        for (i, byte) in bytes.iter().enumerate().skip(8) {
264            let (op2, op1) = byte.as_i64();
265            let instr1 = decode_instruction(op1, ((i - 8) * 2) as i32, &mut register_usage);
266            let instr2 = decode_instruction(op2, (((i - 8) * 2) + 1) as i32, &mut register_usage);
267            program.push(instr1);
268            program.push(instr2);
269        }
270
271        Program {
272            entropy,
273            program,
274            register_usage,
275        }
276    }
277}
278
279impl fmt::Display for Program {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        for instr in &self.program {
282            writeln!(f, "{}", instr)?;
283        }
284        Ok(())
285    }
286}
287
288#[allow(overflowing_literals)]
289pub fn decode_instruction(bytes: i64, i: i32, register_usage: &mut [i32; MAX_REG]) -> Instr {
290    let op = bytes & 0xFF;
291    let dst = ((bytes & 0xFF00) >> 8) as usize;
292    let src = ((bytes & 0xFF0000) >> 16) as usize;
293    let modi = ((bytes & 0xFF000000) >> 24) as u8;
294    let imm = ((bytes & 0xFFFFFFFF00000000) >> 32) as i32;
295    if op < Opcode::IADD_RS as i64 {
296        let dst_reg = r_reg(dst);
297        let imm_val;
298        if dst_reg == REG_NEEDS_DISPLACEMENT {
299            imm_val = Some(imm);
300        } else {
301            imm_val = None;
302        }
303        register_usage[dst % MAX_REG] = i;
304        return Instr {
305            op: Opcode::IADD_RS,
306            dst: dst_reg,
307            src: r_reg(src),
308            imm: imm_val,
309            unsigned_imm: false,
310            mode: mod_shft(modi),
311            target: None,
312            effect: Vm::exec_iadd_rs,
313        };
314    }
315    if op < Opcode::IADD_M as i64 {
316        register_usage[dst % MAX_REG] = i;
317        return new_lcache_instr(Opcode::IADD_M, r_reg(dst), src, imm, modi, Vm::exec_iadd_m);
318    }
319    if op < Opcode::ISUB_R as i64 {
320        register_usage[dst % MAX_REG] = i;
321        return new_instr(
322            Opcode::ISUB_R,
323            r_reg(dst),
324            r_reg(src),
325            imm,
326            Mode::None,
327            Vm::exec_isub_r,
328        );
329    }
330    if op < Opcode::ISUB_M as i64 {
331        register_usage[dst % MAX_REG] = i;
332        return new_lcache_instr(Opcode::ISUB_M, r_reg(dst), src, imm, modi, Vm::exec_isub_m);
333    }
334    if op < Opcode::IMUL_R as i64 {
335        register_usage[dst % MAX_REG] = i;
336        return new_instr(
337            Opcode::IMUL_R,
338            r_reg(dst),
339            r_reg(src),
340            imm,
341            Mode::None,
342            Vm::exec_imul_r,
343        );
344    }
345    if op < Opcode::IMUL_M as i64 {
346        register_usage[dst % MAX_REG] = i;
347        return new_lcache_instr(Opcode::IMUL_M, r_reg(dst), src, imm, modi, Vm::exec_imul_m);
348    }
349    if op < Opcode::IMULH_R as i64 {
350        register_usage[dst % MAX_REG] = i;
351        return Instr {
352            op: Opcode::IMULH_R,
353            dst: r_reg(dst),
354            src: r_reg(src),
355            imm: None,
356            unsigned_imm: false,
357            mode: Mode::None,
358            target: None,
359            effect: Vm::exec_imulh_r,
360        };
361    }
362    if op < Opcode::IMULH_M as i64 {
363        register_usage[dst % MAX_REG] = i;
364        return new_lcache_instr(
365            Opcode::IMULH_M,
366            r_reg(dst),
367            src,
368            imm,
369            modi,
370            Vm::exec_imulh_m,
371        );
372    }
373    if op < Opcode::ISMULH_R as i64 {
374        register_usage[dst % MAX_REG] = i;
375        return Instr {
376            op: Opcode::ISMULH_R,
377            dst: r_reg(dst),
378            src: r_reg(src),
379            imm: None,
380            unsigned_imm: false,
381            mode: Mode::None,
382            target: None,
383            effect: Vm::exec_ismulh_r,
384        };
385    }
386    if op < Opcode::ISMULH_M as i64 {
387        register_usage[dst % MAX_REG] = i;
388        return new_lcache_instr(
389            Opcode::ISMULH_M,
390            r_reg(dst),
391            src,
392            imm,
393            modi,
394            Vm::exec_ismulh_m,
395        );
396    }
397    if op < Opcode::IMUL_RCP as i64 {
398        if !is_zero_or_power_of_2(imm as u64) {
399            register_usage[dst % MAX_REG] = i;
400        }
401        let mut instr = new_imm_instr(
402            Opcode::IMUL_RCP,
403            r_reg(dst),
404            imm,
405            Mode::None,
406            Vm::exec_imul_rcp,
407        );
408        instr.unsigned_imm = true;
409        return instr;
410    }
411    if op < Opcode::INEG_R as i64 {
412        register_usage[dst % MAX_REG] = i;
413        return new_instr(
414            Opcode::INEG_R,
415            r_reg(dst),
416            Store::NONE,
417            imm,
418            Mode::None,
419            Vm::exec_ineg_r,
420        );
421    }
422    if op < Opcode::IXOR_R as i64 {
423        register_usage[dst % MAX_REG] = i;
424        return new_instr(
425            Opcode::IXOR_R,
426            r_reg(dst),
427            r_reg(src),
428            imm,
429            Mode::None,
430            Vm::exec_ixor_r,
431        );
432    }
433    if op < Opcode::IXOR_M as i64 {
434        register_usage[dst % MAX_REG] = i;
435        return new_lcache_instr(Opcode::IXOR_M, r_reg(dst), src, imm, modi, Vm::exec_ixor_m);
436    }
437    if op < Opcode::IROR_R as i64 {
438        register_usage[dst % MAX_REG] = i;
439        return new_instr(
440            Opcode::IROR_R,
441            r_reg(dst),
442            r_reg(src),
443            imm & 63,
444            Mode::None,
445            Vm::exec_iror_r,
446        );
447    }
448    if op < Opcode::IROL_R as i64 {
449        register_usage[dst % MAX_REG] = i;
450        return new_instr(
451            Opcode::IROL_R,
452            r_reg(dst),
453            r_reg(src),
454            imm & 63,
455            Mode::None,
456            Vm::exec_irol_r,
457        );
458    }
459    if op < Opcode::ISWAP_R as i64 {
460        let dst_r = dst % MAX_REG;
461        let src_r = src % MAX_REG;
462        if src_r != dst_r {
463            register_usage[dst_r] = i;
464            register_usage[src_r] = i;
465            return Instr {
466                op: Opcode::ISWAP_R,
467                dst: r_reg(dst),
468                src: r_reg(src),
469                imm: None,
470                unsigned_imm: false,
471                mode: Mode::None,
472                target: None,
473                effect: Vm::exec_iswap_r,
474            };
475        } else {
476            return new_instr(Opcode::NOP, Store::NONE, Store::NONE, imm, Mode::None, nop);
477        }
478    }
479    if op < Opcode::FSWAP_R as i64 {
480        let dst_ix = dst % MAX_REG;
481        if dst_ix >= MAX_FLOAT_REG {
482            return new_instr(
483                Opcode::FSWAP_R,
484                e_reg_ix(dst_ix % MAX_FLOAT_REG),
485                Store::NONE,
486                imm,
487                Mode::None,
488                Vm::exec_fswap_r,
489            );
490        } else {
491            return new_instr(
492                Opcode::FSWAP_R,
493                f_reg_ix(dst_ix % MAX_FLOAT_REG),
494                Store::NONE,
495                imm,
496                Mode::None,
497                Vm::exec_fswap_r,
498            );
499        }
500    }
501    if op < Opcode::FADD_R as i64 {
502        return new_instr(
503            Opcode::FADD_R,
504            f_reg(dst),
505            a_reg(src),
506            imm,
507            Mode::None,
508            Vm::exec_fadd_r,
509        );
510    }
511    if op < Opcode::FADD_M as i64 {
512        return new_lcache_instr(Opcode::FADD_M, f_reg(dst), src, imm, modi, Vm::exec_fadd_m);
513    }
514    if op < Opcode::FSUB_R as i64 {
515        return new_instr(
516            Opcode::FSUB_R,
517            f_reg(dst),
518            a_reg(src),
519            imm,
520            Mode::None,
521            Vm::exec_fsub_r,
522        );
523    }
524    if op < Opcode::FSUB_M as i64 {
525        return new_lcache_instr(Opcode::FSUB_M, f_reg(dst), src, imm, modi, Vm::exec_fsub_m);
526    }
527    if op < Opcode::FSCAL_R as i64 {
528        return new_instr(
529            Opcode::FSCAL_R,
530            f_reg(dst),
531            Store::NONE,
532            imm,
533            Mode::None,
534            Vm::exec_fscal_r,
535        );
536    }
537    if op < Opcode::FMUL_R as i64 {
538        return new_instr(
539            Opcode::FMUL_R,
540            e_reg(dst),
541            a_reg(src),
542            imm,
543            Mode::None,
544            Vm::exec_fmul_r,
545        );
546    }
547    if op < Opcode::FDIV_M as i64 {
548        return new_lcache_instr(Opcode::FDIV_M, e_reg(dst), src, imm, modi, Vm::exec_fdiv_m);
549    }
550    if op < Opcode::FSQRT_R as i64 {
551        return new_instr(
552            Opcode::FSQRT_R,
553            e_reg(dst),
554            Store::NONE,
555            imm,
556            Mode::None,
557            Vm::exec_fsqrt_r,
558        );
559    }
560    if op < Opcode::CBRANCH as i64 {
561        let target = register_usage[dst % MAX_REG];
562        for usage in register_usage.iter_mut().take(MAX_REG) {
563            *usage = i;
564        }
565        return Instr {
566            op: Opcode::CBRANCH,
567            dst: r_reg(dst),
568            src: Store::NONE,
569            imm: Some(imm),
570            unsigned_imm: false,
571            mode: mod_cond(modi),
572            target: Some(target),
573            effect: Vm::exec_cbranch,
574        };
575    }
576    if op < Opcode::CFROUND as i64 {
577        return Instr {
578            op: Opcode::CFROUND,
579            dst: Store::NONE,
580            src: r_reg(src),
581            imm: Some(imm & 63),
582            unsigned_imm: false,
583            mode: Mode::None,
584            target: None,
585            effect: Vm::exec_cfround,
586        };
587    }
588    if op < Opcode::ISTORE as i64 {
589        return Instr {
590            op: Opcode::ISTORE,
591            dst: l_cache(dst, modi),
592            src: r_reg(src),
593            imm: Some(imm),
594            unsigned_imm: false,
595            mode: Mode::None,
596            target: None,
597            effect: Vm::exec_istore,
598        };
599    }
600    new_instr(Opcode::NOP, Store::NONE, Store::NONE, imm, Mode::None, nop)
601}
602
603pub fn r_reg(dst: usize) -> Store {
604    match dst % MAX_REG {
605        0 => Store::R(0),
606        1 => Store::R(1),
607        2 => Store::R(2),
608        3 => Store::R(3),
609        4 => Store::R(4),
610        5 => Store::R(5),
611        6 => Store::R(6),
612        7 => Store::R(7),
613        _ => Store::R(0),
614    }
615}
616
617pub fn a_reg(dst: usize) -> Store {
618    match dst % MAX_FLOAT_REG {
619        0 => Store::A(0),
620        1 => Store::A(1),
621        2 => Store::A(2),
622        3 => Store::A(3),
623        _ => Store::A(0),
624    }
625}
626
627pub fn e_reg(dst: usize) -> Store {
628    e_reg_ix(dst % MAX_FLOAT_REG)
629}
630
631fn e_reg_ix(ix: usize) -> Store {
632    match ix {
633        0 => Store::E(0),
634        1 => Store::E(1),
635        2 => Store::E(2),
636        3 => Store::E(3),
637        _ => Store::E(0),
638    }
639}
640
641pub fn f_reg(dst: usize) -> Store {
642    f_reg_ix(dst % MAX_FLOAT_REG)
643}
644
645fn f_reg_ix(ix: usize) -> Store {
646    match ix {
647        0 => Store::F(0),
648        1 => Store::F(1),
649        2 => Store::F(2),
650        3 => Store::F(3),
651        _ => Store::F(0),
652    }
653}
654
655fn l_cache(dst: usize, modi: u8) -> Store {
656    let reg = r_reg(dst);
657    let cond = mod_cond_u8(modi);
658    if cond < STORE_L3_CONDITION {
659        if mod_mem_u8(modi) == 0 {
660            return Store::L2(Box::new(reg));
661        }
662        return Store::L1(Box::new(reg));
663    }
664    Store::L3(Box::new(reg))
665}
666
667fn l12_cache(src: usize, modi: u8) -> Store {
668    let reg = r_reg(src);
669    if mod_mem_u8(modi) == 0 {
670        return Store::L2(Box::new(reg));
671    }
672    Store::L1(Box::new(reg))
673}
674
675fn is_l_cache(store: &Store) -> bool {
676    matches!(store, Store::L1(_) | Store::L2(_) | Store::L3(_))
677}
678
679fn mod_mem_u8(modi: u8) -> u8 {
680    modi % 4 //bit 0-1
681}
682
683fn mod_cond_u8(modi: u8) -> u8 {
684    modi >> 4 //bits 4-7
685}
686
687fn mod_cond(modi: u8) -> Mode {
688    Mode::Cond(mod_cond_u8(modi))
689}
690
691fn mod_shft(modi: u8) -> Mode {
692    Mode::Shft((modi >> 2) % 4)
693}
694
695pub fn nop(_state: &mut Vm, _instr: &Instr) {}