sbpf_common/
instruction.rs

1use {
2    crate::{
3        errors::SBPFError,
4        inst_handler::{OPCODE_TO_HANDLER, OPCODE_TO_TYPE},
5        inst_param::{Number, Register},
6        opcode::{Opcode, OperationType},
7    },
8    core::ops::Range,
9    either::Either,
10    serde::{Deserialize, Serialize},
11};
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct Instruction {
15    pub opcode: Opcode,
16    pub dst: Option<Register>,
17    pub src: Option<Register>,
18    pub off: Option<Either<String, i16>>,
19    pub imm: Option<Either<String, Number>>,
20    pub span: Range<usize>,
21}
22
23impl Instruction {
24    pub fn get_size(&self) -> u64 {
25        match self.opcode {
26            Opcode::Lddw => 16,
27            _ => 8,
28        }
29    }
30
31    fn get_opcode_type(&self) -> OperationType {
32        *OPCODE_TO_TYPE.get(&self.opcode).unwrap()
33    }
34
35    pub fn is_jump(&self) -> bool {
36        matches!(
37            self.get_opcode_type(),
38            OperationType::Jump | OperationType::JumpImmediate | OperationType::JumpRegister
39        )
40    }
41
42    pub fn needs_relocation(&self) -> bool {
43        match self.opcode {
44            Opcode::Call | Opcode::Lddw => {
45                matches!(&self.imm, Some(Either::Left(_identifier)))
46            }
47            _ => false,
48        }
49    }
50
51    // only used for be/le
52    pub fn op_imm_bits(&self) -> Result<String, SBPFError> {
53        match &self.imm {
54            Some(Either::Right(Number::Int(imm))) => match *imm {
55                16 => Ok(format!("{}16", self.opcode)),
56                32 => Ok(format!("{}32", self.opcode)),
57                64 => Ok(format!("{}64", self.opcode)),
58                _ => Err(SBPFError::BytecodeError {
59                    error: format!(
60                        "Invalid immediate value: {:?} for opcode: {:?}",
61                        self.imm, self.opcode
62                    ),
63                    span: self.span.clone(),
64                    custom_label: None,
65                }),
66            },
67            _ => Err(SBPFError::BytecodeError {
68                error: format!("Expected immediate value for opcode: {:?}", self.opcode),
69                span: self.span.clone(),
70                custom_label: None,
71            }),
72        }
73    }
74
75    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SBPFError> {
76        let opcode: Opcode = bytes[0].try_into()?;
77        if let Some(handler) = OPCODE_TO_HANDLER.get(&opcode) {
78            (handler.decode)(bytes)
79        } else {
80            Err(SBPFError::BytecodeError {
81                error: format!("no decode handler for opcode {}", opcode),
82                span: 0..1,
83                custom_label: Some("Invalid opcode".to_string()),
84            })
85        }
86    }
87
88    pub fn from_bytes_sbpf_v2(bytes: &[u8]) -> Result<Self, SBPFError> {
89        // Preprocess the opcode byte for SBPF v2 (e_flags == 0x02)
90        let mut processed_bytes = bytes.to_vec();
91
92        match processed_bytes[0] {
93            // New opcodes in v2 that map to existing instructions
94            0x8C => processed_bytes[0] = 0x61, // v2: 0x8C -> ldxw dst, [src + off]
95            0x8F => processed_bytes[0] = 0x63, // v2: 0x8F -> stxw [dst + off], src
96            // Repurposed opcodes in v2
97            0x2C => processed_bytes[0] = 0x71, // v2: mul32 dst, src -> ldxb dst, [src + off]
98            0x3C => processed_bytes[0] = 0x69, // v2: div32 dst, src -> ldxh dst, [src + off]
99            0x9C => processed_bytes[0] = 0x79, // v2: mod32 dst, src -> ldxdw dst, [src + off]
100            0x27 => processed_bytes[0] = 0x72, // v2: mul64 dst, imm -> stb [dst + off], imm
101            0x2F => processed_bytes[0] = 0x73, // v2: mul64 dst, src -> stxb [dst + off], src
102            0x37 => processed_bytes[0] = 0x6A, // v2: div64 dst, imm -> sth [dst + off], imm
103            0x3F => processed_bytes[0] = 0x6B, // v2: div64 dst, src -> stxh [dst + off], src
104            0x87 => processed_bytes[0] = 0x62, // v2: neg64 dst -> stw [dst + off], imm
105            0x97 => processed_bytes[0] = 0x7A, // v2: mod64 dst, imm -> stdw [dst + off], imm
106            0x9F => processed_bytes[0] = 0x7B, // v2: mod64 dst, src -> stxdw [dst + off], src
107            // Revert Lddw
108            0x21 => {
109                if let Some(lddw_2) = processed_bytes.get(8)
110                    && lddw_2 == &0xf7
111                {
112                    processed_bytes[0] = 0x18;
113                    processed_bytes[8..12].clone_from_slice(&[0u8; 4]);
114                }
115            }
116            // Move callx target from src to dst
117            0x8D => processed_bytes[1] >>= 4,
118            // All other opcodes remain unchanged
119            _ => (),
120        }
121
122        Self::from_bytes(&processed_bytes)
123    }
124
125    pub fn to_bytes(&self) -> Result<Vec<u8>, SBPFError> {
126        let dst_val = self.dst.as_ref().map(|r| r.n).unwrap_or(0);
127        let src_val = if self.opcode == Opcode::Call {
128            1
129        } else {
130            self.src.as_ref().map(|r| r.n).unwrap_or(0)
131        };
132        let off_val = match &self.off {
133            Some(Either::Left(ident)) => {
134                unreachable!("Identifier '{}' should have been resolved earlier", ident)
135            }
136            Some(Either::Right(off)) => *off,
137            None => 0,
138        };
139        let imm_val = match &self.imm {
140            Some(Either::Left(ident)) => {
141                if self.opcode == Opcode::Call {
142                    -1i64 // FF FF FF FF
143                } else {
144                    unreachable!("Identifier '{}' should have been resolved earlier", ident)
145                }
146            }
147            Some(Either::Right(Number::Int(imm))) | Some(Either::Right(Number::Addr(imm))) => *imm,
148            None => 0,
149        };
150        // fix callx encoding in sbpf
151        let (dst_val, imm_val) = match self.opcode {
152            Opcode::Callx => (0, dst_val as i64), // callx: dst register encoded in imm
153            _ => (dst_val, imm_val),
154        };
155
156        let mut b = vec![self.opcode.into(), src_val << 4 | dst_val];
157        b.extend_from_slice(&off_val.to_le_bytes());
158        b.extend_from_slice(&(imm_val as i32).to_le_bytes());
159        if self.opcode == Opcode::Lddw {
160            b.extend_from_slice(&[0; 4]);
161            b.extend_from_slice(&((imm_val >> 32) as i32).to_le_bytes());
162        }
163        Ok(b)
164    }
165
166    pub fn to_asm(&self) -> Result<String, SBPFError> {
167        if let Some(handler) = OPCODE_TO_HANDLER.get(&self.opcode) {
168            match (handler.validate)(self) {
169                Ok(()) => {
170                    let mut asm = if self.opcode == Opcode::Le || self.opcode == Opcode::Be {
171                        self.op_imm_bits()?
172                    } else {
173                        format!("{}", self.opcode)
174                    };
175                    let mut param = vec![];
176
177                    fn off_str(off: &Either<String, i16>) -> String {
178                        match off {
179                            Either::Left(ident) => ident.clone(),
180                            Either::Right(offset) => {
181                                if offset.is_negative() {
182                                    offset.to_string()
183                                } else {
184                                    format!("+{}", offset)
185                                }
186                            }
187                        }
188                    }
189
190                    fn mem_off(r: &Register, off: &Either<String, i16>) -> String {
191                        format!("[r{}{}]", r.n, off_str(off))
192                    }
193
194                    if self.get_opcode_type() == OperationType::LoadMemory {
195                        param.push(format!("r{}", self.dst.as_ref().unwrap().n));
196                        param.push(mem_off(
197                            self.src.as_ref().unwrap(),
198                            self.off.as_ref().unwrap(),
199                        ));
200                    } else if self.get_opcode_type() == OperationType::StoreImmediate {
201                        param.push(mem_off(
202                            self.dst.as_ref().unwrap(),
203                            self.off.as_ref().unwrap(),
204                        ));
205                        param.push(format!("{}", self.imm.as_ref().unwrap()));
206                    } else if self.get_opcode_type() == OperationType::StoreRegister {
207                        param.push(mem_off(
208                            self.dst.as_ref().unwrap(),
209                            self.off.as_ref().unwrap(),
210                        ));
211                        param.push(format!("r{}", self.src.as_ref().unwrap().n));
212                    } else {
213                        if let Some(dst) = &self.dst {
214                            param.push(format!("r{}", dst.n));
215                        }
216                        if let Some(src) = &self.src {
217                            // Skip src register for syscalls
218                            let is_syscall = self.opcode == Opcode::Call
219                                && matches!(&self.imm, Some(Either::Left(_)));
220                            if !is_syscall {
221                                param.push(format!("r{}", src.n));
222                            }
223                        }
224                        if let Some(imm) = &self.imm
225                            && self.opcode != Opcode::Le
226                            && self.opcode != Opcode::Be
227                        {
228                            param.push(format!("{}", imm));
229                        }
230                        if let Some(off) = &self.off {
231                            param.push(off_str(off).to_string());
232                        }
233                    }
234                    if !param.is_empty() {
235                        asm.push(' ');
236                        asm.push_str(&param.join(", "));
237                    }
238                    Ok(asm)
239                }
240                Err(e) => Err(e),
241            }
242        } else {
243            Err(SBPFError::BytecodeError {
244                error: format!("no validate handler for opcode {}", self.opcode),
245                span: self.span.clone(),
246                custom_label: None,
247            })
248        }
249    }
250}
251
252#[cfg(test)]
253mod test {
254    use {
255        crate::{
256            inst_param::{Number, Register},
257            instruction::Instruction,
258            opcode::Opcode,
259        },
260        either::Either,
261        hex_literal::hex,
262    };
263
264    #[test]
265    fn serialize_e2e() {
266        let b = hex!("9700000000000000");
267        let i = Instruction::from_bytes(&b).unwrap();
268        assert_eq!(i.to_bytes().unwrap(), &b);
269        assert_eq!(i.to_asm().unwrap(), "mod64 r0, 0");
270    }
271
272    #[test]
273    fn serialize_e2e_lddw() {
274        let b = hex!("18010000000000000000000000000000");
275        let i = Instruction::from_bytes(&b).unwrap();
276        assert_eq!(i.to_bytes().unwrap(), &b);
277        assert_eq!(i.to_asm().unwrap(), "lddw r1, 0");
278    }
279
280    #[test]
281    fn serialize_e2e_add64_imm() {
282        let b = hex!("0701000000000000");
283        let i = Instruction::from_bytes(&b).unwrap();
284        assert_eq!(i.to_bytes().unwrap(), &b);
285        assert_eq!(i.to_asm().unwrap(), "add64 r1, 0");
286    }
287
288    #[test]
289    fn serialize_e2e_add64_reg() {
290        let b = hex!("0f12000000000000");
291        let i = Instruction::from_bytes(&b).unwrap();
292        assert_eq!(i.to_bytes().unwrap(), &b);
293        assert_eq!(i.to_asm().unwrap(), "add64 r2, r1");
294    }
295
296    #[test]
297    fn serialize_e2e_ja() {
298        let b = hex!("05000a0000000000");
299        let i = Instruction::from_bytes(&b).unwrap();
300        assert_eq!(i.to_bytes().unwrap(), &b);
301        assert_eq!(i.to_asm().unwrap(), "ja +10");
302    }
303
304    #[test]
305    fn serialize_e2e_jeq_imm() {
306        let b = hex!("15030a0001000000");
307        let i = Instruction::from_bytes(&b).unwrap();
308        assert_eq!(i.to_bytes().unwrap(), &b);
309        assert_eq!(i.to_asm().unwrap(), "jeq r3, 1, +10");
310    }
311
312    #[test]
313    fn serialize_e2e_jeq_reg() {
314        let b = hex!("1d210a0000000000");
315        let i = Instruction::from_bytes(&b).unwrap();
316        assert_eq!(i.to_bytes().unwrap(), &b);
317        assert_eq!(i.to_asm().unwrap(), "jeq r1, r2, +10");
318    }
319
320    #[test]
321    fn serialize_e2e_ldxw() {
322        let b = hex!("6112000000000000");
323        let i = Instruction::from_bytes(&b).unwrap();
324        assert_eq!(i.to_bytes().unwrap(), &b);
325        assert_eq!(i.to_asm().unwrap(), "ldxw r2, [r1+0]");
326    }
327
328    #[test]
329    fn serialize_e2e_stxw() {
330        let b = hex!("6312000000000000");
331        let i = Instruction::from_bytes(&b).unwrap();
332        assert_eq!(i.to_bytes().unwrap(), &b);
333        assert_eq!(i.to_asm().unwrap(), "stxw [r2+0], r1");
334    }
335
336    #[test]
337    fn serialize_e2e_stb() {
338        let b = hex!("7200000000000000");
339        let i = Instruction::from_bytes(&b).unwrap();
340        assert_eq!(i.opcode, Opcode::Stb);
341        assert!(i.src.is_none());
342        assert_eq!(i.to_bytes().unwrap(), &b);
343        assert_eq!(i.to_asm().unwrap(), "stb [r0+0], 0");
344    }
345
346    #[test]
347    fn serialize_e2e_sth() {
348        let b = hex!("6a01040034120000");
349        let i = Instruction::from_bytes(&b).unwrap();
350        assert_eq!(i.opcode, Opcode::Sth);
351        assert!(i.src.is_none());
352        assert_eq!(i.to_bytes().unwrap(), &b);
353        assert_eq!(i.to_asm().unwrap(), "sth [r1+4], 4660");
354    }
355
356    #[test]
357    fn serialize_e2e_stw() {
358        let b = hex!("6201080064000000");
359        let i = Instruction::from_bytes(&b).unwrap();
360        assert_eq!(i.opcode, Opcode::Stw);
361        assert!(i.src.is_none());
362        assert_eq!(i.to_bytes().unwrap(), &b);
363        assert_eq!(i.to_asm().unwrap(), "stw [r1+8], 100");
364    }
365
366    #[test]
367    fn serialize_e2e_stdw() {
368        let b = hex!("7a021000efbeadde");
369        let i = Instruction::from_bytes(&b).unwrap();
370        assert_eq!(i.opcode, Opcode::Stdw);
371        assert!(i.src.is_none());
372        assert_eq!(i.to_bytes().unwrap(), &b);
373        assert_eq!(i.to_asm().unwrap(), "stdw [r2+16], -559038737");
374    }
375
376    #[test]
377    fn serialize_e2e_le16() {
378        let b = hex!("d401000010000000");
379        let i = Instruction::from_bytes(&b).unwrap();
380        assert_eq!(i.opcode, Opcode::Le);
381        assert_eq!(i.to_bytes().unwrap(), &b);
382        assert_eq!(i.to_asm().unwrap(), "le16 r1");
383    }
384
385    #[test]
386    fn serialize_e2e_le32() {
387        let b = hex!("d401000020000000");
388        let i = Instruction::from_bytes(&b).unwrap();
389        assert_eq!(i.opcode, Opcode::Le);
390        assert_eq!(i.to_bytes().unwrap(), &b);
391        assert_eq!(i.to_asm().unwrap(), "le32 r1");
392    }
393
394    #[test]
395    fn serialize_e2e_le64() {
396        let b = hex!("d403000040000000");
397        let i = Instruction::from_bytes(&b).unwrap();
398        assert_eq!(i.opcode, Opcode::Le);
399        assert_eq!(i.to_bytes().unwrap(), &b);
400        assert_eq!(i.to_asm().unwrap(), "le64 r3");
401    }
402
403    #[test]
404    fn serialize_e2e_be16() {
405        let b = hex!("dc01000010000000");
406        let i = Instruction::from_bytes(&b).unwrap();
407        assert_eq!(i.opcode, Opcode::Be);
408        assert_eq!(i.to_bytes().unwrap(), &b);
409        assert_eq!(i.to_asm().unwrap(), "be16 r1");
410    }
411
412    #[test]
413    fn serialize_e2e_be32() {
414        let b = hex!("dc02000020000000");
415        let i = Instruction::from_bytes(&b).unwrap();
416        assert_eq!(i.opcode, Opcode::Be);
417        assert_eq!(i.to_bytes().unwrap(), &b);
418        assert_eq!(i.to_asm().unwrap(), "be32 r2");
419    }
420
421    #[test]
422    fn serialize_e2e_be64() {
423        let b = hex!("dc03000040000000");
424        let i = Instruction::from_bytes(&b).unwrap();
425        assert_eq!(i.opcode, Opcode::Be);
426        assert_eq!(i.to_bytes().unwrap(), &b);
427        assert_eq!(i.to_asm().unwrap(), "be64 r3");
428    }
429
430    #[test]
431    fn serialize_e2e_neg64() {
432        let b = hex!("8700000000000000");
433        let i = Instruction::from_bytes(&b).unwrap();
434        assert_eq!(i.to_bytes().unwrap(), &b);
435        assert_eq!(i.to_asm().unwrap(), "neg64 r0");
436    }
437
438    #[test]
439    fn test_instruction_size() {
440        let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
441        assert_eq!(exit.get_size(), 8);
442
443        let lddw = Instruction::from_bytes(&hex!("18010000000000000000000000000000")).unwrap();
444        assert_eq!(lddw.get_size(), 16);
445    }
446
447    #[test]
448    fn test_is_jump() {
449        let ja = Instruction::from_bytes(&hex!("0500000000000000")).unwrap();
450        assert!(ja.is_jump());
451
452        let jeq_imm = Instruction::from_bytes(&hex!("1502000000000000")).unwrap();
453        assert!(jeq_imm.is_jump());
454
455        let jeq_reg = Instruction::from_bytes(&hex!("1d12000000000000")).unwrap();
456        assert!(jeq_reg.is_jump());
457
458        let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
459        assert!(!exit.is_jump());
460
461        let add64 = Instruction::from_bytes(&hex!("0701000000000000")).unwrap();
462        assert!(!add64.is_jump());
463    }
464
465    #[test]
466    fn test_invalid_opcode() {
467        let result = Instruction::from_bytes(&hex!("ff00000000000000"));
468        assert!(result.is_err());
469    }
470
471    #[test]
472    fn test_unsupported_opcode() {
473        let add32 = Instruction::from_bytes(&hex!("1300000000000000"));
474        assert!(add32.is_err());
475    }
476
477    #[test]
478    fn test_op_imm_bits_16() {
479        let inst = Instruction {
480            opcode: Opcode::Le,
481            dst: Some(Register { n: 1 }),
482            src: None,
483            off: None,
484            imm: Some(Either::Right(Number::Int(16))),
485            span: 0..8,
486        };
487        assert_eq!(inst.op_imm_bits().unwrap(), "le16");
488    }
489
490    #[test]
491    fn test_op_imm_bits_32() {
492        let inst = Instruction {
493            opcode: Opcode::Le,
494            dst: Some(Register { n: 1 }),
495            src: None,
496            off: None,
497            imm: Some(Either::Right(Number::Int(32))),
498            span: 0..8,
499        };
500        assert_eq!(inst.op_imm_bits().unwrap(), "le32");
501    }
502
503    #[test]
504    fn test_op_imm_bits_64() {
505        let inst = Instruction {
506            opcode: Opcode::Be,
507            dst: Some(Register { n: 1 }),
508            src: None,
509            off: None,
510            imm: Some(Either::Right(Number::Int(64))),
511            span: 0..8,
512        };
513        assert_eq!(inst.op_imm_bits().unwrap(), "be64");
514    }
515
516    #[test]
517    fn test_op_imm_bits_invalid() {
518        let inst = Instruction {
519            opcode: Opcode::Le,
520            dst: Some(Register { n: 1 }),
521            src: None,
522            off: None,
523            imm: Some(Either::Right(Number::Int(8))),
524            span: 0..8,
525        };
526        assert!(inst.op_imm_bits().is_err());
527    }
528
529    #[test]
530    fn test_op_imm_bits_no_imm() {
531        let inst = Instruction {
532            opcode: Opcode::Le,
533            dst: Some(Register { n: 1 }),
534            src: None,
535            off: None,
536            imm: None,
537            span: 0..8,
538        };
539        assert!(inst.op_imm_bits().is_err());
540    }
541
542    #[test]
543    fn test_to_bytes_callx() {
544        // callx r5 - dst register encoded in imm
545        let inst = Instruction {
546            opcode: Opcode::Callx,
547            dst: Some(Register { n: 5 }),
548            src: None,
549            off: None,
550            imm: None,
551            span: 0..8,
552        };
553        let bytes = inst.to_bytes().unwrap();
554        assert_eq!(bytes[0], 0x8d);
555        assert_eq!(bytes[4], 5);
556    }
557
558    #[test]
559    fn test_to_bytes_call_with_identifier() {
560        let inst = Instruction {
561            opcode: Opcode::Call,
562            dst: None,
563            src: Some(Register { n: 1 }),
564            off: None,
565            imm: Some(Either::Left("function".to_string())),
566            span: 0..8,
567        };
568        let bytes = inst.to_bytes().unwrap();
569        // Should encode -1 for unresolved identifier
570        assert_eq!(
571            i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
572            -1
573        );
574    }
575
576    #[test]
577    fn test_to_asm_with_imm_addr() {
578        // Test Number::Addr variant in to_bytes
579        let inst = Instruction {
580            opcode: Opcode::Add64Imm,
581            dst: Some(Register { n: 1 }),
582            src: None,
583            off: None,
584            imm: Some(Either::Right(Number::Addr(100))),
585            span: 0..8,
586        };
587        let bytes = inst.to_bytes().unwrap();
588        assert_eq!(bytes[0], 0x07); // add64 imm opcode
589        assert_eq!(
590            i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
591            100
592        );
593    }
594
595    #[test]
596    fn test_from_bytes_sbpf_v2() {
597        // Test all v2 opcode mappings and repurposed opcodes
598        let test_cases = vec![
599            // New opcodes in v2
600            (hex!("8c12000000000000"), Opcode::Ldxw, "v2: 0x8C -> ldxw"),
601            (hex!("8f12000000000000"), Opcode::Stxw, "v2: 0x8F -> stxw"),
602            // Repurposed opcodes in v2
603            (
604                hex!("2c12000000000000"),
605                Opcode::Ldxb,
606                "v2: 0x2C (mul32 reg) -> ldxb",
607            ),
608            (
609                hex!("3c12000000000000"),
610                Opcode::Ldxh,
611                "v2: 0x3C (div32 reg) -> ldxh",
612            ),
613            (
614                hex!("9c12000000000000"),
615                Opcode::Ldxdw,
616                "v2: 0x9C (mod32 reg) -> ldxdw",
617            ),
618            (
619                hex!("2701040064000000"),
620                Opcode::Stb,
621                "v2: 0x27 (mul64 imm) -> stb",
622            ),
623            (
624                hex!("2f12040000000000"),
625                Opcode::Stxb,
626                "v2: 0x2F (mul64 reg) -> stxb",
627            ),
628            (
629                hex!("3701040064000000"),
630                Opcode::Sth,
631                "v2: 0x37 (div64 imm) -> sth",
632            ),
633            (
634                hex!("3f12040000000000"),
635                Opcode::Stxh,
636                "v2: 0x3F (div64 reg) -> stxh",
637            ),
638            (
639                hex!("8701040064000000"),
640                Opcode::Stw,
641                "v2: 0x87 (neg64) -> stw",
642            ),
643            (
644                hex!("9701040064000000"),
645                Opcode::Stdw,
646                "v2: 0x97 (mod64 imm) -> stdw",
647            ),
648            (
649                hex!("9f12040000000000"),
650                Opcode::Stxdw,
651                "v2: 0x9F (mod64 reg) -> stxdw",
652            ),
653        ];
654
655        for (bytes, expected_opcode, description) in test_cases {
656            let inst = Instruction::from_bytes_sbpf_v2(&bytes).unwrap();
657            assert_eq!(inst.opcode, expected_opcode, "{}", description);
658        }
659
660        // Test callx
661        let callx_bytes = hex!("8d50000000000000");
662        let callx_inst = Instruction::from_bytes_sbpf_v2(&callx_bytes).unwrap();
663        assert_eq!(callx_inst.opcode, Opcode::Callx);
664        assert_eq!(callx_inst.dst.unwrap().n, 5);
665
666        // Test lddw
667        let mut lddw_bytes = vec![0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
668        lddw_bytes.extend_from_slice(&[0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
669        let lddw_inst = Instruction::from_bytes_sbpf_v2(&lddw_bytes).unwrap();
670        assert_eq!(lddw_inst.opcode, Opcode::Lddw);
671    }
672}