cairo-lang-casm 2.17.0

Cairo assembly encoding.
Documentation
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};

use num_bigint::BigInt;
use pretty_assertions::assert_eq;
use test_case::test_case;

use crate::inline::CasmContext;
use crate::instructions::{
    AssertEqInstruction, Blake2sCompressInstruction, Instruction, InstructionBody,
};
use crate::{casm, cell_ref, res};

#[test_case(
    casm!(jmp abs 3;),
    0x8780017fff7fffu64,
    Some(3);
    "jmp abs 3;"
)]
#[test_case(
    casm!(jmp rel -5, ap++;),
    0x90780017fff7fffu64,
    Some(-5); "jmp rel -5, ap++;"
)]
#[test_case(
    casm!(call abs 3;),
    0x1084800180018000u64,
    Some(3);
    "call abs 3;"
)]
#[test_case(
    casm!(call rel (-5);),
    0x1104800180018000u64,
    Some(-5);
    "call rel (-5);"
)]
#[test_case(
    casm!(jmp rel 205 if [ap + 5] != 0;),
    0x20680017fff8005u64,
    Some(205);
    "jmp rel 205 if [ap + 5] != 0;"
)]
#[test_case(
    casm!(jmp rel 2 if [ap - 1] != 0, ap++;),
    0xa0680017fff7fffu64,
    Some(2);
    "jmp rel 2 if [ap + (-1)] != 0;"
)]
#[test_case(
    casm!([ap + 5] = 205;),
    0x400680017fff8005u64,
    Some(205);
    "[ap + 5] = 205;"
)]
#[test_case(
    casm!(ret;),
    0x208b7fff7fff7ffeu64,
    None;
    "ret;"
)]
#[test_case(
    casm!(ap += 205;),
    0x40780017fff7fffu64,
    Some(205);
    "ap += 205;"
)]
#[test_case(
    casm!([ap + 0] = [fp + -5], ap++;),
    0x480a7ffb7fff8000,
    None;
    "[ap + 0] = [fp + -5], ap++;"
)]
#[test_case(
    casm!([ap] = [ap - 3], ap++;),
    0x48127ffd7fff8000,
    None;
    "[ap + 0] = [ap + -3], ap++;"
)]
fn test_encode(mut casm: CasmContext, encoding: u64, immediate: Option<i16>) {
    let enc = BigInt::from(encoding);
    assert_eq!(
        casm.instructions.remove(0).assemble().encode(),
        if let Some(imm) = immediate { vec![enc, BigInt::from(imm)] } else { vec![enc] }
    );
}

#[test_case(
    casm! {
        [ap + 0] = 1, ap++;
        [ap + 0] = 1, ap++;
        [ap + 0] = 13, ap++;
        call rel 3;
        ret;
        jmp rel 5 if [fp + -3] != 0;
        [ap + 0] = [fp + -5], ap++;
        jmp rel 8;
        [ap + 0] = [fp + -4], ap++;
        [ap + 0] = [fp + -5] + [fp + -4], ap++;
        [fp + -3] = [ap + 0] + 1, ap++;
        call rel (-9);
        ret;
    },
    vec![
        0x480680017fff8000, 1, 0x480680017fff8000, 1, 0x480680017fff8000, 13, 0x1104800180018000,
        3, 0x208b7fff7fff7ffe, 0x20780017fff7ffd, 5, 0x480a7ffb7fff8000, 0x10780017fff7fff, 8,
        0x480a7ffc7fff8000, 0x482a7ffc7ffb8000, 0x4825800180007ffd, 1, 0x1104800180018000, -9,
        0x208b7fff7fff7ffe
    ];
    "fib(1, 1, 13)"
)]
fn test_encode_multiple(casm: CasmContext, expected: Vec<i128>) {
    let exp: Vec<BigInt> = expected.into_iter().map(BigInt::from).collect();
    let enc: Vec<BigInt> =
        casm.instructions.iter().flat_map(|inst| inst.assemble().encode()).collect();
    assert_eq!(enc, exp);
}

#[test_case(
    Instruction::new(
        InstructionBody::Blake2sCompress(Blake2sCompressInstruction {
            state: cell_ref!([fp + 0]),
            message: cell_ref!([ap + 0]),
            byte_count: cell_ref!([fp + -1]),
            finalize: false,
        }),
        true,
    ),
    0x8813_8000_8000_7fff,
    None;
    "blake2s[state=[fp + 0], message=[ap + 0], byte_count=[fp + -1], finalize=false] => [ap + 0];"
)]
#[test_case(
    Instruction::new(
        InstructionBody::Blake2sCompress(Blake2sCompressInstruction {
            state: cell_ref!([fp + 0]),
            message: cell_ref!([ap + 0]),
            byte_count: cell_ref!([fp + -1]),
            finalize: true,
        }),
        true,
    ),
    0x1_0813_8000_8000_7fff,
    None;
    "blake2s[state=[fp + 0], message=[ap + 0], byte_count=[fp + -1], finalize=true] => [ap + 0];"
)]
#[test_case(
    Instruction::new(
        InstructionBody::QM31AssertEq(AssertEqInstruction {
            a: cell_ref!([ap + 0]),
            b: res!([fp + -3]),
        }),
        true,
    ),
    0x1_c80a_7ffd_7fff_8000,
    None;
    "{QM31} [ap + 0] = [fp - 3], ap++;"
)]
#[test_case(
    Instruction::new(
        InstructionBody::QM31AssertEq(AssertEqInstruction {
            a: cell_ref!([ap + 2]),
            b: res!([fp + -1]),
        }),
        false,
    ),
    0x1_c00a_7fff_7fff_8002,
    None;
    "{QM31} [ap + 2] = [fp - 1];"
)]
#[test_case(
    Instruction::new(
        InstructionBody::QM31AssertEq(AssertEqInstruction {
            a: cell_ref!([ap + 2]),
            b: res!([fp + -1] + 8),
        }),
        false,
    ),
    0x1_c026_8001_7fff_8002,
    Some(BigInt::from(8));
    "{QM31} [ap + 2] = [fp - 1] + 8;"
)]
fn test_encode_instruction(instruction: Instruction, encoding: u128, immediate: Option<BigInt>) {
    let enc = BigInt::from(encoding);
    assert_eq!(
        instruction.assemble().encode(),
        if let Some(imm) = immediate { vec![enc, imm] } else { vec![enc] }
    );
}