Bladeren bron

Merge branch 'master' of https://github.com/blueshift-gg/sbpf

Dean 利迪恩 3 weken geleden
bovenliggende
commit
82369f4fa3
40 gewijzigde bestanden met toevoegingen van 1148 en 740 verwijderingen
  1. 42 0
      .github/workflows/ci.yml
  2. 12 8
      Cargo.toml
  3. 1 0
      crates/assembler/Cargo.toml
  4. 54 55
      crates/assembler/src/ast.rs
  5. 10 7
      crates/assembler/src/astnode.rs
  6. 1 2
      crates/assembler/src/errors.rs
  7. 72 46
      crates/assembler/src/instruction.rs
  8. 5 3
      crates/assembler/src/lexer.rs
  9. 30 32
      crates/assembler/src/parser.rs
  10. 12 12
      crates/assembler/src/program.rs
  11. 12 11
      crates/assembler/src/section.rs
  12. 1 2
      crates/assembler/src/syscall.rs
  13. 7 7
      crates/assembler/src/wasm.rs
  14. 4 6
      crates/assembler/tests/regression.rs
  15. 2 1
      crates/common/Cargo.toml
  16. 335 0
      crates/common/src/decode.rs
  17. 1 2
      crates/common/src/errors.rs
  18. 101 0
      crates/common/src/inst_handler.rs
  19. 82 0
      crates/common/src/inst_param.rs
  20. 48 390
      crates/common/src/instruction.rs
  21. 3 0
      crates/common/src/lib.rs
  22. 151 5
      crates/common/src/opcode.rs
  23. 1 0
      crates/disassembler/Cargo.toml
  24. 15 13
      crates/disassembler/src/elf_header.rs
  25. 1 2
      crates/disassembler/src/errors.rs
  26. 8 10
      crates/disassembler/src/program.rs
  27. 7 10
      crates/disassembler/src/program_header.rs
  28. 7 11
      crates/disassembler/src/section_header.rs
  29. 20 13
      crates/disassembler/src/section_header_entry.rs
  30. 6 4
      crates/disassembler/src/wasm.rs
  31. 3 0
      rustfmt.toml
  32. 31 31
      src/commands/build.rs
  33. 9 10
      src/commands/clean.rs
  34. 10 13
      src/commands/deploy.rs
  35. 5 5
      src/commands/disassemble.rs
  36. 12 8
      src/commands/init.rs
  37. 6 10
      src/commands/test.rs
  38. 5 3
      src/main.rs
  39. 2 2
      tests/test_memo.rs
  40. 14 6
      tests/utils.rs

+ 42 - 0
.github/workflows/ci.yml

@@ -0,0 +1,42 @@
+name: CI
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+
+env:
+  CARGO_TERM_COLOR: always
+
+jobs:
+  fmt:
+    name: rustfmt (nightly)
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v5
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: nightly
+          components: rustfmt
+      - uses: Swatinem/rust-cache@v2
+        with:
+          cache-on-failure: true
+      - name: rustfmt (check)
+        run: cargo +nightly fmt --all -- --check
+
+  lint-test:
+    name: clippy & tests (stable)
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v5
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: stable
+          components: clippy
+      - uses: Swatinem/rust-cache@v2
+        with:
+          cache-on-failure: true
+      - name: clippy (deny warnings)
+        run: cargo clippy --all-targets --all-features -- -D warnings
+      - name: tests
+        run: cargo test --all-features --all-targets --no-fail-fast

+ 12 - 8
Cargo.toml

@@ -1,14 +1,18 @@
 [package]
 name = "sbpf"
 version = "0.1.5"
-authors = ["Dean Little <dean@blueshift.gg>", "Claire Fan <claire@blueshift.gg>"]
-edition = "2024"
+authors = [
+    "Dean Little <dean@blueshift.gg>",
+    "Claire Fan <claire@blueshift.gg>",
+]
+edition.workspace = true
 description = "A complete toolchain for building and deploying Solana BPF programs"
-license = "MIT"
-repository = "https://github.com/blueshift-gg/sbpf"
+license.workspace = true
+repository.workspace = true
 readme = "README.md"
 keywords = ["solana", "bpf", "blockchain", "assembler"]
 categories = ["development-tools", "command-line-utilities"]
+rust-version.workspace = true
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
@@ -30,14 +34,14 @@ exclude = ["examples"]
 
 [workspace.package]
 version = "0.1.5"
-edition = "2021"
+edition = "2024"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/blueshift-gg/sbpf"
-authors = ["Dean Little <dean@blueshift.gg>", "Claire Fan <claire@blueshift.gg>"]
+rust-version = "1.90"
 
 [workspace.dependencies]
-num-derive    = "0.4"
-num-traits    = "0.2"
+num-derive = "0.4"
+num-traits = "0.2"
 thiserror = "2.0.12"
 anyhow = "1.0.86"
 object = "0.37.3"

+ 1 - 0
crates/assembler/Cargo.toml

@@ -8,6 +8,7 @@ authors.workspace = true
 description = "Assembler for SBPF (Solana BPF) assembly language"
 keywords = ["solana", "bpf", "assembler", "blockchain"]
 categories = ["development-tools", "compilers"]
+rust-version.workspace = true
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 54 - 55
crates/assembler/src/ast.rs

