cairo_lang_casm/
encoder.rs

1#[cfg(not(feature = "std"))]
2use alloc::{vec, vec::Vec};
3
4use num_bigint::BigInt;
5
6use crate::assembler::{
7    ApUpdate, FpUpdate, InstructionRepr, Op1Addr, Opcode, OpcodeExtension, PcUpdate, Res,
8};
9use crate::operand::Register;
10
11#[cfg(test)]
12#[path = "encoder_test.rs"]
13mod test;
14
15const OFFSET_BITS: u32 = 16;
16
17const DST_REG_BIT: i32 = 0;
18const OP0_REG_BIT: i32 = 1;
19const OP1_IMM_BIT: i32 = 2;
20const OP1_FP_BIT: i32 = 3;
21const OP1_AP_BIT: i32 = 4;
22const RES_ADD_BIT: i32 = 5;
23const RES_MUL_BIT: i32 = 6;
24const PC_JUMP_ABS_BIT: i32 = 7;
25const PC_JUMP_REL_BIT: i32 = 8;
26const PC_JNZ_BIT: i32 = 9;
27const AP_ADD_BIT: i32 = 10;
28const AP_ADD1_BIT: i32 = 11;
29const OPCODE_CALL_BIT: i32 = 12;
30const OPCODE_RET_BIT: i32 = 13;
31const OPCODE_ASSERT_EQ_BIT: i32 = 14;
32const OPCODE_EXT_OFFSET: u64 = 63;
33
34impl InstructionRepr {
35    pub fn encode(&self) -> Vec<BigInt> {
36        // Convert the offsets from possibly negative numbers in the range [-2^15, 2^15)
37        // to positive numbers in the range [0, 2^16) centered around 2^15.
38        let off0_enc: u64 = ((self.off0 as i32) + (1 << (OFFSET_BITS - 1))) as u64;
39        let off1_enc: u64 = ((self.off1 as i32) + (1 << (OFFSET_BITS - 1))) as u64;
40        let off2_enc: u64 = ((self.off2 as i32) + (1 << (OFFSET_BITS - 1))) as u64;
41
42        let mut flags = 0;
43        if self.dst_register == Register::FP {
44            flags |= 1 << DST_REG_BIT;
45        }
46        if self.op0_register == Register::FP {
47            flags |= 1 << OP0_REG_BIT;
48        }
49        assert_eq!(
50            self.imm.is_some(),
51            self.op1_addr == Op1Addr::Imm,
52            "Immediate must appear iff op1_addr is Op1Addr.IMM"
53        );
54        flags |= match self.op1_addr {
55            Op1Addr::Imm => 1 << OP1_IMM_BIT,
56            Op1Addr::AP => 1 << OP1_AP_BIT,
57            Op1Addr::FP => 1 << OP1_FP_BIT,
58            Op1Addr::Op0 => 0,
59        };
60
61        flags |= match self.res {
62            Res::Add => 1 << RES_ADD_BIT,
63            Res::Mul => 1 << RES_MUL_BIT,
64            Res::Op1 => 0,
65            Res::Unconstrained => 0,
66        };
67
68        assert_eq!(
69            self.res == Res::Unconstrained,
70            self.pc_update == PcUpdate::Jnz,
71            "res must be Unconstrained iff pc_update is Jnz"
72        );
73
74        flags |= match self.pc_update {
75            PcUpdate::Jump => 1 << PC_JUMP_ABS_BIT,
76            PcUpdate::JumpRel => 1 << PC_JUMP_REL_BIT,
77            PcUpdate::Jnz => 1 << PC_JNZ_BIT,
78            PcUpdate::Regular => 0,
79        };
80
81        assert_eq!(
82            self.ap_update == ApUpdate::Add2,
83            self.opcode == Opcode::Call,
84            "ap_update is Add2 iff opcode is Call"
85        );
86
87        flags |= match self.ap_update {
88            ApUpdate::Add => 1 << AP_ADD_BIT,
89            ApUpdate::Add1 => 1 << AP_ADD1_BIT,
90            ApUpdate::Add2 => 0,
91            ApUpdate::Regular => 0,
92        };
93
94        assert_eq!(
95            self.fp_update,
96            match self.opcode {
97                Opcode::Nop => FpUpdate::Regular,
98                Opcode::Call => FpUpdate::ApPlus2,
99                Opcode::Ret => FpUpdate::Dst,
100                Opcode::AssertEq => FpUpdate::Regular,
101            },
102            "fp_update {:?} does not match opcode {:?}.",
103            self.fp_update,
104            self.opcode
105        );
106
107        flags |= match self.opcode {
108            Opcode::Call => 1 << OPCODE_CALL_BIT,
109            Opcode::Ret => 1 << OPCODE_RET_BIT,
110            Opcode::AssertEq => 1 << OPCODE_ASSERT_EQ_BIT,
111            Opcode::Nop => 0,
112        };
113
114        let mut encoding: u64 = flags << (3 * OFFSET_BITS);
115        encoding |= off2_enc << (2 * OFFSET_BITS);
116        encoding |= off1_enc << (OFFSET_BITS);
117        encoding |= off0_enc;
118
119        let mut bigint_encoding = BigInt::from(encoding);
120        match self.opcode_extension {
121            OpcodeExtension::Stone => {}
122            OpcodeExtension::Blake2s => bigint_encoding |= BigInt::from(1) << OPCODE_EXT_OFFSET,
123            OpcodeExtension::Blake2sFinalize => {
124                bigint_encoding |= BigInt::from(2) << OPCODE_EXT_OFFSET
125            }
126            OpcodeExtension::QM31 => bigint_encoding |= BigInt::from(3) << OPCODE_EXT_OFFSET,
127        };
128        if let Some(imm) = self.imm.clone() {
129            vec![bigint_encoding, imm]
130        } else {
131            vec![bigint_encoding]
132        }
133    }
134}