Skip to main content

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        syscalls::REGISTERED_SYSCALLS,
8    },
9    core::ops::Range,
10    either::Either,
11    serde::{Deserialize, Serialize},
12};
13
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
15pub enum AsmFormat {
16    #[default]
17    Default,
18    Llvm,
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct Instruction {
23    pub opcode: Opcode,
24    pub dst: Option<Register>,
25    pub src: Option<Register>,
26    pub off: Option<Either<String, i16>>,
27    pub imm: Option<Either<String, Number>>,
28    pub span: Range<usize>,
29}
30
31impl Instruction {
32    pub fn get_size(&self) -> u64 {
33        match self.opcode {
34            Opcode::Lddw => 16,
35            _ => 8,
36        }
37    }
38
39    fn get_opcode_type(&self) -> OperationType {
40        *OPCODE_TO_TYPE.get(&self.opcode).unwrap()
41    }
42
43    pub fn is_jump(&self) -> bool {
44        matches!(
45            self.get_opcode_type(),
46            OperationType::Jump
47                | OperationType::JumpImmediate
48                | OperationType::JumpRegister
49                | OperationType::Jump32Immediate
50                | OperationType::Jump32Register
51        )
52    }
53
54    /// Checks if the instruction is a syscall.
55    /// This should be used only when the call label hasn't been resolved to -1.
56    pub fn is_syscall(&self) -> bool {
57        if self.opcode == Opcode::Call
58            && let Some(Either::Left(identifier)) = &self.imm
59        {
60            return REGISTERED_SYSCALLS.contains(&identifier.as_str());
61        }
62        false
63    }
64
65    // only used for be/le
66    pub fn op_imm_bits(&self) -> Result<String, SBPFError> {
67        match &self.imm {
68            Some(Either::Right(Number::Int(imm))) => match *imm {
69                16 => Ok(format!("{}16", self.opcode)),
70                32 => Ok(format!("{}32", self.opcode)),
71                64 => Ok(format!("{}64", self.opcode)),
72                _ => Err(SBPFError::BytecodeError {
73                    error: format!(
74                        "Invalid immediate value: {:?} for opcode: {:?}",
75                        self.imm, self.opcode
76                    ),
77                    span: self.span.clone(),
78                    custom_label: None,
79                }),
80            },
81            _ => Err(SBPFError::BytecodeError {
82                error: format!("Expected immediate value for opcode: {:?}", self.opcode),
83                span: self.span.clone(),
84                custom_label: None,
85            }),
86        }
87    }
88
89    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SBPFError> {
90        let opcode: Opcode = bytes[0].try_into()?;
91        if let Some(handler) = OPCODE_TO_HANDLER.get(&opcode) {
92            (handler.decode)(bytes)
93        } else {
94            Err(SBPFError::BytecodeError {
95                error: format!("no decode handler for opcode {}", opcode),
96                span: 0..1,
97                custom_label: Some("Invalid opcode".to_string()),
98            })
99        }
100    }
101
102    pub fn from_bytes_sbpf_v2(bytes: &[u8]) -> Result<Self, SBPFError> {
103        // Preprocess the opcode byte for SBPF v2 (e_flags == 0x02)
104        let mut processed_bytes = bytes.to_vec();
105
106        match processed_bytes[0] {
107            // New opcodes in v2 that map to existing instructions
108            0x8C => processed_bytes[0] = 0x61, // v2: 0x8C -> ldxw dst, [src + off]
109            0x8F => processed_bytes[0] = 0x63, // v2: 0x8F -> stxw [dst + off], src
110            // Repurposed opcodes in v2
111            0x2C => processed_bytes[0] = 0x71, // v2: mul32 dst, src -> ldxb dst, [src + off]
112            0x3C => processed_bytes[0] = 0x69, // v2: div32 dst, src -> ldxh dst, [src + off]
113            0x9C => processed_bytes[0] = 0x79, // v2: mod32 dst, src -> ldxdw dst, [src + off]
114            0x27 => processed_bytes[0] = 0x72, // v2: mul64 dst, imm -> stb [dst + off], imm
115            0x2F => processed_bytes[0] = 0x73, // v2: mul64 dst, src -> stxb [dst + off], src
116            0x37 => processed_bytes[0] = 0x6A, // v2: div64 dst, imm -> sth [dst + off], imm
117            0x3F => processed_bytes[0] = 0x6B, // v2: div64 dst, src -> stxh [dst + off], src
118            0x87 => processed_bytes[0] = 0x62, // v2: neg64 dst -> stw [dst + off], imm
119            0x97 => processed_bytes[0] = 0x7A, // v2: mod64 dst, imm -> stdw [dst + off], imm
120            0x9F => processed_bytes[0] = 0x7B, // v2: mod64 dst, src -> stxdw [dst + off], src
121            // Revert Lddw
122            0x21 => {
123                if let Some(lddw_2) = processed_bytes.get(8)
124                    && lddw_2 == &0xf7
125                {
126                    processed_bytes[0] = 0x18;
127                    processed_bytes[8..12].clone_from_slice(&[0u8; 4]);
128                }
129            }
130            // Move callx target from src to dst
131            0x8D => processed_bytes[1] >>= 4,
132            // All other opcodes remain unchanged
133            _ => (),
134        }
135
136        Self::from_bytes(&processed_bytes)
137    }
138
139    pub fn from_bytes_sbpf_v3(bytes: &[u8]) -> Result<Self, SBPFError> {
140        let opcode = Opcode::try_from_sbpf_v3(bytes[0])?;
141        OPCODE_TO_HANDLER
142            .get(&opcode)
143            .ok_or_else(|| SBPFError::BytecodeError {
144                error: format!("no decode handler for opcode {}", opcode),
145                span: 0..1,
146                custom_label: Some("Invalid opcode".to_string()),
147            })
148            .and_then(|handler| (handler.decode)(bytes))
149    }
150
151    pub fn to_bytes(&self) -> Result<Vec<u8>, SBPFError> {
152        let dst_val = self.dst.as_ref().map(|r| r.n).unwrap_or(0);
153        let src_val = self.src.as_ref().map(|r| r.n).unwrap_or(0);
154        let off_val = match &self.off {
155            Some(Either::Left(ident)) => {
156                unreachable!("Identifier '{}' should have been resolved earlier", ident)
157            }
158            Some(Either::Right(off)) => *off,
159            None => 0,
160        };
161        let imm_val = match &self.imm {
162            Some(Either::Left(ident)) => {
163                unreachable!("Identifier '{}' should have been resolved earlier", ident)
164            }
165            Some(Either::Right(Number::Int(imm))) | Some(Either::Right(Number::Addr(imm))) => *imm,
166            None => 0,
167        };
168        // fix callx encoding in sbpf
169        let (dst_val, imm_val) = match self.opcode {
170            Opcode::Callx => (0, dst_val as i64), // callx: dst register encoded in imm
171            _ => (dst_val, imm_val),
172        };
173
174        let mut b = vec![self.opcode.into(), src_val << 4 | dst_val];
175        b.extend_from_slice(&off_val.to_le_bytes());
176        b.extend_from_slice(&(imm_val as i32).to_le_bytes());
177        if self.opcode == Opcode::Lddw {
178            b.extend_from_slice(&[0; 4]);
179            b.extend_from_slice(&((imm_val >> 32) as i32).to_le_bytes());
180        }
181        Ok(b)
182    }
183
184    pub fn to_asm(&self, format: AsmFormat) -> Result<String, SBPFError> {
185        match format {
186            AsmFormat::Default => self.to_default_asm(),
187            AsmFormat::Llvm => self.to_llvm_asm(),
188        }
189    }
190
191    fn to_default_asm(&self) -> Result<String, SBPFError> {
192        if let Some(handler) = OPCODE_TO_HANDLER.get(&self.opcode) {
193            match (handler.validate)(self) {
194                Ok(()) => {
195                    let mut asm = if self.opcode == Opcode::Le || self.opcode == Opcode::Be {
196                        self.op_imm_bits()?
197                    } else {
198                        format!("{}", self.opcode)
199                    };
200                    let mut param = vec![];
201
202                    fn fmt_mem_off(r: &Register, off: &Either<String, i16>) -> String {
203                        format!("[r{}{}]", r.n, fmt_off(off))
204                    }
205
206                    if self.get_opcode_type() == OperationType::LoadMemory {
207                        param.push(format!("r{}", self.dst.as_ref().unwrap().n));
208                        param.push(fmt_mem_off(
209                            self.src.as_ref().unwrap(),
210                            self.off.as_ref().unwrap(),
211                        ));
212                    } else if self.get_opcode_type() == OperationType::StoreImmediate {
213                        param.push(fmt_mem_off(
214                            self.dst.as_ref().unwrap(),
215                            self.off.as_ref().unwrap(),
216                        ));
217                        param.push(fmt_imm(self.imm.as_ref().unwrap()));
218                    } else if self.get_opcode_type() == OperationType::StoreRegister {
219                        param.push(fmt_mem_off(
220                            self.dst.as_ref().unwrap(),
221                            self.off.as_ref().unwrap(),
222                        ));
223                        param.push(format!("r{}", self.src.as_ref().unwrap().n));
224                    } else {
225                        if let Some(dst) = &self.dst {
226                            param.push(format!("r{}", dst.n));
227                        }
228                        if let Some(src) = &self.src
229                            && self.opcode != Opcode::Call
230                        {
231                            param.push(format!("r{}", src.n));
232                        }
233                        if let Some(imm) = &self.imm
234                            && self.opcode != Opcode::Le
235                            && self.opcode != Opcode::Be
236                        {
237                            param.push(fmt_imm(imm));
238                        }
239                        if let Some(off) = &self.off {
240                            param.push(fmt_off(off));
241                        }
242                    }
243                    if !param.is_empty() {
244                        asm.push(' ');
245                        asm.push_str(&param.join(", "));
246                    }
247                    Ok(asm)
248                }
249                Err(e) => Err(e),
250            }
251        } else {
252            Err(SBPFError::BytecodeError {
253                error: format!("no validate handler for opcode {}", self.opcode),
254                span: self.span.clone(),
255                custom_label: None,
256            })
257        }
258    }
259
260    fn to_llvm_asm(&self) -> Result<String, SBPFError> {
261        let op_type = self.get_opcode_type();
262
263        fn fmt_mem_off(off: &Either<String, i16>) -> String {
264            match off {
265                Either::Left(label) => label.clone(),
266                Either::Right(v) if *v < 0 => format!("- 0x{:x}", -(*v as i32)),
267                Either::Right(v) => format!("+ 0x{:x}", v),
268            }
269        }
270
271        match op_type {
272            OperationType::BinaryImmediate | OperationType::BinaryRegister => {
273                let op = self
274                    .opcode
275                    .to_operator()
276                    .ok_or_else(|| SBPFError::BytecodeError {
277                        error: format!("unsupported opcode in LLVM format: {}", self.opcode),
278                        span: self.span.clone(),
279                        custom_label: None,
280                    })?;
281                let prefix = if self.opcode.is_32bit() { "w" } else { "r" };
282                let dst = self.dst.as_ref().unwrap().n;
283                let rhs = if op_type == OperationType::BinaryRegister {
284                    format!("{}{}", prefix, self.src.as_ref().unwrap().n)
285                } else {
286                    fmt_imm(self.imm.as_ref().unwrap())
287                };
288                Ok(format!("{}{} {} {}", prefix, dst, op, rhs))
289            }
290            OperationType::Endian => {
291                let bits = self.op_imm_bits()?;
292                let dst = self.dst.as_ref().unwrap().n;
293                Ok(format!("r{} = {} r{}", dst, bits, dst))
294            }
295            OperationType::Unary => {
296                let prefix = if self.opcode == Opcode::Neg32 {
297                    "w"
298                } else {
299                    "r"
300                };
301                let dst = self.dst.as_ref().unwrap().n;
302                Ok(format!("{}{} = -{}{}", prefix, dst, prefix, dst))
303            }
304            OperationType::LoadImmediate => {
305                let dst = self.dst.as_ref().unwrap().n;
306                let imm = fmt_imm(self.imm.as_ref().unwrap());
307                Ok(format!("r{} = {} ll", dst, imm))
308            }
309            OperationType::LoadMemory => {
310                let size = self.opcode.to_size().unwrap();
311                let dst_prefix = if self.opcode == Opcode::Ldxdw {
312                    "r"
313                } else {
314                    "w"
315                };
316                let dst = self.dst.as_ref().unwrap().n;
317                let src = self.src.as_ref().unwrap().n;
318                let off = fmt_mem_off(self.off.as_ref().unwrap());
319                Ok(format!(
320                    "{}{} = *({} *)(r{} {})",
321                    dst_prefix, dst, size, src, off
322                ))
323            }
324            OperationType::StoreImmediate => {
325                let size = self.opcode.to_size().unwrap();
326                let dst = self.dst.as_ref().unwrap().n;
327                let off = fmt_mem_off(self.off.as_ref().unwrap());
328                let imm = fmt_imm(self.imm.as_ref().unwrap());
329                Ok(format!("*({} *)(r{} {}) = {}", size, dst, off, imm))
330            }
331            OperationType::StoreRegister => {
332                let size = self.opcode.to_size().unwrap();
333                let dst = self.dst.as_ref().unwrap().n;
334                let off = fmt_mem_off(self.off.as_ref().unwrap());
335                let src_prefix = if self.opcode == Opcode::Stxdw {
336                    "r"
337                } else {
338                    "w"
339                };
340                let src = self.src.as_ref().unwrap().n;
341                Ok(format!(
342                    "*({} *)(r{} {}) = {}{}",
343                    size, dst, off, src_prefix, src
344                ))
345            }
346            OperationType::Jump => {
347                let off = fmt_off(self.off.as_ref().unwrap());
348                Ok(format!("goto {}", off))
349            }
350            OperationType::JumpImmediate | OperationType::Jump32Immediate => {
351                let dst = self.dst.as_ref().unwrap().n;
352                let op = self.opcode.to_operator().unwrap();
353                let imm = fmt_imm(self.imm.as_ref().unwrap());
354                let off = fmt_off(self.off.as_ref().unwrap());
355                let prefix = if is_jump32_opcode(self.opcode) {
356                    "w"
357                } else {
358                    "r"
359                };
360                Ok(format!("if {}{} {} {} goto {}", prefix, dst, op, imm, off))
361            }
362            OperationType::JumpRegister | OperationType::Jump32Register => {
363                let dst = self.dst.as_ref().unwrap().n;
364                let op = self.opcode.to_operator().unwrap();
365                let src = self.src.as_ref().unwrap().n;
366                let off = fmt_off(self.off.as_ref().unwrap());
367                let prefix = if is_jump32_opcode(self.opcode) {
368                    "w"
369                } else {
370                    "r"
371                };
372                Ok(format!(
373                    "if {}{} {} {}{} goto {}",
374                    prefix, dst, op, prefix, src, off
375                ))
376            }
377            OperationType::CallImmediate | OperationType::CallRegister | OperationType::Exit => {
378                self.to_default_asm()
379            }
380        }
381    }
382}
383
384fn fmt_off(off: &Either<String, i16>) -> String {
385    match off {
386        Either::Left(label) => label.clone(),
387        Either::Right(v) if *v < 0 => format!("-0x{:x}", -(*v as i32)),
388        Either::Right(v) => format!("+0x{:x}", v),
389    }
390}
391
392fn fmt_imm(imm: &Either<String, Number>) -> String {
393    match imm {
394        Either::Left(label) => label.clone(),
395        Either::Right(Number::Int(v)) | Either::Right(Number::Addr(v)) => {
396            if *v < 0 {
397                format!("-0x{:x}", -v)
398            } else {
399                format!("0x{:x}", v)
400            }
401        }
402    }
403}
404
405fn is_jump32_opcode(opcode: Opcode) -> bool {
406    matches!(
407        opcode,
408        Opcode::Jeq32Imm
409            | Opcode::Jeq32Reg
410            | Opcode::Jgt32Imm
411            | Opcode::Jgt32Reg
412            | Opcode::Jge32Imm
413            | Opcode::Jge32Reg
414            | Opcode::Jlt32Imm
415            | Opcode::Jlt32Reg
416            | Opcode::Jle32Imm
417            | Opcode::Jle32Reg
418            | Opcode::Jset32Imm
419            | Opcode::Jset32Reg
420            | Opcode::Jne32Imm
421            | Opcode::Jne32Reg
422            | Opcode::Jsgt32Imm
423            | Opcode::Jsgt32Reg
424            | Opcode::Jsge32Imm
425            | Opcode::Jsge32Reg
426            | Opcode::Jslt32Imm
427            | Opcode::Jslt32Reg
428            | Opcode::Jsle32Imm
429            | Opcode::Jsle32Reg
430    )
431}
432
433#[cfg(test)]
434mod test {
435    use {
436        crate::{
437            inst_param::{Number, Register},
438            instruction::{AsmFormat, Instruction},
439            opcode::Opcode,
440        },
441        either::Either,
442        hex_literal::hex,
443        syscall_map::murmur3_32,
444    };
445
446    #[test]
447    fn serialize_e2e() {
448        let b = hex!("9700000000000000");
449        let i = Instruction::from_bytes(&b).unwrap();
450        assert_eq!(i.to_bytes().unwrap(), &b);
451        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "mod64 r0, 0x0");
452        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r0 %= 0x0");
453    }
454
455    #[test]
456    fn serialize_e2e_lddw() {
457        let b = hex!("18010000000000000000000000000000");
458        let i = Instruction::from_bytes(&b).unwrap();
459        assert_eq!(i.to_bytes().unwrap(), &b);
460        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "lddw r1, 0x0");
461        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = 0x0 ll");
462    }
463
464    #[test]
465    fn serialize_e2e_add64_imm() {
466        let b = hex!("0701000000000000");
467        let i = Instruction::from_bytes(&b).unwrap();
468        assert_eq!(i.to_bytes().unwrap(), &b);
469        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "add64 r1, 0x0");
470        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 += 0x0");
471    }
472
473    #[test]
474    fn serialize_e2e_add64_reg() {
475        let b = hex!("0f12000000000000");
476        let i = Instruction::from_bytes(&b).unwrap();
477        assert_eq!(i.to_bytes().unwrap(), &b);
478        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "add64 r2, r1");
479        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r2 += r1");
480    }
481
482    #[test]
483    fn serialize_e2e_ja() {
484        let b = hex!("05000a0000000000");
485        let i = Instruction::from_bytes(&b).unwrap();
486        assert_eq!(i.to_bytes().unwrap(), &b);
487        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "ja +0xa");
488        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "goto +0xa");
489    }
490
491    #[test]
492    fn serialize_e2e_jeq_imm() {
493        let b = hex!("15030a0001000000");
494        let i = Instruction::from_bytes(&b).unwrap();
495        assert_eq!(i.to_bytes().unwrap(), &b);
496        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jeq r3, 0x1, +0xa");
497        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if r3 == 0x1 goto +0xa");
498    }
499
500    #[test]
501    fn serialize_e2e_jeq_reg() {
502        let b = hex!("1d210a0000000000");
503        let i = Instruction::from_bytes(&b).unwrap();
504        assert_eq!(i.to_bytes().unwrap(), &b);
505        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jeq r1, r2, +0xa");
506        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if r1 == r2 goto +0xa");
507    }
508
509    #[test]
510    fn serialize_e2e_ldxw() {
511        let b = hex!("6112000000000000");
512        let i = Instruction::from_bytes(&b).unwrap();
513        assert_eq!(i.to_bytes().unwrap(), &b);
514        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "ldxw r2, [r1+0x0]");
515        assert_eq!(
516            i.to_asm(AsmFormat::Llvm).unwrap(),
517            "w2 = *(u32 *)(r1 + 0x0)"
518        );
519    }
520
521    #[test]
522    fn serialize_e2e_stxw() {
523        let b = hex!("6312000000000000");
524        let i = Instruction::from_bytes(&b).unwrap();
525        assert_eq!(i.to_bytes().unwrap(), &b);
526        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "stxw [r2+0x0], r1");
527        assert_eq!(
528            i.to_asm(AsmFormat::Llvm).unwrap(),
529            "*(u32 *)(r2 + 0x0) = w1"
530        );
531    }
532
533    #[test]
534    fn serialize_e2e_stb() {
535        let b = hex!("7200000000000000");
536        let i = Instruction::from_bytes(&b).unwrap();
537        assert_eq!(i.opcode, Opcode::Stb);
538        assert!(i.src.is_none());
539        assert_eq!(i.to_bytes().unwrap(), &b);
540        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "stb [r0+0x0], 0x0");
541        assert_eq!(
542            i.to_asm(AsmFormat::Llvm).unwrap(),
543            "*(u8 *)(r0 + 0x0) = 0x0"
544        );
545    }
546
547    #[test]
548    fn serialize_e2e_sth() {
549        let b = hex!("6a01040034120000");
550        let i = Instruction::from_bytes(&b).unwrap();
551        assert_eq!(i.opcode, Opcode::Sth);
552        assert!(i.src.is_none());
553        assert_eq!(i.to_bytes().unwrap(), &b);
554        assert_eq!(
555            i.to_asm(AsmFormat::Default).unwrap(),
556            "sth [r1+0x4], 0x1234"
557        );
558        assert_eq!(
559            i.to_asm(AsmFormat::Llvm).unwrap(),
560            "*(u16 *)(r1 + 0x4) = 0x1234"
561        );
562    }
563
564    #[test]
565    fn serialize_e2e_stw() {
566        let b = hex!("6201080064000000");
567        let i = Instruction::from_bytes(&b).unwrap();
568        assert_eq!(i.opcode, Opcode::Stw);
569        assert!(i.src.is_none());
570        assert_eq!(i.to_bytes().unwrap(), &b);
571        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "stw [r1+0x8], 0x64");
572        assert_eq!(
573            i.to_asm(AsmFormat::Llvm).unwrap(),
574            "*(u32 *)(r1 + 0x8) = 0x64"
575        );
576    }
577
578    #[test]
579    fn serialize_e2e_stdw() {
580        let b = hex!("7a021000efbeadde");
581        let i = Instruction::from_bytes(&b).unwrap();
582        assert_eq!(i.opcode, Opcode::Stdw);
583        assert!(i.src.is_none());
584        assert_eq!(i.to_bytes().unwrap(), &b);
585        assert_eq!(
586            i.to_asm(AsmFormat::Default).unwrap(),
587            "stdw [r2+0x10], -0x21524111"
588        );
589        assert_eq!(
590            i.to_asm(AsmFormat::Llvm).unwrap(),
591            "*(u64 *)(r2 + 0x10) = -0x21524111"
592        );
593    }
594
595    #[test]
596    fn serialize_e2e_le16() {
597        let b = hex!("d401000010000000");
598        let i = Instruction::from_bytes(&b).unwrap();
599        assert_eq!(i.opcode, Opcode::Le);
600        assert_eq!(i.to_bytes().unwrap(), &b);
601        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "le16 r1");
602        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = le16 r1");
603    }
604
605    #[test]
606    fn serialize_e2e_le32() {
607        let b = hex!("d401000020000000");
608        let i = Instruction::from_bytes(&b).unwrap();
609        assert_eq!(i.opcode, Opcode::Le);
610        assert_eq!(i.to_bytes().unwrap(), &b);
611        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "le32 r1");
612        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = le32 r1");
613    }
614
615    #[test]
616    fn serialize_e2e_le64() {
617        let b = hex!("d403000040000000");
618        let i = Instruction::from_bytes(&b).unwrap();
619        assert_eq!(i.opcode, Opcode::Le);
620        assert_eq!(i.to_bytes().unwrap(), &b);
621        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "le64 r3");
622        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r3 = le64 r3");
623    }
624
625    #[test]
626    fn serialize_e2e_be16() {
627        let b = hex!("dc01000010000000");
628        let i = Instruction::from_bytes(&b).unwrap();
629        assert_eq!(i.opcode, Opcode::Be);
630        assert_eq!(i.to_bytes().unwrap(), &b);
631        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "be16 r1");
632        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = be16 r1");
633    }
634
635    #[test]
636    fn serialize_e2e_be32() {
637        let b = hex!("dc02000020000000");
638        let i = Instruction::from_bytes(&b).unwrap();
639        assert_eq!(i.opcode, Opcode::Be);
640        assert_eq!(i.to_bytes().unwrap(), &b);
641        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "be32 r2");
642        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r2 = be32 r2");
643    }
644
645    #[test]
646    fn serialize_e2e_be64() {
647        let b = hex!("dc03000040000000");
648        let i = Instruction::from_bytes(&b).unwrap();
649        assert_eq!(i.opcode, Opcode::Be);
650        assert_eq!(i.to_bytes().unwrap(), &b);
651        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "be64 r3");
652        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r3 = be64 r3");
653    }
654
655    #[test]
656    fn serialize_e2e_neg64() {
657        let b = hex!("8700000000000000");
658        let i = Instruction::from_bytes(&b).unwrap();
659        assert_eq!(i.to_bytes().unwrap(), &b);
660        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "neg64 r0");
661        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r0 = -r0");
662    }
663
664    #[test]
665    fn serialize_e2e_exit() {
666        let b = hex!("9500000000000000");
667        let i = Instruction::from_bytes(&b).unwrap();
668        assert_eq!(i.to_bytes().unwrap(), &b);
669        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "exit");
670        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "exit");
671    }
672
673    #[test]
674    fn serialize_e2e_jset_imm() {
675        let b = hex!("45030a0010000000");
676        let i = Instruction::from_bytes(&b).unwrap();
677        assert_eq!(i.to_bytes().unwrap(), &b);
678        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jset r3, 0x10, +0xa");
679        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if r3 & 0x10 goto +0xa");
680    }
681
682    #[test]
683    fn serialize_e2e_jset32_imm() {
684        let b = hex!("46030a0010000000");
685        let i = Instruction::from_bytes_sbpf_v3(&b).unwrap();
686        assert_eq!(
687            i.to_asm(AsmFormat::Default).unwrap(),
688            "jset32 r3, 0x10, +0xa"
689        );
690        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if w3 & 0x10 goto +0xa");
691    }
692
693    #[test]
694    fn serialize_e2e_jset32_reg() {
695        let b = hex!("4e230a0000000000");
696        let i = Instruction::from_bytes_sbpf_v3(&b).unwrap();
697        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jset32 r3, r2, +0xa");
698        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if w3 & w2 goto +0xa");
699    }
700
701    #[test]
702    fn serialize_e2e_sub32_imm() {
703        let b = hex!("1401000042000000");
704        let i = Instruction::from_bytes(&b).unwrap();
705        assert_eq!(i.to_bytes().unwrap(), &b);
706        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "sub32 r1, 0x42");
707        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "w1 -= 0x42");
708    }
709
710    #[test]
711    fn serialize_e2e_mov32_imm() {
712        let b = hex!("b400000001000000");
713        let i = Instruction::from_bytes(&b).unwrap();
714        assert_eq!(i.to_bytes().unwrap(), &b);
715        assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "mov32 r0, 0x1");
716        assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "w0 = 0x1");
717    }
718
719    #[test]
720    fn test_instruction_size() {
721        let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
722        assert_eq!(exit.get_size(), 8);
723
724        let lddw = Instruction::from_bytes(&hex!("18010000000000000000000000000000")).unwrap();
725        assert_eq!(lddw.get_size(), 16);
726    }
727
728    #[test]
729    fn test_is_jump() {
730        let ja = Instruction::from_bytes(&hex!("0500000000000000")).unwrap();
731        assert!(ja.is_jump());
732
733        let jeq_imm = Instruction::from_bytes(&hex!("1502000000000000")).unwrap();
734        assert!(jeq_imm.is_jump());
735
736        let jeq_reg = Instruction::from_bytes(&hex!("1d12000000000000")).unwrap();
737        assert!(jeq_reg.is_jump());
738
739        let jset32_imm = Instruction::from_bytes_sbpf_v3(&hex!("46030a0010000000")).unwrap();
740        assert!(jset32_imm.is_jump());
741
742        let jset32_reg = Instruction::from_bytes_sbpf_v3(&hex!("4e230a0000000000")).unwrap();
743        assert!(jset32_reg.is_jump());
744
745        let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
746        assert!(!exit.is_jump());
747
748        let add64 = Instruction::from_bytes(&hex!("0701000000000000")).unwrap();
749        assert!(!add64.is_jump());
750    }
751
752    #[test]
753    fn test_invalid_opcode() {
754        let result = Instruction::from_bytes(&hex!("ff00000000000000"));
755        assert!(result.is_err());
756    }
757
758    #[test]
759    fn test_unsupported_opcode() {
760        let add32 = Instruction::from_bytes(&hex!("1300000000000000"));
761        assert!(add32.is_err());
762    }
763
764    #[test]
765    fn test_op_imm_bits_16() {
766        let inst = Instruction {
767            opcode: Opcode::Le,
768            dst: Some(Register { n: 1 }),
769            src: None,
770            off: None,
771            imm: Some(Either::Right(Number::Int(16))),
772            span: 0..8,
773        };
774        assert_eq!(inst.op_imm_bits().unwrap(), "le16");
775    }
776
777    #[test]
778    fn test_op_imm_bits_32() {
779        let inst = Instruction {
780            opcode: Opcode::Le,
781            dst: Some(Register { n: 1 }),
782            src: None,
783            off: None,
784            imm: Some(Either::Right(Number::Int(32))),
785            span: 0..8,
786        };
787        assert_eq!(inst.op_imm_bits().unwrap(), "le32");
788    }
789
790    #[test]
791    fn test_op_imm_bits_64() {
792        let inst = Instruction {
793            opcode: Opcode::Be,
794            dst: Some(Register { n: 1 }),
795            src: None,
796            off: None,
797            imm: Some(Either::Right(Number::Int(64))),
798            span: 0..8,
799        };
800        assert_eq!(inst.op_imm_bits().unwrap(), "be64");
801    }
802
803    #[test]
804    fn test_op_imm_bits_invalid() {
805        let inst = Instruction {
806            opcode: Opcode::Le,
807            dst: Some(Register { n: 1 }),
808            src: None,
809            off: None,
810            imm: Some(Either::Right(Number::Int(8))),
811            span: 0..8,
812        };
813        assert!(inst.op_imm_bits().is_err());
814    }
815
816    #[test]
817    fn test_op_imm_bits_no_imm() {
818        let inst = Instruction {
819            opcode: Opcode::Le,
820            dst: Some(Register { n: 1 }),
821            src: None,
822            off: None,
823            imm: None,
824            span: 0..8,
825        };
826        assert!(inst.op_imm_bits().is_err());
827    }
828
829    #[test]
830    fn test_to_bytes_callx() {
831        // callx r5 - dst register encoded in imm
832        let inst = Instruction {
833            opcode: Opcode::Callx,
834            dst: Some(Register { n: 5 }),
835            src: None,
836            off: None,
837            imm: None,
838            span: 0..8,
839        };
840        let bytes = inst.to_bytes().unwrap();
841        assert_eq!(bytes[0], 0x8d);
842        assert_eq!(bytes[4], 5);
843    }
844
845    #[test]
846    #[should_panic(expected = "should have been resolved earlier")]
847    fn test_to_bytes_call_with_identifier() {
848        let inst = Instruction {
849            opcode: Opcode::Call,
850            dst: None,
851            src: None,
852            off: None,
853            imm: Some(Either::Left("function".to_string())),
854            span: 0..8,
855        };
856        // This should panic because "function" does not exist
857        let _ = inst.to_bytes().unwrap();
858    }
859
860    #[test]
861    fn test_to_asm_with_imm_addr() {
862        // Test Number::Addr variant in to_bytes
863        let inst = Instruction {
864            opcode: Opcode::Add64Imm,
865            dst: Some(Register { n: 1 }),
866            src: None,
867            off: None,
868            imm: Some(Either::Right(Number::Addr(100))),
869            span: 0..8,
870        };
871        let bytes = inst.to_bytes().unwrap();
872        assert_eq!(bytes[0], 0x07); // add64 imm opcode
873        assert_eq!(
874            i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
875            100
876        );
877    }
878
879    #[test]
880    fn test_from_bytes_sbpf_v2() {
881        // Test all v2 opcode mappings and repurposed opcodes
882        let test_cases = vec![
883            // New opcodes in v2
884            (hex!("8c12000000000000"), Opcode::Ldxw, "v2: 0x8C -> ldxw"),
885            (hex!("8f12000000000000"), Opcode::Stxw, "v2: 0x8F -> stxw"),
886            // Repurposed opcodes in v2
887            (
888                hex!("2c12000000000000"),
889                Opcode::Ldxb,
890                "v2: 0x2C (mul32 reg) -> ldxb",
891            ),
892            (
893                hex!("3c12000000000000"),
894                Opcode::Ldxh,
895                "v2: 0x3C (div32 reg) -> ldxh",
896            ),
897            (
898                hex!("9c12000000000000"),
899                Opcode::Ldxdw,
900                "v2: 0x9C (mod32 reg) -> ldxdw",
901            ),
902            (
903                hex!("2701040064000000"),
904                Opcode::Stb,
905                "v2: 0x27 (mul64 imm) -> stb",
906            ),
907            (
908                hex!("2f12040000000000"),
909                Opcode::Stxb,
910                "v2: 0x2F (mul64 reg) -> stxb",
911            ),
912            (
913                hex!("3701040064000000"),
914                Opcode::Sth,
915                "v2: 0x37 (div64 imm) -> sth",
916            ),
917            (
918                hex!("3f12040000000000"),
919                Opcode::Stxh,
920                "v2: 0x3F (div64 reg) -> stxh",
921            ),
922            (
923                hex!("8701040064000000"),
924                Opcode::Stw,
925                "v2: 0x87 (neg64) -> stw",
926            ),
927            (
928                hex!("9701040064000000"),
929                Opcode::Stdw,
930                "v2: 0x97 (mod64 imm) -> stdw",
931            ),
932            (
933                hex!("9f12040000000000"),
934                Opcode::Stxdw,
935                "v2: 0x9F (mod64 reg) -> stxdw",
936            ),
937        ];
938
939        for (bytes, expected_opcode, description) in test_cases {
940            let inst = Instruction::from_bytes_sbpf_v2(&bytes).unwrap();
941            assert_eq!(inst.opcode, expected_opcode, "{}", description);
942        }
943
944        // Test callx
945        let callx_bytes = hex!("8d50000000000000");
946        let callx_inst = Instruction::from_bytes_sbpf_v2(&callx_bytes).unwrap();
947        assert_eq!(callx_inst.opcode, Opcode::Callx);
948        assert_eq!(callx_inst.dst.unwrap().n, 5);
949
950        // Test lddw
951        let mut lddw_bytes = vec![0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
952        lddw_bytes.extend_from_slice(&[0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
953        let lddw_inst = Instruction::from_bytes_sbpf_v2(&lddw_bytes).unwrap();
954        assert_eq!(lddw_inst.opcode, Opcode::Lddw);
955    }
956
957    #[test]
958    fn test_is_syscall() {
959        let test_cases = vec![
960            // Syscalls
961            ("sol_log_", true),
962            ("sol_invoke_signed_c", true),
963            ("abort", true),
964            ("sol_sha256", true),
965            ("sol_memcpy_", true),
966            // Non-syscalls
967            ("my_fn", false),
968            ("helper_function", false),
969            ("entrypoint", false),
970            ("random", false),
971        ];
972
973        for (name, expected) in test_cases {
974            let inst = Instruction {
975                opcode: Opcode::Call,
976                dst: None,
977                src: Some(Register { n: 1 }),
978                off: None,
979                imm: Some(Either::Left(name.to_string())),
980                span: 0..8,
981            };
982            assert_eq!(inst.is_syscall(), expected);
983        }
984    }
985
986    #[test]
987    fn test_to_bytes_syscall_dynamic() {
988        let inst = Instruction {
989            opcode: Opcode::Call,
990            dst: None,
991            src: Some(Register { n: 1 }),
992            off: None,
993            imm: Some(Either::Right(Number::Int(-1))),
994            span: 0..8,
995        };
996        let bytes = inst.to_bytes().unwrap();
997        assert_eq!(bytes[0], 0x85);
998        assert_eq!(bytes[1], 0x10);
999
1000        // imm should be -1 (FF FF FF FF)
1001        assert_eq!(&bytes[4..8], &[0xFF, 0xFF, 0xFF, 0xFF]);
1002    }
1003
1004    #[test]
1005    fn test_to_bytes_syscall_static() {
1006        let syscall_hash = murmur3_32("sol_log_");
1007        let inst = Instruction {
1008            opcode: Opcode::Call,
1009            dst: None,
1010            src: Some(Register { n: 0 }),
1011            off: None,
1012            imm: Some(Either::Right(Number::Int(syscall_hash as i64))),
1013            span: 0..8,
1014        };
1015        let bytes = inst.to_bytes().unwrap();
1016        assert_eq!(bytes[0], 0x85);
1017        assert_eq!(bytes[1], 0x00);
1018
1019        // imm should be the murmur3_32 hash
1020        let actual_imm = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
1021        assert_eq!(actual_imm, syscall_hash);
1022    }
1023}