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 R(usize),
52 F(usize),
53 E(usize),
54 A(usize),
55 #[strum(serialize = "i")]
56 Imm, 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 => { }
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 => { }
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 }
682
683fn mod_cond_u8(modi: u8) -> u8 {
684 modi >> 4 }
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) {}