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