@@ -1,13 +1,16 @@
-use crate::CompileError;
-use crate::astnode::{ASTNode, ROData};
-use crate::dynsym::{DynamicSymbolMap, RelDynMap, RelocationType};
-use crate::instruction::Instruction;
-use crate::lexer::{ImmediateValue, Token};
-use crate::parser::ParseResult;
-use crate::section::{CodeSection, DataSection};
-use sbpf_common::opcode::Opcode;
-
-use std::collections::HashMap;
+use {
+    crate::{
+        CompileError,
+        astnode::{ASTNode, ROData},
+        dynsym::{DynamicSymbolMap, RelDynMap, RelocationType},
+        instruction::Instruction,
+        lexer::{ImmediateValue, Token},
+        parser::ParseResult,
+        section::{CodeSection, DataSection},
+    },
+    sbpf_common::opcode::Opcode,
+    std::collections::HashMap,
+};
 
 #[derive(Default)]
 pub struct AST {
@@ -105,25 +108,23 @@ impl AST {
             } = node
             {
                 // For jump/call instructions, replace label with relative offsets
-                if inst.is_jump() || inst.opcode == Opcode::Call {
-                    if let Some(Token::Identifier(label, span)) = inst.operands.last() {
-                        let label = label.clone();
-                        if let Some(target_offset) = label_offset_map.get(&label) {
-                            let rel_offset = (*target_offset as i64 - *offset as i64) / 8 - 1;
-                            let last_idx = inst.operands.len() - 1;
-                            inst.operands[last_idx] = Token::ImmediateValue(
-                                ImmediateValue::Int(rel_offset),
-                                span.clone(),
-                            );
-                        } else if inst.is_jump() {
-                            // only error out unresolved jump labels, since call
-                            // labels could exist externally
-                            errors.push(CompileError::UndefinedLabel {
-                                label: label.clone(),
-                                span: span.clone(),
-                                custom_label: None,
-                            });
-                        }
+                if (inst.is_jump() || inst.opcode == Opcode::Call)
+                    && let Some(Token::Identifier(label, span)) = inst.operands.last()
+                {
+                    let label = label.clone();
+                    if let Some(target_offset) = label_offset_map.get(&label) {
+                        let rel_offset = (*target_offset as i64 - *offset as i64) / 8 - 1;
+                        let last_idx = inst.operands.len() - 1;
+                        inst.operands[last_idx] =
+                            Token::ImmediateValue(ImmediateValue::Int(rel_offset), span.clone());
+                    } else if inst.is_jump() {
+                        // only error out unresolved jump labels, since call
+                        // labels could exist externally
+                        errors.push(CompileError::UndefinedLabel {
+                            label: label.clone(),
+                            span: span.clone(),
+                            custom_label: None,
+                        });
                     }
                 }
                 // This has to be done before resolving lddw labels since lddw
@@ -136,38 +137,36 @@ impl AST {
                         dynamic_symbols.add_call_target(label.clone(), *offset);
                     }
                 }
-                if inst.opcode == Opcode::Lddw {
-                    if let Some(Token::Identifier(name, span)) = inst.operands.last() {
-                        let label = name.clone();
-                        if let Some(target_offset) = label_offset_map.get(&label) {
-                            // actually lddw with label makes a program dynamic, so
-                            // we should be able to hard code ph_offset
-                            let ph_count = if program_is_static { 1 } else { 3 };
-                            let ph_offset = 64 + (ph_count as u64 * 56) as i64;
-                            let abs_offset = *target_offset as i64 + ph_offset;
-                            // Replace label with immediate value
-                            let last_idx = inst.operands.len() - 1;
-                            inst.operands[last_idx] = Token::ImmediateValue(
-                                ImmediateValue::Addr(abs_offset),
-                                span.clone(),
-                            );
-                        } else {
-                            errors.push(CompileError::UndefinedLabel {
-                                label: name.clone(),
-                                span: span.clone(),
-                                custom_label: None,
-                            });
-                        }
+                if inst.opcode == Opcode::Lddw
+                    && let Some(Token::Identifier(name, span)) = inst.operands.last()
+                {
+                    let label = name.clone();
+                    if let Some(target_offset) = label_offset_map.get(&label) {
+                        // actually lddw with label makes a program dynamic, so
+                        // we should be able to hard code ph_offset
+                        let ph_count = if program_is_static { 1 } else { 3 };
+                        let ph_offset = 64 + (ph_count as u64 * 56) as i64;
+                        let abs_offset = *target_offset as i64 + ph_offset;
+                        // Replace label with immediate value
+                        let last_idx = inst.operands.len() - 1;
+                        inst.operands[last_idx] =
+                            Token::ImmediateValue(ImmediateValue::Addr(abs_offset), span.clone());
+                    } else {
+                        errors.push(CompileError::UndefinedLabel {
+                            label: name.clone(),
+                            span: span.clone(),
+                            custom_label: None,
+                        });
                     }
                 }
             }
         }
 
         // Set entry point offset if an entry label was specified
-        if let Some(entry_label) = &self.entry_label {
-            if let Some(offset) = label_offset_map.get(entry_label) {
-                dynamic_symbols.add_entry_point(entry_label.clone(), *offset);
-            }
+        if let Some(entry_label) = &self.entry_label
+            && let Some(offset) = label_offset_map.get(entry_label)
+        {
+            dynamic_symbols.add_entry_point(entry_label.clone(), *offset);
         }
 
         if !errors.is_empty() {

+ 10 - 7
crates/assembler/src/astnode.rs

@@ -1,10 +1,13 @@
-use crate::debuginfo::{DebugInfo, RegisterHint, RegisterType};
-use crate::errors::CompileError;
-use crate::instruction::Instruction;
-use crate::lexer::{ImmediateValue, Token};
-use sbpf_common::opcode::Opcode;
-use std::collections::HashMap;
-use std::ops::Range;
+use {
+    crate::{
+        debuginfo::{DebugInfo, RegisterHint, RegisterType},
+        errors::CompileError,
+        instruction::Instruction,
+        lexer::{ImmediateValue, Token},
+    },
+    sbpf_common::opcode::Opcode,
+    std::{collections::HashMap, ops::Range},
+};
 
 #[derive(Debug, Clone)]
 pub enum ASTNode {

+ 1 - 2
crates/assembler/src/errors.rs

@@ -1,5 +1,4 @@
-use crate::define_compile_errors;
-use std::ops::Range;
+use {crate::define_compile_errors, std::ops::Range};
 
 // labels could be overridden by passing a valid custom_label in the error variant
 // if not provided, the label will use default messages from below

+ 72 - 46
crates/assembler/src/instruction.rs

@@ -1,10 +1,13 @@
-use crate::dynsym::RelocationType;
-use crate::lexer::{ImmediateValue, Token};
-use crate::syscall::SYSCALLS;
-use crate::errors::CompileError;
-use sbpf_common::opcode::Opcode;
-
-use std::ops::Range;
+use {
+    crate::{
+        dynsym::RelocationType,
+        errors::CompileError,
+        lexer::{ImmediateValue, Token},
+        syscall::SYSCALLS,
+    },
+    sbpf_common::opcode::Opcode,
+    std::ops::Range,
+};
 
 #[derive(Debug, Clone)]
 pub struct Instruction {
@@ -33,32 +36,9 @@ impl Instruction {
 
     //
     pub fn is_jump(&self) -> bool {
-        matches!(
-            self.opcode,
-            Opcode::Ja
-                | Opcode::JeqImm
-                | Opcode::JgtImm
-                | Opcode::JgeImm
-                | Opcode::JltImm
-                | Opcode::JleImm
-                | Opcode::JsetImm
-                | Opcode::JneImm
-                | Opcode::JsgtImm
-                | Opcode::JsgeImm
-                | Opcode::JsltImm
-                | Opcode::JsleImm
-                | Opcode::JeqReg
-                | Opcode::JgtReg
-                | Opcode::JgeReg
-                | Opcode::JltReg
-                | Opcode::JleReg
-                | Opcode::JsetReg
-                | Opcode::JneReg
-                | Opcode::JsgtReg
-                | Opcode::JsgeReg
-                | Opcode::JsltReg
-                | Opcode::JsleReg
-        )
+        JUMP_OPS.contains(&self.opcode) 
+            || JUMP_IMM_OPS.contains(&self.opcode) 
+            || JUMP_REG_OPS.contains(&self.opcode)
     }
     //
     pub fn get_relocation_info(&self) -> (RelocationType, String) {
@@ -100,7 +80,11 @@ impl Instruction {
             Opcode::Lddw => {
                 if src != 0 || off != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Lddw instruction expects src and off to be 0, but got src: {}, off: {}", src, off),
+                        error: format!(
+                            "Lddw instruction expects src and off to be 0, but got src: {}, off: \
+                             {}",
+                            src, off
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -113,7 +97,11 @@ impl Instruction {
                 if let Some(name) = SYSCALLS.get(&(imm as u32)) {
                     if reg != 0 || off != 0 {
                         return Err(CompileError::BytecodeError {
-                            error: format!("Call instruction with syscall expects reg and off to be 0, but got reg: {}, off: {}", reg, off),
+                            error: format!(
+                                "Call instruction with syscall expects reg and off to be 0, but \
+                                 got reg: {}, off: {}",
+                                reg, off
+                            ),
                             span: span.clone(),
                             custom_label: None,
                         });
@@ -122,7 +110,11 @@ impl Instruction {
                 } else {
                     if reg != 16 || off != 0 {
                         return Err(CompileError::BytecodeError {
-                            error: format!("Call instruction with immediate expects reg to be 16 and off to be 0, but got reg: {}, off: {}", reg, off),
+                            error: format!(
+                                "Call instruction with immediate expects reg to be 16 and off to \
+                                 be 0, but got reg: {}, off: {}",
+                                reg, off
+                            ),
                             span: span.clone(),
                             custom_label: None,
                         });
@@ -134,7 +126,11 @@ impl Instruction {
             Opcode::Callx => {
                 if src != 0 || off != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Callx instruction expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
+                        error: format!(
+                            "Callx instruction expects src, off, and imm to be 0, but got src: \
+                             {}, off: {}, imm: {}",
+                            src, off, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -146,7 +142,10 @@ impl Instruction {
             Opcode::Ja => {
                 if reg != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}", reg, imm),
+                        error: format!(
+                            "Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}",
+                            reg, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -167,7 +166,10 @@ impl Instruction {
             | Opcode::JsleImm => {
                 if src != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Jump instruction with immediate expects src to be 0, but got src: {}", src),
+                        error: format!(
+                            "Jump instruction with immediate expects src to be 0, but got src: {}",
+                            src
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -190,7 +192,10 @@ impl Instruction {
             | Opcode::JsleReg => {
                 if imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Jump instruction with register expects imm to be 0, but got imm: {}", imm),
+                        error: format!(
+                            "Jump instruction with register expects imm to be 0, but got imm: {}",
+                            imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -242,7 +247,11 @@ impl Instruction {
             | Opcode::Le => {
                 if src != 0 || off != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Arithmetic instruction with immediate expects src and off to be 0, but got src: {}, off: {}", src, off),
+                        error: format!(
+                            "Arithmetic instruction with immediate expects src and off to be 0, \
+                             but got src: {}, off: {}",
+                            src, off
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -290,7 +299,11 @@ impl Instruction {
             | Opcode::Srem64Reg => {
                 if off != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Arithmetic instruction with register expects off and imm to be 0, but got off: {}, imm: {}", off, imm),
+                        error: format!(
+                            "Arithmetic instruction with register expects off and imm to be 0, \
+                             but got off: {}, imm: {}",
+                            off, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -302,7 +315,10 @@ impl Instruction {
             Opcode::Ldxw | Opcode::Ldxh | Opcode::Ldxb | Opcode::Ldxdw => {
                 if imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Load instruction expects imm to be 0, but got imm: {}", imm),
+                        error: format!(
+                            "Load instruction expects imm to be 0, but got imm: {}",
+                            imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -315,7 +331,10 @@ impl Instruction {
             Opcode::Stw | Opcode::Sth | Opcode::Stb | Opcode::Stdw => {
                 if src != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Store instruction expects src to be 0, but got src: {}", src),
+                        error: format!(
+                            "Store instruction expects src to be 0, but got src: {}",
+                            src
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -328,7 +347,10 @@ impl Instruction {
             Opcode::Stxb | Opcode::Stxh | Opcode::Stxw | Opcode::Stxdw => {
                 if imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Store instruction with register expects imm to be 0, but got imm: {}", imm),
+                        error: format!(
+                            "Store instruction with register expects imm to be 0, but got imm: {}",
+                            imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -342,7 +364,11 @@ impl Instruction {
             Opcode::Neg32 | Opcode::Neg64 | Opcode::Exit => {
                 if src != 0 || off != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Unary operation expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
+                        error: format!(
+                            "Unary operation expects src, off, and imm to be 0, but got src: {}, \
+                             off: {}, imm: {}",
+                            src, off, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });

+ 5 - 3
crates/assembler/src/lexer.rs

@@ -1,6 +1,8 @@
-use crate::errors::CompileError;
-use sbpf_common::opcode::Opcode;
-use std::{ops::Range, str::FromStr as _};
+use {
+    crate::errors::CompileError,
+    sbpf_common::opcode::Opcode,
+    std::{ops::Range, str::FromStr as _},
+};
 
 #[derive(Debug, Clone)]
 pub enum Op {

+ 30 - 32
crates/assembler/src/parser.rs

@@ -1,18 +1,19 @@
-use crate::ast::AST;
-use crate::astnode::{
-    ASTNode, Directive, EquDecl, ExternDecl, GlobalDecl, Label, ROData, RodataDecl,
+use {
+    crate::{
+        ast::AST,
+        astnode::{ASTNode, Directive, EquDecl, ExternDecl, GlobalDecl, Label, ROData, RodataDecl},
+        bug,
+        dynsym::{DynamicSymbolMap, RelDynMap},
+        errors::CompileError,
+        instruction::Instruction,
+        lexer::{ImmediateValue, Op, Token},
+        messages::*,
+        section::{CodeSection, DataSection},
+    },
+    num_traits::FromPrimitive,
+    sbpf_common::opcode::Opcode,
+    std::collections::HashMap,
 };
-use crate::bug;
-use crate::dynsym::{DynamicSymbolMap, RelDynMap};
-use crate::errors::CompileError;
-use crate::instruction::Instruction;
-use crate::lexer::Op;
-use crate::lexer::{ImmediateValue, Token};
-use crate::messages::*;
-use crate::section::{CodeSection, DataSection};
-use num_traits::FromPrimitive;
-use sbpf_common::opcode::Opcode;
-use std::collections::HashMap;
 
 pub struct ParseResult {
     // TODO: parse result is basically 1. static part 2. dynamic part of the program
@@ -944,12 +945,11 @@ fn inline_and_fold_constant_with_map(
                 expect_number = false;
 
                 // Immediately fold * / if top
-                if stack.len() > 2 {
-                    if let Token::BinaryOp(op, _) = &stack[stack.len() - 2] {
-                        if matches!(op, Op::Mul | Op::Div) {
-                            fold_top(&mut stack);
-                        }
-                    }
+                if stack.len() > 2
+                    && let Token::BinaryOp(op, _) = &stack[stack.len() - 2]
+                    && matches!(op, Op::Mul | Op::Div)
+                {
+                    fold_top(&mut stack);
                 }
             }
 
@@ -959,12 +959,11 @@ fn inline_and_fold_constant_with_map(
                         stack.push(Token::ImmediateValue(val.clone(), span.clone()));
                         expect_number = false;
 
-                        if stack.len() > 2 {
-                            if let Token::BinaryOp(op, _) = &stack[stack.len() - 2] {
-                                if matches!(op, Op::Mul | Op::Div) {
-                                    fold_top(&mut stack);
-                                }
-                            }
+                        if stack.len() > 2
+                            && let Token::BinaryOp(op, _) = &stack[stack.len() - 2]
+                            && matches!(op, Op::Mul | Op::Div)
+                        {
+                            fold_top(&mut stack);
                         }
                     } else {
                         return (None, idx);
@@ -998,12 +997,11 @@ fn inline_and_fold_constant_with_map(
                     stack.push(Token::ImmediateValue(v, span.clone()));
                     expect_number = false;
 
-                    if stack.len() > 2 {
-                        if let Token::BinaryOp(op, _) = &stack[stack.len() - 2] {
-                            if matches!(op, Op::Mul | Op::Div) {
-                                fold_top(&mut stack);
-                            }
-                        }
+                    if stack.len() > 2
+                        && let Token::BinaryOp(op, _) = &stack[stack.len() - 2]
+                        && matches!(op, Op::Mul | Op::Div)
+                    {
+                        fold_top(&mut stack);
                     }
                 } else {
                     return (None, idx);

+ 12 - 12
crates/assembler/src/program.rs

@@ -1,16 +1,16 @@
-use crate::debuginfo::DebugInfo;
-use crate::dynsym::{DynamicSymbol, RelDyn, RelocationType};
-use crate::header::ElfHeader;
-use crate::header::ProgramHeader;
-use crate::parser::ParseResult;
-use crate::section::{
-    DynStrSection, DynSymSection, DynamicSection, NullSection, RelDynSection, Section, SectionType,
-    ShStrTabSection,
+use {
+    crate::{
+        debuginfo::DebugInfo,
+        dynsym::{DynamicSymbol, RelDyn, RelocationType},
+        header::{ElfHeader, ProgramHeader},
+        parser::ParseResult,
+        section::{
+            DynStrSection, DynSymSection, DynamicSection, NullSection, RelDynSection, Section,
+            SectionType, ShStrTabSection,
+        },
+    },
+    std::{collections::HashMap, fs::File, io::Write, path::Path},
 };
-use std::collections::HashMap;
-use std::fs::File;
-use std::io::Write;
-use std::path::Path;
 
 #[derive(Debug)]
 pub struct Program {

+ 12 - 11
crates/assembler/src/section.rs

@@ -1,11 +1,13 @@
-use crate::astnode::ASTNode;
-use crate::astnode::ROData;
-use crate::debuginfo::DebugInfo;
-use crate::dynsym::DynamicSymbol;
-use crate::dynsym::RelDyn;
-use crate::header::SectionHeader;
-use crate::lexer::Token;
-use std::collections::HashMap;
+use {
+    crate::{
+        astnode::{ASTNode, ROData},
+        debuginfo::DebugInfo,
+        dynsym::{DynamicSymbol, RelDyn},
+        header::SectionHeader,
+        lexer::Token,
+    },
+    std::collections::HashMap,
+};
 
 // Base Section trait
 pub trait Section {
@@ -146,10 +148,9 @@ impl DataSection {
                 rodata: ROData { name, args, .. },
                 offset,
             } = node
+                && let Some(Token::StringLiteral(str_literal, _)) = args.get(1)
             {
-                if let Some(Token::StringLiteral(str_literal, _)) = args.get(1) {
-                    ro_data_labels.push((name.clone(), *offset as usize, str_literal.clone()));
-                }
+                ro_data_labels.push((name.clone(), *offset as usize, str_literal.clone()));
             }
         }
         ro_data_labels

+ 1 - 2
crates/assembler/src/syscall.rs

@@ -1,5 +1,4 @@
-use phf::Map;
-use phf_macros::phf_map;
+use {phf::Map, phf_macros::phf_map};
 
 pub static SYSCALLS: Map<u32, &'static str> = phf_map! {
     0xb6fc1a11u32 => "abort",

+ 7 - 7
crates/assembler/src/wasm.rs

@@ -1,10 +1,10 @@
-use crate::lexer::tokenize;
-use crate::parser::parse_tokens;
-use crate::program::Program;
-use serde::Serialize;
-use serde_wasm_bindgen::to_value;
-use std::ops::Range;
-use wasm_bindgen::prelude::*;
+use {
+    crate::{lexer::tokenize, parser::parse_tokens, program::Program},
+    serde::Serialize,
+    serde_wasm_bindgen::to_value,
+    std::ops::Range,
+    wasm_bindgen::prelude::*,
+};
 
 #[derive(Serialize)]
 struct CompileErrorInfo {

+ 4 - 6
crates/assembler/tests/regression.rs

@@ -1,9 +1,7 @@
-use std::collections::BTreeMap;
-use std::env;
-use std::fs;
-use std::path::PathBuf;
-
-use serde::{Deserialize, Serialize};
+use {
+    serde::{Deserialize, Serialize},
+    std::{collections::BTreeMap, env, fs, path::PathBuf},
+};
 
 #[derive(Debug, Deserialize, Serialize)]
 struct Manifest {

+ 2 - 1
crates/common/Cargo.toml

@@ -8,6 +8,7 @@ authors.workspace = true
 description = "Common types and utilities for SBPF (Solana BPF)"
 keywords = ["solana", "bpf", "blockchain"]
 categories = ["development-tools"]
+rust-version.workspace = true
 
 [lib]
 crate-type = ["cdylib", "lib"]
@@ -19,7 +20,7 @@ num-traits = { workspace = true }
 thiserror = { workspace = true }
 serde = { version = "1.0.228", features = ["derive"] }
 sbpf-syscall-map = { workspace = true }
-
+once_cell = "1.21.3"
 
 [dev-dependencies]
 hex-literal = "1.0.0"

+ 335 - 0
crates/common/src/decode.rs

@@ -0,0 +1,335 @@
+use crate::errors::SBPFError;
+use crate::instruction::Instruction;
+use crate::inst_param::{
+    Register,
+    Number,
+};
+use crate::opcode::Opcode;
+use crate::syscall::SYSCALLS;
+
+// TODO: passing span for error reporting (not sure if it's necessary)
+
+#[inline]
+fn parse_bytes(bytes: &[u8]) -> Result<(Opcode, u8, u8, i16, i32), SBPFError> {
+    let opcode = Opcode::from_u8(bytes[0]).ok_or(SBPFError::BytecodeError {
+        error: format!("Invalid opcode: {:?}", bytes[0]),
+        span: 0..bytes.len(),
+        custom_label: None,
+    })?;
+    let reg = bytes[1];
+    let dst = reg & 0x0f;
+    let src = reg >> 4;
+    let off = i16::from_le_bytes([bytes[2], bytes[3]]);
+    let imm = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
+    Ok((opcode, dst, src, off, imm))
+}
+
+pub fn decode_load_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 16);
+    let (opcode, dst, src, off, imm_low) = parse_bytes(bytes)?;
+    if src != 0 || off != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has src: {}, off: {} supposed to be zero"
+                    , opcode, src, off
+            ),
+            span: 0..16,
+            custom_label: None,
+        });
+    }
+    let imm_high = i32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
+    let imm = ((imm_high as i64) << 32) | (imm_low as u32 as i64);
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: None,
+        off: None,
+        imm: Some(Number::Int(imm.into())),
+        span: 0..16,
+    })
+}
+
+pub fn decode_load_memory(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has imm: {} supposed to be zero" , opcode, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: Some(Register { n: src }),
+        off: Some(off),
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_store_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if src != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has src: {} supposed to be zero", opcode, src
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: None,
+        off: Some(off),
+        imm: Some(Number::Int(imm.into())),
+        span: 0..8,
+    })
+}
+
+pub fn decode_store_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has imm: {} supposed to be zero", opcode, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: Some(Register { n: src }),
+        off: Some(off),
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_binary_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if src != 0 || off != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has src: {}, off: {} supposed to be zeros"
+                    , opcode, src, off
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: None,
+        off: None,
+        imm: Some(Number::Int(imm.into())),
+        span: 0..8,
+    })
+}
+
+pub fn decode_binary_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if off != 0 || imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has off: {}, imm: {} supposed to be zeros"
+                    , opcode, off, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: Some(Register { n: src }),
+        off: None,
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_unary(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if src != 0 || off != 0 || imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has src: {}, off: {}, imm: {} supposed to be zeros"
+                    , opcode, src, off, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: None,
+        off: None,
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_jump(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if  dst != 0 || src != 0 || imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has dst: {}, src: {}, imm: {} supposed to be zeros"
+                    , opcode, dst, src, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: None,
+        src: None,
+        off: Some(off),
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_jump_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if src != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has src: {} supposed to be zero", opcode, src
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: None,
+        off: Some(off),
+        imm: Some(Number::Int(imm.into())),
+        span: 0..8,
+    })
+}
+
+pub fn decode_jump_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has imm: {} supposed to be zero", opcode, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: Some(Register { n: src }),
+        off: Some(off),
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_call_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if SYSCALLS.get(&(imm as u32)).is_some() {
+        if dst != 0 || src != 0 || off != 0 {
+            return Err(SBPFError::BytecodeError {
+                error: format!(
+                    "{} instruction has dst: {}, src: {}, off: {} supposed to be zeros"
+                        , opcode, dst, src, off
+                ),
+                span: 0..8,
+                custom_label: None,
+            });
+        }
+    } else {
+        if dst != 0 || src != 1 || off != 0 {
+            return Err(SBPFError::BytecodeError {
+                error: format!(
+                    "{} instruction has dst: {}, src: {}, off: {} 
+                        supposed to be sixteen and zero" , opcode, dst, src, off
+                ),
+                span: 0..8,
+                custom_label: None,
+            });
+        }
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: None,
+        src: None,
+        off: None,
+        imm: Some(Number::Int(imm.into())),
+        span: 0..8,
+    })
+}
+
+pub fn decode_call_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    // TODO: sbpf encodes dst_reg in immediate
+    if src != 0 || off != 0 || imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction has src: {}, off: {}, imm: {} supposed to be zeros"
+                    , opcode, src, off, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: Some(Register { n: dst }),
+        src: None,
+        off: None,
+        imm: None,
+        span: 0..8,
+    })
+}
+
+pub fn decode_exit(bytes: &[u8]) -> Result<Instruction, SBPFError> {
+    assert!(bytes.len() >= 8);
+    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
+    if dst != 0 || src != 0 || off != 0 || imm != 0 {
+        return Err(SBPFError::BytecodeError {
+            error: format!(
+                "{} instruction dst: {}, src: {}, off: {}, imm: {} supposed to be zero"
+                    , opcode, dst, src, off, imm
+            ),
+            span: 0..8,
+            custom_label: None,
+        });
+    }
+    Ok(Instruction {
+        opcode: opcode,
+        dst: None,
+        src: None,
+        off: None,
+        imm: None,
+        span: 0..8,
+    })
+}

+ 1 - 2
crates/common/src/errors.rs

@@ -1,5 +1,4 @@
-use std::ops::Range;
-use thiserror::Error;
+use {std::ops::Range, thiserror::Error};
 
 #[derive(Debug, Error)]
 pub enum SBPFError {

+ 101 - 0
crates/common/src/inst_handler.rs

@@ -0,0 +1,101 @@
+use crate::decode::{
+    decode_load_immediate,
+    decode_load_memory,
+    decode_store_immediate,
+    decode_store_register,
+    decode_binary_immediate,
+    decode_binary_register,
+    decode_unary,
+    decode_jump,
+    decode_jump_immediate,
+    decode_jump_register,
+    decode_call_immediate,
+    decode_call_register,
+    decode_exit,
+};
+use crate::errors::SBPFError;
+use crate::instruction::Instruction;
+use crate::opcode::{
+    Opcode,
+    OperationType,
+    LOAD_IMM_OPS,
+    LOAD_MEMORY_OPS,
+    STORE_IMM_OPS,
+    STORE_REG_OPS,
+    BIN_IMM_OPS,
+    BIN_REG_OPS,
+    UNARY_OPS,
+    JUMP_OPS,
+    JUMP_IMM_OPS,
+    JUMP_REG_OPS,
+    CALL_IMM_OPS,
+    CALL_REG_OPS,
+    EXIT_OPS,
+};
+
+type DecodeFn = fn(&[u8]) -> Result<Instruction, SBPFError>;
+
+pub struct InstructionHandler {
+    pub decode: DecodeFn,
+}
+
+use once_cell::sync::Lazy;
+use std::collections::HashMap;
+
+pub static OPCODE_TO_HANDLER: 
+    Lazy<HashMap<Opcode, InstructionHandler>> = Lazy::new(|| {
+    //
+    let mut map = HashMap::new();
+
+    fn register_group(map: &mut HashMap<Opcode, InstructionHandler>
+                    , ops: &[Opcode]
+                    , decode: DecodeFn) {
+        for &op in ops {
+            map.insert(op, InstructionHandler { decode });
+        }
+    }
+    
+    register_group(&mut map, LOAD_IMM_OPS, decode_load_immediate);
+    register_group(&mut map, LOAD_MEMORY_OPS, decode_load_memory);
+    register_group(&mut map, STORE_IMM_OPS, decode_store_immediate);
+    register_group(&mut map, STORE_REG_OPS, decode_store_register);
+    register_group(&mut map, BIN_IMM_OPS, decode_binary_immediate);
+    register_group(&mut map, BIN_REG_OPS, decode_binary_register);
+    register_group(&mut map, UNARY_OPS, decode_unary);
+    register_group(&mut map, JUMP_OPS, decode_jump);
+    register_group(&mut map, JUMP_IMM_OPS, decode_jump_immediate);
+    register_group(&mut map, JUMP_REG_OPS, decode_jump_register);
+    register_group(&mut map, CALL_IMM_OPS, decode_call_immediate);
+    register_group(&mut map, CALL_REG_OPS, decode_call_register);
+    register_group(&mut map, EXIT_OPS, decode_exit);
+
+    map
+});
+
+pub static OPCODE_TO_TYPE: Lazy<HashMap<Opcode, OperationType>> = Lazy::new(|| {
+    let mut map = HashMap::new();
+
+    fn register_group(map: &mut HashMap<Opcode, OperationType>
+                    , ops: &[Opcode]
+                    , op_type: OperationType) {
+        for &op in ops {
+            map.insert(op, op_type);
+        }
+    }
+    
+    register_group(&mut map, LOAD_IMM_OPS, OperationType::LoadImmediate);
+    register_group(&mut map, LOAD_MEMORY_OPS, OperationType::LoadMemory);
+    register_group(&mut map, STORE_IMM_OPS, OperationType::StoreImmediate);
+    register_group(&mut map, STORE_REG_OPS, OperationType::StoreRegister);
+    register_group(&mut map, BIN_IMM_OPS, OperationType::BinaryImmediate);
+    register_group(&mut map, BIN_REG_OPS, OperationType::BinaryRegister);
+    register_group(&mut map, UNARY_OPS, OperationType::Unary);
+    register_group(&mut map, JUMP_OPS, OperationType::Jump);
+    register_group(&mut map, JUMP_IMM_OPS, OperationType::JumpImmediate);
+    register_group(&mut map, JUMP_REG_OPS, OperationType::JumpRegister);
+    register_group(&mut map, CALL_IMM_OPS, OperationType::CallImmediate);
+    register_group(&mut map, CALL_REG_OPS, OperationType::CallRegister);
+    register_group(&mut map, EXIT_OPS, OperationType::Exit);
+
+    map
+});

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

@@ -0,0 +1,82 @@
+
+use core::fmt;
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct Register {
+    pub n: u8,
+}
+
+impl fmt::Display for Register {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "r{}", self.n)
+    }
+}
+
+pub enum OperandValue {
+    Number(Number),
+    Ident(String),
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub enum Number {
+    Int(i64),
+    Addr(i64),
+}
+
+impl fmt::Display for Number {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Number::Int(i) => write!(f, "{}", i),
+            Number::Addr(a) => write!(f, "{}", a),
+        }
+    }
+}
+
+impl std::ops::Add for Number {
+    type Output = Number;
+    fn add(self, other: Self) -> Number {
+        match (self, other) {
+            (Number::Int(a), Number::Int(b)) => Number::Int(a + b),
+            (Number::Addr(a), Number::Addr(b)) => Number::Addr(a + b),
+            (Number::Int(a), Number::Addr(b)) => Number::Addr(a + b),
+            (Number::Addr(a), Number::Int(b)) => Number::Addr(a + b),
+        }
+    }
+}
+
+impl std::ops::Sub for Number {
+    type Output = Number;
+    fn sub(self, other: Self) -> Number {
+        match (self, other) {
+            (Number::Int(a), Number::Int(b)) => Number::Int(a - b),
+            (Number::Addr(a), Number::Addr(b)) => Number::Addr(a - b),
+            (Number::Int(a), Number::Addr(b)) => Number::Addr(a - b),
+            (Number::Addr(a), Number::Int(b)) => Number::Addr(a - b),
+        }
+    }
+}
+
+impl std::ops::Mul for Number {
+    type Output = Number;
+    fn mul(self, other: Self) -> Number {
+        match (self, other) {
+            (Number::Int(a), Number::Int(b)) => Number::Int(a * b),
+            (Number::Addr(a), Number::Addr(b)) => Number::Addr(a * b),
+            (Number::Int(a), Number::Addr(b)) => Number::Addr(a * b),
+            (Number::Addr(a), Number::Int(b)) => Number::Addr(a * b),
+        }
+    }
+}
+
+impl std::ops::Div for Number {
+    type Output = Number;
+    fn div(self, other: Self) -> Number {
+        match (self, other) {
+            (Number::Int(a), Number::Int(b)) => Number::Int(a / b),
+            (Number::Addr(a), Number::Addr(b)) => Number::Addr(a / b),
+            (Number::Int(a), Number::Addr(b)) => Number::Addr(a / b),
+            (Number::Addr(a), Number::Int(b)) => Number::Addr(a / b),
+        }
+    }
+}

+ 48 - 390
crates/common/src/instruction.rs

@@ -1,36 +1,33 @@
-use crate::errors::SBPFError;
-use crate::opcode::Opcode;
-use crate::syscalls::SYSCALLS;
-
-use core::fmt;
-use core::ops::Range;
-use serde::{Deserialize, Serialize};
+use {
+    crate::{
+      errors::SBPFError, 
+      inst_param::{
+        Number,
+        Register,
+      },
+      opcode::Opcode, 
+      syscall::SYSCALLS,
+    },
+    core::{fmt, ops::Range},
+    serde::{Deserialize, Serialize},
+};
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Register {
     pub n: u8,
 }
+use crate::errors::SBPFError;
 
-impl fmt::Display for Register {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "r{}", self.n)
-    }
-}
+use crate::inst_param::{
+    Register,
+    Number
+};
 
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub enum Number {
-    Int(i64),
-    Addr(i64),
-}
+use crate::opcode::{Opcode, OperationType};
+use crate::inst_handler::{OPCODE_TO_HANDLER, OPCODE_TO_TYPE};
 
-impl fmt::Display for Number {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            Number::Int(i) => write!(f, "{}", i),
-            Number::Addr(a) => write!(f, "{}", a),
-        }
-    }
-}
+use core::ops::Range;
+use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Instruction {
@@ -49,33 +46,17 @@ impl Instruction {
             _ => 8,
         }
     }
-
+    
+    fn get_opcode_type(&self) -> OperationType {
+        *OPCODE_TO_TYPE.get(&self.opcode).unwrap()
+    }
+    
     pub fn is_jump(&self) -> bool {
         matches!(
-            self.opcode,
-            Opcode::Ja
-                | Opcode::JeqImm
-                | Opcode::JgtImm
-                | Opcode::JgeImm
-                | Opcode::JltImm
-                | Opcode::JleImm
-                | Opcode::JsetImm
-                | Opcode::JneImm
-                | Opcode::JsgtImm
-                | Opcode::JsgeImm
-                | Opcode::JsltImm
-                | Opcode::JsleImm
-                | Opcode::JeqReg
-                | Opcode::JgtReg
-                | Opcode::JgeReg
-                | Opcode::JltReg
-                | Opcode::JleReg
-                | Opcode::JsetReg
-                | Opcode::JneReg
-                | Opcode::JsgtReg
-                | Opcode::JsgeReg
-                | Opcode::JsltReg
-                | Opcode::JsleReg
+            self.get_opcode_type(),
+            OperationType::Jump 
+                | OperationType::JumpImmediate 
+                | OperationType::JumpRegister
         )
     }
 
@@ -105,7 +86,7 @@ impl Instruction {
             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 {
@@ -130,342 +111,16 @@ impl Instruction {
     }
 
     pub fn from_bytes(bytes: &[u8]) -> Result<Self, SBPFError> {
-        let span = 0..bytes.len();
-
-        let opcode = Opcode::from_u8(bytes[0]).ok_or(SBPFError::BytecodeError {
-            error: format!("Invalid opcode: {:?}", bytes[0]),
-            span: span.clone(),
-            custom_label: None,
-        })?;
-        let reg = bytes[1];
-        let src = reg >> 4;
-        let dst = reg & 0x0f;
-        let off = i16::from_le_bytes([bytes[2], bytes[3]]);
-        let imm = match opcode {
-            Opcode::Lddw => {
-                let imm_low = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
-                let imm_high = i32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
-
-                ((imm_high as i64) << 32) | (imm_low as u32 as i64)
-            }
-            _ => i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as i64,
-        };
-
-        let mut out_dst: Option<Register> = None;
-        let mut out_src: Option<Register> = None;
-        let mut out_off: Option<i16> = None;
-        let mut out_imm: Option<Number> = None;
-
-        match opcode {
-            Opcode::Lddw => {
-                if src != 0 || off != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Lddw instruction expects src and off to be 0, but got src: {}, off: {}",
-                            src, off
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_imm = Some(Number::Int(imm));
-            }
-
-            Opcode::Call => {
-                if SYSCALLS.get(imm as u32).is_some() {
-                    if reg != 0 || off != 0 {
-                        return Err(SBPFError::BytecodeError {
-                            error: format!(
-                                "Call instruction with syscall expects reg and off to be 0, but got reg: {}, off: {}",
-                                reg, off
-                            ),
-                            span: span.clone(),
-                            custom_label: None,
-                        });
-                    }
-                    out_imm = Some(Number::Int(imm));
-                } else {
-                    if reg != 16 || off != 0 {
-                        return Err(SBPFError::BytecodeError {
-                            error: format!(
-                                "Call instruction with immediate expects reg to be 16 and off to be 0, but got reg: {}, off: {}",
-                                reg, off
-                            ),
-                            span: span.clone(),
-                            custom_label: None,
-                        });
-                    }
-                    out_imm = Some(Number::Int(imm));
-                }
-            }
-
-            Opcode::Callx => {
-                if src != 0 || off != 0 || imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Callx instruction expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}",
-                            src, off, imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                // callx destination register is encoded in the dst field
-                out_dst = Some(Register { n: dst });
-            }
-
-            Opcode::Ja => {
-                if reg != 0 || imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}",
-                            reg, imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_off = Some(off);
-            }
-
-            Opcode::JeqImm
-            | Opcode::JgtImm
-            | Opcode::JgeImm
-            | Opcode::JltImm
-            | Opcode::JleImm
-            | Opcode::JsetImm
-            | Opcode::JneImm
-            | Opcode::JsgtImm
-            | Opcode::JsgeImm
-            | Opcode::JsltImm
-            | Opcode::JsleImm => {
-                if src != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Jump instruction with immediate expects src to be 0, but got src: {}",
-                            src
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_imm = Some(Number::Int(imm));
-                out_off = Some(off);
-            }
-
-            Opcode::JeqReg
-            | Opcode::JgtReg
-            | Opcode::JgeReg
-            | Opcode::JltReg
-            | Opcode::JleReg
-            | Opcode::JsetReg
-            | Opcode::JneReg
-            | Opcode::JsgtReg
-            | Opcode::JsgeReg
-            | Opcode::JsltReg
-            | Opcode::JsleReg => {
-                if imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Jump instruction with register expects imm to be 0, but got imm: {}",
-                            imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_src = Some(Register { n: src });
-                out_off = Some(off);
-            }
-
-            // Arithmetic instructions with immediate values
-            Opcode::Add32Imm
-            | Opcode::Sub32Imm
-            | Opcode::Mul32Imm
-            | Opcode::Div32Imm
-            | Opcode::Or32Imm
-            | Opcode::And32Imm
-            | Opcode::Lsh32Imm
-            | Opcode::Rsh32Imm
-            | Opcode::Mod32Imm
-            | Opcode::Xor32Imm
-            | Opcode::Mov32Imm
-            | Opcode::Arsh32Imm
-            | Opcode::Lmul32Imm
-            | Opcode::Udiv32Imm
-            | Opcode::Urem32Imm
-            | Opcode::Sdiv32Imm
-            | Opcode::Srem32Imm
-            | Opcode::Add64Imm
-            | Opcode::Sub64Imm
-            | Opcode::Mul64Imm
-            | Opcode::Div64Imm
-            | Opcode::Or64Imm
-            | Opcode::And64Imm
-            | Opcode::Lsh64Imm
-            | Opcode::Rsh64Imm
-            | Opcode::Mod64Imm
-            | Opcode::Xor64Imm
-            | Opcode::Mov64Imm
-            | Opcode::Arsh64Imm
-            | Opcode::Hor64Imm
-            | Opcode::Lmul64Imm
-            | Opcode::Uhmul64Imm
-            | Opcode::Udiv64Imm
-            | Opcode::Urem64Imm
-            | Opcode::Shmul64Imm
-            | Opcode::Sdiv64Imm
-            | Opcode::Srem64Imm
-            | Opcode::Be
-            | Opcode::Le => {
-                if src != 0 || off != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Arithmetic instruction with immediate expects src and off to be 0, but got src: {}, off: {}",
-                            src, off
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_imm = Some(Number::Int(imm));
-            }
-
-            // Arithmetic instructions with register operands
-            Opcode::Add32Reg
-            | Opcode::Sub32Reg
-            | Opcode::Mul32Reg
-            | Opcode::Div32Reg
-            | Opcode::Or32Reg
-            | Opcode::And32Reg
-            | Opcode::Lsh32Reg
-            | Opcode::Rsh32Reg
-            | Opcode::Mod32Reg
-            | Opcode::Xor32Reg
-            | Opcode::Mov32Reg
-            | Opcode::Arsh32Reg
-            | Opcode::Lmul32Reg
-            | Opcode::Udiv32Reg
-            | Opcode::Urem32Reg
-            | Opcode::Sdiv32Reg
-            | Opcode::Srem32Reg
-            | Opcode::Add64Reg
-            | Opcode::Sub64Reg
-            | Opcode::Mul64Reg
-            | Opcode::Div64Reg
-            | Opcode::Or64Reg
-            | Opcode::And64Reg
-            | Opcode::Lsh64Reg
-            | Opcode::Rsh64Reg
-            | Opcode::Mod64Reg
-            | Opcode::Xor64Reg
-            | Opcode::Mov64Reg
-            | Opcode::Arsh64Reg
-            | Opcode::Lmul64Reg
-            | Opcode::Uhmul64Reg
-            | Opcode::Udiv64Reg
-            | Opcode::Urem64Reg
-            | Opcode::Shmul64Reg
-            | Opcode::Sdiv64Reg
-            | Opcode::Srem64Reg => {
-                if off != 0 || imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Arithmetic instruction with register expects off and imm to be 0, but got off: {}, imm: {}",
-                            off, imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_src = Some(Register { n: src });
-            }
-
-            Opcode::Ldxw | Opcode::Ldxh | Opcode::Ldxb | Opcode::Ldxdw => {
-                if imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Load instruction expects imm to be 0, but got imm: {}",
-                            imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_src = Some(Register { n: src });
-                out_off = Some(off);
-            }
-
-            Opcode::Stw | Opcode::Sth | Opcode::Stb | Opcode::Stdw => {
-                if src != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Store instruction expects src to be 0, but got src: {}",
-                            src
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_off = Some(off);
-                out_imm = Some(Number::Int(imm));
-            }
-
-            Opcode::Stxb | Opcode::Stxh | Opcode::Stxw | Opcode::Stxdw => {
-                if imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Store instruction with register expects imm to be 0, but got imm: {}",
-                            imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-                out_src = Some(Register { n: src });
-                out_off = Some(off);
-            }
-
-            // Unary operations
-            Opcode::Neg32 | Opcode::Neg64 | Opcode::Exit => {
-                if src != 0 || off != 0 || imm != 0 {
-                    return Err(SBPFError::BytecodeError {
-                        error: format!(
-                            "Unary operation expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}",
-                            src, off, imm
-                        ),
-                        span: span.clone(),
-                        custom_label: None,
-                    });
-                }
-                out_dst = Some(Register { n: dst });
-            }
-
-            _ => {
-                return Err(SBPFError::BytecodeError {
-                    error: format!("Unsupported opcode: {:?}", opcode),
-                    span: span.clone(),
-                    custom_label: None,
-                });
-            }
+        let opcode = Opcode::from_u8(bytes[0]).unwrap();
+        if let Some(handler) = OPCODE_TO_HANDLER.get(&opcode) {
+            (handler.decode)(bytes)
+        } else {
+            Err(SBPFError::BytecodeError {
+                error: format!("no decode handler for opcode {}", opcode),
+                span: 0..1,
+                custom_label: Some("Invalid opcode".to_string()),
+            })
         }
-
-        Ok(Instruction {
-            opcode,
-            dst: out_dst,
-            src: out_src,
-            off: out_off,
-            imm: out_imm,
-            span,
-        })
     }
 
     pub fn to_bytes(&self) -> Vec<u8> {
@@ -749,10 +404,13 @@ impl Instruction {
 
 #[cfg(test)]
 mod test {
-    use hex_literal::hex;
-
-    use crate::instruction::{Instruction, Register};
-    use crate::opcode::Opcode;
+    use {
+        crate::{
+            instruction::{Instruction, Register},
+            opcode::Opcode,
+        },
+        hex_literal::hex,
+    };
 
     #[test]
     fn serialize_e2e() {

+ 3 - 0
crates/common/src/lib.rs

@@ -1,4 +1,7 @@
+pub mod decode;
 pub mod errors;
+pub mod inst_handler;
+pub mod inst_param;
 pub mod instruction;
 pub mod opcode;
 pub mod syscalls;

+ 151 - 5
crates/common/src/opcode.rs

@@ -1,10 +1,156 @@
-use core::fmt;
-use core::str::FromStr;
+use {
+    core::{fmt, str::FromStr},
+    num_derive::FromPrimitive,
+    serde::{Deserialize, Serialize},
+};
 
-use num_derive::FromPrimitive;
-use serde::{Deserialize, Serialize};
+#[derive(Debug, Clone, Copy)]
+pub enum OperationType {
+    LoadImmediate,
+    LoadMemory,
+    StoreImmediate,
+    StoreRegister,
+    BinaryImmediate,
+    BinaryRegister,
+    Unary,
+    Jump,
+    JumpImmediate,
+    JumpRegister,
+    CallImmediate,
+    CallRegister,
+    Exit,
+}
+
+pub const LOAD_IMM_OPS: &[Opcode] = &[Opcode::Lddw];    // OperationType::LoadImmediate
+
+pub const LOAD_MEMORY_OPS: &[Opcode] = &[Opcode::Ldxb   // OperationType::LoadMemory
+                                , Opcode::Ldxh
+                                , Opcode::Ldxw
+                                , Opcode::Ldxdw];
+
+pub const STORE_IMM_OPS: &[Opcode] = &[Opcode::Stb      // OperationType::StoreImmediate
+                                , Opcode::Sth
+                                , Opcode::Stw
+                                , Opcode::Stdw]; 
+
+pub const STORE_REG_OPS: &[Opcode] = &[Opcode::Stxb     // OperationType::StoreRegister
+                                , Opcode::Stxh
+                                , Opcode::Stxw
+                                , Opcode::Stxdw];
+
+pub const BIN_IMM_OPS: &[Opcode] = &[Opcode::Add32Imm   // OperationType::BinaryImmediate
+                                , Opcode::Sub32Imm
+                                , Opcode::Mul32Imm
+                                , Opcode::Div32Imm
+                                , Opcode::Or32Imm
+                                , Opcode::And32Imm
+                                , Opcode::Lsh32Imm
+                                , Opcode::Rsh32Imm
+                                , Opcode::Mod32Imm
+                                , Opcode::Xor32Imm
+                                , Opcode::Mov32Imm
+                                , Opcode::Arsh32Imm
+                                , Opcode::Lmul32Imm
+                                , Opcode::Udiv32Imm
+                                , Opcode::Urem32Imm
+                                , Opcode::Sdiv32Imm
+                                , Opcode::Srem32Imm
+                                , Opcode::Le
+                                , Opcode::Be
+                                , Opcode::Add64Imm
+                                , Opcode::Sub64Imm
+                                , Opcode::Mul64Imm
+                                , Opcode::Div64Imm
+                                , Opcode::Or64Imm
+                                , Opcode::And64Imm
+                                , Opcode::Lsh64Imm
+                                , Opcode::Rsh64Imm
+                                , Opcode::Mod64Imm
+                                , Opcode::Xor64Imm
+                                , Opcode::Mov64Imm
+                                , Opcode::Arsh64Imm
+                                , Opcode::Hor64Imm
+                                , Opcode::Lmul64Imm
+                                , Opcode::Uhmul64Imm
+                                , Opcode::Udiv64Imm
+                                , Opcode::Urem64Imm
+                                , Opcode::Shmul64Imm
+                                , Opcode::Sdiv64Imm
+                                , Opcode::Srem64Imm];
+
+pub const BIN_REG_OPS: &[Opcode] = &[Opcode::Add32Reg   // OperationType::BinaryRegister
+                                , Opcode::Sub32Reg
+                                , Opcode::Mul32Reg
+                                , Opcode::Div32Reg
+                                , Opcode::Or32Reg
+                                , Opcode::And32Reg
+                                , Opcode::Lsh32Reg
+                                , Opcode::Rsh32Reg
+                                , Opcode::Mod32Reg
+                                , Opcode::Xor32Reg
+                                , Opcode::Mov32Reg
+                                , Opcode::Arsh32Reg
+                                , Opcode::Lmul32Reg
+                                , Opcode::Udiv32Reg 
+                                , Opcode::Urem32Reg
+                                , Opcode::Sdiv32Reg
+                                , Opcode::Srem32Reg
+                                , Opcode::Add64Reg
+                                , Opcode::Sub64Reg
+                                , Opcode::Mul64Reg
+                                , Opcode::Div64Reg
+                                , Opcode::Or64Reg
+                                , Opcode::And64Reg
+                                , Opcode::Lsh64Reg
+                                , Opcode::Rsh64Reg
+                                , Opcode::Mod64Reg
+                                , Opcode::Xor64Reg
+                                , Opcode::Mov64Reg
+                                , Opcode::Arsh64Reg
+                                , Opcode::Lmul64Reg
+                                , Opcode::Uhmul64Reg
+                                , Opcode::Udiv64Reg
+                                , Opcode::Urem64Reg
+                                , Opcode::Shmul64Reg
+                                , Opcode::Sdiv64Reg
+                                , Opcode::Srem64Reg];
+
+pub const UNARY_OPS: &[Opcode] = &[Opcode::Neg32        // OperationType::Unary
+                                , Opcode::Neg64];
+
+pub const JUMP_OPS: &[Opcode] = &[Opcode::Ja];          // OperationType::Jump
+
+pub const JUMP_IMM_OPS: &[Opcode] = &[Opcode::JeqImm    // OperationType::JumpImmediate
+                                , Opcode::JgtImm
+                                , Opcode::JgeImm
+                                , Opcode::JltImm
+                                , Opcode::JleImm
+                                , Opcode::JsetImm
+                                , Opcode::JneImm
+                                , Opcode::JsgtImm
+                                , Opcode::JsgeImm
+                                , Opcode::JsltImm
+                                , Opcode::JsleImm];
+
+pub const JUMP_REG_OPS: &[Opcode] = &[Opcode::JeqReg    // OperationType::JumpRegister
+                                , Opcode::JgtReg
+                                , Opcode::JgeReg
+                                , Opcode::JltReg
+                                , Opcode::JleReg
+                                , Opcode::JsetReg
+                                , Opcode::JneReg
+                                , Opcode::JsgtReg
+                                , Opcode::JsgeReg
+                                , Opcode::JsltReg
+                                , Opcode::JsleReg];
+
+pub const CALL_IMM_OPS: &[Opcode] = &[Opcode::Call];    // OperationType::CallImmediate
+
+pub const CALL_REG_OPS: &[Opcode] = &[Opcode::Callx];   // OperationType::CallRegister
 
-#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, Serialize, Deserialize)]
+pub const EXIT_OPS: &[Opcode] = &[Opcode::Exit];        // OperationType::Exit
+//
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, FromPrimitive, Serialize, Deserialize)]
 #[repr(u8)]
 pub enum Opcode {
     Lddw,

+ 1 - 0
crates/disassembler/Cargo.toml

@@ -8,6 +8,7 @@ authors.workspace = true
 description = "Disassembler for SBPF (Solana BPF) bytecode"
 keywords = ["solana", "bpf", "disassembler", "blockchain"]
 categories = ["development-tools"]
+rust-version.workspace = true
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 15 - 13
crates/disassembler/src/elf_header.rs

@@ -1,10 +1,9 @@
-use std::str;
-
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize, Serializer};
-
-use crate::errors::DisassemblerError;
+use {
+    crate::errors::DisassemblerError,
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize, Serializer},
+    std::str,
+};
 
 pub const EI_MAGIC: [u8; 4] = *b"\x7fELF"; // ELF magic
 pub const EI_CLASS: u8 = 0x02; // 64-bit
@@ -139,13 +138,16 @@ impl ELFHeader {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::elf_header::{
-        E_MACHINE, E_MACHINE_SBPF, E_TYPE, E_VERSION, EI_ABIVERSION, EI_CLASS, EI_DATA, EI_MAGIC,
-        EI_OSABI, EI_PAD, EI_VERSION,
+    use {
+        crate::{
+            elf_header::{
+                E_MACHINE, E_MACHINE_SBPF, E_TYPE, E_VERSION, EI_ABIVERSION, EI_CLASS, EI_DATA,
+                EI_MAGIC, EI_OSABI, EI_PAD, EI_VERSION,
+            },
+            program::Program,
+        },
+        hex_literal::hex,
     };
-    use crate::program::Program;
 
     #[test]
     fn test_elf_header() {

+ 1 - 2
crates/disassembler/src/errors.rs

@@ -1,5 +1,4 @@
-use sbpf_common::errors::SBPFError;
-use thiserror::Error;
+use {sbpf_common::errors::SBPFError, thiserror::Error};
 
 #[derive(Debug, Error)]
 pub enum DisassemblerError {

+ 8 - 10
crates/disassembler/src/program.rs

@@ -1,10 +1,10 @@
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize};
-
-use crate::{
-    elf_header::ELFHeader, errors::DisassemblerError, program_header::ProgramHeader,
-    section_header::SectionHeader, section_header_entry::SectionHeaderEntry,
+use {
+    crate::{
+        elf_header::ELFHeader, errors::DisassemblerError, program_header::ProgramHeader,
+        section_header::SectionHeader, section_header_entry::SectionHeaderEntry,
+    },
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize},
 };
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -40,9 +40,7 @@ impl Program {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::program::Program;
+    use {crate::program::Program, hex_literal::hex};
 
     #[test]
     fn try_deserialize_program() {

+ 7 - 10
crates/disassembler/src/program_header.rs

@@ -1,10 +1,9 @@
-use std::fmt::Debug;
-
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize};
-
-use crate::errors::DisassemblerError;
+use {
+    crate::errors::DisassemblerError,
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize},
+    std::fmt::Debug,
+};
 
 // Program Segment Flags
 pub const PF_X: u8 = 0x01;
@@ -160,9 +159,7 @@ impl ProgramHeader {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::program::Program;
+    use {crate::program::Program, hex_literal::hex};
 
     #[test]
     fn test_program_headers() {

+ 7 - 11
crates/disassembler/src/section_header.rs

@@ -1,11 +1,9 @@
-use std::{fmt::Debug, fmt::Display};
-
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize};
-
-use crate::errors::DisassemblerError;
-use crate::section_header_entry::SectionHeaderEntry;
+use {
+    crate::{errors::DisassemblerError, section_header_entry::SectionHeaderEntry},
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize},
+    std::fmt::{Debug, Display},
+};
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -190,9 +188,7 @@ impl SectionHeader {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::program::Program;
+    use {crate::program::Program, hex_literal::hex};
 
     #[test]
     fn test_section_headers() {

+ 20 - 13
crates/disassembler/src/section_header_entry.rs

@@ -1,10 +1,9 @@
-use std::fmt::Debug;
-
-use serde::{Deserialize, Serialize};
-
-use crate::errors::DisassemblerError;
-use sbpf_common::instruction::Instruction;
-use sbpf_common::opcode::Opcode;
+use {
+    crate::errors::DisassemblerError,
+    sbpf_common::{instruction::Instruction, opcode::Opcode},
+    serde::{Deserialize, Serialize},
+    std::fmt::Debug,
+};
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct SectionHeaderEntry {
@@ -42,7 +41,7 @@ impl SectionHeaderEntry {
     }
 
     pub fn to_ixs(&self) -> Result<Vec<Instruction>, DisassemblerError> {
-        if self.data.len() % 8 != 0 {
+        if !self.data.len().is_multiple_of(8) {
             return Err(DisassemblerError::InvalidDataLength);
         }
         let mut ixs: Vec<Instruction> = vec![];
@@ -74,9 +73,17 @@ impl SectionHeaderEntry {
 
 #[cfg(test)]
 mod test {
-    use crate::section_header_entry::SectionHeaderEntry;
-    use sbpf_common::instruction::{Instruction, Number, Register};
-    use sbpf_common::opcode::Opcode;
+    use {
+        crate::section_header_entry::SectionHeaderEntry,
+        sbpf_common::{
+            inst_param::{
+                Number,
+                Register
+            };
+            instruction::Instruction,
+            opcode::Opcode,
+        },
+    };
 
     #[test]
     fn serialize_e2e() {
@@ -94,11 +101,11 @@ mod test {
                 src: None,
                 off: None,
                 imm: Some(Number::Int(0)),
-                span: 0..24,
+                span: 0..16,
             },
             Instruction {
                 opcode: Opcode::Exit,
-                dst: Some(Register { n: 0 }),
+                dst: None,
                 src: None,
                 off: None,
                 imm: None,

+ 6 - 4
crates/disassembler/src/wasm.rs

@@ -1,7 +1,9 @@
-use crate::program::Program as EBPFProgram;
-use serde::{Deserialize, Serialize};
-use serde_wasm_bindgen::to_value;
-use wasm_bindgen::prelude::*;
+use {
+    crate::program::Program as EBPFProgram,
+    serde::{Deserialize, Serialize},
+    serde_wasm_bindgen::to_value,
+    wasm_bindgen::prelude::*,
+};
 
 #[wasm_bindgen]
 #[derive(Debug, Serialize, Deserialize)]

+ 3 - 0
rustfmt.toml

@@ -0,0 +1,3 @@
+imports_granularity = "One"
+format_strings = true
+group_imports = "One"

+ 31 - 31
src/commands/build.rs

@@ -1,18 +1,16 @@
-use sbpf_assembler::assemble;
-use sbpf_assembler::errors::CompileError;
-
-use ed25519_dalek::SigningKey;
-use rand::rngs::OsRng;
-use std::fs;
-
-use anyhow::{Error, Result};
-use codespan_reporting::diagnostic::{Diagnostic, Label};
-use codespan_reporting::files::SimpleFile;
-use codespan_reporting::term;
-use std::fs::create_dir_all;
-use std::path::Path;
-use std::time::Instant;
-use termcolor::{ColorChoice, StandardStream};
+use {
+    anyhow::{Error, Result},
+    codespan_reporting::{
+        diagnostic::{Diagnostic, Label},
+        files::SimpleFile,
+        term,
+    },
+    ed25519_dalek::SigningKey,
+    rand::rngs::OsRng,
+    sbpf_assembler::{assemble, errors::CompileError},
+    std::{fs, fs::create_dir_all, path::Path, time::Instant},
+    termcolor::{ColorChoice, StandardStream},
+};
 
 pub trait AsDiagnostic {
     // currently only support single source file reporting
@@ -36,8 +34,10 @@ impl AsDiagnostic for CompileError {
                 ]),
             _ => Diagnostic::error()
                 .with_message(self.to_string())
-                .with_labels(vec![Label::primary((), self.span().start..self.span().end)
-                    .with_message(self.label())]),
+                .with_labels(vec![
+                    Label::primary((), self.span().start..self.span().end)
+                        .with_message(self.label()),
+                ]),
         }
     }
 }
@@ -122,20 +122,20 @@ pub fn build() -> Result<()> {
     for entry in src_path.read_dir()? {
         let entry = entry?;
         let path = entry.path();
-        if path.is_dir() {
-            if let Some(subdir) = path.file_name().and_then(|name| name.to_str()) {
-                let asm_file = format!("{}/{}/{}.s", src, subdir, subdir);
-                if Path::new(&asm_file).exists() {
-                    println!("⚡️ Building \"{}\"", subdir);
-                    let start = Instant::now();
-                    compile_assembly(&asm_file, deploy)?;
-                    let duration = start.elapsed();
-                    println!(
-                        "✅ \"{}\" built successfully in {}ms!",
-                        subdir,
-                        duration.as_micros() as f64 / 1000.0
-                    );
-                }
+        if path.is_dir()
+            && let Some(subdir) = path.file_name().and_then(|name| name.to_str())
+        {
+            let asm_file = format!("{}/{}/{}.s", src, subdir, subdir);
+            if Path::new(&asm_file).exists() {
+                println!("⚡️ Building \"{}\"", subdir);
+                let start = Instant::now();
+                compile_assembly(&asm_file, deploy)?;
+                let duration = start.elapsed();
+                println!(
+                    "✅ \"{}\" built successfully in {}ms!",
+                    subdir,
+                    duration.as_micros() as f64 / 1000.0
+                );
             }
         }
     }

+ 9 - 10
src/commands/clean.rs

@@ -1,7 +1,7 @@
-use std::fs;
-use std::path::Path;
-
-use anyhow::{Error, Result};
+use {
+    anyhow::{Error, Result},
+    std::{fs, path::Path},
+};
 
 pub fn clean() -> Result<(), Error> {
     fs::remove_dir_all(".sbpf")?;
@@ -14,12 +14,11 @@ fn clean_directory(directory: &str, extension: &str) -> Result<(), Error> {
     for entry in path.read_dir()? {
         let entry = entry?;
         let path = entry.path();
-        if path.is_file() {
-            if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) {
-                if extension.is_empty() || ext == extension {
-                    fs::remove_file(&path)?;
-                }
-            }
+        if path.is_file()
+            && let Some(ext) = path.extension().and_then(|ext| ext.to_str())
+            && (extension.is_empty() || ext == extension)
+        {
+            fs::remove_file(&path)?;
         }
     }
     Ok(())

+ 10 - 13
src/commands/deploy.rs

@@ -1,8 +1,7 @@
-use std::io;
-use std::path::Path;
-use std::process::Command;
-
-use anyhow::{Error, Result};
+use {
+    anyhow::{Error, Result},
+    std::{io, path::Path, process::Command},
+};
 
 fn deploy_program(program_name: &str, url: &str) -> Result<(), Error> {
     let program_id_file = format!("./deploy/{}-keypair.json", program_name);
@@ -23,10 +22,7 @@ fn deploy_program(program_name: &str, url: &str) -> Result<(), Error> {
 
         if !status.success() {
             eprintln!("Failed to deploy program for {}", program_name);
-            return Err(Error::new(io::Error::new(
-                io::ErrorKind::Other,
-                "❌ Deployment failed",
-            )));
+            return Err(Error::new(io::Error::other("❌ Deployment failed")));
         }
 
         println!("✅ \"{}\" deployed successfully!", program_name);
@@ -47,10 +43,11 @@ fn deploy_all_programs(url: &str) -> Result<(), Error> {
     for entry in deploy_path.read_dir()? {
         let entry = entry?;
         let path = entry.path();
-        if path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some("so") {
-            if let Some(filename) = path.file_stem().and_then(|name| name.to_str()) {
-                deploy_program(filename, url)?;
-            }
+        if path.is_file()
+            && path.extension().and_then(|ext| ext.to_str()) == Some("so")
+            && let Some(filename) = path.file_stem().and_then(|name| name.to_str())
+        {
+            deploy_program(filename, url)?;
         }
     }
 

+ 5 - 5
src/commands/disassemble.rs

@@ -1,8 +1,8 @@
-use std::fs::File;
-use std::io::Read;
-
-use anyhow::{Error, Result};
-use sbpf_disassembler::program::Program;
+use {
+    anyhow::{Error, Result},
+    sbpf_disassembler::program::Program,
+    std::{fs::File, io::Read},
+};
 
 pub fn disassemble(filename: String, asm: bool) -> Result<(), Error> {
     let mut file = File::open(filename)?;

+ 12 - 8
src/commands/init.rs

@@ -1,12 +1,16 @@
-use super::common::{
-    CARGO_TOML, GITIGNORE, PACKAGE_JSON, PROGRAM, README, RUST_TESTS, TSCONFIG, TS_TESTS,
+use {
+    super::common::{
+        CARGO_TOML, GITIGNORE, PACKAGE_JSON, PROGRAM, README, RUST_TESTS, TS_TESTS, TSCONFIG,
+    },
+    anyhow::{Error, Result},
+    ed25519_dalek::SigningKey,
+    rand::rngs::OsRng,
+    std::{
+        fs,
+        io::{self, Write},
+        process::Command,
+    },
 };
-use anyhow::{Error, Result};
-use ed25519_dalek::SigningKey;
-use rand::rngs::OsRng;
-use std::fs;
-use std::io::{self, Write};
-use std::process::Command;
 
 pub fn init(name: Option<String>, ts_tests: bool) -> Result<(), Error> {
     let project_name = match name {

+ 6 - 10
src/commands/test.rs

@@ -1,5 +1,7 @@
-use anyhow::{Error, Result};
-use std::{fs, io, path::Path, process::Command};
+use {
+    anyhow::{Error, Result},
+    std::{fs, io, path::Path, process::Command},
+};
 
 pub fn test() -> Result<(), Error> {
     println!("🧪 Running tests");
@@ -43,10 +45,7 @@ pub fn test() -> Result<(), Error> {
 
             if !output.success() {
                 eprintln!("Failed to run Rust tests");
-                return Err(Error::new(io::Error::new(
-                    io::ErrorKind::Other,
-                    "❌ Rust tests failed",
-                )));
+                return Err(Error::new(io::Error::other("❌ Rust tests failed")));
             }
         }
         (false, true) => {
@@ -56,10 +55,7 @@ pub fn test() -> Result<(), Error> {
 
             if !status.success() {
                 eprintln!("Failed to run tests");
-                return Err(Error::new(io::Error::new(
-                    io::ErrorKind::Other,
-                    "❌ Test failed",
-                )));
+                return Err(Error::new(io::Error::other("❌ Test failed")));
             }
         }
         (false, false) => {

+ 5 - 3
src/main.rs

@@ -1,7 +1,9 @@
 pub mod commands;
-use anyhow::Error;
-use clap::{Args, Parser, Subcommand};
-use commands::{build, clean, deploy, disassemble, init, test};
+use {
+    anyhow::Error,
+    clap::{Args, Parser, Subcommand},
+    commands::{build, clean, deploy, disassemble, init, test},
+};
 
 #[derive(Parser)]
 #[command(version, about, long_about = None)]

+ 2 - 2
tests/test_memo.rs

@@ -1,8 +1,8 @@
 mod utils;
 
 use utils::{
-    init_project, run_build, update_assembly_file, verify_project_structure, verify_so_files,
-    TestEnv,
+    TestEnv, init_project, run_build, update_assembly_file, verify_project_structure,
+    verify_so_files,
 };
 
 #[test]

+ 14 - 6
tests/utils.rs

@@ -8,7 +8,9 @@ impl EnvVarGuard {
     pub fn new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self {
         let key = key.into();
         let original = std::env::var(&key).ok();
-        unsafe { std::env::set_var(&key, value.into()) };
+        unsafe {
+            std::env::set_var(&key, value.into());
+        }
         Self { key, original }
     }
 }
@@ -16,16 +18,22 @@ impl EnvVarGuard {
 impl Drop for EnvVarGuard {
     fn drop(&mut self) {
         if let Some(ref val) = self.original {
-            unsafe { std::env::set_var(&self.key, val) };
+            unsafe {
+                std::env::set_var(&self.key, val);
+            }
         } else {
-            unsafe { std::env::remove_var(&self.key) };
+            unsafe {
+                std::env::remove_var(&self.key);
+            }
         }
     }
 }
 
-use std::fs;
-use std::path::PathBuf;
-use std::process::{Command, Output};
+use std::{
+    fs,
+    path::PathBuf,
+    process::{Command, Output},
+};
 
 /// Test environment setup for SBPF tests
 pub struct TestEnv {