parasol_cpu/proc/ops/
load.rs

1use crate::{
2    Byte, Ciphertext, Error, Memory, Ptr32, Register, Result,
3    proc::{DispatchIsaOp, fhe_processor::FheProcessor, ops::is_invalid_load_store_alignment},
4    tomasulo::{registers::RobEntryRef, tomasulo_processor::RetirementInfo},
5    unwrap_registers,
6};
7
8impl FheProcessor {
9    #[allow(clippy::too_many_arguments)]
10    /// Execute a load instruction.
11    pub fn load(
12        &mut self,
13        retirement_info: RetirementInfo<DispatchIsaOp>,
14        memory: &Memory,
15        src: RobEntryRef<Register>,
16        dst: RobEntryRef<Register>,
17        offset: i32,
18        width: u32,
19        instruction_id: usize,
20        pc: u32,
21    ) {
22        let load_impl = || -> Result<()> {
23            unwrap_registers!((mut dst) (src));
24
25            match src {
26                Register::Plaintext { val: ptr, width: _ } => {
27                    let num_bytes = width / 8;
28
29                    let base_addr = Ptr32::from(*ptr as u32).try_signed_offset(offset)?;
30
31                    if is_invalid_load_store_alignment(base_addr, num_bytes) {
32                        return Err(Error::UnalignedAccess(base_addr.0));
33                    }
34
35                    // Load the first byte and check its type. Then, ensure each subsequent byte
36                    // matches the same time.
37                    match memory.try_load(base_addr)? {
38                        Byte::Plaintext(val) => {
39                            let mut result = val as u128;
40
41                            for i in 1..num_bytes {
42                                // We already checked alignment, so pointer can't overflow.
43                                match memory.try_load(base_addr.try_offset(i).unwrap())? {
44                                    Byte::Plaintext(b) => {
45                                        result |= (b as u128) << (8 * i);
46                                    }
47                                    _ => {
48                                        return Err(Error::buffer_not_a_plaintext());
49                                    }
50                                }
51                            }
52
53                            *dst = Register::Plaintext { val: result, width };
54                        }
55                        Byte::Ciphertext(val) => {
56                            let mut result = val.clone();
57
58                            for i in 1..num_bytes {
59                                // We already checked alignment, so pointer can't overflow.
60                                match memory.try_load(base_addr.try_offset(i).unwrap())? {
61                                    Byte::Ciphertext(mut b) => {
62                                        result.append(&mut b);
63                                    }
64                                    _ => {
65                                        return Err(Error::buffer_not_a_ciphertext());
66                                    }
67                                }
68                            }
69
70                            *dst = Register::Ciphertext(Ciphertext::L1Glwe { data: result });
71                        }
72                    };
73
74                    FheProcessor::retire(&retirement_info, Ok(()));
75                }
76                _ => {
77                    return Err(Error::IllegalOperands {
78                        inst_id: instruction_id,
79                        pc,
80                    });
81                }
82            };
83
84            Ok(())
85        };
86
87        if let Err(e) = load_impl() {
88            FheProcessor::retire(&retirement_info, Err(e));
89        }
90    }
91}