Преглед изворни кода

Merge pull request #55 from clairechingching/master

make instruction class taking identifier for offset/immediate; refactor encode to validate
Claire Fan пре 3 недеља
родитељ
комит
9faa059557

+ 1 - 0
crates/common/Cargo.toml

@@ -19,6 +19,7 @@ num-derive = { workspace = true }
 num-traits = { workspace = true }
 thiserror = { workspace = true }
 serde = { version = "1.0.228", features = ["derive"] }
+either = { version = "1.15.0", features = ["serde"] }
 sbpf-syscall-map = { workspace = true }
 once_cell = "1.21.3"
 

+ 20 - 17
crates/common/src/decode.rs

@@ -1,9 +1,12 @@
-use crate::{
-    errors::SBPFError,
-    inst_param::{Number, Register},
-    instruction::Instruction,
-    opcode::Opcode,
-    syscalls::SYSCALLS,
+use {
+    crate::{
+        errors::SBPFError,
+        inst_param::{Number, Register},
+        instruction::Instruction,
+        opcode::Opcode,
+        syscalls::SYSCALLS,
+    },
+    either::Either,
 };
 
 // TODO: passing span for error reporting (not sure if it's necessary)
@@ -39,7 +42,7 @@ pub fn decode_load_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         dst: Some(Register { n: dst }),
         src: None,
         off: None,
-        imm: Some(Number::Int(imm)),
+        imm: Some(Either::Right(Number::Int(imm))),
         span: 0..16,
     })
 }
@@ -61,7 +64,7 @@ pub fn decode_load_memory(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         opcode,
         dst: Some(Register { n: dst }),
         src: Some(Register { n: src }),
-        off: Some(off),
+        off: Some(Either::Right(off)),
         imm: None,
         span: 0..8,
     })
@@ -84,8 +87,8 @@ pub fn decode_store_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         opcode,
         dst: Some(Register { n: dst }),
         src: None,
-        off: Some(off),
-        imm: Some(Number::Int(imm.into())),
+        off: Some(Either::Right(off)),
+        imm: Some(Either::Right(Number::Int(imm.into()))),
         span: 0..8,
     })
 }
@@ -107,7 +110,7 @@ pub fn decode_store_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         opcode,
         dst: Some(Register { n: dst }),
         src: Some(Register { n: src }),
-        off: Some(off),
+        off: Some(Either::Right(off)),
         imm: None,
         span: 0..8,
     })
@@ -131,7 +134,7 @@ pub fn decode_binary_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         dst: Some(Register { n: dst }),
         src: None,
         off: None,
-        imm: Some(Number::Int(imm.into())),
+        imm: Some(Either::Right(Number::Int(imm.into()))),
         span: 0..8,
     })
 }
@@ -199,7 +202,7 @@ pub fn decode_jump(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         opcode,
         dst: None,
         src: None,
-        off: Some(off),
+        off: Some(Either::Right(off)),
         imm: None,
         span: 0..8,
     })
@@ -222,8 +225,8 @@ pub fn decode_jump_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         opcode,
         dst: Some(Register { n: dst }),
         src: None,
-        off: Some(off),
-        imm: Some(Number::Int(imm.into())),
+        off: Some(Either::Right(off)),
+        imm: Some(Either::Right(Number::Int(imm.into()))),
         span: 0..8,
     })
 }
@@ -245,7 +248,7 @@ pub fn decode_jump_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         opcode,
         dst: Some(Register { n: dst }),
         src: Some(Register { n: src }),
-        off: Some(off),
+        off: Some(Either::Right(off)),
         imm: None,
         span: 0..8,
     })
@@ -281,7 +284,7 @@ pub fn decode_call_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
         dst: None,
         src: None,
         off: None,
-        imm: Some(Number::Int(imm.into())),
+        imm: Some(Either::Right(Number::Int(imm.into()))),
         span: 0..8,
     })
 }

+ 0 - 164
crates/common/src/encode.rs

