1use std::sync::LazyLock;
36
37use crate::inst::{ArithOp, CmpOp, Compare, Convert, Num};
38use crate::repr::Repr;
39
40const NUMS: [Num; 2] = [Num::Int, Num::Float];
42const ARITH_OPS: [ArithOp; 5] = [
44 ArithOp::Add,
45 ArithOp::Sub,
46 ArithOp::Mul,
47 ArithOp::Div,
48 ArithOp::Rem,
49];
50const CMP_OPS: [CmpOp; 6] = [
52 CmpOp::Eq,
53 CmpOp::Ne,
54 CmpOp::Lt,
55 CmpOp::Le,
56 CmpOp::Gt,
57 CmpOp::Ge,
58];
59const COMPARES: [Compare; 6] = [
61 Compare::Int,
62 Compare::Float,
63 Compare::Bool,
64 Compare::Str,
65 Compare::Identity,
66 Compare::Tag,
67];
68const CONVERTS: [Convert; 2] = [Convert::IntToFloat, Convert::FloatToInt];
70
71mod base {
77 use super::{ARITH_OPS, CMP_OPS, COMPARES, CONVERTS, NUMS};
78
79 pub const CONST_UNIT: u8 = 0;
80 pub const CONST_BOOL: u8 = CONST_UNIT + 1;
81 pub const CONST_INT: u8 = CONST_BOOL + 1;
82 pub const FUNC_REF: u8 = CONST_INT + 1;
83 pub const CONST_TAG: u8 = FUNC_REF + 1;
84 pub const CONST_FLOAT: u8 = CONST_TAG + 1;
85 pub const STR: u8 = CONST_FLOAT + 1;
86 pub const COPY: u8 = STR + 1;
87 pub const CLEAR: u8 = COPY + 1;
88 pub const NEG: u8 = CLEAR + 1;
89 pub const ARITH: u8 = NEG + NUMS.len() as u8;
90 pub const CMP: u8 = ARITH + (NUMS.len() * ARITH_OPS.len()) as u8;
91 pub const ARITH_IMM: u8 = CMP + (COMPARES.len() * CMP_OPS.len()) as u8;
92 pub const CMP_IMM: u8 = ARITH_IMM + ARITH_OPS.len() as u8;
93 pub const NOT: u8 = CMP_IMM + CMP_OPS.len() as u8;
94 pub const CONVERT: u8 = NOT + 1;
95 pub const JUMP: u8 = CONVERT + CONVERTS.len() as u8;
96 pub const BRANCH_FALSE: u8 = JUMP + 1;
97 pub const SWITCH: u8 = BRANCH_FALSE + 1;
98 pub const RETURN: u8 = SWITCH + 1;
99 pub const CALL: u8 = RETURN + 1;
100 pub const CALL_CLOSURE: u8 = CALL + 1;
101 pub const CALL_HOST: u8 = CALL_CLOSURE + 1;
102 pub const CALL_RESOURCE: u8 = CALL_HOST + 1;
103 pub const CALL_BUILTIN: u8 = CALL_RESOURCE + 1;
104 pub const ALLOC_FIXED: u8 = CALL_BUILTIN + 1;
105 pub const ALLOC_IMM: u8 = ALLOC_FIXED + 1;
106 pub const ALLOC_SLOT: u8 = ALLOC_IMM + 1;
107 pub const LOAD_FIELD: u8 = ALLOC_SLOT + 1;
108 pub const STORE_FIELD: u8 = LOAD_FIELD + 1;
109 pub const LOAD_ELEM: u8 = STORE_FIELD + 1;
110 pub const STORE_ELEM: u8 = LOAD_ELEM + 1;
111 pub const BYTE_AT: u8 = STORE_ELEM + 1;
112 pub const ALLOC_BYTES: u8 = BYTE_AT + 1;
116 pub const WRITE_BYTE: u8 = ALLOC_BYTES + 1;
117 pub const COPY_BYTES: u8 = WRITE_BYTE + 1;
118 pub const FINISH_STRING: u8 = COPY_BYTES + 1;
119 pub const ALLOC_BUFFER: u8 = FINISH_STRING + 1;
123 pub const APPEND_BYTE: u8 = ALLOC_BUFFER + 1;
124 pub const APPEND_BYTES: u8 = APPEND_BYTE + 1;
125 pub const FINISH_BUFFER: u8 = APPEND_BYTES + 1;
126 pub const LEN: u8 = FINISH_BUFFER + 1;
127 pub const LAYOUT_OF: u8 = LEN + 1;
128 pub const ADDR_OF_SLOT: u8 = LAYOUT_OF + 1;
129 pub const ADDR_OF_FIELD: u8 = ADDR_OF_SLOT + 1;
130 pub const ADDR_OF_ELEM: u8 = ADDR_OF_FIELD + 1;
131 pub const ADDR_OF_PART: u8 = ADDR_OF_ELEM + 1;
132 pub const LOAD: u8 = ADDR_OF_PART + 1;
133 pub const STORE: u8 = LOAD + 1;
134 pub const BOX: u8 = STORE + 1;
135 pub const UNBOX: u8 = BOX + 1;
136 pub const SCOPE_ENTER: u8 = UNBOX + 1;
137 pub const SCOPE_LEAVE: u8 = SCOPE_ENTER + 1;
138 pub const SCOPE_CANCEL: u8 = SCOPE_LEAVE + 1;
139 pub const SPAWN: u8 = SCOPE_CANCEL + 1;
140 pub const AWAIT: u8 = SPAWN + 1;
141 pub const CANCEL: u8 = AWAIT + 1;
142 pub const SETTLED: u8 = CANCEL + 1;
143 pub const SHARED_LOCK: u8 = SETTLED + 1;
144 pub const SHARED_UNLOCK: u8 = SHARED_LOCK + 1;
145 pub const TRAP: u8 = SHARED_UNLOCK + 1;
146 pub const ASSERT_FAILED: u8 = TRAP + 1;
147 pub const END: u8 = ASSERT_FAILED + 1;
149}
150
151pub const OPCODES: usize = base::END as usize;
153
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
160pub enum Op {
161 ConstUnit,
162 ConstBool,
163 ConstInt,
164 FuncRef,
165 ConstTag,
166 ConstFloat,
167 Str,
168 Copy,
169 Clear,
170 Neg(Num),
171 Arith(Num, ArithOp),
172 Cmp(Compare, CmpOp),
173 ArithImm(ArithOp),
174 CmpImm(CmpOp),
175 Not,
176 Convert(Convert),
177 Jump,
178 BranchFalse,
179 Switch,
180 Return,
181 Call,
182 CallClosure,
183 CallHost,
184 CallResource,
185 CallBuiltin,
186 AllocFixed,
187 AllocImm,
188 AllocSlot,
189 LoadField,
190 StoreField,
191 LoadElem,
192 StoreElem,
193 ByteAt,
194 AllocBytes,
195 WriteByte,
196 CopyBytes,
197 FinishString,
198 AllocBuffer,
199 AppendByte,
200 AppendBytes,
201 FinishBuffer,
202 Len,
203 LayoutOf,
204 AddrOfSlot,
205 AddrOfField,
206 AddrOfElem,
207 AddrOfPart,
208 Load,
209 Store,
210 Box,
211 Unbox,
212 ScopeEnter,
213 ScopeLeave,
214 ScopeCancel,
215 Spawn,
216 Await,
217 Cancel,
218 Settled,
219 SharedLock,
220 SharedUnlock,
221 Trap,
222 AssertFailed,
223}
224
225#[derive(Clone, Copy, Debug, PartialEq, Eq)]
231pub enum Operand {
232 Unused,
238 Word(&'static [Repr]),
245 Value,
252}
253
254pub const ANY: &[Repr] = &[];
257
258const INT: &[Repr] = &[Repr::Int, Repr::Duration];
261const FLOAT: &[Repr] = &[Repr::Float];
262const BOOL: &[Repr] = &[Repr::Bool];
263const UNIT: &[Repr] = &[Repr::Unit];
264const REF: &[Repr] = &[Repr::Ref];
265const ADDR: &[Repr] = &[Repr::Addr];
266const HOST: &[Repr] = &[Repr::Host];
267const TASK: &[Repr] = &[Repr::Task];
268const SCOPE: &[Repr] = &[Repr::Scope];
269const TAG: &[Repr] = &[Repr::Tag];
272const SWITCHED: &[Repr] = &[Repr::Tag, Repr::Int];
277
278#[derive(Clone, Copy, Debug, PartialEq, Eq)]
280pub enum Payload {
281 Empty,
283 Bool,
285 Imm,
288 Displacement,
290 Halves(Half, Half),
292}
293
294#[derive(Clone, Copy, Debug, PartialEq, Eq)]
299pub enum Half {
300 Unused,
302 Function,
303 Str,
304 Layout,
305 Table,
306 Args,
307 Builtin,
308 HostOp,
309 Count,
311 Case,
318 Offset,
320}
321
322impl Half {
323 pub fn name(self) -> &'static str {
325 match self {
326 Half::Unused => "unused",
327 Half::Function => "function",
328 Half::Str => "string",
329 Half::Layout => "layout",
330 Half::Table => "table",
331 Half::Args => "argument list",
332 Half::Builtin => "builtin",
333 Half::HostOp => "host op",
334 Half::Count => "count",
335 Half::Case => "case",
336 Half::Offset => "offset",
337 }
338 }
339}
340
341#[derive(Clone, Copy, Debug, PartialEq, Eq)]
343pub struct Fields {
344 pub a: Operand,
345 pub b: Operand,
346 pub c: Operand,
347 pub payload: Payload,
348}
349
350impl Fields {
351 pub fn operands(&self) -> [Operand; 3] {
353 [self.a, self.b, self.c]
354 }
355}
356
357fn fields(a: Operand, b: Operand, c: Operand, payload: Payload) -> Fields {
359 Fields { a, b, c, payload }
360}
361
362const NONE: Operand = Operand::Unused;
364
365macro_rules! index_of {
383 ($held:ident, $wanted:expr) => {{
384 let wanted = $wanted as u8;
385 let mut at = 0;
386 while at < $held.len() && $held[at] as u8 != wanted {
387 at += 1;
388 }
389 at as u8
390 }};
391}
392
393impl Op {
394 pub fn all() -> Vec<Op> {
399 let mut all = vec![
400 Op::ConstUnit,
401 Op::ConstBool,
402 Op::ConstInt,
403 Op::FuncRef,
404 Op::ConstTag,
405 Op::ConstFloat,
406 Op::Str,
407 Op::Copy,
408 Op::Clear,
409 ];
410 all.extend(NUMS.map(Op::Neg));
411 for num in NUMS {
412 for op in ARITH_OPS {
413 all.push(Op::Arith(num, op));
414 }
415 }
416 for on in COMPARES {
417 for op in CMP_OPS {
418 all.push(Op::Cmp(on, op));
419 }
420 }
421 all.extend(ARITH_OPS.map(Op::ArithImm));
422 all.extend(CMP_OPS.map(Op::CmpImm));
423 all.push(Op::Not);
424 all.extend(CONVERTS.map(Op::Convert));
425 all.extend([
426 Op::Jump,
427 Op::BranchFalse,
428 Op::Switch,
429 Op::Return,
430 Op::Call,
431 Op::CallClosure,
432 Op::CallHost,
433 Op::CallResource,
434 Op::CallBuiltin,
435 Op::AllocFixed,
436 Op::AllocImm,
437 Op::AllocSlot,
438 Op::LoadField,
439 Op::StoreField,
440 Op::LoadElem,
441 Op::StoreElem,
442 Op::ByteAt,
443 Op::AllocBytes,
444 Op::WriteByte,
445 Op::CopyBytes,
446 Op::FinishString,
447 Op::AllocBuffer,
448 Op::AppendByte,
449 Op::AppendBytes,
450 Op::FinishBuffer,
451 Op::Len,
452 Op::LayoutOf,
453 Op::AddrOfSlot,
454 Op::AddrOfField,
455 Op::AddrOfElem,
456 Op::AddrOfPart,
457 Op::Load,
458 Op::Store,
459 Op::Box,
460 Op::Unbox,
461 Op::ScopeEnter,
462 Op::ScopeLeave,
463 Op::ScopeCancel,
464 Op::Spawn,
465 Op::Await,
466 Op::Cancel,
467 Op::Settled,
468 Op::SharedLock,
469 Op::SharedUnlock,
470 Op::Trap,
471 Op::AssertFailed,
472 ]);
473 all
474 }
475
476 pub const fn number(self) -> u8 {
485 match self {
486 Op::ConstUnit => base::CONST_UNIT,
487 Op::ConstBool => base::CONST_BOOL,
488 Op::ConstInt => base::CONST_INT,
489 Op::FuncRef => base::FUNC_REF,
490 Op::ConstTag => base::CONST_TAG,
491 Op::ConstFloat => base::CONST_FLOAT,
492 Op::Str => base::STR,
493 Op::Copy => base::COPY,
494 Op::Clear => base::CLEAR,
495 Op::Neg(num) => base::NEG + index_of!(NUMS, num),
496 Op::Arith(num, op) => {
497 base::ARITH
498 + index_of!(NUMS, num) * ARITH_OPS.len() as u8
499 + index_of!(ARITH_OPS, op)
500 }
501 Op::Cmp(on, op) => {
502 base::CMP + index_of!(COMPARES, on) * CMP_OPS.len() as u8 + index_of!(CMP_OPS, op)
503 }
504 Op::ArithImm(op) => base::ARITH_IMM + index_of!(ARITH_OPS, op),
505 Op::CmpImm(op) => base::CMP_IMM + index_of!(CMP_OPS, op),
506 Op::Not => base::NOT,
507 Op::Convert(to) => base::CONVERT + index_of!(CONVERTS, to),
508 Op::Jump => base::JUMP,
509 Op::BranchFalse => base::BRANCH_FALSE,
510 Op::Switch => base::SWITCH,
511 Op::Return => base::RETURN,
512 Op::Call => base::CALL,
513 Op::CallClosure => base::CALL_CLOSURE,
514 Op::CallHost => base::CALL_HOST,
515 Op::CallResource => base::CALL_RESOURCE,
516 Op::CallBuiltin => base::CALL_BUILTIN,
517 Op::AllocFixed => base::ALLOC_FIXED,
518 Op::AllocImm => base::ALLOC_IMM,
519 Op::AllocSlot => base::ALLOC_SLOT,
520 Op::LoadField => base::LOAD_FIELD,
521 Op::StoreField => base::STORE_FIELD,
522 Op::LoadElem => base::LOAD_ELEM,
523 Op::StoreElem => base::STORE_ELEM,
524 Op::ByteAt => base::BYTE_AT,
525 Op::AllocBytes => base::ALLOC_BYTES,
526 Op::WriteByte => base::WRITE_BYTE,
527 Op::CopyBytes => base::COPY_BYTES,
528 Op::FinishString => base::FINISH_STRING,
529 Op::AllocBuffer => base::ALLOC_BUFFER,
530 Op::AppendByte => base::APPEND_BYTE,
531 Op::AppendBytes => base::APPEND_BYTES,
532 Op::FinishBuffer => base::FINISH_BUFFER,
533 Op::Len => base::LEN,
534 Op::LayoutOf => base::LAYOUT_OF,
535 Op::AddrOfSlot => base::ADDR_OF_SLOT,
536 Op::AddrOfField => base::ADDR_OF_FIELD,
537 Op::AddrOfElem => base::ADDR_OF_ELEM,
538 Op::AddrOfPart => base::ADDR_OF_PART,
539 Op::Load => base::LOAD,
540 Op::Store => base::STORE,
541 Op::Box => base::BOX,
542 Op::Unbox => base::UNBOX,
543 Op::ScopeEnter => base::SCOPE_ENTER,
544 Op::ScopeLeave => base::SCOPE_LEAVE,
545 Op::ScopeCancel => base::SCOPE_CANCEL,
546 Op::Spawn => base::SPAWN,
547 Op::Await => base::AWAIT,
548 Op::Cancel => base::CANCEL,
549 Op::Settled => base::SETTLED,
550 Op::SharedLock => base::SHARED_LOCK,
551 Op::SharedUnlock => base::SHARED_UNLOCK,
552 Op::Trap => base::TRAP,
553 Op::AssertFailed => base::ASSERT_FAILED,
554 }
555 }
556
557 pub fn from_number(number: u8) -> Option<Op> {
562 static BY_NUMBER: LazyLock<[Option<Op>; 256]> = LazyLock::new(|| {
563 let mut table = [None; 256];
564 for op in Op::all() {
565 table[op.number() as usize] = Some(op);
566 }
567 table
568 });
569 BY_NUMBER[number as usize]
570 }
571
572 pub fn fields(self) -> Fields {
578 let ids = |lo: Half, hi: Half| Payload::Halves(lo, hi);
579 let one = |lo: Half| Payload::Halves(lo, Half::Unused);
580 match self {
581 Op::ConstUnit => fields(Operand::Word(UNIT), NONE, NONE, Payload::Empty),
582 Op::ConstBool => fields(Operand::Word(BOOL), NONE, NONE, Payload::Bool),
583 Op::ConstInt => fields(Operand::Word(INT), NONE, NONE, Payload::Imm),
584 Op::FuncRef => fields(Operand::Word(INT), NONE, NONE, one(Half::Function)),
590 Op::ConstTag => fields(
592 Operand::Word(TAG),
593 NONE,
594 NONE,
595 ids(Half::Case, Half::Layout),
596 ),
597 Op::ConstFloat => fields(Operand::Word(FLOAT), NONE, NONE, Payload::Imm),
598 Op::Str => fields(Operand::Word(REF), NONE, NONE, one(Half::Str)),
599 Op::Copy => fields(Operand::Value, Operand::Value, NONE, one(Half::Layout)),
600 Op::Clear => fields(Operand::Value, NONE, NONE, one(Half::Layout)),
601 Op::Neg(num) => {
602 let want = numeric(num);
603 fields(
604 Operand::Word(want),
605 Operand::Word(want),
606 NONE,
607 Payload::Empty,
608 )
609 }
610 Op::Arith(num, _) => {
611 let want = numeric(num);
612 fields(
613 Operand::Word(want),
614 Operand::Word(want),
615 Operand::Word(want),
616 Payload::Empty,
617 )
618 }
619 Op::Cmp(on, _) => {
620 let want = compared(on);
621 fields(
622 Operand::Word(BOOL),
623 Operand::Word(want),
624 Operand::Word(want),
625 Payload::Empty,
626 )
627 }
628 Op::ArithImm(_) => fields(Operand::Word(INT), Operand::Word(INT), NONE, Payload::Imm),
629 Op::CmpImm(_) => fields(Operand::Word(BOOL), Operand::Word(INT), NONE, Payload::Imm),
630 Op::Not => fields(
631 Operand::Word(BOOL),
632 Operand::Word(BOOL),
633 NONE,
634 Payload::Empty,
635 ),
636 Op::Convert(to) => {
637 let (from, into) = match to {
638 Convert::IntToFloat => (INT, FLOAT),
639 Convert::FloatToInt => (FLOAT, INT),
640 };
641 fields(
642 Operand::Word(into),
643 Operand::Word(from),
644 NONE,
645 Payload::Empty,
646 )
647 }
648 Op::Jump => fields(NONE, NONE, NONE, Payload::Displacement),
649 Op::BranchFalse => fields(Operand::Word(BOOL), NONE, NONE, Payload::Displacement),
650 Op::Switch => fields(Operand::Word(SWITCHED), NONE, NONE, one(Half::Table)),
653 Op::Return => fields(Operand::Word(ANY), NONE, NONE, Payload::Empty),
657 Op::Call => fields(
660 Operand::Word(ANY),
661 NONE,
662 NONE,
663 ids(Half::Function, Half::Args),
664 ),
665 Op::CallClosure => fields(
671 Operand::Value,
672 Operand::Word(REF),
673 NONE,
674 ids(Half::Args, Half::Layout),
675 ),
676 Op::CallHost => fields(
677 Operand::Word(ANY),
678 NONE,
679 NONE,
680 ids(Half::HostOp, Half::Args),
681 ),
682 Op::CallResource => fields(
683 Operand::Word(ANY),
684 Operand::Word(HOST),
685 NONE,
686 ids(Half::HostOp, Half::Args),
687 ),
688 Op::CallBuiltin => fields(
689 Operand::Word(ANY),
690 NONE,
691 NONE,
692 ids(Half::Builtin, Half::Args),
693 ),
694 Op::AllocFixed => fields(Operand::Word(REF), NONE, NONE, one(Half::Layout)),
695 Op::AllocImm => fields(
696 Operand::Word(REF),
697 NONE,
698 NONE,
699 ids(Half::Layout, Half::Count),
700 ),
701 Op::AllocSlot => fields(
702 Operand::Word(REF),
703 Operand::Word(INT),
704 NONE,
705 one(Half::Layout),
706 ),
707 Op::LoadField => fields(
708 Operand::Value,
709 Operand::Word(REF),
710 NONE,
711 ids(Half::Offset, Half::Layout),
712 ),
713 Op::StoreField => fields(
714 Operand::Word(REF),
715 Operand::Value,
716 NONE,
717 ids(Half::Offset, Half::Layout),
718 ),
719 Op::LoadElem => fields(
720 Operand::Value,
721 Operand::Word(REF),
722 Operand::Word(INT),
723 one(Half::Layout),
724 ),
725 Op::StoreElem => fields(
726 Operand::Word(REF),
727 Operand::Word(INT),
728 Operand::Value,
729 one(Half::Layout),
730 ),
731 Op::ByteAt => fields(
732 Operand::Word(INT),
733 Operand::Word(REF),
734 Operand::Word(INT),
735 Payload::Empty,
736 ),
737 Op::AllocBytes => fields(Operand::Word(REF), Operand::Word(INT), NONE, Payload::Empty),
741 Op::WriteByte => fields(
742 Operand::Word(REF),
743 Operand::Word(INT),
744 Operand::Word(INT),
745 Payload::Empty,
746 ),
747 Op::CopyBytes => fields(NONE, NONE, NONE, one(Half::Args)),
753 Op::FinishString => {
754 fields(Operand::Word(REF), Operand::Word(REF), NONE, Payload::Empty)
755 }
756 Op::AllocBuffer => fields(Operand::Word(REF), Operand::Word(INT), NONE, Payload::Empty),
761 Op::AppendByte => fields(Operand::Word(REF), Operand::Word(INT), NONE, Payload::Empty),
762 Op::AppendBytes => fields(NONE, NONE, NONE, one(Half::Args)),
768 Op::FinishBuffer => {
769 fields(Operand::Word(REF), Operand::Word(REF), NONE, Payload::Empty)
770 }
771 Op::Len => fields(Operand::Word(INT), Operand::Word(REF), NONE, Payload::Empty),
772 Op::LayoutOf => fields(Operand::Word(INT), Operand::Word(REF), NONE, Payload::Empty),
773 Op::AddrOfSlot => fields(
774 Operand::Word(ADDR),
775 Operand::Word(ANY),
776 NONE,
777 Payload::Empty,
778 ),
779 Op::AddrOfField => fields(
780 Operand::Word(ADDR),
781 Operand::Word(REF),
782 NONE,
783 one(Half::Offset),
784 ),
785 Op::AddrOfElem => fields(
786 Operand::Word(ADDR),
787 Operand::Word(REF),
788 Operand::Word(INT),
789 one(Half::Layout),
790 ),
791 Op::AddrOfPart => fields(
796 Operand::Word(ADDR),
797 Operand::Word(ADDR),
798 NONE,
799 one(Half::Offset),
800 ),
801 Op::Load => fields(Operand::Value, Operand::Word(ADDR), NONE, one(Half::Layout)),
802 Op::Store => fields(Operand::Word(ADDR), Operand::Value, NONE, one(Half::Layout)),
803 Op::Box => fields(Operand::Word(REF), Operand::Value, NONE, one(Half::Layout)),
804 Op::Unbox => fields(Operand::Value, Operand::Word(REF), NONE, one(Half::Layout)),
805 Op::ScopeEnter => fields(Operand::Word(SCOPE), NONE, NONE, one(Half::Str)),
806 Op::ScopeLeave => fields(
807 Operand::Word(SCOPE),
808 Operand::Word(BOOL),
809 Operand::Value,
810 one(Half::Layout),
811 ),
812 Op::ScopeCancel => fields(Operand::Word(SCOPE), NONE, NONE, Payload::Empty),
813 Op::Spawn => fields(
816 Operand::Word(TASK),
817 Operand::Word(SCOPE),
818 Operand::Word(REF),
819 one(Half::Layout),
820 ),
821 Op::Await => fields(Operand::Value, Operand::Word(TASK), NONE, one(Half::Layout)),
822 Op::Cancel => fields(Operand::Word(TASK), NONE, NONE, Payload::Empty),
823 Op::Settled => fields(Operand::Word(TASK), Operand::Value, NONE, one(Half::Layout)),
824 Op::SharedLock => fields(Operand::Word(REF), NONE, NONE, Payload::Empty),
825 Op::SharedUnlock => fields(Operand::Word(REF), NONE, NONE, Payload::Empty),
826 Op::Trap => fields(NONE, NONE, NONE, one(Half::Str)),
827 Op::AssertFailed => fields(Operand::Word(REF), NONE, NONE, Payload::Empty),
828 }
829 }
830}
831
832fn numeric(num: Num) -> &'static [Repr] {
834 match num {
835 Num::Int => INT,
836 Num::Float => FLOAT,
837 }
838}
839
840fn compared(on: Compare) -> &'static [Repr] {
842 match on {
843 Compare::Int => INT,
844 Compare::Float => FLOAT,
845 Compare::Bool => BOOL,
846 Compare::Str => REF,
847 Compare::Identity => REF,
850 Compare::Tag => TAG,
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857
858 #[test]
872 fn there_are_a_hundred_and_seventeen_opcodes() {
873 assert_eq!(Op::all().len(), 117);
874 assert_eq!(OPCODES, 117);
875 }
876
877 #[test]
881 fn an_opcode_number_is_its_position_in_the_generated_table() {
882 for (at, op) in Op::all().into_iter().enumerate() {
883 assert_eq!(op.number() as usize, at, "{op:?}");
884 }
885 }
886
887 #[test]
889 fn no_two_opcodes_share_a_number() {
890 let mut seen = vec![None; 256];
891 for op in Op::all() {
892 let at = op.number() as usize;
893 assert_eq!(seen[at], None, "{op:?} and {:?} share {at}", seen[at]);
894 seen[at] = Some(op);
895 }
896 }
897
898 #[test]
902 fn every_byte_either_names_one_opcode_or_none_at_all() {
903 for byte in 0u8..=255 {
904 match Op::from_number(byte) {
905 Some(op) => {
906 assert!((byte as usize) < OPCODES);
907 assert_eq!(op.number(), byte);
908 }
909 None => assert!((byte as usize) >= OPCODES, "{byte} names nothing"),
910 }
911 }
912 }
913
914 #[test]
918 fn the_arithmetic_families_are_the_cross_products_the_adr_gives() {
919 let all = Op::all();
920 let count = |f: fn(&Op) -> bool| all.iter().filter(|op| f(op)).count();
921 assert_eq!(count(|op| matches!(op, Op::Arith(_, _))), 10);
922 assert_eq!(count(|op| matches!(op, Op::Cmp(_, _))), 36);
923 assert_eq!(count(|op| matches!(op, Op::ArithImm(_))), 5);
924 assert_eq!(count(|op| matches!(op, Op::CmpImm(_))), 6);
925 assert_eq!(count(|op| matches!(op, Op::Neg(_))), 2);
926 assert_eq!(count(|op| matches!(op, Op::Convert(_))), 2);
927 assert_eq!(
928 count(|op| matches!(op, Op::AllocFixed | Op::AllocImm | Op::AllocSlot)),
929 3
930 );
931 }
932
933 #[test]
938 fn no_opcode_uses_a_field_the_format_does_not_have() {
939 for op in Op::all() {
940 let used = op.fields();
941 let live: Vec<bool> = used
945 .operands()
946 .iter()
947 .map(|one| *one != Operand::Unused)
948 .collect();
949 assert!(
950 live.windows(2).all(|pair| pair[0] || !pair[1]),
951 "{op:?} leaves a hole in a, b, c"
952 );
953 }
954 }
955
956 #[test]
959 fn a_value_operand_always_has_a_layout_in_the_payload() {
960 for op in Op::all() {
961 if !op.fields().operands().contains(&Operand::Value) {
962 continue;
963 }
964 let held = match op.fields().payload {
965 Payload::Halves(lo, hi) => [lo, hi],
966 other => panic!("{op:?} has a value operand and a {other:?} payload"),
967 };
968 assert!(
969 held.contains(&Half::Layout),
970 "{op:?} has a value operand and names no layout"
971 );
972 }
973 }
974}