1use std::fmt;
2
3use super::instruction::{
4 ArrayInstruction, AtomArrangementInstruction, CpuInstruction, DetectorObservableInstruction,
5 Instruction, LaneConstInstruction, MeasurementInstruction, QuantumGateInstruction,
6};
7use super::opcode::{OpCodeCategory, decode_opcode};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum DecodeError {
11 UnknownOpcode(u16),
12 UnknownDeviceCode(u8),
13 InvalidOperand { opcode: u16, message: String },
14}
15
16impl fmt::Display for DecodeError {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 DecodeError::UnknownOpcode(op) => write!(f, "unknown opcode: 0x{:04x}", op),
20 DecodeError::UnknownDeviceCode(dc) => {
21 write!(f, "unknown device code: 0x{:02x}", dc)
22 }
23 DecodeError::InvalidOperand { opcode, message } => {
24 write!(
25 f,
26 "invalid operand for opcode 0x{:04x}: {}",
27 opcode, message
28 )
29 }
30 }
31 }
32}
33
34impl std::error::Error for DecodeError {}
35
36impl Instruction {
37 pub fn encode(&self) -> (u32, u32, u32, u32) {
41 let opcode = self.opcode() as u32;
42
43 match self {
44 Instruction::Cpu(CpuInstruction::ConstFloat(f)) => {
46 let bits = f.to_bits();
47 let data0 = bits as u32;
48 let data1 = (bits >> 32) as u32;
49 (opcode, data0, data1, 0)
50 }
51 Instruction::Cpu(CpuInstruction::ConstInt(v)) => {
53 let bits = *v as u64;
54 let data0 = bits as u32;
55 let data1 = (bits >> 32) as u32;
56 (opcode, data0, data1, 0)
57 }
58 Instruction::LaneConst(LaneConstInstruction::ConstLoc(v)) => (opcode, *v, 0, 0),
60 Instruction::LaneConst(LaneConstInstruction::ConstLane(d0, d1)) => {
62 (opcode, *d0, *d1, 0)
63 }
64 Instruction::LaneConst(LaneConstInstruction::ConstZone(v)) => (opcode, *v, 0, 0),
66
67 Instruction::Cpu(CpuInstruction::Pop)
69 | Instruction::Cpu(CpuInstruction::Dup)
70 | Instruction::Cpu(CpuInstruction::Swap)
71 | Instruction::Cpu(CpuInstruction::Return)
72 | Instruction::Cpu(CpuInstruction::Halt) => (opcode, 0, 0, 0),
73
74 Instruction::Array(ArrayInstruction::NewArray {
76 type_tag,
77 dim0,
78 dim1,
79 }) => {
80 let data0 = ((*type_tag as u32) << 24) | (*dim0 as u32);
81 let data1 = *dim1 as u32;
82 (opcode, data0, data1, 0)
83 }
84
85 Instruction::Array(ArrayInstruction::GetItem { ndims }) => {
87 (opcode, *ndims as u32, 0, 0)
88 }
89
90 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity })
92 | Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity })
93 | Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity })
94 | Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity })
95 | Instruction::QuantumGate(QuantumGateInstruction::LocalRz { arity })
96 | Instruction::Measurement(MeasurementInstruction::Measure { arity }) => {
97 (opcode, *arity, 0, 0)
98 }
99
100 Instruction::QuantumGate(QuantumGateInstruction::GlobalR)
102 | Instruction::QuantumGate(QuantumGateInstruction::GlobalRz)
103 | Instruction::QuantumGate(QuantumGateInstruction::CZ)
104 | Instruction::Measurement(MeasurementInstruction::AwaitMeasure)
105 | Instruction::DetectorObservable(DetectorObservableInstruction::SetDetector)
106 | Instruction::DetectorObservable(DetectorObservableInstruction::SetObservable) => {
107 (opcode, 0, 0, 0)
108 }
109 }
110 }
111
112 pub fn decode(word: u32, data0: u32, data1: u32, _data2: u32) -> Result<Self, DecodeError> {
114 let category = decode_opcode(word)?;
115
116 match category {
117 OpCodeCategory::Cpu(cpu_op) => {
118 use super::opcode::CpuInstCode::*;
119 match cpu_op {
120 ConstFloat => {
121 let bits = (data0 as u64) | ((data1 as u64) << 32);
122 Ok(Instruction::Cpu(CpuInstruction::ConstFloat(
123 f64::from_bits(bits),
124 )))
125 }
126 ConstInt => {
127 let bits = (data0 as u64) | ((data1 as u64) << 32);
128 Ok(Instruction::Cpu(CpuInstruction::ConstInt(bits as i64)))
129 }
130 Pop => Ok(Instruction::Cpu(CpuInstruction::Pop)),
131 Dup => Ok(Instruction::Cpu(CpuInstruction::Dup)),
132 Swap => Ok(Instruction::Cpu(CpuInstruction::Swap)),
133 Return => Ok(Instruction::Cpu(CpuInstruction::Return)),
134 Halt => Ok(Instruction::Cpu(CpuInstruction::Halt)),
135 }
136 }
137 OpCodeCategory::LaneConstants(lc_op) => {
138 use super::opcode::LaneConstInstCode::*;
139 match lc_op {
140 ConstLoc => Ok(Instruction::LaneConst(LaneConstInstruction::ConstLoc(
141 data0,
142 ))),
143 ConstLane => Ok(Instruction::LaneConst(LaneConstInstruction::ConstLane(
144 data0, data1,
145 ))),
146 ConstZone => Ok(Instruction::LaneConst(LaneConstInstruction::ConstZone(
147 data0,
148 ))),
149 }
150 }
151 OpCodeCategory::AtomArrangement(aa_op) => {
152 use super::opcode::AtomArrangementInstCode::*;
153 let arity = data0;
154 match aa_op {
155 InitialFill => Ok(Instruction::AtomArrangement(
156 AtomArrangementInstruction::InitialFill { arity },
157 )),
158 Fill => Ok(Instruction::AtomArrangement(
159 AtomArrangementInstruction::Fill { arity },
160 )),
161 Move => Ok(Instruction::AtomArrangement(
162 AtomArrangementInstruction::Move { arity },
163 )),
164 }
165 }
166 OpCodeCategory::QuantumGate(qg_op) => {
167 use super::opcode::QuantumGateInstCode::*;
168 match qg_op {
169 LocalR => Ok(Instruction::QuantumGate(QuantumGateInstruction::LocalR {
170 arity: data0,
171 })),
172 LocalRz => Ok(Instruction::QuantumGate(QuantumGateInstruction::LocalRz {
173 arity: data0,
174 })),
175 GlobalR => Ok(Instruction::QuantumGate(QuantumGateInstruction::GlobalR)),
176 GlobalRz => Ok(Instruction::QuantumGate(QuantumGateInstruction::GlobalRz)),
177 CZ => Ok(Instruction::QuantumGate(QuantumGateInstruction::CZ)),
178 }
179 }
180 OpCodeCategory::Measurement(m_op) => {
181 use super::opcode::MeasurementInstCode::*;
182 match m_op {
183 Measure => Ok(Instruction::Measurement(MeasurementInstruction::Measure {
184 arity: data0,
185 })),
186 AwaitMeasure => Ok(Instruction::Measurement(
187 MeasurementInstruction::AwaitMeasure,
188 )),
189 }
190 }
191 OpCodeCategory::Array(arr_op) => {
192 use super::opcode::ArrayInstCode::*;
193 match arr_op {
194 NewArray => {
195 let type_tag = ((data0 >> 24) & 0xFF) as u8;
196 let dim0 = (data0 & 0xFFFF) as u16;
197 let dim1 = (data1 & 0xFFFF) as u16;
198 Ok(Instruction::Array(ArrayInstruction::NewArray {
199 type_tag,
200 dim0,
201 dim1,
202 }))
203 }
204 GetItem => Ok(Instruction::Array(ArrayInstruction::GetItem {
205 ndims: data0 as u16,
206 })),
207 }
208 }
209 OpCodeCategory::DetectorObservable(dob_op) => {
210 use super::opcode::DetectorObservableInstCode::*;
211 match dob_op {
212 SetDetector => Ok(Instruction::DetectorObservable(
213 DetectorObservableInstruction::SetDetector,
214 )),
215 SetObservable => Ok(Instruction::DetectorObservable(
216 DetectorObservableInstruction::SetObservable,
217 )),
218 }
219 }
220 }
221 }
222
223 pub fn to_bytes(&self) -> [u8; 16] {
225 let (word, data0, data1, data2) = self.encode();
226 let mut bytes = [0u8; 16];
227 bytes[0..4].copy_from_slice(&word.to_le_bytes());
228 bytes[4..8].copy_from_slice(&data0.to_le_bytes());
229 bytes[8..12].copy_from_slice(&data1.to_le_bytes());
230 bytes[12..16].copy_from_slice(&data2.to_le_bytes());
231 bytes
232 }
233
234 pub fn from_bytes(bytes: &[u8; 16]) -> Result<Self, DecodeError> {
236 let word = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
237 let data0 = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
238 let data1 = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
239 let data2 = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
240 Self::decode(word, data0, data1, data2)
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 fn round_trip(instr: Instruction) {
249 let (word, data0, data1, data2) = instr.encode();
250 let decoded = Instruction::decode(word, data0, data1, data2).unwrap();
251 assert_eq!(instr, decoded, "round-trip failed for {:?}", instr);
252 }
253
254 fn round_trip_bytes(instr: Instruction) {
255 let bytes = instr.to_bytes();
256 let decoded = Instruction::from_bytes(&bytes).unwrap();
257 assert_eq!(instr, decoded, "bytes round-trip failed for {:?}", instr);
258 }
259
260 #[test]
261 fn test_round_trip_all_instructions() {
262 let instructions = vec![
263 Instruction::Cpu(CpuInstruction::ConstFloat(1.5)),
264 Instruction::Cpu(CpuInstruction::ConstInt(-42)),
265 Instruction::Cpu(CpuInstruction::Pop),
266 Instruction::Cpu(CpuInstruction::Dup),
267 Instruction::Cpu(CpuInstruction::Swap),
268 Instruction::Cpu(CpuInstruction::Return),
269 Instruction::Cpu(CpuInstruction::Halt),
270 Instruction::LaneConst(LaneConstInstruction::ConstLoc(0x1234)),
271 Instruction::LaneConst(LaneConstInstruction::ConstLane(0x00125678, 0xC0009ABC)),
272 Instruction::LaneConst(LaneConstInstruction::ConstZone(0x9ABC)),
273 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 4 }),
274 Instruction::AtomArrangement(AtomArrangementInstruction::Fill { arity: 3 }),
275 Instruction::AtomArrangement(AtomArrangementInstruction::Move { arity: 2 }),
276 Instruction::QuantumGate(QuantumGateInstruction::LocalR { arity: 1 }),
277 Instruction::QuantumGate(QuantumGateInstruction::LocalRz { arity: 5 }),
278 Instruction::QuantumGate(QuantumGateInstruction::GlobalR),
279 Instruction::QuantumGate(QuantumGateInstruction::GlobalRz),
280 Instruction::QuantumGate(QuantumGateInstruction::CZ),
281 Instruction::Measurement(MeasurementInstruction::Measure { arity: 10 }),
282 Instruction::Measurement(MeasurementInstruction::AwaitMeasure),
283 Instruction::Array(ArrayInstruction::NewArray {
284 type_tag: 0x2,
285 dim0: 100,
286 dim1: 200,
287 }),
288 Instruction::Array(ArrayInstruction::GetItem { ndims: 2 }),
289 Instruction::DetectorObservable(DetectorObservableInstruction::SetDetector),
290 Instruction::DetectorObservable(DetectorObservableInstruction::SetObservable),
291 ];
292
293 for instr in &instructions {
294 round_trip(*instr);
295 round_trip_bytes(*instr);
296 }
297 }
298
299 #[test]
300 fn test_const_float_bit_pattern() {
301 let instr = Instruction::Cpu(CpuInstruction::ConstFloat(1.0));
302 let (word, data0, data1, _data2) = instr.encode();
303 assert_eq!(word, 0x0300); let bits = 1.0_f64.to_bits();
305 assert_eq!(data0, bits as u32);
306 assert_eq!(data1, (bits >> 32) as u32);
307 }
308
309 #[test]
310 fn test_const_int_bit_pattern() {
311 let instr = Instruction::Cpu(CpuInstruction::ConstInt(42));
312 let (word, data0, data1, _data2) = instr.encode();
313 assert_eq!(word, 0x0200); assert_eq!(data0, 42);
315 assert_eq!(data1, 0);
316 }
317
318 #[test]
319 fn test_const_int_negative() {
320 let instr = Instruction::Cpu(CpuInstruction::ConstInt(-1));
321 let (word, data0, data1, _data2) = instr.encode();
322 assert_eq!(word, 0x0200);
323 assert_eq!(data0, 0xFFFF_FFFF); assert_eq!(data1, 0xFFFF_FFFF); let decoded = Instruction::decode(word, data0, data1, 0).unwrap();
326 assert_eq!(decoded, instr);
327 }
328
329 #[test]
330 fn test_new_array_bit_pattern() {
331 let instr = Instruction::Array(ArrayInstruction::NewArray {
332 type_tag: 0x2,
333 dim0: 10,
334 dim1: 20,
335 });
336 let (word, data0, data1, _data2) = instr.encode();
337 assert_eq!(word, 0x0013); assert_eq!((data0 >> 24) & 0xFF, 0x2); assert_eq!(data0 & 0xFFFF, 10); assert_eq!(data1 & 0xFFFF, 20); }
342
343 #[test]
344 fn test_unknown_opcode_error() {
345 let result = Instruction::decode(0x0001, 0, 0, 0);
347 assert!(matches!(result, Err(DecodeError::UnknownDeviceCode(0x01))));
348 }
349
350 #[test]
351 fn test_to_bytes_little_endian() {
352 let instr = Instruction::Cpu(CpuInstruction::Halt);
353 let bytes = instr.to_bytes();
354 assert_eq!(bytes.len(), 16);
355 assert_eq!(bytes[0], 0x00); assert_eq!(bytes[1], 0xFF); assert_eq!(bytes[2], 0);
359 assert_eq!(bytes[3], 0);
360 assert_eq!(&bytes[4..], &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
361 }
362
363 #[test]
364 fn test_to_bytes_device_instruction() {
365 let instr =
366 Instruction::AtomArrangement(AtomArrangementInstruction::InitialFill { arity: 3 });
367 let bytes = instr.to_bytes();
368 assert_eq!(bytes.len(), 16);
369 assert_eq!(bytes[0], 0x10); assert_eq!(bytes[1], 0x00); assert_eq!(bytes[2], 0);
373 assert_eq!(bytes[3], 0);
374 assert_eq!(bytes[4], 3);
376 assert_eq!(bytes[5], 0);
377 }
378}