@@ -1,164 +0,0 @@
-use crate::{errors::SBPFError, instruction::Instruction, opcode::Opcode};
-
-pub fn encode_load_immediate(inst: &Instruction) -> Result<String, SBPFError> {
-    match (&inst.dst, &inst.imm) {
-        (Some(dst), Some(imm)) => Ok(format!("{} r{}, {}", inst.opcode, dst.n, imm)),
-        _ => Err(SBPFError::BytecodeError {
-            error: "Lddw instruction missing destination register or immediate value".to_string(),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_load_memory(inst: &Instruction) -> Result<String, SBPFError> {
-    match &inst.dst {
-        Some(dst) => Ok(format!("{} r{}, {}", inst.opcode, dst.n, inst.src_off())),
-        None => Err(SBPFError::BytecodeError {
-            error: format!("{} instruction missing destination register", inst.opcode),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_store_immediate(inst: &Instruction) -> Result<String, SBPFError> {
-    match &inst.imm {
-        Some(imm) => Ok(format!("{} {}, {}", inst.opcode, inst.dst_off(), imm)),
-        None => Err(SBPFError::BytecodeError {
-            error: format!("{} instruction missing immediate value", inst.opcode),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_store_register(inst: &Instruction) -> Result<String, SBPFError> {
-    match &inst.src {
-        Some(src) => Ok(format!("{} {}, r{}", inst.opcode, inst.dst_off(), src.n)),
-        None => Err(SBPFError::BytecodeError {
-            error: format!("{} instruction missing source register", inst.opcode),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_unary(inst: &Instruction) -> Result<String, SBPFError> {
-    match &inst.dst {
-        Some(dst) => Ok(format!("{} r{}", inst.opcode, dst.n)),
-        None => Err(SBPFError::BytecodeError {
-            error: format!("{} instruction missing destination register", inst.opcode),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_binary_immediate(inst: &Instruction) -> Result<String, SBPFError> {
-    match inst.opcode {
-        Opcode::Le | Opcode::Be => match &inst.dst {
-            Some(dst) => Ok(format!("{}{}", inst.op_imm_bits()?, dst.n)),
-            None => Err(SBPFError::BytecodeError {
-                error: format!("{} instruction missing destination register", inst.opcode),
-                span: inst.span.clone(),
-                custom_label: None,
-            }),
-        },
-        _ => match (&inst.dst, &inst.imm) {
-            (Some(dst), Some(imm)) => Ok(format!("{} r{}, {}", inst.opcode, dst.n, imm)),
-            _ => Err(SBPFError::BytecodeError {
-                error: format!(
-                    "{} instruction missing destination register or immediate value",
-                    inst.opcode
-                ),
-                span: inst.span.clone(),
-                custom_label: None,
-            }),
-        },
-    }
-}
-
-pub fn encode_binary_register(inst: &Instruction) -> Result<String, SBPFError> {
-    match (&inst.dst, &inst.src) {
-        (Some(dst), Some(src)) => Ok(format!("{} r{}, r{}", inst.opcode, dst.n, src.n)),
-        _ => Err(SBPFError::BytecodeError {
-            error: format!(
-                "{} instruction missing destination or source register",
-                inst.opcode
-            ),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_jump(inst: &Instruction) -> Result<String, SBPFError> {
-    Ok(format!("{} {}", inst.opcode, inst.off_str()))
-}
-
-pub fn encode_jump_immediate(inst: &Instruction) -> Result<String, SBPFError> {
-    match (&inst.dst, &inst.imm) {
-        (Some(dst), Some(imm)) => Ok(format!(
-            "{} r{}, {}, {}",
-            inst.opcode,
-            dst.n,
-            imm,
-            inst.off_str()
-        )),
-        _ => Err(SBPFError::BytecodeError {
-            error: format!(
-                "{} instruction missing destination register or immediate value",
-                inst.opcode
-            ),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_jump_register(inst: &Instruction) -> Result<String, SBPFError> {
-    match (&inst.dst, &inst.src) {
-        (Some(dst), Some(src)) => Ok(format!(
-            "{} r{}, r{}, {}",
-            inst.opcode,
-            dst.n,
-            src.n,
-            inst.off_str()
-        )),
-        _ => Err(SBPFError::BytecodeError {
-            error: format!(
-                "{} instruction missing destination or source register",
-                inst.opcode
-            ),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_call_immediate(inst: &Instruction) -> Result<String, SBPFError> {
-    match &inst.imm {
-        Some(imm) => Ok(format!("call {}", imm)),
-        None => Err(SBPFError::BytecodeError {
-            error: "Call instruction missing immediate value".to_string(),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_call_register(inst: &Instruction) -> Result<String, SBPFError> {
-    match &inst.src {
-        Some(src) => Ok(format!("call r{}", src.n)),
-        None => Err(SBPFError::BytecodeError {
-            error: "Callx instruction missing source register".to_string(),
-            span: inst.span.clone(),
-            custom_label: None,
-        }),
-    }
-}
-
-pub fn encode_exit(inst: &Instruction) -> Result<String, SBPFError> {
-    Ok(format!("{}", inst.opcode))
-}

+ 23 - 23
crates/common/src/inst_handler.rs

@@ -5,12 +5,6 @@ use crate::{
         decode_jump_register, decode_load_immediate, decode_load_memory, decode_store_immediate,
         decode_store_register, decode_unary,
     },
-    encode::{
-        encode_binary_immediate, encode_binary_register, encode_call_immediate,
-        encode_call_register, encode_exit, encode_jump, encode_jump_immediate,
-        encode_jump_register, encode_load_immediate, encode_load_memory, encode_store_immediate,
-        encode_store_register, encode_unary,
-    },
     errors::SBPFError,
     instruction::Instruction,
     opcode::{
@@ -18,14 +12,20 @@ use crate::{
         JUMP_REG_OPS, LOAD_IMM_OPS, LOAD_MEMORY_OPS, Opcode, OperationType, STORE_IMM_OPS,
         STORE_REG_OPS, UNARY_OPS,
     },
+    validate::{
+        validate_binary_immediate, validate_binary_register, validate_call_immediate,
+        validate_call_register, validate_exit, validate_jump, validate_jump_immediate,
+        validate_jump_register, validate_load_immediate, validate_load_memory,
+        validate_store_immediate, validate_store_register, validate_unary,
+    },
 };
 
 type DecodeFn = fn(&[u8]) -> Result<Instruction, SBPFError>;
-type EncodeFn = fn(&Instruction) -> Result<String, SBPFError>;
+type ValidateFn = fn(&Instruction) -> Result<(), SBPFError>;
 
 pub struct InstructionHandler {
     pub decode: DecodeFn,
-    pub encode: EncodeFn,
+    pub validate: ValidateFn,
 }
 
 use {once_cell::sync::Lazy, std::collections::HashMap};
@@ -38,10 +38,10 @@ pub static OPCODE_TO_HANDLER: Lazy<HashMap<Opcode, InstructionHandler>> = Lazy::
         map: &mut HashMap<Opcode, InstructionHandler>,
         ops: &[Opcode],
         decode: DecodeFn,
-        encode: EncodeFn,
+        validate: ValidateFn,
     ) {
         for &op in ops {
-            map.insert(op, InstructionHandler { decode, encode });
+            map.insert(op, InstructionHandler { decode, validate });
         }
     }
 
@@ -49,65 +49,65 @@ pub static OPCODE_TO_HANDLER: Lazy<HashMap<Opcode, InstructionHandler>> = Lazy::
         &mut map,
         LOAD_IMM_OPS,
         decode_load_immediate,
-        encode_load_immediate,
+        validate_load_immediate,
     );
     register_group(
         &mut map,
         LOAD_MEMORY_OPS,
         decode_load_memory,
-        encode_load_memory,
+        validate_load_memory,
     );
     register_group(
         &mut map,
         STORE_IMM_OPS,
         decode_store_immediate,
-        encode_store_immediate,
+        validate_store_immediate,
     );
     register_group(
         &mut map,
         STORE_REG_OPS,
         decode_store_register,
-        encode_store_register,
+        validate_store_register,
     );
     register_group(
         &mut map,
         BIN_IMM_OPS,
         decode_binary_immediate,
-        encode_binary_immediate,
+        validate_binary_immediate,
     );
     register_group(
         &mut map,
         BIN_REG_OPS,
         decode_binary_register,
-        encode_binary_register,
+        validate_binary_register,
     );
-    register_group(&mut map, UNARY_OPS, decode_unary, encode_unary);
-    register_group(&mut map, JUMP_OPS, decode_jump, encode_jump);
+    register_group(&mut map, UNARY_OPS, decode_unary, validate_unary);
+    register_group(&mut map, JUMP_OPS, decode_jump, validate_jump);
     register_group(
         &mut map,
         JUMP_IMM_OPS,
         decode_jump_immediate,
-        encode_jump_immediate,
+        validate_jump_immediate,
     );
     register_group(
         &mut map,
         JUMP_REG_OPS,
         decode_jump_register,
-        encode_jump_register,
+        validate_jump_register,
     );
     register_group(
         &mut map,
         CALL_IMM_OPS,
         decode_call_immediate,
-        encode_call_immediate,
+        validate_call_immediate,
     );
     register_group(
         &mut map,
         CALL_REG_OPS,
         decode_call_register,
-        encode_call_register,
+        validate_call_register,
     );
-    register_group(&mut map, EXIT_OPS, decode_exit, encode_exit);
+    register_group(&mut map, EXIT_OPS, decode_exit, validate_exit);
 
     map
 });

+ 0 - 5
crates/common/src/inst_param.rs

@@ -14,11 +14,6 @@ impl fmt::Display for Register {
     }
 }
 
-pub enum OperandValue {
-    Number(Number),
-    Ident(String),
-}
-
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub enum Number {
     Int(i64),

+ 92 - 101
crates/common/src/instruction.rs

@@ -6,6 +6,7 @@ use {
         opcode::{Opcode, OperationType},
     },
     core::ops::Range,
+    either::Either,
     serde::{Deserialize, Serialize},
 };
 
@@ -14,8 +15,8 @@ pub struct Instruction {
     pub opcode: Opcode,
     pub dst: Option<Register>,
     pub src: Option<Register>,
-    pub off: Option<i16>,
-    pub imm: Option<Number>,
+    pub off: Option<Either<String, i16>>,
+    pub imm: Option<Either<String, Number>>,
     pub span: Range<usize>,
 }
 
@@ -38,36 +39,10 @@ impl Instruction {
         )
     }
 
-    pub fn off_str(&self) -> String {
-        match self.off {
-            Some(off) => {
-                if off.is_negative() {
-                    off.to_string()
-                } else {
-                    format!("+{}", off)
-                }
-            }
-            None => "0".to_string(),
-        }
-    }
-
-    pub fn dst_off(&self) -> String {
-        match &self.dst {
-            Some(dst) => format!("[r{}{}]", dst.n, self.off_str()),
-            None => format!("[r0{}]", self.off_str()),
-        }
-    }
-
-    pub fn src_off(&self) -> String {
-        match &self.src {
-            Some(src) => format!("[r{}{}]", src.n, self.off_str()),
-            None => format!("[r0{}]", self.off_str()),
-        }
-    }
     // only used for be/le
     pub fn op_imm_bits(&self) -> Result<String, SBPFError> {
         match &self.imm {
-            Some(Number::Int(imm)) => match *imm {
+            Some(Either::Right(Number::Int(imm))) => match *imm {
                 16 => Ok(format!("{}16", self.opcode)),
                 32 => Ok(format!("{}32", self.opcode)),
                 64 => Ok(format!("{}64", self.opcode)),
@@ -101,12 +76,21 @@ impl Instruction {
         }
     }
 
-    pub fn to_bytes(&self) -> Vec<u8> {
+    pub fn to_bytes(&self) -> Result<Vec<u8>, SBPFError> {
         let src_val = self.src.as_ref().map(|r| r.n).unwrap_or(0);
         let dst_val = self.dst.as_ref().map(|r| r.n).unwrap_or(0);
-        let off_val = self.off.unwrap_or(0);
+        let off_val = match &self.off {
+            Some(Either::Left(ident)) => {
+                unreachable!("Identifier '{}' should have been resolved earlier", ident)
+            }
+            Some(Either::Right(off)) => *off,
+            None => 0,
+        };
         let imm_val = match &self.imm {
-            Some(Number::Int(imm)) | Some(Number::Addr(imm)) => *imm,
+            Some(Either::Left(ident)) => {
+                unreachable!("Identifier '{}' should have been resolved earlier", ident)
+            }
+            Some(Either::Right(Number::Int(imm))) | Some(Either::Right(Number::Addr(imm))) => *imm,
             None => 0,
         };
 
@@ -117,15 +101,77 @@ impl Instruction {
             b.extend_from_slice(&[0; 4]);
             b.extend_from_slice(&((imm_val >> 32) as i32).to_le_bytes());
         }
-        b
+        Ok(b)
     }
 
     pub fn to_asm(&self) -> Result<String, SBPFError> {
         if let Some(handler) = OPCODE_TO_HANDLER.get(&self.opcode) {
-            (handler.encode)(self)
+            match (handler.validate)(self) {
+                Ok(()) => {
+                    let mut asm = format!("{}", self.opcode);
+                    let mut param = vec![];
+
+                    fn off_str(off: &Either<String, i16>) -> String {
+                        match off {
+                            Either::Left(ident) => ident.clone(),
+                            Either::Right(offset) => {
+                                if offset.is_negative() {
+                                    offset.to_string()
+                                } else {
+                                    format!("+{}", offset)
+                                }
+                            }
+                        }
+                    }
+
+                    fn mem_off(r: &Register, off: &Either<String, i16>) -> String {
+                        format!("[r{}{}]", r.n, off_str(off))
+                    }
+
+                    if self.get_opcode_type() == OperationType::LoadMemory {
+                        param.push(format!("r{}", self.dst.as_ref().unwrap().n));
+                        param.push(mem_off(
+                            self.src.as_ref().unwrap(),
+                            self.off.as_ref().unwrap(),
+                        ));
+                    } else if self.get_opcode_type() == OperationType::StoreImmediate
+                        || self.get_opcode_type() == OperationType::StoreRegister
+                    {
+                        param.push(mem_off(
+                            self.dst.as_ref().unwrap(),
+                            self.off.as_ref().unwrap(),
+                        ));
+                        param.push(format!("r{}", self.src.as_ref().unwrap().n));
+                    } else {
+                        if let Some(dst) = &self.dst {
+                            param.push(format!("r{}", dst.n));
+                        }
+                        if let Some(src) = &self.src {
+                            param.push(format!("r{}", src.n));
+                        }
+                        if let Some(imm) = &self.imm {
+                            if self.opcode == Opcode::Le || self.opcode == Opcode::Be {
+                                todo!("handle le/be")
+                            } else {
+                                param.push(format!("{}", imm));
+                            }
+                        }
+                        if let Some(off) = &self.off {
+                            param.push(off_str(off).to_string());
+                        }
+                        // param.join(", ");
+                    }
+                    if !param.is_empty() {
+                        asm.push(' ');
+                        asm.push_str(&param.join(", "));
+                    }
+                    Ok(asm)
+                }
+                Err(e) => Err(e),
+            }
         } else {
             Err(SBPFError::BytecodeError {
-                error: format!("no encode handler for opcode {}", self.opcode),
+                error: format!("no validate handler for opcode {}", self.opcode),
                 span: self.span.clone(),
                 custom_label: None,
             })
@@ -135,19 +181,13 @@ impl Instruction {
 
 #[cfg(test)]
 mod test {
-    use {
-        crate::{
-            instruction::{Instruction, Register},
-            opcode::Opcode,
-        },
-        hex_literal::hex,
-    };
+    use {crate::instruction::Instruction, hex_literal::hex};
 
     #[test]
     fn serialize_e2e() {
         let b = hex!("9700000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "mod64 r0, 0");
     }
 
@@ -155,7 +195,7 @@ mod test {
     fn serialize_e2e_lddw() {
         let b = hex!("18010000000000000000000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "lddw r1, 0");
     }
 
@@ -163,7 +203,7 @@ mod test {
     fn serialize_e2e_add64_imm() {
         let b = hex!("0701000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "add64 r1, 0");
     }
 
@@ -171,7 +211,7 @@ mod test {
     fn serialize_e2e_add64_reg() {
         let b = hex!("0f12000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "add64 r2, r1");
     }
 
@@ -179,7 +219,7 @@ mod test {
     fn serialize_e2e_ja() {
         let b = hex!("05000a0000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "ja +10");
     }
 
@@ -187,7 +227,7 @@ mod test {
     fn serialize_e2e_jeq_imm() {
         let b = hex!("15030a0001000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "jeq r3, 1, +10");
     }
 
@@ -195,7 +235,7 @@ mod test {
     fn serialize_e2e_jeq_reg() {
         let b = hex!("1d210a0000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "jeq r1, r2, +10");
     }
 
@@ -203,7 +243,7 @@ mod test {
     fn serialize_e2e_ldxw() {
         let b = hex!("6112000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "ldxw r2, [r1+0]");
     }
 
@@ -211,7 +251,7 @@ mod test {
     fn serialize_e2e_stxw() {
         let b = hex!("6312000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "stxw [r2+0], r1");
     }
 
@@ -219,7 +259,7 @@ mod test {
     fn serialize_e2e_neg64() {
         let b = hex!("8700000000000000");
         let i = Instruction::from_bytes(&b).unwrap();
-        assert_eq!(i.to_bytes(), &b);
+        assert_eq!(i.to_bytes().unwrap(), &b);
         assert_eq!(i.to_asm().unwrap(), "neg64 r0");
     }
 
@@ -250,55 +290,6 @@ mod test {
         assert!(!add64.is_jump());
     }
 
-    #[test]
-    fn test_off_str() {
-        let pos_off = Instruction {
-            opcode: Opcode::Ja,
-            dst: None,
-            src: None,
-            off: Some(10),
-            imm: None,
-            span: 0..8,
-        };
-        assert_eq!(pos_off.off_str(), "+10");
-
-        let neg_off = Instruction {
-            opcode: Opcode::Ja,
-            dst: None,
-            src: None,
-            off: Some(-10),
-            imm: None,
-            span: 0..8,
-        };
-        assert_eq!(neg_off.off_str(), "-10");
-    }
-
-    #[test]
-    fn test_dst_off() {
-        let inst = Instruction {
-            opcode: Opcode::Ldxw,
-            dst: Some(Register { n: 1 }),
-            src: Some(Register { n: 2 }),
-            off: Some(10),
-            imm: None,
-            span: 0..8,
-        };
-        assert_eq!(inst.dst_off(), "[r1+10]");
-    }
-
-    #[test]
-    fn test_src_off() {
-        let inst = Instruction {
-            opcode: Opcode::Ldxw,
-            dst: Some(Register { n: 1 }),
-            src: Some(Register { n: 2 }),
-            off: Some(-5),
-            imm: None,
-            span: 0..8,
-        };
-        assert_eq!(inst.src_off(), "[r2-5]");
-    }
-
     #[test]
     fn test_invalid_opcode() {
         let result = Instruction::from_bytes(&hex!("ff00000000000000"));

+ 1 - 1
crates/common/src/lib.rs

@@ -1,5 +1,4 @@
 pub mod decode;
-pub mod encode;
 pub mod errors;
 pub mod inst_handler;
 pub mod inst_param;
@@ -7,3 +6,4 @@ pub mod instruction;
 pub mod opcode;
 pub mod syscalls;
 pub mod syscalls_map;
+pub mod validate;

+ 1 - 1
crates/common/src/opcode.rs

@@ -5,7 +5,7 @@ use {
     serde::{Deserialize, Serialize},
 };
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq)]
 pub enum OperationType {
     LoadImmediate,
     LoadMemory,

+ 168 - 0
crates/common/src/validate.rs

@@ -0,0 +1,168 @@
+use crate::{errors::SBPFError, instruction::Instruction};
+
+// TODO validate fields that are supposed to be None
+
+pub fn validate_load_immediate(inst: &Instruction) -> Result<(), SBPFError> {
+    match (&inst.dst, &inst.imm) {
+        (Some(_dst), Some(_imm)) => Ok(()),
+        // Ok(format!("{} r{}, {}", inst.opcode, dst.n, imm)),
+        _ => Err(SBPFError::BytecodeError {
+            error: "Lddw instruction missing destination register or immediate value".to_string(),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_load_memory(inst: &Instruction) -> Result<(), SBPFError> {
+    match &inst.dst {
+        Some(_dst) => Ok(()),
+        // Ok(format!("{} r{}, {}", inst.opcode, dst.n, inst.src_off())),
+        None => Err(SBPFError::BytecodeError {
+            error: format!("{} instruction missing destination register", inst.opcode),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_store_immediate(inst: &Instruction) -> Result<(), SBPFError> {
+    match &inst.imm {
+        Some(_imm) => Ok(()),
+        // Ok(format!("{} {}, {}", inst.opcode, inst.dst_off(), imm)),
+        None => Err(SBPFError::BytecodeError {
+            error: format!("{} instruction missing immediate value", inst.opcode),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_store_register(inst: &Instruction) -> Result<(), SBPFError> {
+    match &inst.src {
+        Some(_src) => Ok(()),
+        // Ok(format!("{} {}, r{}", inst.opcode, inst.dst_off(), src.n)),
+        None => Err(SBPFError::BytecodeError {
+            error: format!("{} instruction missing source register", inst.opcode),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_unary(inst: &Instruction) -> Result<(), SBPFError> {
+    match &inst.dst {
+        Some(_dst) => Ok(()),
+        // Ok(format!("{} r{}", inst.opcode, dst.n)),
+        None => Err(SBPFError::BytecodeError {
+            error: format!("{} instruction missing destination register", inst.opcode),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_binary_immediate(inst: &Instruction) -> Result<(), SBPFError> {
+    //     match inst.opcode {
+    //         Opcode::Le | Opcode::Be => match &inst.dst {
+    //             Some(dst) => Ok(format!("{}{}", inst.op_imm_bits()?, dst.n)),
+    //             None => Err(SBPFError::BytecodeError {
+    //                 error: format!("{} instruction missing destination register", inst.opcode),
+    //                 span: inst.span.clone(),
+    //                 custom_label: None,
+    //             }),
+    //         },
+    //         _ =>
+    match (&inst.dst, &inst.imm) {
+        (Some(_dst), Some(_imm)) => Ok(()),
+        // Ok(format!("{} r{}, {}", inst.opcode, dst.n, imm)),
+        _ => Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction missing destination register or immediate value",
+                inst.opcode
+            ),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+// }
+
+pub fn validate_binary_register(inst: &Instruction) -> Result<(), SBPFError> {
+    match (&inst.dst, &inst.src) {
+        (Some(_dst), Some(_src)) => Ok(()),
+        // Ok(format!("{} r{}, r{}", inst.opcode, dst.n, src.n)),
+        _ => Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction missing destination or source register",
+                inst.opcode
+            ),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_jump(_inst: &Instruction) -> Result<(), SBPFError> {
+    Ok(())
+    // Ok(format!("{} {}", inst.opcode, inst.off_str()))
+}
+
+pub fn validate_jump_immediate(inst: &Instruction) -> Result<(), SBPFError> {
+    match (&inst.dst, &inst.imm) {
+        (Some(_dst), Some(_imm)) => Ok(()),
+        // Ok(format!("{} r{}, {}, {}", inst.opcode, dst.n, imm, inst.off_str())),
+        _ => Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction missing destination register or immediate value",
+                inst.opcode
+            ),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_jump_register(inst: &Instruction) -> Result<(), SBPFError> {
+    match (&inst.dst, &inst.src) {
+        (Some(_dst), Some(_src)) => Ok(()),
+        // Ok(format!("{} r{}, r{}, {}", inst.opcode, dst.n, src.n, inst.off_str())),
+        _ => Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction missing destination or source register",
+                inst.opcode
+            ),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_call_immediate(inst: &Instruction) -> Result<(), SBPFError> {
+    match &inst.imm {
+        Some(_imm) => Ok(()),
+        // Ok(format!("call {}", imm)),
+        None => Err(SBPFError::BytecodeError {
+            error: "Call instruction missing immediate value".to_string(),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_call_register(inst: &Instruction) -> Result<(), SBPFError> {
+    match &inst.src {
+        Some(_src) => Ok(()),
+        // Ok(format!("call r{}", src.n)),
+        None => Err(SBPFError::BytecodeError {
+            error: "Callx instruction missing source register".to_string(),
+            span: inst.span.clone(),
+            custom_label: None,
+        }),
+    }
+}
+
+pub fn validate_exit(_inst: &Instruction) -> Result<(), SBPFError> {
+    Ok(())
+    // Ok(format!("{}", inst.opcode))
+}

+ 1 - 0
crates/disassembler/Cargo.toml

@@ -19,6 +19,7 @@ object = { workspace = true }
 sbpf-common = { workspace = true }
 thiserror = "2.0.17"
 serde = { version = "1.0.228", features = ["derive"] }
+either = { version = "1.15.0", features = ["serde"] }
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 wasm-bindgen = { version = "0.2.92", features = ["serde-serialize"] }

+ 3 - 2
crates/disassembler/src/section_header_entry.rs

@@ -75,6 +75,7 @@ impl SectionHeaderEntry {
 mod test {
     use {
         crate::section_header_entry::SectionHeaderEntry,
+        either::Either,
         sbpf_common::{
             inst_param::{Number, Register},
             instruction::Instruction,
@@ -97,7 +98,7 @@ mod test {
                 dst: Some(Register { n: 1 }),
                 src: None,
                 off: None,
-                imm: Some(Number::Int(0)),
+                imm: Some(Either::Right(Number::Int(0))),
                 span: 0..16,
             },
             Instruction {
@@ -116,7 +117,7 @@ mod test {
             h.to_ixs()
                 .expect("Invalid IX")
                 .into_iter()
-                .flat_map(|i| i.to_bytes())
+                .flat_map(|i| i.to_bytes().unwrap())
                 .collect::<Vec<u8>>()
         )
     }