bloqade_lanes_bytecode_core/bytecode/
opcode.rs

1use super::encode::DecodeError;
2
3/// Device code identifying which hardware subsystem an instruction targets.
4///
5/// The device code occupies the least significant byte of the packed opcode:
6/// `full_opcode = (instruction_code << 8) | device_code`
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[repr(u8)]
9pub enum DeviceCode {
10    Cpu = 0x00,
11    LaneConstants = 0x0F,
12    AtomArrangement = 0x10,
13    QuantumGate = 0x11,
14    Measurement = 0x12,
15    Array = 0x13,
16    DetectorObservable = 0x14,
17}
18
19impl DeviceCode {
20    /// Extract the device code from a raw opcode word.
21    pub fn from_opcode(opcode: u32) -> Result<Self, DecodeError> {
22        let device_byte = (opcode & 0xFF) as u8;
23        Self::from_byte(device_byte)
24    }
25
26    /// Parse a device code from a single byte.
27    pub fn from_byte(byte: u8) -> Result<Self, DecodeError> {
28        match byte {
29            0x00 => Ok(DeviceCode::Cpu),
30            0x0F => Ok(DeviceCode::LaneConstants),
31            0x10 => Ok(DeviceCode::AtomArrangement),
32            0x11 => Ok(DeviceCode::QuantumGate),
33            0x12 => Ok(DeviceCode::Measurement),
34            0x13 => Ok(DeviceCode::Array),
35            0x14 => Ok(DeviceCode::DetectorObservable),
36            _ => Err(DecodeError::UnknownDeviceCode(byte)),
37        }
38    }
39
40    /// Returns the hex device code value.
41    pub fn code(&self) -> u8 {
42        *self as u8
43    }
44}
45
46/// Cpu instruction codes (FLAIR-aligned).
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48#[repr(u8)]
49pub enum CpuInstCode {
50    ConstInt = 0x02,
51    ConstFloat = 0x03,
52    Dup = 0x04,
53    Pop = 0x05,
54    Swap = 0x06,
55    Return = 0x64,
56    Halt = 0xFF,
57}
58
59/// LaneConstants instruction codes.
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61#[repr(u8)]
62pub enum LaneConstInstCode {
63    ConstLoc = 0x00,
64    ConstLane = 0x01,
65    ConstZone = 0x02,
66}
67
68/// AtomArrangement instruction codes.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[repr(u8)]
71pub enum AtomArrangementInstCode {
72    InitialFill = 0x00,
73    Fill = 0x01,
74    Move = 0x02,
75}
76
77/// QuantumGate instruction codes.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[repr(u8)]
80pub enum QuantumGateInstCode {
81    LocalR = 0x00,
82    LocalRz = 0x01,
83    GlobalR = 0x02,
84    GlobalRz = 0x03,
85    CZ = 0x04,
86}
87
88/// Measurement instruction codes.
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[repr(u8)]
91pub enum MeasurementInstCode {
92    Measure = 0x00,
93    AwaitMeasure = 0x01,
94}
95
96/// Array instruction codes.
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98#[repr(u8)]
99pub enum ArrayInstCode {
100    NewArray = 0x00,
101    GetItem = 0x01,
102}
103
104/// DetectorObservable instruction codes.
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106#[repr(u8)]
107pub enum DetectorObservableInstCode {
108    SetDetector = 0x00,
109    SetObservable = 0x01,
110}
111
112/// Pack a device code and instruction code into a full opcode.
113pub const fn pack_opcode(device: u8, inst: u8) -> u16 {
114    ((inst as u16) << 8) | (device as u16)
115}
116
117/// Categorized opcode after decoding.
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum OpCodeCategory {
120    Cpu(CpuInstCode),
121    LaneConstants(LaneConstInstCode),
122    AtomArrangement(AtomArrangementInstCode),
123    QuantumGate(QuantumGateInstCode),
124    Measurement(MeasurementInstCode),
125    Array(ArrayInstCode),
126    DetectorObservable(DetectorObservableInstCode),
127}
128
129/// Decode a u32 opcode word into an OpCodeCategory.
130pub fn decode_opcode(word: u32) -> Result<OpCodeCategory, DecodeError> {
131    let device_byte = (word & 0xFF) as u8;
132    let inst_byte = ((word >> 8) & 0xFF) as u8;
133
134    // Verify upper 16 bits are zero
135    if word & 0xFFFF_0000 != 0 {
136        return Err(DecodeError::UnknownOpcode(word as u16));
137    }
138
139    let device = DeviceCode::from_byte(device_byte)?;
140
141    match device {
142        DeviceCode::Cpu => match inst_byte {
143            0x02 => Ok(OpCodeCategory::Cpu(CpuInstCode::ConstInt)),
144            0x03 => Ok(OpCodeCategory::Cpu(CpuInstCode::ConstFloat)),
145            0x04 => Ok(OpCodeCategory::Cpu(CpuInstCode::Dup)),
146            0x05 => Ok(OpCodeCategory::Cpu(CpuInstCode::Pop)),
147            0x06 => Ok(OpCodeCategory::Cpu(CpuInstCode::Swap)),
148            0x64 => Ok(OpCodeCategory::Cpu(CpuInstCode::Return)),
149            0xFF => Ok(OpCodeCategory::Cpu(CpuInstCode::Halt)),
150            _ => Err(DecodeError::UnknownOpcode(word as u16)),
151        },
152        DeviceCode::LaneConstants => match inst_byte {
153            0x00 => Ok(OpCodeCategory::LaneConstants(LaneConstInstCode::ConstLoc)),
154            0x01 => Ok(OpCodeCategory::LaneConstants(LaneConstInstCode::ConstLane)),
155            0x02 => Ok(OpCodeCategory::LaneConstants(LaneConstInstCode::ConstZone)),
156            _ => Err(DecodeError::UnknownOpcode(word as u16)),
157        },
158        DeviceCode::AtomArrangement => match inst_byte {
159            0x00 => Ok(OpCodeCategory::AtomArrangement(
160                AtomArrangementInstCode::InitialFill,
161            )),
162            0x01 => Ok(OpCodeCategory::AtomArrangement(
163                AtomArrangementInstCode::Fill,
164            )),
165            0x02 => Ok(OpCodeCategory::AtomArrangement(
166                AtomArrangementInstCode::Move,
167            )),
168            _ => Err(DecodeError::UnknownOpcode(word as u16)),
169        },
170        DeviceCode::QuantumGate => match inst_byte {
171            0x00 => Ok(OpCodeCategory::QuantumGate(QuantumGateInstCode::LocalR)),
172            0x01 => Ok(OpCodeCategory::QuantumGate(QuantumGateInstCode::LocalRz)),
173            0x02 => Ok(OpCodeCategory::QuantumGate(QuantumGateInstCode::GlobalR)),
174            0x03 => Ok(OpCodeCategory::QuantumGate(QuantumGateInstCode::GlobalRz)),
175            0x04 => Ok(OpCodeCategory::QuantumGate(QuantumGateInstCode::CZ)),
176            _ => Err(DecodeError::UnknownOpcode(word as u16)),
177        },
178        DeviceCode::Measurement => match inst_byte {
179            0x00 => Ok(OpCodeCategory::Measurement(MeasurementInstCode::Measure)),
180            0x01 => Ok(OpCodeCategory::Measurement(
181                MeasurementInstCode::AwaitMeasure,
182            )),
183            _ => Err(DecodeError::UnknownOpcode(word as u16)),
184        },
185        DeviceCode::Array => match inst_byte {
186            0x00 => Ok(OpCodeCategory::Array(ArrayInstCode::NewArray)),
187            0x01 => Ok(OpCodeCategory::Array(ArrayInstCode::GetItem)),
188            _ => Err(DecodeError::UnknownOpcode(word as u16)),
189        },
190        DeviceCode::DetectorObservable => match inst_byte {
191            0x00 => Ok(OpCodeCategory::DetectorObservable(
192                DetectorObservableInstCode::SetDetector,
193            )),
194            0x01 => Ok(OpCodeCategory::DetectorObservable(
195                DetectorObservableInstCode::SetObservable,
196            )),
197            _ => Err(DecodeError::UnknownOpcode(word as u16)),
198        },
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_pack_opcode() {
208        // const_int: device 0x00, inst 0x02 -> 0x0200
209        assert_eq!(pack_opcode(0x00, 0x02), 0x0200);
210        // const_loc: device 0x0F, inst 0x00 -> 0x000F
211        assert_eq!(pack_opcode(0x0F, 0x00), 0x000F);
212        // initial_fill: device 0x10, inst 0x00 -> 0x0010
213        assert_eq!(pack_opcode(0x10, 0x00), 0x0010);
214        // local_r: device 0x11, inst 0x00 -> 0x0011
215        assert_eq!(pack_opcode(0x11, 0x00), 0x0011);
216        // halt: device 0x00, inst 0xFF -> 0xFF00
217        assert_eq!(pack_opcode(0x00, 0xFF), 0xFF00);
218    }
219
220    #[test]
221    fn test_all_opcodes_decode() {
222        let cases: Vec<(u32, OpCodeCategory)> = vec![
223            (0x0200, OpCodeCategory::Cpu(CpuInstCode::ConstInt)),
224            (0x0300, OpCodeCategory::Cpu(CpuInstCode::ConstFloat)),
225            (0x0400, OpCodeCategory::Cpu(CpuInstCode::Dup)),
226            (0x0500, OpCodeCategory::Cpu(CpuInstCode::Pop)),
227            (0x0600, OpCodeCategory::Cpu(CpuInstCode::Swap)),
228            (0x6400, OpCodeCategory::Cpu(CpuInstCode::Return)),
229            (0xFF00, OpCodeCategory::Cpu(CpuInstCode::Halt)),
230            (
231                0x000F,
232                OpCodeCategory::LaneConstants(LaneConstInstCode::ConstLoc),
233            ),
234            (
235                0x010F,
236                OpCodeCategory::LaneConstants(LaneConstInstCode::ConstLane),
237            ),
238            (
239                0x020F,
240                OpCodeCategory::LaneConstants(LaneConstInstCode::ConstZone),
241            ),
242            (
243                0x0010,
244                OpCodeCategory::AtomArrangement(AtomArrangementInstCode::InitialFill),
245            ),
246            (
247                0x0110,
248                OpCodeCategory::AtomArrangement(AtomArrangementInstCode::Fill),
249            ),
250            (
251                0x0210,
252                OpCodeCategory::AtomArrangement(AtomArrangementInstCode::Move),
253            ),
254            (
255                0x0011,
256                OpCodeCategory::QuantumGate(QuantumGateInstCode::LocalR),
257            ),
258            (
259                0x0111,
260                OpCodeCategory::QuantumGate(QuantumGateInstCode::LocalRz),
261            ),
262            (
263                0x0211,
264                OpCodeCategory::QuantumGate(QuantumGateInstCode::GlobalR),
265            ),
266            (
267                0x0311,
268                OpCodeCategory::QuantumGate(QuantumGateInstCode::GlobalRz),
269            ),
270            (0x0411, OpCodeCategory::QuantumGate(QuantumGateInstCode::CZ)),
271            (
272                0x0012,
273                OpCodeCategory::Measurement(MeasurementInstCode::Measure),
274            ),
275            (
276                0x0112,
277                OpCodeCategory::Measurement(MeasurementInstCode::AwaitMeasure),
278            ),
279            (0x0013, OpCodeCategory::Array(ArrayInstCode::NewArray)),
280            (0x0113, OpCodeCategory::Array(ArrayInstCode::GetItem)),
281            (
282                0x0014,
283                OpCodeCategory::DetectorObservable(DetectorObservableInstCode::SetDetector),
284            ),
285            (
286                0x0114,
287                OpCodeCategory::DetectorObservable(DetectorObservableInstCode::SetObservable),
288            ),
289        ];
290
291        for (opcode, expected) in cases {
292            assert_eq!(
293                decode_opcode(opcode).unwrap(),
294                expected,
295                "failed for 0x{:04x}",
296                opcode
297            );
298        }
299    }
300
301    #[test]
302    fn test_unknown_opcode() {
303        // Unknown device code
304        assert!(decode_opcode(0x0001).is_err()); // device 0x01 is reserved
305        // Unknown instruction within known device
306        assert!(decode_opcode(0x0100).is_err()); // Cpu device, inst 0x01 not assigned
307        // Upper bits set
308        assert!(decode_opcode(0x00010200).is_err());
309    }
310
311    #[test]
312    fn test_device_code_from_opcode() {
313        assert_eq!(DeviceCode::from_opcode(0x0200).unwrap(), DeviceCode::Cpu);
314        assert_eq!(
315            DeviceCode::from_opcode(0x000F).unwrap(),
316            DeviceCode::LaneConstants
317        );
318        assert_eq!(
319            DeviceCode::from_opcode(0x0010).unwrap(),
320            DeviceCode::AtomArrangement
321        );
322    }
323
324    #[test]
325    fn test_device_code_values() {
326        assert_eq!(DeviceCode::Cpu.code(), 0x00);
327        assert_eq!(DeviceCode::LaneConstants.code(), 0x0F);
328        assert_eq!(DeviceCode::AtomArrangement.code(), 0x10);
329        assert_eq!(DeviceCode::QuantumGate.code(), 0x11);
330        assert_eq!(DeviceCode::Measurement.code(), 0x12);
331        assert_eq!(DeviceCode::Array.code(), 0x13);
332        assert_eq!(DeviceCode::DetectorObservable.code(), 0x14);
333    }
334}