pub(crate) fn eval_ecall<AB: SP1AirBuilder>(
        &self,
        builder: &mut AB,
        local: &SyscallInstrColumns<AB::Var>,
    ) {
        // The syscall code is the read-in value of op_a at the start of the instruction.
        let syscall_code = local.op_a_access.prev_value();

        // We interpret the syscall_code as little-endian bytes and interpret each byte as a u8
        // with different information.
        let syscall_id = syscall_code[0];
        let send_to_table = syscall_code[1];

        // SAFETY: Assert that for non real row, the send_to_table value is 0 so that the
        // `send_syscall` interaction is not activated.
        builder.when(AB::Expr::one() - local.is_real).assert_zero(send_to_table);

        builder.send_syscall(
            local.shard,
            local.clk,
            syscall_id,
            local.op_b_value.reduce::<AB>(),
            local.op_c_value.reduce::<AB>(),
            send_to_table,
            InteractionScope::Local,
        );

        // Compute whether this ecall is ENTER_UNCONSTRAINED.
        let is_enter_unconstrained = {
            IsZeroOperation::<AB::F>::eval(
                builder,
                syscall_id -
                    AB::Expr::from_canonical_u32(SyscallCode::ENTER_UNCONSTRAINED.syscall_id()),
                local.is_enter_unconstrained,
                local.is_real.into(),
            );
            local.is_enter_unconstrained.result
        };

        // Compute whether this ecall is HINT_LEN.
        let is_hint_len = {
            IsZeroOperation::<AB::F>::eval(
                builder,
                syscall_id - AB::Expr::from_canonical_u32(SyscallCode::HINT_LEN.syscall_id()),
                local.is_hint_len,
                local.is_real.into(),
            );
            local.is_hint_len.result
        };

        // `op_a_val` is constrained.

        // When syscall_id is ENTER_UNCONSTRAINED, the new value of op_a should be 0.
        let zero_word = Word::<AB::F>::from(0);
        builder
            .when(local.is_real)
            .when(is_enter_unconstrained)
            .assert_word_eq(*local.op_a_access.value(), zero_word);

        // When the syscall is not one of ENTER_UNCONSTRAINED or HINT_LEN, op_a shouldn't change.
        builder
            .when(local.is_real)
            .when_not(is_enter_unconstrained + is_hint_len)
            .assert_word_eq(*local.op_a_access.value(), *local.op_a_access.prev_value());

        // SAFETY: This leaves the case where syscall is `HINT_LEN`.
        // In this case, `op_a`'s value can be arbitrary, but it still must be a valid word if
        // `is_real = 1`. This is due to `op_a_val` being connected to the CpuChip.
        // In the CpuChip, `op_a_val` is constrained to be a valid word via `eval_registers`.
        // As this is a syscall for HINT, the value itself being arbitrary is fine, as long as it is
        // a valid word.

        // Verify value of ecall_range_check_operand column.
        // SAFETY: If `is_real = 0`, then `ecall_range_check_operand = 0`.
        // If `is_real = 1`, then `is_halt_check` and `is_commit_deferred_proofs` are constrained.
        // The two results will both be boolean due to `IsZeroOperation`, and both cannot be `1` at
        // the same time. Both of them being `1` will require `syscall_id` being `HALT` and
        // `COMMIT_DEFERRED_PROOFS` at the same time. This implies that if `is_real = 1`,
        // `ecall_range_check_operand` will be correct, and boolean.
        builder.assert_eq(
            local.ecall_range_check_operand,
            local.is_real * (local.is_halt_check.result + local.is_commit_deferred_proofs.result),
        );

        // Babybear range check the operand_to_check word.
        // SAFETY: `ecall_range_check_operand` is boolean, and no interactions can be made in
        // padding rows. `operand_to_check` is already known to be a valid word, as it is
        // either
        // - `op_b_val` in the case of `HALT`
        // - `op_c_val` in the case of `COMMIT_DEFERRED_PROOFS`
        BabyBearWordRangeChecker::<AB::F>::range_check::<AB>(
            builder,
            local.operand_to_check,
            local.operand_range_check_cols,
            local.ecall_range_check_operand.into(),
        );
    }
Constraints related to the ECALL opcode.

This method will do the following:
1. Send the syscall to the precompile table, if needed.
2. Check for valid op_a values.
crates/core/machine/src/syscall/instructions/air.rs